mke2fs: support creating bigalloc file systems

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index cda6b6e..efe03be 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -94,6 +94,7 @@
 	blk_t		numblocks;
 	int		rsv_gdt;
 	int		csum_flag;
+	int		bigalloc_flag;
 	int		io_flags;
 	char		*buf = 0;
 	char		c;
@@ -134,13 +135,26 @@
 
 #define set_field(field, default) (super->field = param->field ? \
 				   param->field : (default))
+#define assign_field(field)	(super->field = param->field)
 
 	super->s_magic = EXT2_SUPER_MAGIC;
 	super->s_state = EXT2_VALID_FS;
 
-	set_field(s_log_block_size, 0);	/* default blocksize: 1024 bytes */
-	set_field(s_log_cluster_size, 0);
-	set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
+	bigalloc_flag = EXT2_HAS_RO_COMPAT_FEATURE(param,
+				   EXT4_FEATURE_RO_COMPAT_BIGALLOC);
+
+	assign_field(s_log_block_size);
+
+	if (bigalloc_flag) {
+		set_field(s_log_cluster_size, super->s_log_block_size+4);
+		if (super->s_log_block_size > super->s_log_cluster_size) {
+			retval = EXT2_ET_INVALID_ARGUMENT;
+			goto cleanup;
+		}
+	} else
+		super->s_log_cluster_size = super->s_log_block_size;
+
+	set_field(s_first_data_block, super->s_log_cluster_size ? 0 : 1);
 	set_field(s_max_mnt_count, 0);
 	set_field(s_errors, EXT2_ERRORS_DEFAULT);
 	set_field(s_feature_compat, 0);
@@ -185,13 +199,35 @@
 	fs->cluster_ratio_bits = super->s_log_cluster_size -
 		super->s_log_block_size;
 
-	/* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */
-	set_field(s_blocks_per_group, fs->blocksize * 8);
-	if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
-		super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
-	super->s_clusters_per_group = super->s_blocks_per_group;
+	if (bigalloc_flag) {
+		if (param->s_blocks_per_group &&
+		    param->s_clusters_per_group &&
+		    ((param->s_clusters_per_group * EXT2FS_CLUSTER_RATIO(fs)) !=
+		     param->s_blocks_per_group)) {
+			retval = EXT2_ET_INVALID_ARGUMENT;
+			goto cleanup;
+		}
+		if (param->s_clusters_per_group)
+			assign_field(s_clusters_per_group);
+		else if (param->s_blocks_per_group)
+			super->s_clusters_per_group = 
+				param->s_blocks_per_group /
+				EXT2FS_CLUSTER_RATIO(fs);
+		else
+			super->s_clusters_per_group = fs->blocksize * 8;
+		if (super->s_clusters_per_group > EXT2_MAX_CLUSTERS_PER_GROUP(super))
+			super->s_blocks_per_group = EXT2_MAX_CLUSTERS_PER_GROUP(super);
+		super->s_blocks_per_group = EXT2FS_C2B(fs,
+				       super->s_clusters_per_group);
+	} else {
+		set_field(s_blocks_per_group, fs->blocksize * 8);
+		if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
+			super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
+		super->s_clusters_per_group = super->s_blocks_per_group;
+	}
 
