Module Name:    src
Committed By:   jdolecek
Date:           Sat Aug 20 19:47:44 UTC 2016

Modified Files:
        src/sys/ufs/ext2fs: ext2fs.h ext2fs_alloc.c ext2fs_bswap.c
            ext2fs_extern.h ext2fs_vfsops.c

Log Message:
add support for GDT_CSUM AKA uninit_bg feature


To generate a diff of this commit:
cvs rdiff -u -r1.47 -r1.48 src/sys/ufs/ext2fs/ext2fs.h
cvs rdiff -u -r1.48 -r1.49 src/sys/ufs/ext2fs/ext2fs_alloc.c
cvs rdiff -u -r1.23 -r1.24 src/sys/ufs/ext2fs/ext2fs_bswap.c
cvs rdiff -u -r1.54 -r1.55 src/sys/ufs/ext2fs/ext2fs_extern.h
cvs rdiff -u -r1.199 -r1.200 src/sys/ufs/ext2fs/ext2fs_vfsops.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/ufs/ext2fs/ext2fs.h
diff -u src/sys/ufs/ext2fs/ext2fs.h:1.47 src/sys/ufs/ext2fs/ext2fs.h:1.48
--- src/sys/ufs/ext2fs/ext2fs.h:1.47	Mon Aug 15 18:46:11 2016
+++ src/sys/ufs/ext2fs/ext2fs.h	Sat Aug 20 19:47:44 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs.h,v 1.47 2016/08/15 18:46:11 jdolecek Exp $	*/
+/*	$NetBSD: ext2fs.h,v 1.48 2016/08/20 19:47:44 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1993
@@ -249,10 +249,10 @@ struct m_ext2fs {
 	int64_t e2fs_qbmask;	/* ~fs_bmask - for use with quad size */
 	int32_t	e2fs_fsbtodb;	/* fsbtodb and dbtofsb shift constant */
 	int32_t	e2fs_ncg;	/* number of cylinder groups */
-	int32_t	e2fs_ngdb;	/* number of group descriptor block */
+	int32_t	e2fs_ngdb;	/* number of group descriptor blocks */
 	int32_t	e2fs_ipb;	/* number of inodes per block */
-	int32_t	e2fs_itpg;	/* number of inode table per group */
-	struct	ext2_gd *e2fs_gd; /* group descripors */
+	int32_t	e2fs_itpg;	/* number of inode table blocks per group */
+	struct	ext2_gd *e2fs_gd; /* group descriptors (data not byteswapped) */
 };
 
 
@@ -366,7 +366,8 @@ struct m_ext2fs {
 					 | EXT2F_ROCOMPAT_LARGEFILE \
 					 | EXT2F_ROCOMPAT_HUGE_FILE \
 					 | EXT2F_ROCOMPAT_EXTRA_ISIZE \
-					 | EXT2F_ROCOMPAT_DIR_NLINK)
+					 | EXT2F_ROCOMPAT_DIR_NLINK \
+					 | EXT2F_ROCOMPAT_GDT_CSUM)
 #define EXT2F_INCOMPAT_SUPP		(EXT2F_INCOMPAT_FTYPE \
 					 | EXT2F_INCOMPAT_EXTENTS \
 					 | EXT2F_INCOMPAT_FLEX_BG)
