xref: /minix3/sbin/newfs_ext2fs/mke2fs.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1*0a6a1f1dSLionel Sambuc /*	$NetBSD: mke2fs.c,v 1.22 2015/06/16 23:18:55 christos Exp $	*/
294715d8eSBen Gras 
394715d8eSBen Gras /*-
494715d8eSBen Gras  * Copyright (c) 2007 Izumi Tsutsui.  All rights reserved.
594715d8eSBen Gras  *
694715d8eSBen Gras  * Redistribution and use in source and binary forms, with or without
794715d8eSBen Gras  * modification, are permitted provided that the following conditions
894715d8eSBen Gras  * are met:
994715d8eSBen Gras  * 1. Redistributions of source code must retain the above copyright
1094715d8eSBen Gras  *    notice, this list of conditions and the following disclaimer.
1194715d8eSBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
1294715d8eSBen Gras  *    notice, this list of conditions and the following disclaimer in the
1394715d8eSBen Gras  *    documentation and/or other materials provided with the distribution.
1494715d8eSBen Gras  *
1594715d8eSBen Gras  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1694715d8eSBen Gras  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1794715d8eSBen Gras  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1894715d8eSBen Gras  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1994715d8eSBen Gras  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2094715d8eSBen Gras  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2194715d8eSBen Gras  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2294715d8eSBen Gras  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2394715d8eSBen Gras  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2494715d8eSBen Gras  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2594715d8eSBen Gras  */
2694715d8eSBen Gras 
2794715d8eSBen Gras /*
2894715d8eSBen Gras  * Copyright (c) 1980, 1989, 1993
2994715d8eSBen Gras  *	The Regents of the University of California.  All rights reserved.
3094715d8eSBen Gras  *
3194715d8eSBen Gras  * Redistribution and use in source and binary forms, with or without
3294715d8eSBen Gras  * modification, are permitted provided that the following conditions
3394715d8eSBen Gras  * are met:
3494715d8eSBen Gras  * 1. Redistributions of source code must retain the above copyright
3594715d8eSBen Gras  *    notice, this list of conditions and the following disclaimer.
3694715d8eSBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
3794715d8eSBen Gras  *    notice, this list of conditions and the following disclaimer in the
3894715d8eSBen Gras  *    documentation and/or other materials provided with the distribution.
3994715d8eSBen Gras  * 3. Neither the name of the University nor the names of its contributors
4094715d8eSBen Gras  *    may be used to endorse or promote products derived from this software
4194715d8eSBen Gras  *    without specific prior written permission.
4294715d8eSBen Gras  *
4394715d8eSBen Gras  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
4494715d8eSBen Gras  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
4594715d8eSBen Gras  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
4694715d8eSBen Gras  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
4794715d8eSBen Gras  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
4894715d8eSBen Gras  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
4994715d8eSBen Gras  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5094715d8eSBen Gras  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5194715d8eSBen Gras  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5294715d8eSBen Gras  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
5394715d8eSBen Gras  * SUCH DAMAGE.
5494715d8eSBen Gras  */
5594715d8eSBen Gras 
5694715d8eSBen Gras /*
5794715d8eSBen Gras  * Copyright (c) 1997 Manuel Bouyer.
5894715d8eSBen Gras  *
5994715d8eSBen Gras  * Redistribution and use in source and binary forms, with or without
6094715d8eSBen Gras  * modification, are permitted provided that the following conditions
6194715d8eSBen Gras  * are met:
6294715d8eSBen Gras  * 1. Redistributions of source code must retain the above copyright
6394715d8eSBen Gras  *	notice, this list of conditions and the following disclaimer.
6494715d8eSBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
6594715d8eSBen Gras  *	notice, this list of conditions and the following disclaimer in the
6694715d8eSBen Gras  *	documentation and/or other materials provided with the distribution.
6794715d8eSBen Gras  *
6894715d8eSBen Gras  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
6994715d8eSBen Gras  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
7094715d8eSBen Gras  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
7194715d8eSBen Gras  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
7294715d8eSBen Gras  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
7394715d8eSBen Gras  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
7494715d8eSBen Gras  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
7594715d8eSBen Gras  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
7694715d8eSBen Gras  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
7794715d8eSBen Gras  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7894715d8eSBen Gras  */
7994715d8eSBen Gras 
8094715d8eSBen Gras /*
8194715d8eSBen Gras  * mke2fs.c: "re-invent (dumb but non-GPLed) wheel as a fun project"
8294715d8eSBen Gras  *
8394715d8eSBen Gras  *	In spite of this name, there is no piece of code
8494715d8eSBen Gras  *	derived from GPLed e2fsprogs written for Linux.
8594715d8eSBen Gras  *	I referred them only to see how each structure
8694715d8eSBen Gras  *	member should be initialized.
8794715d8eSBen Gras  *
8894715d8eSBen Gras  * Reference:
8994715d8eSBen Gras  *	- All NetBSD sources under src/sys/ufs/ext2fs and src/sbin/fsck_ext2fs
9094715d8eSBen Gras  *	- Ext2fs Home Page
9194715d8eSBen Gras  *		http://e2fsprogs.sourceforge.net/ext2.html
9294715d8eSBen Gras  *	- Design and Implementation of the Second Extended Filesystem
9394715d8eSBen Gras  *		http://e2fsprogs.sourceforge.net/ext2intro.html
9494715d8eSBen Gras  *	- Linux Documentation "The Second Extended Filesystem"
9594715d8eSBen Gras  *		http://www.kernel.org/doc/Documentation/filesystems/ext2.txt
9694715d8eSBen Gras  */
9794715d8eSBen Gras 
9894715d8eSBen Gras #include <sys/cdefs.h>
9994715d8eSBen Gras #ifndef lint
10094715d8eSBen Gras #if 0
10194715d8eSBen Gras static char sccsid[] = "@(#)mkfs.c	8.11 (Berkeley) 5/3/95";
10294715d8eSBen Gras #else
103*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: mke2fs.c,v 1.22 2015/06/16 23:18:55 christos Exp $");
10494715d8eSBen Gras #endif
10594715d8eSBen Gras #endif /* not lint */
10694715d8eSBen Gras 
10794715d8eSBen Gras #include <sys/param.h>
10894715d8eSBen Gras #include <sys/mman.h>
10994715d8eSBen Gras #include <sys/time.h>
11094715d8eSBen Gras #include <ufs/ext2fs/ext2fs_dinode.h>
11194715d8eSBen Gras #include <ufs/ext2fs/ext2fs_dir.h>
11294715d8eSBen Gras #include <ufs/ext2fs/ext2fs.h>
11394715d8eSBen Gras #include <sys/ioctl.h>
11494715d8eSBen Gras 
11594715d8eSBen Gras #include <err.h>
11694715d8eSBen Gras #include <errno.h>
11794715d8eSBen Gras #include <string.h>
11894715d8eSBen Gras #include <unistd.h>
11994715d8eSBen Gras #include <stdlib.h>
12094715d8eSBen Gras #include <stddef.h>
12194715d8eSBen Gras #include <stdio.h>
12294715d8eSBen Gras #include <uuid.h>
12394715d8eSBen Gras 
12494715d8eSBen Gras #include "extern.h"
12594715d8eSBen Gras 
12694715d8eSBen Gras static void initcg(uint);
12784d9c625SLionel Sambuc static void zap_old_sblock(int);
12894715d8eSBen Gras static uint cgoverhead(uint);
12994715d8eSBen Gras static int fsinit(const struct timeval *);
13094715d8eSBen Gras static int makedir(struct ext2fs_direct *, int);
13194715d8eSBen Gras static void copy_dir(struct ext2fs_direct *, struct ext2fs_direct *);
13294715d8eSBen Gras static void init_resizeino(const struct timeval *);
13394715d8eSBen Gras static uint32_t alloc(uint32_t, uint16_t);
13494715d8eSBen Gras static void iput(struct ext2fs_dinode *, ino_t);
13594715d8eSBen Gras static void rdfs(daddr_t, int, void *);
13694715d8eSBen Gras static void wtfs(daddr_t, int, void *);
13794715d8eSBen Gras static int ilog2(uint);
13894715d8eSBen Gras static int skpc(int, size_t, uint8_t *);
13994715d8eSBen Gras 
14094715d8eSBen Gras /* XXX: some of these macro should be into <ufs/ext2fs/ext2fs.h>? */
14194715d8eSBen Gras #define EXT2_DEF_MAX_MNT_COUNT	20
14294715d8eSBen Gras #define EXT2_DEF_FSCKINTV	(180 * 24 * 60 * 60)	/* 180 days */
14394715d8eSBen Gras #define EXT2_RESERVED_INODES	(EXT2_FIRSTINO - 1)
14494715d8eSBen Gras #define EXT2_UMASK		0755
14594715d8eSBen Gras 
14694715d8eSBen Gras #define EXT2_INO_INDEX(ino)	((ino) - 1)		/* no inode zero */
14794715d8eSBen Gras 
14894715d8eSBen Gras #define EXT2_LOSTFOUNDSIZE	16384
14994715d8eSBen Gras #define EXT2_LOSTFOUNDINO	EXT2_FIRSTINO		/* XXX: not quite */
15094715d8eSBen Gras #define EXT2_LOSTFOUNDUMASK	0700
15194715d8eSBen Gras 
15294715d8eSBen Gras #define EXT2_RESIZEINOUMASK	0600
15394715d8eSBen Gras 
15494715d8eSBen Gras #define NBLOCK_SUPERBLOCK	1
15594715d8eSBen Gras #define NBLOCK_BLOCK_BITMAP	1
15694715d8eSBen Gras #define NBLOCK_INODE_BITMAP	1
15794715d8eSBen Gras 
15894715d8eSBen Gras #define cgbase(fs, c)	\
15994715d8eSBen Gras 	((fs)->e2fs.e2fs_first_dblock + (fs)->e2fs.e2fs_bpg * (c))
16094715d8eSBen Gras 
16194715d8eSBen Gras 
16294715d8eSBen Gras /*
16394715d8eSBen Gras  * ext2fs super block and group descriptor structures
16494715d8eSBen Gras  *
16594715d8eSBen Gras  *   We don't have to use or setup whole in-memory m_ext2fs structure,
16694715d8eSBen Gras  *   but prepare it to use several macro defined in kernel headers.
16794715d8eSBen Gras  */
16894715d8eSBen Gras union {
16994715d8eSBen Gras 	struct m_ext2fs m_ext2fs;
17094715d8eSBen Gras 	char pad[SBSIZE];
17194715d8eSBen Gras } ext2fsun;
17294715d8eSBen Gras #define sblock	ext2fsun.m_ext2fs
17394715d8eSBen Gras #define gd	ext2fsun.m_ext2fs.e2fs_gd
17494715d8eSBen Gras 
17594715d8eSBen Gras static uint8_t *iobuf;		/* for superblock and group descriptors */
17694715d8eSBen Gras static int iobufsize;
17794715d8eSBen Gras 
17894715d8eSBen Gras static uint8_t buf[MAXBSIZE];	/* for initcg() and makedir() ops */
17994715d8eSBen Gras 
18084d9c625SLionel Sambuc static int fsi, fso;
18194715d8eSBen Gras 
18294715d8eSBen Gras void
mke2fs(const char * fsys,int fi,int fo)18394715d8eSBen Gras mke2fs(const char *fsys, int fi, int fo)
18494715d8eSBen Gras {
18594715d8eSBen Gras 	struct timeval tv;
18694715d8eSBen Gras 	int64_t minfssize;
18794715d8eSBen Gras 	uint bcount, fbcount, ficount;
18894715d8eSBen Gras 	uint blocks_gd, blocks_per_cg, inodes_per_cg, iblocks_per_cg;
18994715d8eSBen Gras 	uint minblocks_per_cg, blocks_lastcg;
19094715d8eSBen Gras 	uint ncg, cylno, sboff;
19194715d8eSBen Gras 	uuid_t uuid;
19294715d8eSBen Gras 	uint32_t uustat;
19394715d8eSBen Gras 	int i, len, col, delta, fld_width, max_cols;
19494715d8eSBen Gras 	struct winsize winsize;
19594715d8eSBen Gras 
19694715d8eSBen Gras 	gettimeofday(&tv, NULL);
19794715d8eSBen Gras 	fsi = fi;
19894715d8eSBen Gras 	fso = fo;
19994715d8eSBen Gras 
20094715d8eSBen Gras 	/*
20194715d8eSBen Gras 	 * collect and verify the block and fragment sizes
20294715d8eSBen Gras 	 */
20394715d8eSBen Gras 	if (!powerof2(bsize)) {
20494715d8eSBen Gras 		errx(EXIT_FAILURE,
205*0a6a1f1dSLionel Sambuc 		    "block size must be a power of 2, not %u",
20694715d8eSBen Gras 		    bsize);
20794715d8eSBen Gras 	}
20894715d8eSBen Gras 	if (!powerof2(fsize)) {
20994715d8eSBen Gras 		errx(EXIT_FAILURE,
210*0a6a1f1dSLionel Sambuc 		    "fragment size must be a power of 2, not %u",
21194715d8eSBen Gras 		    fsize);
21294715d8eSBen Gras 	}
21394715d8eSBen Gras 	if (fsize < sectorsize) {
21494715d8eSBen Gras 		errx(EXIT_FAILURE,
215*0a6a1f1dSLionel Sambuc 		    "fragment size %u is too small, minimum is %u",
21694715d8eSBen Gras 		    fsize, sectorsize);
21794715d8eSBen Gras 	}
21894715d8eSBen Gras 	if (bsize < MINBSIZE) {
21994715d8eSBen Gras 		errx(EXIT_FAILURE,
220*0a6a1f1dSLionel Sambuc 		    "block size %u is too small, minimum is %u",
22194715d8eSBen Gras 		    bsize, MINBSIZE);
22294715d8eSBen Gras 	}
22394715d8eSBen Gras 	if (bsize > EXT2_MAXBSIZE) {
22494715d8eSBen Gras 		errx(EXIT_FAILURE,
225*0a6a1f1dSLionel Sambuc 		    "block size %u is too large, maximum is %u",
22694715d8eSBen Gras 		    bsize, MAXBSIZE);
22794715d8eSBen Gras 	}
22894715d8eSBen Gras 	if (bsize != fsize) {
22994715d8eSBen Gras 		/*
23094715d8eSBen Gras 		 * There is no fragment support on current ext2fs (yet?),
23194715d8eSBen Gras 		 * but some kernel code refers fsize or fpg as bsize or bpg
23294715d8eSBen Gras 		 * and Linux seems to set the same values to them.
23394715d8eSBen Gras 		 */
23494715d8eSBen Gras 		errx(EXIT_FAILURE,
23594715d8eSBen Gras 		    "block size (%u) can't be different from "
236*0a6a1f1dSLionel Sambuc 		    "fragment size (%u)",
23794715d8eSBen Gras 		    bsize, fsize);
23894715d8eSBen Gras 	}
23994715d8eSBen Gras 
24094715d8eSBen Gras 	/* variable inodesize is REV1 feature */
24194715d8eSBen Gras 	if (Oflag == 0 && inodesize != EXT2_REV0_DINODE_SIZE) {
24294715d8eSBen Gras 		errx(EXIT_FAILURE, "GOOD_OLD_REV file system format"
243*0a6a1f1dSLionel Sambuc 		    " doesn't support %d byte inode", inodesize);
24494715d8eSBen Gras 	}
24594715d8eSBen Gras 
24694715d8eSBen Gras 	sblock.e2fs.e2fs_log_bsize = ilog2(bsize) - LOG_MINBSIZE;
24794715d8eSBen Gras 	/* Umm, why not e2fs_log_fsize? */
24894715d8eSBen Gras 	sblock.e2fs.e2fs_fsize = ilog2(fsize) - LOG_MINBSIZE;
24994715d8eSBen Gras 
25094715d8eSBen Gras 	sblock.e2fs_bsize = bsize;
25194715d8eSBen Gras 	sblock.e2fs_bshift = sblock.e2fs.e2fs_log_bsize + LOG_MINBSIZE;
25294715d8eSBen Gras 	sblock.e2fs_qbmask = sblock.e2fs_bsize - 1;
25394715d8eSBen Gras 	sblock.e2fs_bmask = ~sblock.e2fs_qbmask;
25494715d8eSBen Gras 	sblock.e2fs_fsbtodb = ilog2(sblock.e2fs_bsize) - ilog2(sectorsize);
25594715d8eSBen Gras 	sblock.e2fs_ipb = sblock.e2fs_bsize / inodesize;
25694715d8eSBen Gras 
25794715d8eSBen Gras 	/*
25894715d8eSBen Gras 	 * Ext2fs preserves BBSIZE (1024 bytes) space at the top for
25994715d8eSBen Gras 	 * bootloader (though it is not enough at all for our bootloader).
26094715d8eSBen Gras 	 * If bsize == BBSIZE we have to preserve one block.
26194715d8eSBen Gras 	 * If bsize > BBSIZE, the first block already contains BBSIZE space
26294715d8eSBen Gras 	 * before superblock because superblock is allocated at SBOFF and
26394715d8eSBen Gras 	 * bsize is a power of two (i.e. 2048 bytes or more).
26494715d8eSBen Gras 	 */
26594715d8eSBen Gras 	sblock.e2fs.e2fs_first_dblock = (sblock.e2fs_bsize > BBSIZE) ? 0 : 1;
26684d9c625SLionel Sambuc 	minfssize = EXT2_FSBTODB(&sblock,
26794715d8eSBen Gras 	    sblock.e2fs.e2fs_first_dblock +
26894715d8eSBen Gras 	    NBLOCK_SUPERBLOCK +
26994715d8eSBen Gras 	    1 /* at least one group descriptor */ +
27094715d8eSBen Gras 	    NBLOCK_BLOCK_BITMAP	+
27194715d8eSBen Gras 	    NBLOCK_INODE_BITMAP +
27294715d8eSBen Gras 	    1 /* at least one inode table block */ +
27394715d8eSBen Gras 	    1 /* at least one data block for rootdir */ +
27494715d8eSBen Gras 	    1 /* at least one data block for data */
27594715d8eSBen Gras 	    );			/* XXX and more? */
27694715d8eSBen Gras 
27794715d8eSBen Gras 	if (fssize < minfssize)
27894715d8eSBen Gras 		errx(EXIT_FAILURE, "Filesystem size %" PRId64
279*0a6a1f1dSLionel Sambuc 		    " < minimum size of %" PRId64, fssize, minfssize);
28094715d8eSBen Gras 
28184d9c625SLionel Sambuc 	bcount = EXT2_DBTOFSB(&sblock, fssize);
28294715d8eSBen Gras 
28394715d8eSBen Gras 	/*
28494715d8eSBen Gras 	 * While many people claim that ext2fs is a (bad) clone of ufs/ffs,
28594715d8eSBen Gras 	 * it isn't actual ffs so maybe we should call it "block group"
28694715d8eSBen Gras 	 * as their native name rather than ffs derived "cylinder group."
28794715d8eSBen Gras 	 * But we'll use the latter here since other kernel sources use it.
28894715d8eSBen Gras 	 * (I also agree "cylinder" based allocation is obsolete though)
28994715d8eSBen Gras 	 */
29094715d8eSBen Gras 
29194715d8eSBen Gras 	/* maybe "simple is the best" */
29294715d8eSBen Gras 	blocks_per_cg = sblock.e2fs_bsize * NBBY;
29394715d8eSBen Gras 
29494715d8eSBen Gras 	ncg = howmany(bcount - sblock.e2fs.e2fs_first_dblock, blocks_per_cg);
29594715d8eSBen Gras 	blocks_gd = howmany(sizeof(struct ext2_gd) * ncg, bsize);
29694715d8eSBen Gras 
29794715d8eSBen Gras 	/* check range of inode number */
29894715d8eSBen Gras 	if (num_inodes < EXT2_FIRSTINO)
29994715d8eSBen Gras 		num_inodes = EXT2_FIRSTINO;	/* needs reserved inodes + 1 */
30094715d8eSBen Gras 	if (num_inodes > UINT16_MAX * ncg)
30194715d8eSBen Gras 		num_inodes = UINT16_MAX * ncg;	/* ext2bgd_nifree is uint16_t */
30294715d8eSBen Gras 
30394715d8eSBen Gras 	inodes_per_cg = num_inodes / ncg;
30494715d8eSBen Gras 	iblocks_per_cg = howmany(inodesize * inodes_per_cg, bsize);
30594715d8eSBen Gras 
30694715d8eSBen Gras 	/* Check that the last cylinder group has enough space for inodes */
30794715d8eSBen Gras 	minblocks_per_cg =
30894715d8eSBen Gras 	    NBLOCK_BLOCK_BITMAP +
30994715d8eSBen Gras 	    NBLOCK_INODE_BITMAP +
31094715d8eSBen Gras 	    iblocks_per_cg +
31194715d8eSBen Gras 	    1;	/* at least one data block */
31294715d8eSBen Gras 	if (Oflag == 0 || cg_has_sb(ncg - 1) != 0)
31394715d8eSBen Gras 		minblocks_per_cg += NBLOCK_SUPERBLOCK + blocks_gd;
31494715d8eSBen Gras 
31594715d8eSBen Gras 	blocks_lastcg = bcount - sblock.e2fs.e2fs_first_dblock -
31694715d8eSBen Gras 	    blocks_per_cg * (ncg - 1);
31794715d8eSBen Gras 	if (blocks_lastcg < minblocks_per_cg) {
31894715d8eSBen Gras 		/*
31994715d8eSBen Gras 		 * Since we make all the cylinder groups the same size, the
32094715d8eSBen Gras 		 * last will only be small if there are more than one
32194715d8eSBen Gras 		 * cylinder groups. If the last one is too small to store
32294715d8eSBen Gras 		 * filesystem data, just kill it.
32394715d8eSBen Gras 		 *
32494715d8eSBen Gras 		 * XXX: Does fsck_ext2fs(8) properly handle this case?
32594715d8eSBen Gras 		 */
32694715d8eSBen Gras 		bcount -= blocks_lastcg;
32794715d8eSBen Gras 		ncg--;
32894715d8eSBen Gras 		blocks_lastcg = blocks_per_cg;
32994715d8eSBen Gras 		blocks_gd = howmany(sizeof(struct ext2_gd) * ncg, bsize);
33094715d8eSBen Gras 		inodes_per_cg = num_inodes / ncg;
33194715d8eSBen Gras 	}
33294715d8eSBen Gras 	/* roundup inodes_per_cg to make it use whole inode table blocks */
33394715d8eSBen Gras 	inodes_per_cg = roundup(inodes_per_cg, sblock.e2fs_ipb);
33494715d8eSBen Gras 	num_inodes = inodes_per_cg * ncg;
33594715d8eSBen Gras 	iblocks_per_cg = inodes_per_cg / sblock.e2fs_ipb;
33694715d8eSBen Gras 
33794715d8eSBen Gras 	/* XXX: probably we should check these adjusted values again */
33894715d8eSBen Gras 
33994715d8eSBen Gras 	sblock.e2fs.e2fs_bcount = bcount;
34094715d8eSBen Gras 	sblock.e2fs.e2fs_icount = num_inodes;
34194715d8eSBen Gras 
34294715d8eSBen Gras 	sblock.e2fs_ncg = ncg;
34394715d8eSBen Gras 	sblock.e2fs_ngdb = blocks_gd;
34494715d8eSBen Gras 	sblock.e2fs_itpg = iblocks_per_cg;
34594715d8eSBen Gras 
34694715d8eSBen Gras 	sblock.e2fs.e2fs_rbcount = sblock.e2fs.e2fs_bcount * minfree / 100;
34794715d8eSBen Gras 	/* e2fs_fbcount will be accounted later */
34894715d8eSBen Gras 	/* e2fs_ficount will be accounted later */
34994715d8eSBen Gras 
35094715d8eSBen Gras 	sblock.e2fs.e2fs_bpg = blocks_per_cg;
35194715d8eSBen Gras 	sblock.e2fs.e2fs_fpg = blocks_per_cg;
35294715d8eSBen Gras 
35394715d8eSBen Gras 	sblock.e2fs.e2fs_ipg = inodes_per_cg;
35494715d8eSBen Gras 
35594715d8eSBen Gras 	sblock.e2fs.e2fs_mtime = 0;
35694715d8eSBen Gras 	sblock.e2fs.e2fs_wtime = tv.tv_sec;
35794715d8eSBen Gras 	sblock.e2fs.e2fs_mnt_count = 0;
35894715d8eSBen Gras 	/* XXX: should add some entropy to avoid checking all fs at once? */
35994715d8eSBen Gras 	sblock.e2fs.e2fs_max_mnt_count = EXT2_DEF_MAX_MNT_COUNT;
36094715d8eSBen Gras 
36194715d8eSBen Gras 	sblock.e2fs.e2fs_magic = E2FS_MAGIC;
36294715d8eSBen Gras 	sblock.e2fs.e2fs_state = E2FS_ISCLEAN;
36394715d8eSBen Gras 	sblock.e2fs.e2fs_beh = E2FS_BEH_DEFAULT;
36494715d8eSBen Gras 	sblock.e2fs.e2fs_minrev = 0;
36594715d8eSBen Gras 	sblock.e2fs.e2fs_lastfsck = tv.tv_sec;
36694715d8eSBen Gras 	sblock.e2fs.e2fs_fsckintv = EXT2_DEF_FSCKINTV;
36794715d8eSBen Gras 
36894715d8eSBen Gras 	/*
36994715d8eSBen Gras 	 * Maybe we can use E2FS_OS_FREEBSD here and it would be more proper,
37094715d8eSBen Gras 	 * but the purpose of this newfs_ext2fs(8) command is to provide
37194715d8eSBen Gras 	 * a filesystem which can be recognized by firmware on some
37294715d8eSBen Gras 	 * Linux based appliances that can load bootstrap files only from
37394715d8eSBen Gras 	 * (their native) ext2fs, and anyway we will (and should) try to
37494715d8eSBen Gras 	 * act like them as much as possible.
37594715d8eSBen Gras 	 *
37694715d8eSBen Gras 	 * Anyway, I hope that all newer such boxes will keep their support
37794715d8eSBen Gras 	 * for the "GOOD_OLD_REV" ext2fs.
37894715d8eSBen Gras 	 */
37994715d8eSBen Gras 	sblock.e2fs.e2fs_creator = E2FS_OS_LINUX;
38094715d8eSBen Gras 
38194715d8eSBen Gras 	if (Oflag == 0) {
38294715d8eSBen Gras 		sblock.e2fs.e2fs_rev = E2FS_REV0;
38394715d8eSBen Gras 		sblock.e2fs.e2fs_features_compat   = 0;
38494715d8eSBen Gras 		sblock.e2fs.e2fs_features_incompat = 0;
38594715d8eSBen Gras 		sblock.e2fs.e2fs_features_rocompat = 0;
38694715d8eSBen Gras 	} else {
38794715d8eSBen Gras 		sblock.e2fs.e2fs_rev = E2FS_REV1;
38894715d8eSBen Gras 		/*
38994715d8eSBen Gras 		 * e2fsprogs say "REV1" is "dynamic" so
39094715d8eSBen Gras 		 * it isn't quite a version and maybe it means
39194715d8eSBen Gras 		 * "extended from REV0 so check compat features."
39294715d8eSBen Gras 		 *
39394715d8eSBen Gras 		 * XXX: We don't have any native tool to activate
39494715d8eSBen Gras 		 *      the EXT2F_COMPAT_RESIZE feature and
39594715d8eSBen Gras 		 *      fsck_ext2fs(8) might not fix structures for it.
39694715d8eSBen Gras 		 */
39794715d8eSBen Gras 		sblock.e2fs.e2fs_features_compat   = EXT2F_COMPAT_RESIZE;
39894715d8eSBen Gras 		sblock.e2fs.e2fs_features_incompat = EXT2F_INCOMPAT_FTYPE;
39994715d8eSBen Gras 		sblock.e2fs.e2fs_features_rocompat =
40094715d8eSBen Gras 		    EXT2F_ROCOMPAT_SPARSESUPER | EXT2F_ROCOMPAT_LARGEFILE;
40194715d8eSBen Gras 	}
40294715d8eSBen Gras 
40394715d8eSBen Gras 	sblock.e2fs.e2fs_ruid = geteuid();
40494715d8eSBen Gras 	sblock.e2fs.e2fs_rgid = getegid();
40594715d8eSBen Gras 
40694715d8eSBen Gras 	sblock.e2fs.e2fs_first_ino = EXT2_FIRSTINO;
40794715d8eSBen Gras 	sblock.e2fs.e2fs_inode_size = inodesize;
40894715d8eSBen Gras 
40994715d8eSBen Gras 	/* e2fs_block_group_nr is set on writing superblock to each group */
41094715d8eSBen Gras 
41194715d8eSBen Gras 	uuid_create(&uuid, &uustat);
41294715d8eSBen Gras 	if (uustat != uuid_s_ok)
413*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "Failed to generate uuid");
41494715d8eSBen Gras 	uuid_enc_le(sblock.e2fs.e2fs_uuid, &uuid);
41594715d8eSBen Gras 	if (volname != NULL) {
41694715d8eSBen Gras 		if (strlen(volname) > sizeof(sblock.e2fs.e2fs_vname))
41794715d8eSBen Gras 			errx(EXIT_FAILURE, "Volume name is too long");
41894715d8eSBen Gras 		strlcpy(sblock.e2fs.e2fs_vname, volname,
41994715d8eSBen Gras 		    sizeof(sblock.e2fs.e2fs_vname));
42094715d8eSBen Gras 	}
42194715d8eSBen Gras 
42294715d8eSBen Gras 	sblock.e2fs.e2fs_fsmnt[0] = '\0';
42394715d8eSBen Gras 	sblock.e2fs_fsmnt[0] = '\0';
42494715d8eSBen Gras 
42594715d8eSBen Gras 	sblock.e2fs.e2fs_algo = 0;		/* XXX unsupported? */
42694715d8eSBen Gras 	sblock.e2fs.e2fs_prealloc = 0;		/* XXX unsupported? */
42794715d8eSBen Gras 	sblock.e2fs.e2fs_dir_prealloc = 0;	/* XXX unsupported? */
42894715d8eSBen Gras 
42994715d8eSBen Gras 	/* calculate blocks for reserved group descriptors for resize */
43094715d8eSBen Gras 	sblock.e2fs.e2fs_reserved_ngdb = 0;
43194715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
43294715d8eSBen Gras 	    (sblock.e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) != 0) {
43394715d8eSBen Gras 		uint64_t target_blocks;
43494715d8eSBen Gras 		uint target_ncg, target_ngdb, reserved_ngdb;
43594715d8eSBen Gras 
43694715d8eSBen Gras 		/* reserve descriptors for size as 1024 times as current */
43794715d8eSBen Gras 		target_blocks =
43894715d8eSBen Gras 		    (sblock.e2fs.e2fs_bcount - sblock.e2fs.e2fs_first_dblock)
43994715d8eSBen Gras 		    * 1024ULL;
44094715d8eSBen Gras 		/* number of blocks must be in uint32_t */
44194715d8eSBen Gras 		if (target_blocks > UINT32_MAX)
44294715d8eSBen Gras 			target_blocks = UINT32_MAX;
44394715d8eSBen Gras 		target_ncg = howmany(target_blocks, sblock.e2fs.e2fs_bpg);
44494715d8eSBen Gras 		target_ngdb = howmany(sizeof(struct ext2_gd) * target_ncg,
44594715d8eSBen Gras 		    sblock.e2fs_bsize);
44694715d8eSBen Gras 		/*
44794715d8eSBen Gras 		 * Reserved group descriptor blocks are preserved as
44894715d8eSBen Gras 		 * the second level double indirect reference blocks in
44994715d8eSBen Gras 		 * the EXT2_RESIZEINO inode, so the maximum number of
45084d9c625SLionel Sambuc 		 * the blocks is EXT2_NINDIR(fs).
45194715d8eSBen Gras 		 * (see also descriptions in init_resizeino() function)
45294715d8eSBen Gras 		 *
45394715d8eSBen Gras 		 * We check a number including current e2fs_ngdb here
45494715d8eSBen Gras 		 * because they will be moved into reserved gdb on
45594715d8eSBen Gras 		 * possible future size shrink, though e2fsprogs don't
45694715d8eSBen Gras 		 * seem to care about it.
45794715d8eSBen Gras 		 */
45884d9c625SLionel Sambuc 		if (target_ngdb > EXT2_NINDIR(&sblock))
45984d9c625SLionel Sambuc 			target_ngdb = EXT2_NINDIR(&sblock);
46094715d8eSBen Gras 
46194715d8eSBen Gras 		reserved_ngdb = target_ngdb - sblock.e2fs_ngdb;
46294715d8eSBen Gras 
46394715d8eSBen Gras 		/* make sure reserved_ngdb fits in the last cg */
46494715d8eSBen Gras 		if (reserved_ngdb >= blocks_lastcg - cgoverhead(ncg - 1))
46594715d8eSBen Gras 			reserved_ngdb = blocks_lastcg - cgoverhead(ncg - 1);
46694715d8eSBen Gras 		if (reserved_ngdb == 0) {
46794715d8eSBen Gras 			/* if no space for reserved gdb, disable the feature */
46894715d8eSBen Gras 			sblock.e2fs.e2fs_features_compat &=
46994715d8eSBen Gras 			    ~EXT2F_COMPAT_RESIZE;
47094715d8eSBen Gras 		}
47194715d8eSBen Gras 		sblock.e2fs.e2fs_reserved_ngdb = reserved_ngdb;
47294715d8eSBen Gras 	}
47394715d8eSBen Gras 
47494715d8eSBen Gras 	/*
47594715d8eSBen Gras 	 * Initialize group descriptors
47694715d8eSBen Gras 	 */
47794715d8eSBen Gras 	gd = malloc(sblock.e2fs_ngdb * bsize);
47894715d8eSBen Gras 	if (gd == NULL)
47994715d8eSBen Gras 		errx(EXIT_FAILURE, "Can't allocate descriptors buffer");
48094715d8eSBen Gras 	memset(gd, 0, sblock.e2fs_ngdb * bsize);
48194715d8eSBen Gras 
48294715d8eSBen Gras 	fbcount = 0;
48394715d8eSBen Gras 	ficount = 0;
48494715d8eSBen Gras 	for (cylno = 0; cylno < ncg; cylno++) {
48594715d8eSBen Gras 		uint boffset;
48694715d8eSBen Gras 
48794715d8eSBen Gras 		boffset = cgbase(&sblock, cylno);
48894715d8eSBen Gras 		if (sblock.e2fs.e2fs_rev == E2FS_REV0 ||
48994715d8eSBen Gras 		    (sblock.e2fs.e2fs_features_rocompat &
49094715d8eSBen Gras 		     EXT2F_ROCOMPAT_SPARSESUPER) == 0 ||
49194715d8eSBen Gras 		    cg_has_sb(cylno)) {
49294715d8eSBen Gras 			boffset += NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb;
49394715d8eSBen Gras 			if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
49494715d8eSBen Gras 			    (sblock.e2fs.e2fs_features_compat &
49594715d8eSBen Gras 			     EXT2F_COMPAT_RESIZE) != 0)
49694715d8eSBen Gras 				boffset += sblock.e2fs.e2fs_reserved_ngdb;
49794715d8eSBen Gras 		}
49894715d8eSBen Gras 		gd[cylno].ext2bgd_b_bitmap = boffset;
49994715d8eSBen Gras 		boffset += NBLOCK_BLOCK_BITMAP;
50094715d8eSBen Gras 		gd[cylno].ext2bgd_i_bitmap = boffset;
50194715d8eSBen Gras 		boffset += NBLOCK_INODE_BITMAP;
50294715d8eSBen Gras 		gd[cylno].ext2bgd_i_tables = boffset;
50394715d8eSBen Gras 		if (cylno == (ncg - 1))
50494715d8eSBen Gras 			gd[cylno].ext2bgd_nbfree =
50594715d8eSBen Gras 			    blocks_lastcg - cgoverhead(cylno);
50694715d8eSBen Gras 		else
50794715d8eSBen Gras 			gd[cylno].ext2bgd_nbfree =
50894715d8eSBen Gras 			    sblock.e2fs.e2fs_bpg - cgoverhead(cylno);
50994715d8eSBen Gras 		fbcount += gd[cylno].ext2bgd_nbfree;
51094715d8eSBen Gras 		gd[cylno].ext2bgd_nifree = sblock.e2fs.e2fs_ipg;
51194715d8eSBen Gras 		if (cylno == 0) {
51294715d8eSBen Gras 			/* take reserved inodes off nifree */
51394715d8eSBen Gras 			gd[cylno].ext2bgd_nifree -= EXT2_RESERVED_INODES;
51494715d8eSBen Gras 		}
51594715d8eSBen Gras 		ficount += gd[cylno].ext2bgd_nifree;
51694715d8eSBen Gras 		gd[cylno].ext2bgd_ndirs = 0;
51794715d8eSBen Gras 	}
51894715d8eSBen Gras 	sblock.e2fs.e2fs_fbcount = fbcount;
51994715d8eSBen Gras 	sblock.e2fs.e2fs_ficount = ficount;
52094715d8eSBen Gras 
52194715d8eSBen Gras 	/*
52294715d8eSBen Gras 	 * Dump out summary information about file system.
52394715d8eSBen Gras 	 */
52494715d8eSBen Gras 	if (verbosity > 0) {
52594715d8eSBen Gras 		printf("%s: %u.%1uMB (%" PRId64 " sectors) "
52694715d8eSBen Gras 		    "block size %u, fragment size %u\n",
52794715d8eSBen Gras 		    fsys,
52894715d8eSBen Gras 		    (uint)(((uint64_t)bcount * bsize) / (1024 * 1024)),
52994715d8eSBen Gras 		    (uint)((uint64_t)bcount * bsize -
53094715d8eSBen Gras 		    rounddown((uint64_t)bcount * bsize, 1024 * 1024))
53194715d8eSBen Gras 		    / 1024 / 100,
53294715d8eSBen Gras 		    fssize, bsize, fsize);
53394715d8eSBen Gras 		printf("\tusing %u block groups of %u.0MB, %u blks, "
53494715d8eSBen Gras 		    "%u inodes.\n",
53594715d8eSBen Gras 		    ncg, bsize * sblock.e2fs.e2fs_bpg / (1024 * 1024),
53694715d8eSBen Gras 		    sblock.e2fs.e2fs_bpg, sblock.e2fs.e2fs_ipg);
53794715d8eSBen Gras 	}
53894715d8eSBen Gras 
53994715d8eSBen Gras 	/*
54094715d8eSBen Gras 	 * allocate space for superblock and group descriptors
54194715d8eSBen Gras 	 */
54294715d8eSBen Gras 	iobufsize = (NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb) * sblock.e2fs_bsize;
54394715d8eSBen Gras 	iobuf = mmap(0, iobufsize, PROT_READ|PROT_WRITE,
54494715d8eSBen Gras 	    MAP_ANON|MAP_PRIVATE, -1, 0);
54594715d8eSBen Gras 	if (iobuf == NULL)
546*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "Cannot allocate I/O buffer");
54794715d8eSBen Gras 	memset(iobuf, 0, iobufsize);
54894715d8eSBen Gras 
54994715d8eSBen Gras 	/*
55094715d8eSBen Gras 	 * We now start writing to the filesystem
55194715d8eSBen Gras 	 */
55294715d8eSBen Gras 
55394715d8eSBen Gras 	if (!Nflag) {
55494715d8eSBen Gras 		static const uint pbsize[] = { 1024, 2048, 4096, 0 };
55584d9c625SLionel Sambuc 		uint pblock;
55694715d8eSBen Gras 		/*
55794715d8eSBen Gras 		 * Validate the given file system size.
55894715d8eSBen Gras 		 * Verify that its last block can actually be accessed.
55994715d8eSBen Gras 		 * Convert to file system fragment sized units.
56094715d8eSBen Gras 		 */
56194715d8eSBen Gras 		if (fssize <= 0)
562*0a6a1f1dSLionel Sambuc 			errx(EXIT_FAILURE, "Preposterous size %" PRId64,
56394715d8eSBen Gras 			    fssize);
56494715d8eSBen Gras 		wtfs(fssize - 1, sectorsize, iobuf);
56594715d8eSBen Gras 
56694715d8eSBen Gras 		/*
56794715d8eSBen Gras 		 * Ensure there is nothing that looks like a filesystem
56894715d8eSBen Gras 		 * superblock anywhere other than where ours will be.
56994715d8eSBen Gras 		 *
57084d9c625SLionel Sambuc 		 * Ext2fs superblock is always placed at the same SBOFF,
57184d9c625SLionel Sambuc 		 * so we just zap possible first backups.
57294715d8eSBen Gras 		 */
57394715d8eSBen Gras 		for (i = 0; pbsize[i] != 0; i++) {
57484d9c625SLionel Sambuc  			pblock = (pbsize[i] > BBSIZE) ? 0 : 1;	/* 1st dblk */
57584d9c625SLionel Sambuc 			pblock += pbsize[i] * NBBY;		/* next bg */
57684d9c625SLionel Sambuc 			/* zap first backup */
57784d9c625SLionel Sambuc 			zap_old_sblock(pblock * pbsize[i]);
57894715d8eSBen Gras 		}
57984d9c625SLionel Sambuc 		/*
58084d9c625SLionel Sambuc 		 * Also zap possbile FFS magic leftover to prevent
58184d9c625SLionel Sambuc 		 * kernel vfs_mountroot() and bootloadres from mis-recognizing
58284d9c625SLionel Sambuc 		 * this file system as FFS.
58384d9c625SLionel Sambuc 		 */
58484d9c625SLionel Sambuc 		zap_old_sblock(8192);	/* SBLOCK_UFS1 */
58584d9c625SLionel Sambuc 		zap_old_sblock(65536);	/* SBLOCK_UFS2 */
58694715d8eSBen Gras 	}
58794715d8eSBen Gras 
58894715d8eSBen Gras 	if (verbosity >= 3)
58994715d8eSBen Gras 		printf("super-block backups (for fsck_ext2fs -b #) at:\n");
59094715d8eSBen Gras 	/* If we are printing more than one line of numbers, line up columns */
59194715d8eSBen Gras 	fld_width = verbosity < 4 ? 1 : snprintf(NULL, 0, "%" PRIu64,
59294715d8eSBen Gras 	    (uint64_t)cgbase(&sblock, ncg - 1));
59394715d8eSBen Gras 	/* Get terminal width */
59494715d8eSBen Gras 	if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) == 0)
59594715d8eSBen Gras 		max_cols = winsize.ws_col;
59694715d8eSBen Gras 	else
59794715d8eSBen Gras 		max_cols = 80;
59894715d8eSBen Gras 	if (Nflag && verbosity == 3)
59994715d8eSBen Gras 		/* Leave space to add " ..." after one row of numbers */
60094715d8eSBen Gras 		max_cols -= 4;
60194715d8eSBen Gras #define BASE 0x10000	/* For some fixed-point maths */
60294715d8eSBen Gras 	col = 0;
60394715d8eSBen Gras 	delta = verbosity > 2 ? 0 : max_cols * BASE / ncg;
60494715d8eSBen Gras 	for (cylno = 0; cylno < ncg; cylno++) {
60594715d8eSBen Gras 		fflush(stdout);
60694715d8eSBen Gras 		initcg(cylno);
60794715d8eSBen Gras 		if (verbosity < 2)
60894715d8eSBen Gras 			continue;
60994715d8eSBen Gras 		/* the first one is a master, not backup */
61094715d8eSBen Gras 		if (cylno == 0)
61194715d8eSBen Gras 			continue;
61294715d8eSBen Gras 		/* skip if this cylinder doesn't have a backup */
61394715d8eSBen Gras 		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
61494715d8eSBen Gras 		    (sblock.e2fs.e2fs_features_rocompat &
61594715d8eSBen Gras 		     EXT2F_ROCOMPAT_SPARSESUPER) != 0 &&
61694715d8eSBen Gras 		    cg_has_sb(cylno) == 0)
61794715d8eSBen Gras 			continue;
61894715d8eSBen Gras 
61994715d8eSBen Gras 		if (delta > 0) {
62094715d8eSBen Gras 			if (Nflag)
62194715d8eSBen Gras 				/* No point doing dots for -N */
62294715d8eSBen Gras 				break;
62394715d8eSBen Gras 			/* Print dots scaled to end near RH margin */
62494715d8eSBen Gras 			for (col += delta; col > BASE; col -= BASE)
62594715d8eSBen Gras 				printf(".");
62694715d8eSBen Gras 			continue;
62794715d8eSBen Gras 		}
62894715d8eSBen Gras 		/* Print superblock numbers */
62984d9c625SLionel Sambuc 		len = printf("%s%*" PRIu64 ",", (col ? " " : ""), fld_width,
63094715d8eSBen Gras 		    (uint64_t)cgbase(&sblock, cylno));
63194715d8eSBen Gras 		col += len;
63294715d8eSBen Gras 		if (col + len < max_cols)
63394715d8eSBen Gras 			/* Next number fits */
63494715d8eSBen Gras 			continue;
63594715d8eSBen Gras 		/* Next number won't fit, need a newline */
63694715d8eSBen Gras 		if (verbosity <= 3) {
63794715d8eSBen Gras 			/* Print dots for subsequent cylinder groups */
63894715d8eSBen Gras 			delta = sblock.e2fs_ncg - cylno - 1;
63994715d8eSBen Gras 			if (delta != 0) {
64094715d8eSBen Gras 				if (Nflag) {
64194715d8eSBen Gras 					printf(" ...");
64294715d8eSBen Gras 					break;
64394715d8eSBen Gras 				}
64494715d8eSBen Gras 				delta = max_cols * BASE / delta;
64594715d8eSBen Gras 			}
64694715d8eSBen Gras 		}
64794715d8eSBen Gras 		col = 0;
64894715d8eSBen Gras 		printf("\n");
64994715d8eSBen Gras 	}
65094715d8eSBen Gras #undef BASE
65194715d8eSBen Gras 	if (col > 0)
65294715d8eSBen Gras 		printf("\n");
65394715d8eSBen Gras 	if (Nflag)
65494715d8eSBen Gras 		return;
65594715d8eSBen Gras 
65694715d8eSBen Gras 	/*
65794715d8eSBen Gras 	 * Now construct the initial file system,
65894715d8eSBen Gras 	 */
65994715d8eSBen Gras 	if (fsinit(&tv) == 0)
66094715d8eSBen Gras 		errx(EXIT_FAILURE, "Error making filesystem");
66194715d8eSBen Gras 	/*
66294715d8eSBen Gras 	 * Write out the superblock and group descriptors
66394715d8eSBen Gras 	 */
66494715d8eSBen Gras 	sblock.e2fs.e2fs_block_group_nr = 0;
66594715d8eSBen Gras 	sboff = 0;
66694715d8eSBen Gras 	if (cgbase(&sblock, 0) == 0) {
66794715d8eSBen Gras 		/*
66894715d8eSBen Gras 		 * If the first block contains the boot block sectors,
66994715d8eSBen Gras 		 * (i.e. in case of sblock.e2fs.e2fs_bsize > BBSIZE)
67094715d8eSBen Gras 		 * we have to preserve data in it.
67194715d8eSBen Gras 		 */
67294715d8eSBen Gras 		sboff = SBOFF;
67394715d8eSBen Gras 	}
67494715d8eSBen Gras 	e2fs_sbsave(&sblock.e2fs, (struct ext2fs *)(iobuf + sboff));
67594715d8eSBen Gras 	e2fs_cgsave(gd, (struct ext2_gd *)(iobuf + sblock.e2fs_bsize),
67694715d8eSBen Gras 	   sizeof(struct ext2_gd) * sblock.e2fs_ncg);
67784d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, cgbase(&sblock, 0)) + sboff / sectorsize,
67894715d8eSBen Gras 	    iobufsize - sboff, iobuf + sboff);
67994715d8eSBen Gras 
68094715d8eSBen Gras 	munmap(iobuf, iobufsize);
68194715d8eSBen Gras }
68294715d8eSBen Gras 
68394715d8eSBen Gras /*
68494715d8eSBen Gras  * Initialize a cylinder (block) group.
68594715d8eSBen Gras  */
68694715d8eSBen Gras void
initcg(uint cylno)68794715d8eSBen Gras initcg(uint cylno)
68894715d8eSBen Gras {
68994715d8eSBen Gras 	uint nblcg, i, j, sboff;
69094715d8eSBen Gras 	struct ext2fs_dinode *dp;
69194715d8eSBen Gras 
69294715d8eSBen Gras 	/*
69394715d8eSBen Gras 	 * Make a copy of the superblock and group descriptors.
69494715d8eSBen Gras 	 */
69594715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev == E2FS_REV0 ||
69694715d8eSBen Gras 	    (sblock.e2fs.e2fs_features_rocompat &
69794715d8eSBen Gras 	     EXT2F_ROCOMPAT_SPARSESUPER) == 0 ||
69894715d8eSBen Gras 	    cg_has_sb(cylno)) {
69994715d8eSBen Gras 		sblock.e2fs.e2fs_block_group_nr = cylno;
70094715d8eSBen Gras 		sboff = 0;
70194715d8eSBen Gras 		if (cgbase(&sblock, cylno) == 0) {
70294715d8eSBen Gras 			/* preserve data in bootblock in cg0 */
70394715d8eSBen Gras 			sboff = SBOFF;
70494715d8eSBen Gras 		}
70594715d8eSBen Gras 		e2fs_sbsave(&sblock.e2fs, (struct ext2fs *)(iobuf + sboff));
70694715d8eSBen Gras 		e2fs_cgsave(gd, (struct ext2_gd *)(iobuf +
70794715d8eSBen Gras 		    sblock.e2fs_bsize * NBLOCK_SUPERBLOCK),
70894715d8eSBen Gras 		    sizeof(struct ext2_gd) * sblock.e2fs_ncg);
70994715d8eSBen Gras 		/* write superblock and group descriptor backups */
71084d9c625SLionel Sambuc 		wtfs(EXT2_FSBTODB(&sblock, cgbase(&sblock, cylno)) +
71194715d8eSBen Gras 		    sboff / sectorsize, iobufsize - sboff, iobuf + sboff);
71294715d8eSBen Gras 	}
71394715d8eSBen Gras 
71494715d8eSBen Gras 	/*
71594715d8eSBen Gras 	 * Initialize block bitmap.
71694715d8eSBen Gras 	 */
71794715d8eSBen Gras 	memset(buf, 0, sblock.e2fs_bsize);
71894715d8eSBen Gras 	if (cylno == (sblock.e2fs_ncg - 1)) {
71994715d8eSBen Gras 		/* The last group could have less blocks than e2fs_bpg. */
72094715d8eSBen Gras 		nblcg = sblock.e2fs.e2fs_bcount -
72194715d8eSBen Gras 		    cgbase(&sblock, sblock.e2fs_ncg - 1);
72294715d8eSBen Gras 		for (i = nblcg; i < roundup(nblcg, NBBY); i++)
72394715d8eSBen Gras 			setbit(buf, i);
72494715d8eSBen Gras 		memset(&buf[i / NBBY], ~0U, sblock.e2fs.e2fs_bpg - i);
72594715d8eSBen Gras 	}
72694715d8eSBen Gras 	/* set overhead (superblock, group descriptor etc.) blocks used */
72794715d8eSBen Gras 	for (i = 0; i < cgoverhead(cylno) / NBBY; i++)
72894715d8eSBen Gras 		buf[i] = ~0;
72994715d8eSBen Gras 	i = i * NBBY;
73094715d8eSBen Gras 	for (; i < cgoverhead(cylno); i++)
73194715d8eSBen Gras 		setbit(buf, i);
73284d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, gd[cylno].ext2bgd_b_bitmap),
73384d9c625SLionel Sambuc 	    sblock.e2fs_bsize, buf);
73494715d8eSBen Gras 
73594715d8eSBen Gras 	/*
73694715d8eSBen Gras 	 * Initialize inode bitmap.
73794715d8eSBen Gras 	 *
73894715d8eSBen Gras 	 *  Assume e2fs_ipg is a multiple of NBBY since
73994715d8eSBen Gras 	 *  it's a multiple of e2fs_ipb (as we did above).
74094715d8eSBen Gras 	 *  Note even (possibly smaller) the last group has the same e2fs_ipg.
74194715d8eSBen Gras 	 */
74294715d8eSBen Gras 	i = sblock.e2fs.e2fs_ipg / NBBY;
74394715d8eSBen Gras 	memset(buf, 0, i);
74494715d8eSBen Gras 	memset(buf + i, ~0U, sblock.e2fs_bsize - i);
74594715d8eSBen Gras 	if (cylno == 0) {
74694715d8eSBen Gras 		/* mark reserved inodes */
74794715d8eSBen Gras 		for (i = 1; i < EXT2_FIRSTINO; i++)
74894715d8eSBen Gras 			setbit(buf, EXT2_INO_INDEX(i));
74994715d8eSBen Gras 	}
75084d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, gd[cylno].ext2bgd_i_bitmap),
75184d9c625SLionel Sambuc 	    sblock.e2fs_bsize, buf);
75294715d8eSBen Gras 
75394715d8eSBen Gras 	/*
75494715d8eSBen Gras 	 * Initialize inode tables.
75594715d8eSBen Gras 	 *
75694715d8eSBen Gras 	 *  Just initialize generation numbers for NFS security.
75794715d8eSBen Gras 	 *  XXX: sys/ufs/ext2fs/ext2fs_alloc.c:ext2fs_valloc() seems
75894715d8eSBen Gras 	 *       to override these generated numbers.
75994715d8eSBen Gras 	 */
76094715d8eSBen Gras 	memset(buf, 0, sblock.e2fs_bsize);
76194715d8eSBen Gras 	for (i = 0; i < sblock.e2fs_itpg; i++) {
76294715d8eSBen Gras 		for (j = 0; j < sblock.e2fs_ipb; j++) {
76394715d8eSBen Gras 			dp = (struct ext2fs_dinode *)(buf + inodesize * j);
76494715d8eSBen Gras 			/* h2fs32() just for consistency */
76594715d8eSBen Gras 			dp->e2di_gen = h2fs32(arc4random());
76694715d8eSBen Gras 		}
76784d9c625SLionel Sambuc 		wtfs(EXT2_FSBTODB(&sblock, gd[cylno].ext2bgd_i_tables + i),
76894715d8eSBen Gras 		    sblock.e2fs_bsize, buf);
76994715d8eSBen Gras 	}
77094715d8eSBen Gras }
77194715d8eSBen Gras 
77294715d8eSBen Gras /*
77394715d8eSBen Gras  * Zap possible lingering old superblock data
77494715d8eSBen Gras  */
77594715d8eSBen Gras static void
zap_old_sblock(int sblkoff)77684d9c625SLionel Sambuc zap_old_sblock(int sblkoff)
77794715d8eSBen Gras {
77884d9c625SLionel Sambuc 	static int cg0_data;
77994715d8eSBen Gras 	uint32_t oldfs[SBSIZE / sizeof(uint32_t)];
78094715d8eSBen Gras 	static const struct fsm {
78194715d8eSBen Gras 		uint32_t offset;
78294715d8eSBen Gras 		uint32_t magic;
78394715d8eSBen Gras 		uint32_t mask;
78494715d8eSBen Gras 	} fs_magics[] = {
78594715d8eSBen Gras 		{offsetof(struct ext2fs, e2fs_magic) / 4, E2FS_MAGIC, 0xffff},
78694715d8eSBen Gras 		{offsetof(struct ext2fs, e2fs_magic) / 4,
78794715d8eSBen Gras 		    E2FS_MAGIC << 16, 0xffff0000},
78894715d8eSBen Gras 		{14, 0xef530000, 0xffff0000},	/* EXT2FS (big) */
78994715d8eSBen Gras 		{0x55c / 4, 0x00011954, ~0U},	/* FS_UFS1_MAGIC */
79094715d8eSBen Gras 		{0x55c / 4, 0x19540119, ~0U},	/* FS_UFS2_MAGIC */
79194715d8eSBen Gras 		{0, 0x70162, ~0U},		/* LFS_MAGIC */
79294715d8eSBen Gras 		{.offset = ~0U},
79394715d8eSBen Gras 	};
79494715d8eSBen Gras 	const struct fsm *fsm;
79594715d8eSBen Gras 
79694715d8eSBen Gras 	if (Nflag)
79794715d8eSBen Gras 		return;
79894715d8eSBen Gras 
79994715d8eSBen Gras 	/* don't override data before superblock */
80084d9c625SLionel Sambuc 	if (sblkoff < SBOFF)
80194715d8eSBen Gras 		return;
80294715d8eSBen Gras 
80394715d8eSBen Gras 	if (cg0_data == 0) {
80494715d8eSBen Gras 		cg0_data =
80594715d8eSBen Gras 		    ((daddr_t)sblock.e2fs.e2fs_first_dblock + cgoverhead(0)) *
80684d9c625SLionel Sambuc 		    sblock.e2fs_bsize;
80794715d8eSBen Gras 	}
80894715d8eSBen Gras 
80994715d8eSBen Gras 	/* Ignore anything that is beyond our filesystem */
81084d9c625SLionel Sambuc 	if (sblkoff / sectorsize >= fssize)
81194715d8eSBen Gras 		return;
81294715d8eSBen Gras 	/* Zero anything inside our filesystem... */
81384d9c625SLionel Sambuc 	if (sblkoff >= sblock.e2fs.e2fs_first_dblock * bsize) {
81494715d8eSBen Gras 		/* ...unless we will write that area anyway */
81584d9c625SLionel Sambuc 		if (sblkoff >= cg0_data)
81694715d8eSBen Gras 			/* assume iobuf is zero'ed here */
81784d9c625SLionel Sambuc 			wtfs(sblkoff / sectorsize,
81884d9c625SLionel Sambuc 			    roundup(SBSIZE, sectorsize), iobuf);
81994715d8eSBen Gras 		return;
82094715d8eSBen Gras 	}
82194715d8eSBen Gras 
82294715d8eSBen Gras 	/*
82394715d8eSBen Gras 	 * The sector might contain boot code, so we must validate it
82494715d8eSBen Gras 	 *
82594715d8eSBen Gras 	 * XXX: ext2fs won't preserve data after SBOFF,
82694715d8eSBen Gras 	 *      but first_dblock could have a different value.
82794715d8eSBen Gras 	 */
82884d9c625SLionel Sambuc 	rdfs(sblkoff / sectorsize, sizeof(oldfs), &oldfs);
82994715d8eSBen Gras 	for (fsm = fs_magics;; fsm++) {
83094715d8eSBen Gras 		uint32_t v;
83194715d8eSBen Gras 		if (fsm->mask == 0)
83294715d8eSBen Gras 			return;
83394715d8eSBen Gras 		v = oldfs[fsm->offset];
83494715d8eSBen Gras 		if ((v & fsm->mask) == fsm->magic ||
83594715d8eSBen Gras 		    (bswap32(v) & fsm->mask) == fsm->magic)
83694715d8eSBen Gras 			break;
83794715d8eSBen Gras 	}
83894715d8eSBen Gras 
83994715d8eSBen Gras 	/* Just zap the magic number */
84094715d8eSBen Gras 	oldfs[fsm->offset] = 0;
84184d9c625SLionel Sambuc 	wtfs(sblkoff / sectorsize, sizeof(oldfs), &oldfs);
84294715d8eSBen Gras }
84394715d8eSBen Gras 
84494715d8eSBen Gras /*
84594715d8eSBen Gras  * uint cgoverhead(uint c)
84694715d8eSBen Gras  *
84794715d8eSBen Gras  * 	Return a number of reserved blocks on the specified group.
84894715d8eSBen Gras  * 	XXX: should be shared with src/sbin/fsck_ext2fs/setup.c
84994715d8eSBen Gras  */
85094715d8eSBen Gras uint
cgoverhead(uint c)85194715d8eSBen Gras cgoverhead(uint c)
85294715d8eSBen Gras {
85394715d8eSBen Gras 	uint overh;
85494715d8eSBen Gras 
85594715d8eSBen Gras 	overh = NBLOCK_BLOCK_BITMAP + NBLOCK_INODE_BITMAP + sblock.e2fs_itpg;
85694715d8eSBen Gras 
85794715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev == E2FS_REV0 ||
85894715d8eSBen Gras 	    (sblock.e2fs.e2fs_features_rocompat &
85994715d8eSBen Gras 	     EXT2F_ROCOMPAT_SPARSESUPER) == 0 ||
86094715d8eSBen Gras 	    cg_has_sb(c) != 0) {
86194715d8eSBen Gras 		overh += NBLOCK_SUPERBLOCK + sblock.e2fs_ngdb;
86294715d8eSBen Gras 
86394715d8eSBen Gras 		if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
86494715d8eSBen Gras 		    (sblock.e2fs.e2fs_features_compat &
86594715d8eSBen Gras 		     EXT2F_COMPAT_RESIZE) != 0)
86694715d8eSBen Gras 			overh += sblock.e2fs.e2fs_reserved_ngdb;
86794715d8eSBen Gras 	}
86894715d8eSBen Gras 
86994715d8eSBen Gras 	return overh;
87094715d8eSBen Gras }
87194715d8eSBen Gras 
87294715d8eSBen Gras /*
87394715d8eSBen Gras  * Initialize the file system
87494715d8eSBen Gras  */
87594715d8eSBen Gras 
87694715d8eSBen Gras #define LOSTDIR		/* e2fsck complains if there is no lost+found */
87794715d8eSBen Gras 
87894715d8eSBen Gras #define	PREDEFDIR	2
87994715d8eSBen Gras 
88094715d8eSBen Gras #ifdef LOSTDIR
88194715d8eSBen Gras #define	PREDEFROOTDIR	(PREDEFDIR + 1)
88294715d8eSBen Gras #else
88394715d8eSBen Gras #define	PREDEFROOTDIR	PREDEFDIR
88494715d8eSBen Gras #endif
88594715d8eSBen Gras 
88694715d8eSBen Gras struct ext2fs_direct root_dir[] = {
88794715d8eSBen Gras 	{ EXT2_ROOTINO, 0, 1, 0, "." },
88894715d8eSBen Gras 	{ EXT2_ROOTINO, 0, 2, 0, ".." },
88994715d8eSBen Gras #ifdef LOSTDIR
89094715d8eSBen Gras 	{ EXT2_LOSTFOUNDINO, 0, 10, 0, "lost+found" },
89194715d8eSBen Gras #endif
89294715d8eSBen Gras };
89394715d8eSBen Gras 
89494715d8eSBen Gras #ifdef LOSTDIR
89594715d8eSBen Gras struct ext2fs_direct lost_found_dir[] = {
89694715d8eSBen Gras 	{ EXT2_LOSTFOUNDINO, 0, 1, 0, "." },
89794715d8eSBen Gras 	{ EXT2_ROOTINO, 0, 2, 0, ".." },
89894715d8eSBen Gras };
89994715d8eSBen Gras struct ext2fs_direct pad_dir = { 0, sizeof(struct ext2fs_direct), 0, 0, "" };
90094715d8eSBen Gras #endif
90194715d8eSBen Gras 
90294715d8eSBen Gras int
fsinit(const struct timeval * tv)90394715d8eSBen Gras fsinit(const struct timeval *tv)
90494715d8eSBen Gras {
90594715d8eSBen Gras 	struct ext2fs_dinode node;
90694715d8eSBen Gras #ifdef LOSTDIR
90794715d8eSBen Gras 	uint i, nblks_lostfound, blk;
90894715d8eSBen Gras #endif
90994715d8eSBen Gras 
91094715d8eSBen Gras 	/*
91194715d8eSBen Gras 	 * Initialize the inode for the resizefs feature
91294715d8eSBen Gras 	 */
91394715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
91494715d8eSBen Gras 	    (sblock.e2fs.e2fs_features_compat & EXT2F_COMPAT_RESIZE) != 0)
91594715d8eSBen Gras 		init_resizeino(tv);
91694715d8eSBen Gras 
91794715d8eSBen Gras 	/*
91894715d8eSBen Gras 	 * Initialize the node
91994715d8eSBen Gras 	 */
92094715d8eSBen Gras 
92194715d8eSBen Gras #ifdef LOSTDIR
92294715d8eSBen Gras 	/*
92394715d8eSBen Gras 	 * Create the lost+found directory
92494715d8eSBen Gras 	 */
92594715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
92694715d8eSBen Gras 	    sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) {
92794715d8eSBen Gras 		lost_found_dir[0].e2d_type = EXT2_FT_DIR;
92894715d8eSBen Gras 		lost_found_dir[1].e2d_type = EXT2_FT_DIR;
92994715d8eSBen Gras 	}
93094715d8eSBen Gras 	(void)makedir(lost_found_dir, __arraycount(lost_found_dir));
93194715d8eSBen Gras 
93294715d8eSBen Gras 	/* prepare a bit large directory for preserved files */
93394715d8eSBen Gras 	nblks_lostfound = EXT2_LOSTFOUNDSIZE / sblock.e2fs_bsize;
93494715d8eSBen Gras 	/* ...but only with direct blocks */
93584d9c625SLionel Sambuc 	if (nblks_lostfound > EXT2FS_NDADDR)
93684d9c625SLionel Sambuc 		nblks_lostfound = EXT2FS_NDADDR;
93794715d8eSBen Gras 
93894715d8eSBen Gras 	memset(&node, 0, sizeof(node));
93994715d8eSBen Gras 	node.e2di_mode = EXT2_IFDIR | EXT2_LOSTFOUNDUMASK;
94094715d8eSBen Gras 	node.e2di_uid = geteuid();
94194715d8eSBen Gras 	node.e2di_size = sblock.e2fs_bsize * nblks_lostfound;
94294715d8eSBen Gras 	node.e2di_atime = tv->tv_sec;
94394715d8eSBen Gras 	node.e2di_ctime = tv->tv_sec;
94494715d8eSBen Gras 	node.e2di_mtime = tv->tv_sec;
94594715d8eSBen Gras 	node.e2di_gid = getegid();
94694715d8eSBen Gras 	node.e2di_nlink = PREDEFDIR;
94794715d8eSBen Gras 	/* e2di_nblock is a number of disk blocks, not ext2fs blocks */
94884d9c625SLionel Sambuc 	node.e2di_nblock = EXT2_FSBTODB(&sblock, nblks_lostfound);
94994715d8eSBen Gras 	node.e2di_blocks[0] = alloc(sblock.e2fs_bsize, node.e2di_mode);
95094715d8eSBen Gras 	if (node.e2di_blocks[0] == 0) {
95194715d8eSBen Gras 		printf("%s: can't allocate block for lost+found\n", __func__);
95294715d8eSBen Gras 		return 0;
95394715d8eSBen Gras 	}
95494715d8eSBen Gras 	for (i = 1; i < nblks_lostfound; i++) {
95594715d8eSBen Gras 		blk = alloc(sblock.e2fs_bsize, 0);
95694715d8eSBen Gras 		if (blk == 0) {
95794715d8eSBen Gras 			printf("%s: can't allocate blocks for lost+found\n",
95894715d8eSBen Gras 			    __func__);
95994715d8eSBen Gras 			return 0;
96094715d8eSBen Gras 		}
96194715d8eSBen Gras 		node.e2di_blocks[i] = blk;
96294715d8eSBen Gras 	}
96384d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, node.e2di_blocks[0]),
96484d9c625SLionel Sambuc 	    sblock.e2fs_bsize, buf);
96594715d8eSBen Gras 	pad_dir.e2d_reclen = sblock.e2fs_bsize;
96694715d8eSBen Gras 	for (i = 1; i < nblks_lostfound; i++) {
96794715d8eSBen Gras 		memset(buf, 0, sblock.e2fs_bsize);
96894715d8eSBen Gras 		copy_dir(&pad_dir, (struct ext2fs_direct *)buf);
96984d9c625SLionel Sambuc 		wtfs(EXT2_FSBTODB(&sblock, node.e2di_blocks[i]),
97084d9c625SLionel Sambuc 		    sblock.e2fs_bsize, buf);
97194715d8eSBen Gras 	}
97294715d8eSBen Gras 	iput(&node, EXT2_LOSTFOUNDINO);
97394715d8eSBen Gras #endif
97494715d8eSBen Gras 	/*
97594715d8eSBen Gras 	 * create the root directory
97694715d8eSBen Gras 	 */
97794715d8eSBen Gras 	memset(&node, 0, sizeof(node));
97894715d8eSBen Gras 	if (sblock.e2fs.e2fs_rev > E2FS_REV0 &&
97994715d8eSBen Gras 	    sblock.e2fs.e2fs_features_incompat & EXT2F_INCOMPAT_FTYPE) {
98094715d8eSBen Gras 		root_dir[0].e2d_type = EXT2_FT_DIR;
98194715d8eSBen Gras 		root_dir[1].e2d_type = EXT2_FT_DIR;
98294715d8eSBen Gras #ifdef LOSTDIR
98394715d8eSBen Gras 		root_dir[2].e2d_type = EXT2_FT_DIR;
98494715d8eSBen Gras #endif
98594715d8eSBen Gras 	}
98694715d8eSBen Gras 	node.e2di_mode = EXT2_IFDIR | EXT2_UMASK;
98794715d8eSBen Gras 	node.e2di_uid = geteuid();
98894715d8eSBen Gras 	node.e2di_size = makedir(root_dir, __arraycount(root_dir));
98994715d8eSBen Gras 	node.e2di_atime = tv->tv_sec;
99094715d8eSBen Gras 	node.e2di_ctime = tv->tv_sec;
99194715d8eSBen Gras 	node.e2di_mtime = tv->tv_sec;
99294715d8eSBen Gras 	node.e2di_gid = getegid();
99394715d8eSBen Gras 	node.e2di_nlink = PREDEFROOTDIR;
99494715d8eSBen Gras 	/* e2di_nblock is a number of disk block, not ext2fs block */
99584d9c625SLionel Sambuc 	node.e2di_nblock = EXT2_FSBTODB(&sblock, 1);
99694715d8eSBen Gras 	node.e2di_blocks[0] = alloc(node.e2di_size, node.e2di_mode);
99794715d8eSBen Gras 	if (node.e2di_blocks[0] == 0) {
99894715d8eSBen Gras 		printf("%s: can't allocate block for root dir\n", __func__);
99994715d8eSBen Gras 		return 0;
100094715d8eSBen Gras 	}
100184d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, node.e2di_blocks[0]),
100284d9c625SLionel Sambuc 	    sblock.e2fs_bsize, buf);
100394715d8eSBen Gras 	iput(&node, EXT2_ROOTINO);
100494715d8eSBen Gras 	return 1;
100594715d8eSBen Gras }
100694715d8eSBen Gras 
100794715d8eSBen Gras /*
100894715d8eSBen Gras  * Construct a set of directory entries in "buf".
100994715d8eSBen Gras  * return size of directory.
101094715d8eSBen Gras  */
101194715d8eSBen Gras int
makedir(struct ext2fs_direct * protodir,int entries)101294715d8eSBen Gras makedir(struct ext2fs_direct *protodir, int entries)
101394715d8eSBen Gras {
101494715d8eSBen Gras 	uint8_t *cp;
101594715d8eSBen Gras 	uint i, spcleft;
101694715d8eSBen Gras 	uint dirblksiz;
101794715d8eSBen Gras 
101894715d8eSBen Gras 	dirblksiz = sblock.e2fs_bsize;
101994715d8eSBen Gras 	memset(buf, 0, dirblksiz);
102094715d8eSBen Gras 	spcleft = dirblksiz;
102194715d8eSBen Gras 	for (cp = buf, i = 0; i < entries - 1; i++) {
102294715d8eSBen Gras 		protodir[i].e2d_reclen = EXT2FS_DIRSIZ(protodir[i].e2d_namlen);
102394715d8eSBen Gras 		copy_dir(&protodir[i], (struct ext2fs_direct *)cp);
102494715d8eSBen Gras 		cp += protodir[i].e2d_reclen;
102594715d8eSBen Gras 		spcleft -= protodir[i].e2d_reclen;
102694715d8eSBen Gras 	}
102794715d8eSBen Gras 	protodir[i].e2d_reclen = spcleft;
102894715d8eSBen Gras 	copy_dir(&protodir[i], (struct ext2fs_direct *)cp);
102994715d8eSBen Gras 	return dirblksiz;
103094715d8eSBen Gras }
103194715d8eSBen Gras 
103294715d8eSBen Gras /*
103394715d8eSBen Gras  * Copy a direntry to a buffer, in fs byte order
103494715d8eSBen Gras  */
103594715d8eSBen Gras static void
copy_dir(struct ext2fs_direct * dir,struct ext2fs_direct * dbuf)103694715d8eSBen Gras copy_dir(struct ext2fs_direct *dir, struct ext2fs_direct *dbuf)
103794715d8eSBen Gras {
103894715d8eSBen Gras 
103994715d8eSBen Gras 	memcpy(dbuf, dir, EXT2FS_DIRSIZ(dir->e2d_namlen));
104094715d8eSBen Gras 	dbuf->e2d_ino = h2fs32(dir->e2d_ino);
104194715d8eSBen Gras 	dbuf->e2d_reclen = h2fs16(dir->e2d_reclen);
104294715d8eSBen Gras }
104394715d8eSBen Gras 
104494715d8eSBen Gras /*
104594715d8eSBen Gras  * void init_resizeino(const struct timeval *tv);
104694715d8eSBen Gras  *
104794715d8eSBen Gras  *	Initialize the EXT2_RESEIZE_INO inode to preserve
104894715d8eSBen Gras  *	reserved group descriptor blocks for future growth of this ext2fs.
104994715d8eSBen Gras  */
105094715d8eSBen Gras void
init_resizeino(const struct timeval * tv)105194715d8eSBen Gras init_resizeino(const struct timeval *tv)
105294715d8eSBen Gras {
105394715d8eSBen Gras 	struct ext2fs_dinode node;
105494715d8eSBen Gras 	uint64_t isize;
105594715d8eSBen Gras 	uint32_t *dindir_block, *reserved_gdb;
105694715d8eSBen Gras 	uint nblock, i, cylno, n;
105794715d8eSBen Gras 
105894715d8eSBen Gras 	memset(&node, 0, sizeof(node));
105994715d8eSBen Gras 
106094715d8eSBen Gras 	/*
106194715d8eSBen Gras 	 * Note this function only prepares required structures for
106294715d8eSBen Gras 	 * future resize. It's a quite different work to implement
106394715d8eSBen Gras 	 * a utility like resize_ext2fs(8) which handles actual
106494715d8eSBen Gras 	 * resize ops even on offline.
106594715d8eSBen Gras 	 *
106694715d8eSBen Gras 	 * Anyway, I'm not sure if there is any documentation about
106794715d8eSBen Gras 	 * this resize ext2fs feature and related data structures,
106894715d8eSBen Gras 	 * and I've written this function based on things what I see
106994715d8eSBen Gras 	 * on some existing implementation and real file system data
107094715d8eSBen Gras 	 * created by existing tools. To be honest, they are not
107194715d8eSBen Gras 	 * so easy to read, so I will try to implement it here without
107294715d8eSBen Gras 	 * any dumb optimization for people who would eventually
107394715d8eSBen Gras 	 * work on "yet another wheel" like resize_ext2fs(8).
107494715d8eSBen Gras 	 */
107594715d8eSBen Gras 
107694715d8eSBen Gras 	/*
107794715d8eSBen Gras 	 * I'm not sure what type is appropriate for this inode.
107894715d8eSBen Gras 	 * The release notes of e2fsprogs says they changed e2fsck to allow
107994715d8eSBen Gras 	 * IFREG for RESIZEINO since a certain resize tool used it. Hmm.
108094715d8eSBen Gras 	 */
108194715d8eSBen Gras 	node.e2di_mode = EXT2_IFREG | EXT2_RESIZEINOUMASK;
108294715d8eSBen Gras 	node.e2di_uid = geteuid();
108394715d8eSBen Gras 	node.e2di_atime = tv->tv_sec;
108494715d8eSBen Gras 	node.e2di_ctime = tv->tv_sec;
108594715d8eSBen Gras 	node.e2di_mtime = tv->tv_sec;
108694715d8eSBen Gras 	node.e2di_gid = getegid();
108794715d8eSBen Gras 	node.e2di_nlink = 1;
108894715d8eSBen Gras 
108994715d8eSBen Gras 	/*
109094715d8eSBen Gras 	 * To preserve the reserved group descriptor blocks,
109194715d8eSBen Gras 	 * EXT2_RESIZEINO uses only double indirect reference
109294715d8eSBen Gras 	 * blocks in its inode entries.
109394715d8eSBen Gras 	 *
109494715d8eSBen Gras 	 * All entries for direct, single indirect and triple
109594715d8eSBen Gras 	 * indirect references are left zero'ed. Maybe it's safe
109694715d8eSBen Gras 	 * because no write operation will happen with this inode.
109794715d8eSBen Gras 	 *
109894715d8eSBen Gras 	 * We have to allocate a block for the first level double
109994715d8eSBen Gras 	 * indirect reference block. Indexes of inode entries in
110094715d8eSBen Gras 	 * this first level dindirect block are corresponding to
110194715d8eSBen Gras 	 * indexes of group descriptors including both used (e2fs_ngdb)
110294715d8eSBen Gras 	 * and reserved (e2fs_reserved_ngdb) group descriptor blocks.
110394715d8eSBen Gras 	 *
110494715d8eSBen Gras 	 * Inode entries of indexes for used (e2fs_ngdb) descriptors are
110594715d8eSBen Gras 	 * left zero'ed. Entries for reserved (e2fs_reserved_ngdb) ones
110694715d8eSBen Gras 	 * have block numbers of actual reserved group descriptors
110794715d8eSBen Gras 	 * allocated at block group zero. This means e2fs_reserved_ngdb
110894715d8eSBen Gras 	 * blocks are reserved as the second level dindirect reference
110994715d8eSBen Gras 	 * blocks, and they actually contain block numbers of indirect
111094715d8eSBen Gras 	 * references. It may be safe since they don't have to keep any
111194715d8eSBen Gras 	 * data yet.
111294715d8eSBen Gras 	 *
111394715d8eSBen Gras 	 * Each these second dindirect blocks (i.e. reserved group
111494715d8eSBen Gras 	 * descriptor blocks in the first block group) should have
111594715d8eSBen Gras 	 * block numbers of its backups in all other block groups.
111694715d8eSBen Gras 	 * I.e. reserved_ngdb[0] block in block group 0 contains block
111794715d8eSBen Gras 	 * numbers of resreved_ngdb[0] from group 1 through (e2fs_ncg - 1).
111894715d8eSBen Gras 	 * The number of backups can be determined by the
111994715d8eSBen Gras 	 * EXT2_ROCOMPAT_SPARSESUPER feature and cg_has_sb() macro
112094715d8eSBen Gras 	 * as done in the above initcg() function.
112194715d8eSBen Gras 	 */
112294715d8eSBen Gras 
112394715d8eSBen Gras 	/* set e2di_size which occupies whole blocks through DINDIR blocks */
112484d9c625SLionel Sambuc 	isize = (uint64_t)sblock.e2fs_bsize * EXT2FS_NDADDR +
112584d9c625SLionel Sambuc 	    (uint64_t)sblock.e2fs_bsize * EXT2_NINDIR(&sblock) +
112684d9c625SLionel Sambuc 	    (uint64_t)sblock.e2fs_bsize * EXT2_NINDIR(&sblock) *
112784d9c625SLionel Sambuc 	    EXT2_NINDIR(&sblock);
112894715d8eSBen Gras 	if (isize > UINT32_MAX &&
112994715d8eSBen Gras 	    (sblock.e2fs.e2fs_features_rocompat &
113094715d8eSBen Gras 	     EXT2F_ROCOMPAT_LARGEFILE) == 0) {
113194715d8eSBen Gras 		/* XXX should enable it here and update all backups? */
113294715d8eSBen Gras 		errx(EXIT_FAILURE, "%s: large_file rocompat feature is "
1133*0a6a1f1dSLionel Sambuc 		    "required to enable resize feature for this filesystem",
113494715d8eSBen Gras 		    __func__);
113594715d8eSBen Gras 	}
113694715d8eSBen Gras 	/* upper 32bit is stored into e2di_dacl on REV1 feature */
113794715d8eSBen Gras 	node.e2di_size = isize & UINT32_MAX;
113894715d8eSBen Gras 	node.e2di_dacl = isize >> 32;
113994715d8eSBen Gras 
114094715d8eSBen Gras #define SINGLE	0	/* index of single indirect block */
114194715d8eSBen Gras #define DOUBLE	1	/* index of double indirect block */
114294715d8eSBen Gras #define TRIPLE	2	/* index of triple indirect block */
114394715d8eSBen Gras 
114494715d8eSBen Gras 	/* zero out entries for direct references */
114584d9c625SLionel Sambuc 	for (i = 0; i < EXT2FS_NDADDR; i++)
114694715d8eSBen Gras 		node.e2di_blocks[i] = 0;
114794715d8eSBen Gras 	/* also zero out entries for single and triple indirect references */
114884d9c625SLionel Sambuc 	node.e2di_blocks[EXT2FS_NDADDR + SINGLE] = 0;
114984d9c625SLionel Sambuc 	node.e2di_blocks[EXT2FS_NDADDR + TRIPLE] = 0;
115094715d8eSBen Gras 
115194715d8eSBen Gras 	/* allocate a block for the first level double indirect reference */
115284d9c625SLionel Sambuc 	node.e2di_blocks[EXT2FS_NDADDR + DOUBLE] =
115394715d8eSBen Gras 	    alloc(sblock.e2fs_bsize, node.e2di_mode);
115484d9c625SLionel Sambuc 	if (node.e2di_blocks[EXT2FS_NDADDR + DOUBLE] == 0)
115594715d8eSBen Gras 		errx(EXIT_FAILURE, "%s: Can't allocate a dindirect block",
115694715d8eSBen Gras 		    __func__);
115794715d8eSBen Gras 
115894715d8eSBen Gras 	/* account this first block */
115984d9c625SLionel Sambuc 	nblock = EXT2_FSBTODB(&sblock, 1);
116094715d8eSBen Gras 
116194715d8eSBen Gras 	/* allocate buffer to set data in the dindirect block */
116294715d8eSBen Gras 	dindir_block = malloc(sblock.e2fs_bsize);
116394715d8eSBen Gras 	if (dindir_block == NULL)
116494715d8eSBen Gras 		errx(EXIT_FAILURE,
116594715d8eSBen Gras 		    "%s: Can't allocate buffer for a dindirect block",
116694715d8eSBen Gras 		    __func__);
116794715d8eSBen Gras 
116894715d8eSBen Gras 	/* allocate buffer to set data in the group descriptor blocks */
116994715d8eSBen Gras 	reserved_gdb = malloc(sblock.e2fs_bsize);
117094715d8eSBen Gras 	if (reserved_gdb == NULL)
117194715d8eSBen Gras 		errx(EXIT_FAILURE,
117294715d8eSBen Gras 		    "%s: Can't allocate buffer for group descriptor blocks",
117394715d8eSBen Gras 		    __func__);
117494715d8eSBen Gras 
117594715d8eSBen Gras 	/*
117694715d8eSBen Gras 	 * Setup block entries in the first level dindirect blocks
117794715d8eSBen Gras 	 */
117894715d8eSBen Gras 	for (i = 0; i < sblock.e2fs_ngdb; i++) {
117994715d8eSBen Gras 		/* no need to handle used group descriptor blocks */
118094715d8eSBen Gras 		dindir_block[i] = 0;
118194715d8eSBen Gras 	}
118294715d8eSBen Gras 	for (; i < sblock.e2fs_ngdb + sblock.e2fs.e2fs_reserved_ngdb; i++) {
118394715d8eSBen Gras 		/*
118494715d8eSBen Gras 		 * point reserved group descriptor block in the first
118594715d8eSBen Gras 		 * (i.e. master) block group
118694715d8eSBen Gras 		 *
118784d9c625SLionel Sambuc 		 * XXX: e2fsprogs seem to use "(i % EXT2_NINDIR(&sblock))" here
118884d9c625SLionel Sambuc 		 *      to store maximum EXT2_NINDIR(&sblock) reserved gdbs.
118994715d8eSBen Gras 		 *      I'm not sure what will be done on future filesystem
119094715d8eSBen Gras 		 *      shrink in that case on their way.
119194715d8eSBen Gras 		 */
119284d9c625SLionel Sambuc 		if (i >= EXT2_NINDIR(&sblock))
119394715d8eSBen Gras 			errx(EXIT_FAILURE, "%s: too many reserved "
119494715d8eSBen Gras 			    "group descriptors (%u) for resize inode",
119594715d8eSBen Gras 			    __func__, sblock.e2fs.e2fs_reserved_ngdb);
119694715d8eSBen Gras 		dindir_block[i] =
119794715d8eSBen Gras 		    h2fs32(cgbase(&sblock, 0) + NBLOCK_SUPERBLOCK + i);
119894715d8eSBen Gras 
119994715d8eSBen Gras 		/*
120094715d8eSBen Gras 		 * Setup block entries in the second dindirect blocks
120194715d8eSBen Gras 		 * (which are primary reserved group descriptor blocks)
120294715d8eSBen Gras 		 * to point their backups.
120394715d8eSBen Gras 		 */
120494715d8eSBen Gras 		for (n = 0, cylno = 1; cylno < sblock.e2fs_ncg; cylno++) {
120594715d8eSBen Gras 			/* skip block groups without backup */
120694715d8eSBen Gras 			if ((sblock.e2fs.e2fs_features_rocompat &
120794715d8eSBen Gras 			     EXT2F_ROCOMPAT_SPARSESUPER) != 0 &&
120894715d8eSBen Gras 			    cg_has_sb(cylno) == 0)
120994715d8eSBen Gras 				continue;
121094715d8eSBen Gras 
121184d9c625SLionel Sambuc 			if (n >= EXT2_NINDIR(&sblock))
121294715d8eSBen Gras 				errx(EXIT_FAILURE, "%s: too many block groups "
121394715d8eSBen Gras 				    "for the resize feature", __func__);
121494715d8eSBen Gras 			/*
121594715d8eSBen Gras 			 * These blocks are already reserved in
121694715d8eSBen Gras 			 * initcg() so no need to use alloc() here.
121794715d8eSBen Gras 			 */
121894715d8eSBen Gras 			reserved_gdb[n++] = h2fs32(cgbase(&sblock, cylno) +
121994715d8eSBen Gras 			    NBLOCK_SUPERBLOCK + i);
122084d9c625SLionel Sambuc 			nblock += EXT2_FSBTODB(&sblock, 1);
122194715d8eSBen Gras 		}
122284d9c625SLionel Sambuc 		for (; n < EXT2_NINDIR(&sblock); n++)
122394715d8eSBen Gras 			reserved_gdb[n] = 0;
122494715d8eSBen Gras 
122594715d8eSBen Gras 		/* write group descriptor block as the second dindirect refs */
122684d9c625SLionel Sambuc 		wtfs(EXT2_FSBTODB(&sblock, fs2h32(dindir_block[i])),
122794715d8eSBen Gras 		    sblock.e2fs_bsize, reserved_gdb);
122884d9c625SLionel Sambuc 		nblock += EXT2_FSBTODB(&sblock, 1);
122994715d8eSBen Gras 	}
123084d9c625SLionel Sambuc 	for (; i < EXT2_NINDIR(&sblock); i++) {
123194715d8eSBen Gras 		/* leave trailing entries unallocated */
123294715d8eSBen Gras 		dindir_block[i] = 0;
123394715d8eSBen Gras 	}
123494715d8eSBen Gras 	free(reserved_gdb);
123594715d8eSBen Gras 
123694715d8eSBen Gras 	/* finally write the first level dindirect block */
123784d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, node.e2di_blocks[EXT2FS_NDADDR + DOUBLE]),
123894715d8eSBen Gras 	    sblock.e2fs_bsize, dindir_block);
123994715d8eSBen Gras 	free(dindir_block);
124094715d8eSBen Gras 
124194715d8eSBen Gras 	node.e2di_nblock = nblock;
124294715d8eSBen Gras 	iput(&node, EXT2_RESIZEINO);
124394715d8eSBen Gras }
124494715d8eSBen Gras 
124594715d8eSBen Gras /*
124694715d8eSBen Gras  * uint32_t alloc(uint32_t size, uint16_t mode)
124794715d8eSBen Gras  *
124894715d8eSBen Gras  *	Allocate a block (from cylinder group 0)
124994715d8eSBen Gras  *	Reference: src/sys/ufs/ext2fs/ext2fs_alloc.c:ext2fs_alloccg()
125094715d8eSBen Gras  */
125194715d8eSBen Gras uint32_t
alloc(uint32_t size,uint16_t mode)125294715d8eSBen Gras alloc(uint32_t size, uint16_t mode)
125394715d8eSBen Gras {
125494715d8eSBen Gras 	uint32_t loc, bno;
125594715d8eSBen Gras 	uint8_t *bbp;
125694715d8eSBen Gras 	uint len, map, i;
125794715d8eSBen Gras 
125894715d8eSBen Gras 	if (gd[0].ext2bgd_nbfree == 0)
125994715d8eSBen Gras 		return 0;
126094715d8eSBen Gras 
126194715d8eSBen Gras 	if (size > sblock.e2fs_bsize)
126294715d8eSBen Gras 		return 0;
126394715d8eSBen Gras 
126494715d8eSBen Gras 	bbp = malloc(sblock.e2fs_bsize);
126594715d8eSBen Gras 	if (bbp == NULL)
126694715d8eSBen Gras 		return 0;
126784d9c625SLionel Sambuc 	rdfs(EXT2_FSBTODB(&sblock, gd[0].ext2bgd_b_bitmap),
126884d9c625SLionel Sambuc 	    sblock.e2fs_bsize, bbp);
126994715d8eSBen Gras 
127094715d8eSBen Gras 	/* XXX: kernel uses e2fs_fpg here */
127194715d8eSBen Gras 	len = sblock.e2fs.e2fs_bpg / NBBY;
127294715d8eSBen Gras 
127394715d8eSBen Gras #if 0	/* no need block allocation for root or lost+found dir */
127494715d8eSBen Gras 	for (loc = 0; loc < len; loc++) {
127594715d8eSBen Gras 		if (bbp[loc] == 0) {
127694715d8eSBen Gras 			bno = loc * NBBY;
127794715d8eSBen Gras 			goto gotit;
127894715d8eSBen Gras 		}
127994715d8eSBen Gras 	}
128094715d8eSBen Gras #endif
128194715d8eSBen Gras 
128294715d8eSBen Gras 	loc = skpc(~0U, len, bbp);
128384d9c625SLionel Sambuc 	if (loc == 0) {
128484d9c625SLionel Sambuc 		free(bbp);
128594715d8eSBen Gras 		return 0;
128684d9c625SLionel Sambuc 	}
128794715d8eSBen Gras 	loc = len - loc;
128894715d8eSBen Gras 	map = bbp[loc];
128994715d8eSBen Gras 	bno = loc * NBBY;
129094715d8eSBen Gras 	for (i = 0; i < NBBY; i++, bno++) {
129194715d8eSBen Gras 		if ((map & (1 << i)) == 0)
129294715d8eSBen Gras 			goto gotit;
129394715d8eSBen Gras 	}
129484d9c625SLionel Sambuc 	free(bbp);
129594715d8eSBen Gras 	return 0;
129694715d8eSBen Gras 
129794715d8eSBen Gras  gotit:
129894715d8eSBen Gras 	if (isset(bbp, bno))
1299*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "%s: inconsistent bitmap", __func__);
130094715d8eSBen Gras 
130194715d8eSBen Gras 	setbit(bbp, bno);
130284d9c625SLionel Sambuc 	wtfs(EXT2_FSBTODB(&sblock, gd[0].ext2bgd_b_bitmap),
130384d9c625SLionel Sambuc 	    sblock.e2fs_bsize, bbp);
130494715d8eSBen Gras 	free(bbp);
130594715d8eSBen Gras 	/* XXX: modified group descriptors won't be written into backups */
130694715d8eSBen Gras 	gd[0].ext2bgd_nbfree--;
130794715d8eSBen Gras 	if ((mode & EXT2_IFDIR) != 0)
130894715d8eSBen Gras 		gd[0].ext2bgd_ndirs++;
130994715d8eSBen Gras 	sblock.e2fs.e2fs_fbcount--;
131094715d8eSBen Gras 
131194715d8eSBen Gras 	return sblock.e2fs.e2fs_first_dblock + bno;
131294715d8eSBen Gras }
131394715d8eSBen Gras 
131494715d8eSBen Gras /*
131594715d8eSBen Gras  * void iput(struct ext2fs_dinode *ip, ino_t ino)
131694715d8eSBen Gras  *
131794715d8eSBen Gras  *	Put an inode entry into the corresponding table.
131894715d8eSBen Gras  */
131994715d8eSBen Gras static void
iput(struct ext2fs_dinode * ip,ino_t ino)132094715d8eSBen Gras iput(struct ext2fs_dinode *ip, ino_t ino)
132194715d8eSBen Gras {
132294715d8eSBen Gras 	daddr_t d;
132394715d8eSBen Gras 	uint c, i;
132494715d8eSBen Gras 	struct ext2fs_dinode *dp;
132594715d8eSBen Gras 	uint8_t *bp;
132694715d8eSBen Gras 
132794715d8eSBen Gras 	bp = malloc(sblock.e2fs_bsize);
132894715d8eSBen Gras 	if (bp == NULL)
1329*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "%s: can't allocate buffer for inode",
133094715d8eSBen Gras 		    __func__);
133194715d8eSBen Gras 
133294715d8eSBen Gras 	/*
133394715d8eSBen Gras 	 * Reserved inodes are allocated and accounted in initcg()
133494715d8eSBen Gras 	 * so skip checks of the bitmap and allocation for them.
133594715d8eSBen Gras 	 */
133694715d8eSBen Gras 	if (ino >= EXT2_FIRSTINO) {
133794715d8eSBen Gras 		c = ino_to_cg(&sblock, ino);
133894715d8eSBen Gras 
133994715d8eSBen Gras 		/* sanity check */
134094715d8eSBen Gras 		if (gd[c].ext2bgd_nifree == 0)
134194715d8eSBen Gras 			errx(EXIT_FAILURE,
1342*0a6a1f1dSLionel Sambuc 			    "%s: no free inode %" PRIu64 " in block group %u",
134394715d8eSBen Gras 			    __func__, (uint64_t)ino, c);
134494715d8eSBen Gras 
134594715d8eSBen Gras 		/* update inode bitmap */
134684d9c625SLionel Sambuc 		rdfs(EXT2_FSBTODB(&sblock, gd[0].ext2bgd_i_bitmap),
134794715d8eSBen Gras 		    sblock.e2fs_bsize, bp);
134894715d8eSBen Gras 
134994715d8eSBen Gras 		/* more sanity */
135094715d8eSBen Gras 		if (isset(bp, EXT2_INO_INDEX(ino)))
135194715d8eSBen Gras 			errx(EXIT_FAILURE, "%s: inode %" PRIu64
1352*0a6a1f1dSLionel Sambuc 			    " already in use", __func__, (uint64_t)ino);
135394715d8eSBen Gras 		setbit(bp, EXT2_INO_INDEX(ino));
135484d9c625SLionel Sambuc 		wtfs(EXT2_FSBTODB(&sblock, gd[0].ext2bgd_i_bitmap),
135594715d8eSBen Gras 		    sblock.e2fs_bsize, bp);
135694715d8eSBen Gras 		gd[c].ext2bgd_nifree--;
135794715d8eSBen Gras 		sblock.e2fs.e2fs_ficount--;
135894715d8eSBen Gras 	}
135994715d8eSBen Gras 
136094715d8eSBen Gras 	if (ino >= sblock.e2fs.e2fs_ipg * sblock.e2fs_ncg)
136194715d8eSBen Gras 		errx(EXIT_FAILURE, "%s: inode value out of range (%" PRIu64
1362*0a6a1f1dSLionel Sambuc 		    ")", __func__, (uint64_t)ino);
136394715d8eSBen Gras 
136494715d8eSBen Gras 	/* update an inode entry in the table */
136584d9c625SLionel Sambuc 	d = EXT2_FSBTODB(&sblock, ino_to_fsba(&sblock, ino));
136694715d8eSBen Gras 	rdfs(d, sblock.e2fs_bsize, bp);
136794715d8eSBen Gras 
136894715d8eSBen Gras 	dp = (struct ext2fs_dinode *)(bp +
136994715d8eSBen Gras 	    inodesize * ino_to_fsbo(&sblock, ino));
137094715d8eSBen Gras 	e2fs_isave(ip, dp);
137194715d8eSBen Gras 	/* e2fs_i_bswap() doesn't swap e2di_blocks addrs */
137294715d8eSBen Gras 	if ((ip->e2di_mode & EXT2_IFMT) != EXT2_IFLNK) {
137384d9c625SLionel Sambuc 		for (i = 0; i < EXT2FS_NDADDR + EXT2FS_NIADDR; i++)
137494715d8eSBen Gras 			dp->e2di_blocks[i] = h2fs32(ip->e2di_blocks[i]);
137594715d8eSBen Gras 	}
137694715d8eSBen Gras 	/* h2fs32() just for consistency */
137794715d8eSBen Gras 	dp->e2di_gen = h2fs32(arc4random());
137894715d8eSBen Gras 
137994715d8eSBen Gras 	wtfs(d, sblock.e2fs_bsize, bp);
138094715d8eSBen Gras 	free(bp);
138194715d8eSBen Gras }
138294715d8eSBen Gras 
138394715d8eSBen Gras /*
138494715d8eSBen Gras  * Read a block from the file system
138594715d8eSBen Gras  */
138694715d8eSBen Gras void
rdfs(daddr_t bno,int size,void * bf)138794715d8eSBen Gras rdfs(daddr_t bno, int size, void *bf)
138894715d8eSBen Gras {
138994715d8eSBen Gras 	int n;
139094715d8eSBen Gras 	off_t offset;
139194715d8eSBen Gras 
139294715d8eSBen Gras 	offset = bno;
139394715d8eSBen Gras 	n = pread(fsi, bf, size, offset * sectorsize);
139494715d8eSBen Gras 	if (n != size)
139594715d8eSBen Gras 		err(EXIT_FAILURE, "%s: read error for sector %" PRId64,
139694715d8eSBen Gras 		    __func__, (int64_t)bno);
139794715d8eSBen Gras }
139894715d8eSBen Gras 
139994715d8eSBen Gras /*
140094715d8eSBen Gras  * Write a block to the file system
140194715d8eSBen Gras  */
140294715d8eSBen Gras void
wtfs(daddr_t bno,int size,void * bf)140394715d8eSBen Gras wtfs(daddr_t bno, int size, void *bf)
140494715d8eSBen Gras {
140594715d8eSBen Gras 	int n;
140694715d8eSBen Gras 	off_t offset;
140794715d8eSBen Gras 
140894715d8eSBen Gras 	if (Nflag)
140994715d8eSBen Gras 		return;
141094715d8eSBen Gras 	offset = bno;
141194715d8eSBen Gras 	n = pwrite(fso, bf, size, offset * sectorsize);
141294715d8eSBen Gras 	if (n != size)
141394715d8eSBen Gras 		err(EXIT_FAILURE, "%s: write error for sector %" PRId64,
141494715d8eSBen Gras 		    __func__, (int64_t)bno);
141594715d8eSBen Gras }
141694715d8eSBen Gras 
141794715d8eSBen Gras int
ilog2(uint val)141894715d8eSBen Gras ilog2(uint val)
141994715d8eSBen Gras {
142094715d8eSBen Gras 
142194715d8eSBen Gras 	if (val == 0 || !powerof2(val))
1422*0a6a1f1dSLionel Sambuc 		errx(EXIT_FAILURE, "%s: %u is not a power of 2",
142394715d8eSBen Gras 		    __func__, val);
142494715d8eSBen Gras 
142594715d8eSBen Gras 	return ffs(val) - 1;
142694715d8eSBen Gras }
142794715d8eSBen Gras 
142894715d8eSBen Gras /*
142994715d8eSBen Gras  * int skpc(int mask, size_t size, uint8_t *cp)
143094715d8eSBen Gras  *
143194715d8eSBen Gras  *	Locate an unsigned character of value mask inside cp[].
143294715d8eSBen Gras  * 	(from src/sys/lib/libkern/skpc.c)
143394715d8eSBen Gras  */
143494715d8eSBen Gras int
skpc(int mask,size_t size,uint8_t * cp)143594715d8eSBen Gras skpc(int mask, size_t size, uint8_t *cp)
143694715d8eSBen Gras {
143794715d8eSBen Gras 	uint8_t *end;
143894715d8eSBen Gras 
143994715d8eSBen Gras 	end = &cp[size];
144094715d8eSBen Gras 	while (cp < end && *cp == (uint8_t)mask)
144194715d8eSBen Gras 		cp++;
144294715d8eSBen Gras 
144394715d8eSBen Gras 	return end - cp;
144494715d8eSBen Gras }
1445