-	ext2fs_blocks_count_set(super, ext2fs_blocks_count(param));
+	ext2fs_blocks_count_set(super, ext2fs_blocks_count(param) &
+				~((blk64_t) EXT2FS_CLUSTER_MASK(fs)));
 	ext2fs_r_blocks_count_set(super, ext2fs_r_blocks_count(param));
 	if (ext2fs_r_blocks_count(super) >= ext2fs_blocks_count(param)) {
 		retval = EXT2_ET_INVALID_ARGUMENT;
@@ -247,7 +283,7 @@
 	 */
 	ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count);
 	if (ipg > fs->blocksize * 8) {
-		if (super->s_blocks_per_group >= 256) {
+		if (!bigalloc_flag && super->s_blocks_per_group >= 256) {
 			/* Try again with slightly different parameters */
 			super->s_blocks_per_group -= 8;
 			ext2fs_blocks_count_set(super,
@@ -365,7 +401,7 @@
 
 	strcpy(buf, "block bitmap for ");
 	strcat(buf, fs->device_name);
-	retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+	retval = ext2fs_allocate_subcluster_bitmap(fs, buf, &fs->block_map);
 	if (retval)
 		goto cleanup;
 
diff --git a/misc/mke2fs.c b/misc/mke2fs.c
index 0b7487f..a246ec1 100644
--- a/misc/mke2fs.c
+++ b/misc/mke2fs.c
@@ -298,7 +298,7 @@
 		exit(1);
 	}
 	while (ext2fs_badblocks_list_iterate(bb_iter, &blk))
-		ext2fs_mark_block_bitmap2(fs->block_map, blk);
+		ext2fs_mark_block_bitmap2(fs->block_map, EXT2FS_B2C(fs, blk));
 	ext2fs_badblocks_list_iterate_end(bb_iter);
 }
 
@@ -816,7 +816,8 @@
 		EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
 		EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
 		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|
-		EXT4_FEATURE_RO_COMPAT_GDT_CSUM
+		EXT4_FEATURE_RO_COMPAT_GDT_CSUM|
+		EXT4_FEATURE_RO_COMPAT_BIGALLOC
 };
 
 
@@ -1268,7 +1269,7 @@
 	}
 
 	while ((c = getopt (argc, argv,
-		    "b:cf:g:G:i:jl:m:no:qr:s:t:vE:FI:J:KL:M:N:O:R:ST:U:V")) != EOF) {
+		    "b:cg:i:jl:m:no:qr:s:t:vC:E:FG:I:J:KL:M:N:O:R:ST:U:V")) != EOF) {
 		switch (c) {
 		case 'b':
 			blocksize = strtol(optarg, &tmp, 0);
@@ -1291,17 +1292,17 @@
 		case 'c':	/* Check for bad blocks */
 			cflag++;
 			break;
-		case 'f':
+		case 'C':
 			size = strtoul(optarg, &tmp, 0);
-			if (size < EXT2_MIN_BLOCK_SIZE ||
-			    size > EXT2_MAX_BLOCK_SIZE || *tmp) {
+			if (size < EXT2_MIN_CLUSTER_SIZE ||
+			    size > EXT2_MAX_CLUSTER_SIZE || *tmp) {
 				com_err(program_name, 0,
 					_("invalid fragment size - %s"),
 					optarg);
 				exit(1);
 			}
-			fprintf(stderr, _("Warning: fragments not supported.  "
-			       "Ignoring -f option\n"));
+			fs_param.s_log_cluster_size =
+				int_log2(size >> EXT2_MIN_CLUSTER_LOG_SIZE);
 			break;
 		case 'g':
 			fs_param.s_blocks_per_group = strtoul(optarg, &tmp, 0);
@@ -1531,8 +1532,6 @@
 		check_plausibility(device_name);
 	check_mount(device_name, force, _("filesystem"));
 
-	fs_param.s_log_cluster_size = fs_param.s_log_block_size;
-
 	/* Determine the size of the device (if possible) */
 	if (noaction && fs_blocks_count) {
 		dev_size = fs_blocks_count;
@@ -1780,16 +1779,24 @@
 		}
 	}
 
+	fs_param.s_log_block_size =
+		int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+	if (fs_param.s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_BIGALLOC) {
+		if (fs_param.s_log_cluster_size == 0)
+			fs_param.s_log_cluster_size =
+				fs_param.s_log_block_size + 4;
+	} else
+		fs_param.s_log_cluster_size = fs_param.s_log_block_size;
+
 	if (inode_ratio == 0) {
 		inode_ratio = get_int_from_profile(fs_types, "inode_ratio",
 						   8192);
 		if (inode_ratio < blocksize)
 			inode_ratio = blocksize;
+		if (inode_ratio < EXT2_CLUSTER_SIZE(&fs_param))
+			inode_ratio = EXT2_CLUSTER_SIZE(&fs_param);
 	}
 
-	fs_param.s_log_cluster_size = fs_param.s_log_block_size =
-		int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
-
 #ifdef HAVE_BLKID_PROBE_GET_TOPOLOGY
 	retval = get_device_geometry(device_name, &fs_param, psector_size);
 	if (retval < 0) {
@@ -2077,6 +2084,33 @@
 	return retval;
 }
 
+static fix_cluster_bg_counts(ext2_filsys fs)
+{
+	blk64_t	cluster, num_clusters, tot_free;
+	int	grp_free, num_free, group, num;
+
+	num_clusters = EXT2FS_B2C(fs, ext2fs_blocks_count(fs->super));
+	tot_free = num_free = num = group = grp_free = 0;
+	for (cluster = EXT2FS_B2C(fs, fs->super->s_first_data_block);
+	     cluster < num_clusters; cluster++) {
+		if (!ext2fs_test_block_bitmap2(fs->block_map,
+					       EXT2FS_C2B(fs, cluster))) {
+			grp_free++;
+			tot_free++;
+		}
+		num++;
+		if ((num == fs->super->s_clusters_per_group) ||
+		    (cluster == num_clusters-1)) {
+			ext2fs_bg_free_blocks_count_set(fs, group, grp_free);
+			ext2fs_group_desc_csum_set(fs, group);
+			num = 0;
+			grp_free = 0;
+			group++;
+		}
+	}
+	ext2fs_free_blocks_count_set(fs->super, tot_free);
+}
+
 int main (int argc, char *argv[])
 {
 	errcode_t	retval = 0;
@@ -2283,6 +2317,14 @@
 	}
 	if (!quiet)
 		printf(_("done                            \n"));
+
+	retval = ext2fs_convert_subcluster_bitmap(fs, &fs->block_map);
+	if (retval) {
+		com_err(program_name, retval,
+			_("\n\twhile converting subcluster bitmap"));
+		exit(1);
+	}
+
 	if (super_only) {
 		fs->super->s_state |= EXT2_ERROR_FS;
 		fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
@@ -2395,6 +2437,9 @@
 	}
 no_journal:
 
+	if (EXT2_HAS_RO_COMPAT_FEATURE(&fs_param,
+				       EXT4_FEATURE_RO_COMPAT_BIGALLOC))
+		fix_cluster_bg_counts(fs);
 	if (!quiet)
 		printf(_("Writing superblocks and "
 		       "filesystem accounting information: "));