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