@@ -415,15 +416,35 @@ struct m_ext2fs {
 struct ext2_gd {
 	uint32_t ext2bgd_b_bitmap;	/* blocks bitmap block */
 	uint32_t ext2bgd_i_bitmap;	/* inodes bitmap block */
-	uint32_t ext2bgd_i_tables;	/* inodes table block  */
+	uint32_t ext2bgd_i_tables;	/* first inodes table block */
 	uint16_t ext2bgd_nbfree;	/* number of free blocks */
 	uint16_t ext2bgd_nifree;	/* number of free inodes */
 	uint16_t ext2bgd_ndirs;		/* number of directories */
-	uint16_t reserved;
-	uint32_t reserved2[3];
+
+	/*
+	 * Following only valid when either GDT_CSUM (AKA uninit_bg) 
+	 * or METADATA_CKSUM feature is on
+	 */
+	uint16_t ext2bgd_flags;		/* ext4 bg flags (INODE_UNINIT, ...)*/
+	uint32_t ext2bgd_exclude_bitmap_lo;	/* snapshot exclude bitmap */
+	uint16_t ext2bgd_block_bitmap_csum_lo;	/* Low block bitmap checksum */
+	uint16_t ext2bgd_inode_bitmap_csum_lo;	/* Low inode bitmap checksum */
+	uint16_t ext2bgd_itable_unused_lo;	/* Low unused inode offset */
+	uint16_t ext2bgd_checksum;		/* Group desc checksum */
+
+	/*
+	 * XXX disk32 Further fields only exist if 64BIT feature is on
+	 * and superblock desc_size > 32, not supported for now.
+	 */
 };
 
+#define E2FS_BG_INODE_UNINIT	0x0001	/* Inode bitmap not used/initialized */
+#define E2FS_BG_BLOCK_UNINIT	0x0002	/* Block bitmap not used/initialized */
+#define E2FS_BG_INODE_ZEROED	0x0004	/* On-disk inode table initialized */
 
+#define E2FS_HAS_GD_CSUM(fs) \
+	EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM|EXT2F_ROCOMPAT_METADATA_CKSUM) != 0
+	
 /*
  * If the EXT2F_ROCOMPAT_SPARSESUPER flag is set, the cylinder group has a
  * copy of the super and cylinder group descriptors blocks only if it's
@@ -457,13 +478,10 @@ cg_has_sb(int i)
 #	define fs2h16(x) (x)
 #	define fs2h32(x) (x)
 #	define fs2h64(x) (x)
-#	define e2fs_sbload(old, new) memcpy((new), (old), SBSIZE);
-#	define e2fs_cgload(old, new, size) memcpy((new), (old), (size));
-#	define e2fs_sbsave(old, new) memcpy((new), (old), SBSIZE);
-#	define e2fs_cgsave(old, new, size) memcpy((new), (old), (size));
+#	define e2fs_sbload(old, new) memcpy((new), (old), SBSIZE)
+#	define e2fs_sbsave(old, new) memcpy((new), (old), SBSIZE)
 #else
 void e2fs_sb_bswap(struct ext2fs *, struct ext2fs *);
-void e2fs_cg_bswap(struct ext2_gd *, struct ext2_gd *, int);
 #	define h2fs16(x) bswap16(x)
 #	define h2fs32(x) bswap32(x)
 #	define h2fs64(x) bswap64(x)
@@ -471,11 +489,13 @@ void e2fs_cg_bswap(struct ext2_gd *, str
 #	define fs2h32(x) bswap32(x)
 #	define fs2h64(x) bswap64(x)
 #	define e2fs_sbload(old, new) e2fs_sb_bswap((old), (new))
-#	define e2fs_cgload(old, new, size) e2fs_cg_bswap((old), (new), (size));
 #	define e2fs_sbsave(old, new) e2fs_sb_bswap((old), (new))
-#	define e2fs_cgsave(old, new, size) e2fs_cg_bswap((old), (new), (size));
 #endif
 
+/* Group descriptors are not byte swapped */
+#define e2fs_cgload(old, new, size) memcpy((new), (old), (size))
+#define e2fs_cgsave(old, new, size) memcpy((new), (old), (size))
+
 /*
  * Turn file system block numbers into disk block addresses.
  * This maps file system blocks to device size blocks.
@@ -491,7 +511,7 @@ void e2fs_cg_bswap(struct ext2_gd *, str
  */
 #define	ino_to_cg(fs, x)	(((x) - 1) / (fs)->e2fs.e2fs_ipg)
 #define	ino_to_fsba(fs, x)						\
-	((fs)->e2fs_gd[ino_to_cg((fs), (x))].ext2bgd_i_tables +		\
+	(fs2h32((fs)->e2fs_gd[ino_to_cg((fs), (x))].ext2bgd_i_tables) +	\
 	(((x) - 1) % (fs)->e2fs.e2fs_ipg) / (fs)->e2fs_ipb)
 #define	ino_to_fsbo(fs, x)	(((x) - 1) % (fs)->e2fs_ipb)
 

Index: src/sys/ufs/ext2fs/ext2fs_alloc.c
diff -u src/sys/ufs/ext2fs/ext2fs_alloc.c:1.48 src/sys/ufs/ext2fs/ext2fs_alloc.c:1.49
--- src/sys/ufs/ext2fs/ext2fs_alloc.c:1.48	Sat Aug 13 07:40:10 2016
+++ src/sys/ufs/ext2fs/ext2fs_alloc.c	Sat Aug 20 19:47:44 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs_alloc.c,v 1.48 2016/08/13 07:40:10 christos Exp $	*/
+/*	$NetBSD: ext2fs_alloc.c,v 1.49 2016/08/20 19:47:44 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1982, 1986, 1989, 1993
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ext2fs_alloc.c,v 1.48 2016/08/13 07:40:10 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_alloc.c,v 1.49 2016/08/20 19:47:44 jdolecek Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -72,6 +72,8 @@ __KERNEL_RCSID(0, "$NetBSD: ext2fs_alloc
 #include <sys/syslog.h>
 #include <sys/kauth.h>
 
+#include <lib/libkern/crc16.h>
+
 #include <ufs/ufs/inode.h>
 #include <ufs/ufs/ufs_extern.h>
 #include <ufs/ufs/ufsmount.h>
@@ -88,6 +90,9 @@ static u_long	ext2fs_hashalloc(struct in
 		    daddr_t (*)(struct inode *, int, daddr_t, int));
 static daddr_t	ext2fs_nodealloccg(struct inode *, int, daddr_t, int);
 static daddr_t	ext2fs_mapsearch(struct m_ext2fs *, char *, daddr_t);
+static __inline void	ext2fs_cg_update(struct m_ext2fs *, int, struct ext2_gd *, int, int, int, daddr_t);
+static uint16_t 	ext2fs_cg_get_csum(struct m_ext2fs *, int, struct ext2_gd *);
+static void		ext2fs_init_bb(struct m_ext2fs *, int, struct ext2_gd *, char *);
 
 /*
  * Allocate a block in the file system.
@@ -191,7 +196,11 @@ ext2fs_valloc(struct vnode *pvp, int mod
 		return error;
 	}
 	ip = VTOI(*vpp);
-	if (ip->i_e2fs_mode && ip->i_e2fs_nlink != 0) {
+
+	KASSERT(!E2FS_HAS_GD_CSUM(fs) || (fs->e2fs_gd[ino_to_cg(fs, ino)].ext2bgd_flags & h2fs16(E2FS_BG_INODE_ZEROED)) != 0);
+
+	/* check for already used inode; makes sense only for ZEROED itable */
+	if (__predict_false(ip->i_e2fs_mode && ip->i_e2fs_nlink != 0)) {
 		printf("mode = 0%o, nlinks %d, inum = %llu, fs = %s\n",
 		    ip->i_e2fs_mode, ip->i_e2fs_nlink,
 		    (unsigned long long)ip->i_number, fs->e2fs_fsmnt);
@@ -229,10 +238,10 @@ ext2fs_dirpref(struct m_ext2fs *fs)
 	maxspace = 0;
 	mincg = -1;
 	for (cg = 0; cg < fs->e2fs_ncg; cg++)
-		if ( fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree) {
-			if (mincg == -1 || fs->e2fs_gd[cg].ext2bgd_nbfree > maxspace) {
+		if (fs2h16(fs->e2fs_gd[cg].ext2bgd_nifree) >= avgifree) {
+			if (mincg == -1 || fs2h16(fs->e2fs_gd[cg].ext2bgd_nbfree) > maxspace) {
 				mincg = cg;
-				maxspace = fs->e2fs_gd[cg].ext2bgd_nbfree;
+				maxspace = fs2h16(fs->e2fs_gd[cg].ext2bgd_nbfree);
 			}
 		}
 	return mincg;
@@ -356,7 +365,7 @@ ext2fs_alloccg(struct inode *ip, int cg,
 	if (fs->e2fs_gd[cg].ext2bgd_nbfree == 0)
 		return 0;
 	error = bread(ip->i_devvp, EXT2_FSBTODB(fs,
-		fs->e2fs_gd[cg].ext2bgd_b_bitmap),
+		fs2h32(fs->e2fs_gd[cg].ext2bgd_b_bitmap)),
 		(int)fs->e2fs_bsize, B_MODIFY, &bp);
 	if (error) {
 		return 0;
@@ -365,6 +374,14 @@ ext2fs_alloccg(struct inode *ip, int cg,
 
 	if (dtog(fs, bpref) != cg)
 		bpref = 0;
+
+	/* initialize block bitmap now if uninit */
+	if (__predict_false(E2FS_HAS_GD_CSUM(fs) &&
+	    (fs->e2fs_gd[cg].ext2bgd_flags & h2fs16(E2FS_BG_BLOCK_UNINIT)))) {
+		ext2fs_init_bb(fs, cg, &fs->e2fs_gd[cg], bbp);
+		fs->e2fs_gd[cg].ext2bgd_flags &= h2fs16(~E2FS_BG_BLOCK_UNINIT);
+	}
+
 	if (bpref != 0) {
 		bpref = dtogd(fs, bpref);
 		/*
@@ -412,7 +429,7 @@ gotit:
 #endif
 	setbit(bbp, (daddr_t)bno);
 	fs->e2fs.e2fs_fbcount--;
-	fs->e2fs_gd[cg].ext2bgd_nbfree--;
+	ext2fs_cg_update(fs, cg, &fs->e2fs_gd[cg], -1, 0, 0, 0);
 	fs->e2fs_fmod = 1;
 	bdwrite(bp);
 	return cg * fs->e2fs.e2fs_fpg + fs->e2fs.e2fs_first_dblock + bno;
@@ -442,12 +459,23 @@ ext2fs_nodealloccg(struct inode *ip, int
 	if (fs->e2fs_gd[cg].ext2bgd_nifree == 0)
 		return 0;
 	error = bread(ip->i_devvp, EXT2_FSBTODB(fs,
-		fs->e2fs_gd[cg].ext2bgd_i_bitmap),
+		fs2h32(fs->e2fs_gd[cg].ext2bgd_i_bitmap)),
 		(int)fs->e2fs_bsize, B_MODIFY, &bp);
 	if (error) {
 		return 0;
 	}
 	ibp = (char *)bp->b_data;
+
+	KASSERT(!E2FS_HAS_GD_CSUM(fs) || (fs->e2fs_gd[cg].ext2bgd_flags & h2fs16(E2FS_BG_INODE_ZEROED)) != 0);
+
+	/* initialize inode bitmap now if uninit */
+	if (__predict_false(E2FS_HAS_GD_CSUM(fs) &&
+	    (fs->e2fs_gd[cg].ext2bgd_flags & h2fs16(E2FS_BG_INODE_UNINIT)))) {
+		KASSERT(fs2h16(fs->e2fs_gd[cg].ext2bgd_nifree) == fs->e2fs.e2fs_ipg);
+		memset(ibp, 0, fs->e2fs_bsize);
+		fs->e2fs_gd[cg].ext2bgd_flags &= h2fs16(~E2FS_BG_INODE_UNINIT);
+	}
+
 	if (ipref) {
 		ipref %= fs->e2fs.e2fs_ipg;
 		if (isclr(ibp, ipref))
@@ -471,17 +499,15 @@ ext2fs_nodealloccg(struct inode *ip, int
 	map = ibp[i] ^ 0xff;
 	if (map == 0) {
 		printf("fs = %s\n", fs->e2fs_fsmnt);
-		panic("ext2fs_nodealloccg: block not in map");
+		panic("ext2fs_nodealloccg: inode not in map");
 	}
 	ipref = i * NBBY + ffs(map) - 1;
 gotit:
 	setbit(ibp, ipref);
 	fs->e2fs.e2fs_ficount--;
-	fs->e2fs_gd[cg].ext2bgd_nifree--;
+	ext2fs_cg_update(fs, cg, &fs->e2fs_gd[cg],
+		0, -1, ((mode & IFMT) == IFDIR) ? 1 : 0, ipref);
 	fs->e2fs_fmod = 1;
-	if ((mode & IFMT) == IFDIR) {
-		fs->e2fs_gd[cg].ext2bgd_ndirs++;
-	}
 	bdwrite(bp);
 	return cg * fs->e2fs.e2fs_ipg + ipref + 1;
 }
@@ -502,6 +528,9 @@ ext2fs_blkfree(struct inode *ip, daddr_t
 
 	fs = ip->i_e2fs;
 	cg = dtog(fs, bno);
+
+	KASSERT(!E2FS_HAS_GD_CSUM(fs) || (fs->e2fs_gd[cg].ext2bgd_flags & h2fs16(E2FS_BG_BLOCK_UNINIT)) == 0);
+
 	if ((u_int)bno >= fs->e2fs.e2fs_bcount) {
 		printf("bad block %lld, ino %llu\n", (long long)bno,
 		    (unsigned long long)ip->i_number);
@@ -509,7 +538,7 @@ ext2fs_blkfree(struct inode *ip, daddr_t
 		return;
 	}
 	error = bread(ip->i_devvp,
-		EXT2_FSBTODB(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap),
+		EXT2_FSBTODB(fs, fs2h32(fs->e2fs_gd[cg].ext2bgd_b_bitmap)),
 		(int)fs->e2fs_bsize, B_MODIFY, &bp);
 	if (error) {
 		return;
@@ -524,8 +553,7 @@ ext2fs_blkfree(struct inode *ip, daddr_t
 	}
 	clrbit(bbp, bno);
 	fs->e2fs.e2fs_fbcount++;
-	fs->e2fs_gd[cg].ext2bgd_nbfree++;
-
+	ext2fs_cg_update(fs, cg, &fs->e2fs_gd[cg], 1, 0, 0, 0);
 	fs->e2fs_fmod = 1;
 	bdwrite(bp);
 }
@@ -546,13 +574,18 @@ ext2fs_vfree(struct vnode *pvp, ino_t in
 
 	pip = VTOI(pvp);
 	fs = pip->i_e2fs;
+
 	if ((u_int)ino > fs->e2fs.e2fs_icount || (u_int)ino < EXT2_FIRSTINO)
 		panic("ifree: range: dev = 0x%llx, ino = %llu, fs = %s",
 		    (unsigned long long)pip->i_dev, (unsigned long long)ino,
 		    fs->e2fs_fsmnt);
+
 	cg = ino_to_cg(fs, ino);
+
+	KASSERT(!E2FS_HAS_GD_CSUM(fs) || (fs->e2fs_gd[cg].ext2bgd_flags & h2fs16(E2FS_BG_INODE_UNINIT)) == 0);
+
 	error = bread(pip->i_devvp,
-		EXT2_FSBTODB(fs, fs->e2fs_gd[cg].ext2bgd_i_bitmap),
+		EXT2_FSBTODB(fs, fs2h32(fs->e2fs_gd[cg].ext2bgd_i_bitmap)),
 		(int)fs->e2fs_bsize, B_MODIFY, &bp);
 	if (error) {
 		return 0;
@@ -568,10 +601,8 @@ ext2fs_vfree(struct vnode *pvp, ino_t in
 	}
 	clrbit(ibp, ino);
 	fs->e2fs.e2fs_ficount++;
-	fs->e2fs_gd[cg].ext2bgd_nifree++;
-	if ((mode & IFMT) == IFDIR) {
-		fs->e2fs_gd[cg].ext2bgd_ndirs--;
-	}
+	ext2fs_cg_update(fs, cg, &fs->e2fs_gd[cg],
+		0, 1, ((mode & IFMT) == IFDIR) ? -1 : 0, 0);
 	fs->e2fs_fmod = 1;
 	bdwrite(bp);
 	return 0;
@@ -631,3 +662,150 @@ ext2fs_fserr(struct m_ext2fs *fs, u_int 
 
 	log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->e2fs_fsmnt, cp);
 }
+
+static __inline void
+ext2fs_cg_update(struct m_ext2fs *fs, int cg, struct ext2_gd *gd, int nbfree, int nifree, int ndirs, daddr_t ioff)
+{
+	/* XXX disk32 */
+	if (nifree) {
+		gd->ext2bgd_nifree = h2fs16(fs2h16(gd->ext2bgd_nifree) + nifree);
+		/*
+		 * If we allocated inode on bigger offset than what was
+		 * ever used before, bump the itable_unused count. This
+		 * member only ever grows, and is used only for initialization
+		 * !INODE_ZEROED groups with used inodes. Of course, by the
+		 * time we get here the itables are already zeroed, but
+		 * e2fstools fsck.ext4 still checks this.
+		 */
+		if (E2FS_HAS_GD_CSUM(fs) && nifree < 0 && (ioff+1) >= (fs->e2fs.e2fs_ipg - fs2h16(gd->ext2bgd_itable_unused_lo))) {
+			gd->ext2bgd_itable_unused_lo = h2fs16(fs->e2fs.e2fs_ipg - (ioff + 1));
+		}
+
+		KASSERT(!E2FS_HAS_GD_CSUM(fs) || gd->ext2bgd_itable_unused_lo <= gd->ext2bgd_nifree);
+	}
+
+
+	if (nbfree)
+		gd->ext2bgd_nbfree = h2fs16(fs2h16(gd->ext2bgd_nbfree) + nbfree);
+
+	if (ndirs)
+		gd->ext2bgd_ndirs = h2fs16(fs2h16(gd->ext2bgd_ndirs) + ndirs);
+
+	if (E2FS_HAS_GD_CSUM(fs))
+		gd->ext2bgd_checksum = ext2fs_cg_get_csum(fs, cg, gd);
+}
+
+/*
+ * Compute group description csum. Structure data must be LE (not host).
+ * Returned as LE (disk encoding).
+ */
+static uint16_t
+ext2fs_cg_get_csum(struct m_ext2fs *fs, int cg, struct ext2_gd *gd)
+{
+	uint16_t crc;
+	uint32_t cg_bswapped = h2fs32((uint32_t)cg);
+	size_t off;
+
+	if (!EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_GDT_CSUM))
+		return 0;
+
+	off = offsetof(struct ext2_gd, ext2bgd_checksum);
+
+	crc = crc16(~0, (uint8_t *)fs->e2fs.e2fs_uuid, sizeof(fs->e2fs.e2fs_uuid));
+	crc = crc16(crc, (uint8_t *)&cg_bswapped, sizeof(cg_bswapped));
+	crc = crc16(crc, (uint8_t *)gd, off);
+	/* XXX ondisk32 */
+
+	return h2fs16(crc);
+}
+
+static void
+ext2fs_init_bb(struct m_ext2fs *fs, int cg, struct ext2_gd *gd, char *bbp)
+{
+	int i;
+
+	memset(bbp, 0, fs->e2fs_bsize);
+
+	/*
+	 * No block was ever allocated on this cg before, so the only used
+	 * blocks are metadata blocks on start of the group. We could optimize
+	 * this to set by bytes, but since this is done once per the group
+	 * in lifetime of filesystem, it really is not worth it.
+	 */
+	for(i=0; i < fs->e2fs.e2fs_bpg - fs2h16(gd->ext2bgd_nbfree); i++)
+		setbit(bbp, i);
+}
+
+/*
+ * Verify csum and initialize itable if not done already
+ */
+int
+ext2fs_cg_verify_and_initialize(struct vnode *devvp, struct m_ext2fs *fs, int ronly)
+{
+	/* XXX disk32 */
+	struct ext2_gd *gd;
+	ino_t ioff;
+	size_t boff;
+	struct buf *bp;
+	int cg, i, error;
+
+	if (!E2FS_HAS_GD_CSUM(fs))
+		return 0;
+
+	for(cg=0; cg < fs->e2fs_ncg; cg++) {
+		gd = &fs->e2fs_gd[cg];
+
+		/* Verify checksum */
+		if (gd->ext2bgd_checksum != ext2fs_cg_get_csum(fs, cg, gd)) {
+			printf("ext2fs_cg_verify_and_initialize: group %d invalid csum\n", cg);
+			return EINVAL;
+		}
+
+		/* if mounting read-write, zero itable if not already done */
+		if (ronly || (gd->ext2bgd_flags & h2fs16(E2FS_BG_INODE_ZEROED)) != 0)
+			continue;
+
+		/*
+		 * We are skipping already used inodes, zero rest of itable
+		 * blocks. First block to zero could be only partial wipe, all
+		 * others are wiped completely. This might take a while,
+		 * there could be many inode table blocks. We use
+		 * delayed writes, so this shouldn't block for very
+		 * long.
+		 */
+		ioff = fs->e2fs.e2fs_ipg - fs2h16(gd->ext2bgd_itable_unused_lo);
+		boff = (ioff % fs->e2fs_ipb) * EXT2_DINODE_SIZE(fs);
+
+		for(i = ioff / fs->e2fs_ipb; i < fs->e2fs_itpg; i++) {
+			if (boff) {
+				/* partial wipe, must read old data */
+				error = bread(devvp,
+					EXT2_FSBTODB(fs, fs2h32(gd->ext2bgd_i_tables) + i),
+					(int)fs->e2fs_bsize, B_MODIFY, &bp);
+				if (error) {
+					printf("ext2fs_cg_verify_and_initialize: can't read itable block");
+					return error;
+				}
+				memset((char *)bp->b_data + boff, 0, fs->e2fs_bsize - boff);
+				boff = 0;
+			} else {
+				/*
+				 * Complete wipe, don't need to read data. This
+				 * assumes nothing else is changing the data.
+				 */
+				bp = getblk(devvp,
+					EXT2_FSBTODB(fs, fs2h32(gd->ext2bgd_i_tables) + i),
+					(int)fs->e2fs_bsize, 0, 0);
+				clrbuf(bp);
+			}
+	
+			bdwrite(bp);
+		}
+
+		gd->ext2bgd_flags |= h2fs16(E2FS_BG_INODE_ZEROED);
+		gd->ext2bgd_checksum = ext2fs_cg_get_csum(fs, cg, gd);
+		fs->e2fs_fmod = 1;
+	}
+
+	return 0;
+}

Index: src/sys/ufs/ext2fs/ext2fs_bswap.c
diff -u src/sys/ufs/ext2fs/ext2fs_bswap.c:1.23 src/sys/ufs/ext2fs/ext2fs_bswap.c:1.24
--- src/sys/ufs/ext2fs/ext2fs_bswap.c:1.23	Mon Aug 15 18:29:34 2016
+++ src/sys/ufs/ext2fs/ext2fs_bswap.c	Sat Aug 20 19:47:44 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs_bswap.c,v 1.23 2016/08/15 18:29:34 jdolecek Exp $	*/
+/*	$NetBSD: ext2fs_bswap.c,v 1.24 2016/08/20 19:47:44 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1997 Manuel Bouyer.
@@ -26,7 +26,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ext2fs_bswap.c,v 1.23 2016/08/15 18:29:34 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_bswap.c,v 1.24 2016/08/20 19:47:44 jdolecek Exp $");
 
 #include <sys/types.h>
 #include <ufs/ext2fs/ext2fs.h>
@@ -83,21 +83,6 @@ e2fs_sb_bswap(struct ext2fs *old, struct
 }
 
 void
-e2fs_cg_bswap(struct ext2_gd *old, struct ext2_gd *new, int size)
-{
-	int i;
-
-	for (i = 0; i < (size / (int)sizeof(struct  ext2_gd)); i++) {
-		new[i].ext2bgd_b_bitmap	= bswap32(old[i].ext2bgd_b_bitmap);
-		new[i].ext2bgd_i_bitmap	= bswap32(old[i].ext2bgd_i_bitmap);
-		new[i].ext2bgd_i_tables	= bswap32(old[i].ext2bgd_i_tables);
-		new[i].ext2bgd_nbfree	= bswap16(old[i].ext2bgd_nbfree);
-		new[i].ext2bgd_nifree	= bswap16(old[i].ext2bgd_nifree);
-		new[i].ext2bgd_ndirs	= bswap16(old[i].ext2bgd_ndirs);
-	}
-}
-
-void
 e2fs_i_bswap(struct ext2fs_dinode *old, struct ext2fs_dinode *new, size_t isize)
 {
 	/* preserve non-swapped and unused fields */ 

Index: src/sys/ufs/ext2fs/ext2fs_extern.h
diff -u src/sys/ufs/ext2fs/ext2fs_extern.h:1.54 src/sys/ufs/ext2fs/ext2fs_extern.h:1.55
--- src/sys/ufs/ext2fs/ext2fs_extern.h:1.54	Fri Aug 19 00:05:43 2016
+++ src/sys/ufs/ext2fs/ext2fs_extern.h	Sat Aug 20 19:47:44 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs_extern.h,v 1.54 2016/08/19 00:05:43 jdolecek Exp $	*/
+/*	$NetBSD: ext2fs_extern.h,v 1.55 2016/08/20 19:47:44 jdolecek Exp $	*/
 
 /*-
  * Copyright (c) 1991, 1993, 1994
@@ -100,6 +100,7 @@ int ext2fs_valloc(struct vnode *, int, k
 daddr_t ext2fs_blkpref(struct inode *, daddr_t, int, int32_t *);
 void ext2fs_blkfree(struct inode *, daddr_t);
 int ext2fs_vfree(struct vnode *, ino_t, int);
+int ext2fs_cg_verify_and_initialize(struct vnode *, struct m_ext2fs *, int);
 
 /* ext2fs_balloc.c */
 int ext2fs_balloc(struct inode *, daddr_t, int, kauth_cred_t,

Index: src/sys/ufs/ext2fs/ext2fs_vfsops.c
diff -u src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.199 src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.200
--- src/sys/ufs/ext2fs/ext2fs_vfsops.c:1.199	Sun Aug 14 11:44:54 2016
+++ src/sys/ufs/ext2fs/ext2fs_vfsops.c	Sat Aug 20 19:47:44 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: ext2fs_vfsops.c,v 1.199 2016/08/14 11:44:54 jdolecek Exp $	*/
+/*	$NetBSD: ext2fs_vfsops.c,v 1.200 2016/08/20 19:47:44 jdolecek Exp $	*/
 
 /*
  * Copyright (c) 1989, 1991, 1993, 1994
@@ -60,7 +60,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ext2fs_vfsops.c,v 1.199 2016/08/14 11:44:54 jdolecek Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ext2fs_vfsops.c,v 1.200 2016/08/20 19:47:44 jdolecek Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_compat_netbsd.h"
@@ -723,6 +723,12 @@ ext2fs_mountfs(struct vnode *devvp, stru
 		bp = NULL;
 	}
 
+	error = ext2fs_cg_verify_and_initialize(devvp, m_fs, ronly);
+	if (error) {
+		kmem_free(m_fs->e2fs_gd, m_fs->e2fs_ngdb * m_fs->e2fs_bsize);
+		goto out;
+	}
+
 	mp->mnt_data = ump;
 	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
 	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_EXT2FS);
@@ -834,7 +840,15 @@ ext2fs_statvfs(struct mount *mp, struct 
 	    fs->e2fs_itpg;
 	overhead = fs->e2fs.e2fs_first_dblock +
 	    fs->e2fs_ncg * overhead_per_group;
-	if (EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_SPARSESUPER)) {
+	if (EXT2F_HAS_COMPAT_FEATURE(fs, EXT2F_COMPAT_SPARSESUPER2)) {
+		/*
+		 * Superblock and group descriptions is in group zero,
+		 * then optionally 0, 1 or 2 extra copies.
+		 */
+		ngroups = 1
+			+ (fs->e2fs.e4fs_backup_bgs[0] ? 1 : 0)
+			+ (fs->e2fs.e4fs_backup_bgs[1] ? 1 : 0);
+	} else if (EXT2F_HAS_ROCOMPAT_FEATURE(fs, EXT2F_ROCOMPAT_SPARSESUPER)) {
 		for (i = 0, ngroups = 0; i < fs->e2fs_ncg; i++) {
 			if (cg_has_sb(i))
 				ngroups++;

Reply via email to