Snap for 7428328 from a3965e992fdfb90af0bdd079a40551196bbc3b39 to sc-release

Change-Id: I077b7cc75e4a951ecf51f1a8ab7b5d68154b51cf
diff --git a/METADATA b/METADATA
index 15545a8..0120fd9 100644
--- a/METADATA
+++ b/METADATA
@@ -12,7 +12,7 @@
   license_type: RESTRICTED
   last_upgrade_date {
     year: 2021
-    month: 4
-    day: 24
+    month: 6
+    day: 3
   }
 }
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index e7d599c..e31d416 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -18,3 +18,4 @@
 	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/defrag.f2fs
 	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/resize.f2fs
 	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/sload.f2fs
+	ln -sf fsck.f2fs $(DESTDIR)/$(sbindir)/f2fslabel
diff --git a/fsck/dump.c b/fsck/dump.c
index 055ce09..042a2e5 100644
--- a/fsck/dump.c
+++ b/fsck/dump.c
@@ -278,7 +278,7 @@
 
 	if (nid == 0) {
 		*ofs += skip;
-		return;
+		goto out;
 	}
 
 	for (i = 0; i < idx; i++, (*ofs)++) {
@@ -297,6 +297,7 @@
 			break;
 		}
 	}
+out:
 	free(node_blk);
 }
 
diff --git a/fsck/fsck.c b/fsck/fsck.c
index e526720..80a6d8e 100644
--- a/fsck/fsck.c
+++ b/fsck/fsck.c
@@ -144,6 +144,7 @@
 static int is_valid_ssa_node_blk(struct f2fs_sb_info *sbi, u32 nid,
 							u32 blk_addr)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_summary_block *sum_blk;
 	struct f2fs_summary *sum_entry;
 	struct seg_entry * se;
@@ -151,6 +152,9 @@
 	int need_fix = 0, ret = 0;
 	int type;
 
+	if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+		return 0;
+
 	segno = GET_SEGNO(sbi, blk_addr);
 	offset = OFFSET_IN_SEG(sbi, blk_addr);
 
@@ -261,6 +265,7 @@
 static int is_valid_ssa_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
 		u32 parent_nid, u16 idx_in_node, u8 version)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_summary_block *sum_blk;
 	struct f2fs_summary *sum_entry;
 	struct seg_entry * se;
@@ -268,6 +273,9 @@
 	int need_fix = 0, ret = 0;
 	int type;
 
+	if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+		return 0;
+
 	segno = GET_SEGNO(sbi, blk_addr);
 	offset = OFFSET_IN_SEG(sbi, blk_addr);
 
@@ -1780,8 +1788,11 @@
 			if (c.preen_mode == PREEN_MODE_1 && !c.fix_on) {
 				get_node_info(sbi, ino, &ni);
 				if (!IS_VALID_NID(sbi, ino) ||
-						!IS_VALID_BLK_ADDR(sbi, ni.blk_addr))
+				    !IS_VALID_BLK_ADDR(sbi, ni.blk_addr)) {
+					free(orphan_blk);
+					free(new_blk);
 					return -EINVAL;
+				}
 
 				continue;
 			}
@@ -2369,10 +2380,15 @@
 
 int check_curseg_offset(struct f2fs_sb_info *sbi, int type)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct curseg_info *curseg = CURSEG_I(sbi, type);
 	struct seg_entry *se;
 	int j, nblocks;
 
+	if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO) &&
+			type != CURSEG_HOT_DATA && type != CURSEG_HOT_NODE)
+		return 0;
+
 	if ((curseg->next_blkoff >> 3) >= SIT_VBLOCK_MAP_SIZE) {
 		ASSERT_MSG("Next block offset:%u is invalid, type:%d",
 			curseg->next_blkoff, type);
@@ -2955,6 +2971,7 @@
 
 int fsck_chk_curseg_info(struct f2fs_sb_info *sbi)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct curseg_info *curseg;
 	struct seg_entry *se;
 	struct f2fs_summary_block *sum_blk;
@@ -2965,6 +2982,10 @@
 		se = get_seg_entry(sbi, curseg->segno);
 		sum_blk = curseg->sum_blk;
 
+		if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+			(i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE))
+			continue;
+
 		if (se->type != i) {
 			ASSERT_MSG("Incorrect curseg [%d]: segno [0x%x] "
 				   "type(SIT) [%d]", i, curseg->segno,
@@ -3047,7 +3068,10 @@
 		}
 		c.bug_on = 1;
 	}
-
+	printf("[FSCK] Max image size: %"PRIu64" MB, Free space: %u MB\n",
+		c.max_size >> 20,
+		(sbi->user_block_count - sbi->total_valid_block_count) >>
+		(20 - F2FS_BLKSIZE_BITS));
 	printf("[FSCK] Unreachable nat entries                       ");
 	if (nr_unref_nid == 0x0) {
 		printf(" [Ok..] [0x%x]\n", nr_unref_nid);
diff --git a/fsck/main.c b/fsck/main.c
index 64efa87..c07be1e 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -71,6 +71,7 @@
 	MSG(0, "  -d debug level [default:0]\n");
 	MSG(0, "  -f check/fix entire partition\n");
 	MSG(0, "  -g add default options\n");
+	MSG(0, "  -l show superblock/checkpoint\n");
 	MSG(0, "  -O feature1[feature2,feature3,...] e.g. \"encrypt\"\n");
 	MSG(0, "  -p preen mode [default:0 the same as -a [0|1]]\n");
 	MSG(0, "  -S sparse_mode\n");
@@ -155,6 +156,14 @@
 	exit(1);
 }
 
+void label_usage()
+{
+	MSG(0, "\nUsage: f2fslabel [options] device [volume-label]\n");
+	MSG(0, "[options]:\n");
+	MSG(0, "  -V print the version number and exit\n");
+	exit(1);
+}
+
 static int is_digits(char *optarg)
 {
 	unsigned int i;
@@ -177,6 +186,8 @@
 		resize_usage();
 	else if (!strcmp("sload.f2fs", prog))
 		sload_usage();
+	else if (!strcmp("f2fslabel", prog))
+		label_usage();
 	else
 		MSG(0, "\nWrong program.\n");
 }
@@ -216,7 +227,7 @@
 	}
 
 	if (!strcmp("fsck.f2fs", prog)) {
-		const char *option_string = ":aC:c:m:d:fg:O:p:q:StyV";
+		const char *option_string = ":aC:c:m:d:fg:lO:p:q:StyV";
 		int opt = 0, val;
 		char *token;
 		struct option long_opt[] = {
@@ -263,6 +274,9 @@
 				if (!strcmp(optarg, "android"))
 					c.defset = CONF_ANDROID;
 				break;
+			case 'l':
+				c.layout = 1;
+				break;
 			case 'O':
 				if (parse_feature(feature_table, optarg))
 					fsck_usage();
@@ -722,6 +736,39 @@
 			}
 		}
 #endif /* WITH_SLOAD */
+	} else if (!strcmp("f2fslabel", prog)) {
+#ifdef WITH_LABEL
+		const char *option_string = "V";
+
+		c.func = LABEL;
+		while ((option = getopt(argc, argv, option_string)) != EOF) {
+			switch (option) {
+			case 'V':
+				show_version(prog);
+				exit(0);
+			default:
+				err = EUNKNOWN_OPT;
+				break;
+			}
+			if (err != NOERROR)
+				break;
+		}
+
+		if (argc > (optind + 2)) { /* unknown argument(s) is(are) passed */
+			optind += 2;
+			err = EUNKNOWN_ARG;
+		} else if (argc == (optind + 2)) { /* change label */
+			c.vol_label = argv[optind + 1];
+			argc--;
+		} else { /* print label */
+			/*
+			 * Since vol_label was initialized as "", in order to
+			 * distinguish between clear label and print, set
+			 * vol_label as NULL for print case
+			 */
+			c.vol_label = NULL;
+		}
+#endif /* WITH_LABEL */
 	}
 
 	if (err == NOERROR) {
@@ -868,6 +915,11 @@
 {
 	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 
+	if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) {
+		MSG(0, "Not support on readonly image.\n");
+		return -1;
+	}
+
 	if (c.defrag_start > get_sb(block_count))
 		goto out_range;
 	if (c.defrag_start < SM_I(sbi)->main_blkaddr)
@@ -971,6 +1023,36 @@
 }
 #endif
 
+#ifdef WITH_LABEL
+static int do_label(struct f2fs_sb_info *sbi)
+{
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+
+	if (!c.vol_label) {
+		char label[MAX_VOLUME_NAME];
+
+		utf16_to_utf8(label, sb->volume_name,
+			      MAX_VOLUME_NAME, MAX_VOLUME_NAME);
+		MSG(0, "Info: volume label = %s\n", label);
+		return 0;
+	}
+
+	if (strlen(c.vol_label) > MAX_VOLUME_NAME) {
+		ERR_MSG("Label should not exceed %d characters\n", MAX_VOLUME_NAME);
+		return -1;
+	}
+
+	utf8_to_utf16(sb->volume_name, (const char *)c.vol_label,
+		      MAX_VOLUME_NAME, strlen(c.vol_label));
+
+	update_superblock(sb, SB_MASK_ALL);
+
+	MSG(0, "Info: volume label is changed to %s\n", c.vol_label);
+
+	return 0;
+}
+#endif
+
 #if defined(__APPLE__)
 static u64 get_boottime_ns()
 {
@@ -1085,6 +1167,12 @@
 		c.fix_on = 1;
 		goto fsck_again;
 #endif
+#ifdef WITH_LABEL
+	case LABEL:
+		if (do_label(sbi))
+			goto out_err;
+		break;
+#endif
 	default:
 		ERR_MSG("Wrong program name\n");
 		ASSERT(0);
diff --git a/fsck/mount.c b/fsck/mount.c
index 6b2f17e..de692b6 100644
--- a/fsck/mount.c
+++ b/fsck/mount.c
@@ -369,11 +369,16 @@
 	char buffer[MAX_VOLUME_NAME];
 
 	utf16_to_utf8(buffer, name, MAX_VOLUME_NAME, MAX_VOLUME_NAME);
-	printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
+	if (c.layout)
+		printf("%-30s %s\n", "Filesystem volume name:", buffer);
+	else
+		printf("%-30s" "\t\t[%s]\n", "volum_name", buffer);
 }
 
 void print_raw_sb_info(struct f2fs_super_block *sb)
 {
+	if (c.layout)
+		goto printout;
 	if (!c.dbg_lv)
 		return;
 
@@ -381,7 +386,7 @@
 	printf("+--------------------------------------------------------+\n");
 	printf("| Super block                                            |\n");
 	printf("+--------------------------------------------------------+\n");
-
+printout:
 	DISP_u32(sb, magic);
 	DISP_u32(sb, major_ver);
 
@@ -427,6 +432,8 @@
 {
 	struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
 
+	if (c.layout)
+		goto printout;
 	if (!c.dbg_lv)
 		return;
 
@@ -434,7 +441,7 @@
 	printf("+--------------------------------------------------------+\n");
 	printf("| Checkpoint                                             |\n");
 	printf("+--------------------------------------------------------+\n");
-
+printout:
 	DISP_u64(cp, checkpoint_ver);
 	DISP_u64(cp, user_block_count);
 	DISP_u64(cp, valid_block_count);
@@ -561,6 +568,9 @@
 	if (f & cpu_to_le32(F2FS_FEATURE_COMPRESSION)) {
 		MSG(0, "%s", " compression");
 	}
+	if (f & cpu_to_le32(F2FS_FEATURE_RO)) {
+		MSG(0, "%s", " ro");
+	}
 	MSG(0, "\n");
 	MSG(0, "Info: superblock encrypt level = %d, salt = ",
 					sb->encryption_level);
@@ -856,9 +866,10 @@
 		return -1;
 	}
 
-	if (total_sections > segment_count ||
+	if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+			(total_sections > segment_count ||
 			total_sections < F2FS_MIN_SEGMENTS ||
-			segs_per_sec > segment_count || !segs_per_sec) {
+			segs_per_sec > segment_count || !segs_per_sec)) {
 		MSG(0, "\tInvalid segment/section count (%u, %u x %u)\n",
 			segment_count, total_sections, segs_per_sec);
 		return 1;
@@ -967,12 +978,13 @@
 					c.sb_version, c.version);
 		if (!c.no_kernel_check &&
 				memcmp(c.sb_version, c.version, VERSION_LEN)) {
+			c.auto_fix = 0;
+			c.fix_on = 1;
+		}
+		if (c.fix_on) {
 			memcpy(sbi->raw_super->version,
 						c.version, VERSION_LEN);
 			update_superblock(sbi->raw_super, SB_MASK(sb_addr));
-
-			c.auto_fix = 0;
-			c.fix_on = 1;
 		}
 		print_sb_state(sbi->raw_super);
 		return 0;
@@ -1254,14 +1266,16 @@
 	ovp_segments = get_cp(overprov_segment_count);
 	reserved_segments = get_cp(rsvd_segment_count);
 
-	if (fsmeta < F2FS_MIN_SEGMENT || ovp_segments == 0 ||
-					reserved_segments == 0) {
+	if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+		(fsmeta < F2FS_MIN_SEGMENT || ovp_segments == 0 ||
+					reserved_segments == 0)) {
 		MSG(0, "\tWrong layout: check mkfs.f2fs version\n");
 		return 1;
 	}
 
 	user_block_count = get_cp(user_block_count);
-	segment_count_main = get_sb(segment_count_main);
+	segment_count_main = get_sb(segment_count_main) +
+				(cpu_to_le32(F2FS_FEATURE_RO) ? 1 : 0);
 	log_blocks_per_seg = get_sb(log_blocks_per_seg);
 	if (!user_block_count || user_block_count >=
 			segment_count_main << log_blocks_per_seg) {
@@ -1884,11 +1898,15 @@
 void update_sum_entry(struct f2fs_sb_info *sbi, block_t blk_addr,
 					struct f2fs_summary *sum)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_summary_block *sum_blk;
 	u32 segno, offset;
 	int type, ret;
 	struct seg_entry *se;
 
+	if (get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))
+		return;
+
 	segno = GET_SEGNO(sbi, blk_addr);
 	offset = OFFSET_IN_SEG(sbi, blk_addr);
 
@@ -2723,18 +2741,17 @@
 		bitmap = get_seg_bitmap(sbi, se);
 		type = get_seg_type(sbi, se);
 
-		if (vblocks == sbi->blocks_per_seg ||
-				IS_CUR_SEGNO(sbi, segno)) {
+		if (vblocks == sbi->blocks_per_seg) {
+next_segment:
 			*to = left ? START_BLOCK(sbi, segno) - 1:
 						START_BLOCK(sbi, segno + 1);
 			continue;
 		}
-
-		if (vblocks == 0 && not_enough) {
-			*to = left ? START_BLOCK(sbi, segno) - 1:
-						START_BLOCK(sbi, segno + 1);
-			continue;
-		}
+		if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+						IS_CUR_SEGNO(sbi, segno))
+			goto next_segment;
+		if (vblocks == 0 && not_enough)
+			goto next_segment;
 
 		if (vblocks == 0 && !(segno % sbi->segs_per_sec)) {
 			struct seg_entry *se2;
@@ -2765,17 +2782,24 @@
 static void move_one_curseg_info(struct f2fs_sb_info *sbi, u64 from, int left,
 				 int i)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct curseg_info *curseg = CURSEG_I(sbi, i);
 	struct f2fs_summary_block buf;
 	u32 old_segno;
 	u64 ssa_blk, to;
 	int ret;
 
+	if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))) {
+		if (i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE)
+			return;
+		goto bypass_ssa;
+	}
+
 	/* update original SSA too */
 	ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
 	ret = dev_write_block(curseg->sum_blk, ssa_blk);
 	ASSERT(ret >= 0);
-
+bypass_ssa:
 	to = from;
 	ret = find_next_free_block(sbi, &to, left, i,
 				   c.zoned_model == F2FS_ZONED_HM);
@@ -3014,10 +3038,12 @@
 		ret = dev_write_block(curseg->sum_blk, cp_blk_no++);
 		ASSERT(ret >= 0);
 
-		/* update original SSA too */
-		ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
-		ret = dev_write_block(curseg->sum_blk, ssa_blk);
-		ASSERT(ret >= 0);
+		if (!(get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO))) {
+			/* update original SSA too */
+			ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+			ret = dev_write_block(curseg->sum_blk, ssa_blk);
+			ASSERT(ret >= 0);
+		}
 	}
 
 	/* Write nat bits */
@@ -3534,6 +3560,8 @@
 		if (get_cp(ckpt_flags) & CP_QUOTA_NEED_FSCK_FLAG)
 			c.fix_on = 1;
 	}
+	if (c.layout)
+		return 1;
 
 	if (tune_sb_features(sbi))
 		return -1;
diff --git a/fsck/node.c b/fsck/node.c
index 1d291ca..c7988cb 100644
--- a/fsck/node.c
+++ b/fsck/node.c
@@ -61,6 +61,7 @@
 block_t new_node_block(struct f2fs_sb_info *sbi,
 				struct dnode_of_data *dn, unsigned int ofs)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_node *f2fs_inode;
 	struct f2fs_node *node_blk;
 	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -89,6 +90,10 @@
 			type = CURSEG_WARM_NODE;
 	}
 
+	if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+					type != CURSEG_HOT_NODE)
+		type = CURSEG_HOT_NODE;
+
 	get_node_info(sbi, dn->nid, &ni);
 	set_summary(&sum, dn->nid, 0, ni.version);
 	ret = reserve_new_block(sbi, &blkaddr, &sum, type, !ofs);
diff --git a/fsck/resize.c b/fsck/resize.c
index b043cd9..78d578e 100644
--- a/fsck/resize.c
+++ b/fsck/resize.c
@@ -383,6 +383,7 @@
 		ASSERT(ret >= 0);
 		DBG(3, "Write NAT: %lx\n", block_addr);
 	}
+	free(nat_block);
 	DBG(0, "Info: Done to migrate NAT blocks: nat_blkaddr = 0x%x -> 0x%x\n",
 			old_nat_blkaddr, new_nat_blkaddr);
 }
diff --git a/fsck/segment.c b/fsck/segment.c
index 365c7f8..0156690 100644
--- a/fsck/segment.c
+++ b/fsck/segment.c
@@ -104,11 +104,16 @@
 int new_data_block(struct f2fs_sb_info *sbi, void *block,
 				struct dnode_of_data *dn, int type)
 {
+	struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
 	struct f2fs_summary sum;
 	struct node_info ni;
 	unsigned int blkaddr = datablock_addr(dn->node_blk, dn->ofs_in_node);
 	int ret;
 
+	if ((get_sb(feature) & cpu_to_le32(F2FS_FEATURE_RO)) &&
+					type != CURSEG_HOT_DATA)
+		type = CURSEG_HOT_DATA;
+
 	ASSERT(dn->node_blk);
 	memset(block, 0, BLOCK_SZ);
 
@@ -516,9 +521,9 @@
 		node_blk->i.i_compress_algrithm = c.compress.alg;
 		node_blk->i.i_log_cluster_size =
 				c.compress.cc.log_cluster_size;
-		node_blk->i.i_flags = cpu_to_le32(
-				F2FS_COMPR_FL |
-				(c.compress.readonly ? FS_IMMUTABLE_FL : 0));
+		node_blk->i.i_flags = cpu_to_le32(F2FS_COMPR_FL);
+		if (c.compress.readonly)
+			node_blk->i.i_inline |= F2FS_COMPRESS_RELEASED;
 		ASSERT(write_inode(node_blk, ni.blk_addr) >= 0);
 
 		while (!eof && (n = bulkread(fd, rbuf, c.compress.cc.rlen,
diff --git a/include/f2fs_fs.h b/include/f2fs_fs.h
index cdcce2c..8969ae2 100644
--- a/include/f2fs_fs.h
+++ b/include/f2fs_fs.h
@@ -35,6 +35,7 @@
 #define WITH_DEFRAG
 #define WITH_RESIZE
 #define WITH_SLOAD
+#define WITH_LABEL
 #endif
 
 #include <inttypes.h>
@@ -239,14 +240,14 @@
 
 #define MSG(n, fmt, ...)						\
 	do {								\
-		if (c.dbg_lv >= n) {					\
+		if (c.dbg_lv >= n && !c.layout) {			\
 			printf(fmt, ##__VA_ARGS__);			\
 		}							\
 	} while (0)
 
 #define DBG(n, fmt, ...)						\
 	do {								\
-		if (c.dbg_lv >= n) {					\
+		if (c.dbg_lv >= n && !c.layout) {			\
 			printf("[%s:%4d] " fmt,				\
 				__func__, __LINE__, ##__VA_ARGS__);	\
 		}							\
@@ -261,7 +262,11 @@
 #define DISP_u16(ptr, member)						\
 	do {								\
 		assert(sizeof((ptr)->member) == 2);			\
-		printf("%-30s" "\t\t[0x%8x : %u]\n",			\
+		if (c.layout)						\
+			printf("%-30s %u\n",				\
+			#member":", le16_to_cpu(((ptr)->member)));	\
+		else							\
+			printf("%-30s" "\t\t[0x%8x : %u]\n",		\
 			#member, le16_to_cpu(((ptr)->member)),		\
 			le16_to_cpu(((ptr)->member)));			\
 	} while (0)
@@ -269,7 +274,11 @@
 #define DISP_u32(ptr, member)						\
 	do {								\
 		assert(sizeof((ptr)->member) <= 4);			\
-		printf("%-30s" "\t\t[0x%8x : %u]\n",			\
+		if (c.layout)						\
+			printf("%-30s %u\n",				\
+			#member":", le32_to_cpu(((ptr)->member)));	\
+		else							\
+			printf("%-30s" "\t\t[0x%8x : %u]\n",		\
 			#member, le32_to_cpu(((ptr)->member)),		\
 			le32_to_cpu(((ptr)->member)));			\
 	} while (0)
@@ -277,14 +286,23 @@
 #define DISP_u64(ptr, member)						\
 	do {								\
 		assert(sizeof((ptr)->member) == 8);			\
-		printf("%-30s" "\t\t[0x%8llx : %llu]\n",		\
+		if (c.layout)						\
+			printf("%-30s %llu\n",				\
+			#member":", le64_to_cpu(((ptr)->member)));	\
+		else							\
+			printf("%-30s" "\t\t[0x%8llx : %llu]\n",	\
 			#member, le64_to_cpu(((ptr)->member)),		\
 			le64_to_cpu(((ptr)->member)));			\
 	} while (0)
 
 #define DISP_utf(ptr, member)						\
 	do {								\
-		printf("%-30s" "\t\t[%s]\n", #member, ((ptr)->member)); \
+		if (c.layout)						\
+			printf("%-30s %s\n", #member":",		\
+					((ptr)->member)); 		\
+		else							\
+			printf("%-30s" "\t\t[%s]\n", #member,		\
+					((ptr)->member));		\
 	} while (0)
 
 /* Display to buffer */
@@ -343,6 +361,7 @@
 	DEFRAG,
 	RESIZE,
 	SLOAD,
+	LABEL,
 };
 
 enum default_set {
@@ -439,6 +458,7 @@
 	u_int64_t wanted_total_sectors;
 	u_int64_t wanted_sector_size;
 	u_int64_t target_sectors;
+	u_int64_t max_size;
 	u_int32_t sectors_per_blk;
 	u_int32_t blks_per_seg;
 	__u8 init_version[VERSION_LEN + 1];
@@ -470,6 +490,7 @@
 	int bug_nat_bits;
 	int alloc_failed;
 	int auto_fix;
+	int layout;
 	int quota_fix;
 	int preen_mode;
 	int ro;
@@ -696,7 +717,8 @@
 #define F2FS_FEATURE_VERITY		0x0400	/* reserved */
 #define F2FS_FEATURE_SB_CHKSUM		0x0800
 #define F2FS_FEATURE_CASEFOLD		0x1000
- #define F2FS_FEATURE_COMPRESSION	0x2000
+#define F2FS_FEATURE_COMPRESSION	0x2000
+#define F2FS_FEATURE_RO			0x4000
 
 #define MAX_VOLUME_NAME		512
 
@@ -867,6 +889,8 @@
 #define F2FS_DATA_EXIST		0x08	/* file inline data exist flag */
 #define F2FS_INLINE_DOTS	0x10	/* file having implicit dot dentries */
 #define F2FS_EXTRA_ATTR		0x20	/* file having extra attribute */
+#define F2FS_PIN_FILE		0x40	/* file should not be gced */
+#define F2FS_COMPRESS_RELEASED	0x80	/* file released compressed blocks */
 
 #if !defined(offsetof)
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -1546,6 +1570,7 @@
 	{ "sb_checksum",		F2FS_FEATURE_SB_CHKSUM },	\
 	{ "casefold",			F2FS_FEATURE_CASEFOLD },	\
 	{ "compression",		F2FS_FEATURE_COMPRESSION },	\
+	{ "ro",				F2FS_FEATURE_RO},		\
 	{ NULL,				0x0},				\
 };
 
diff --git a/lib/libf2fs.c b/lib/libf2fs.c
index 85107ee..0add901 100644
--- a/lib/libf2fs.c
+++ b/lib/libf2fs.c
@@ -835,7 +835,7 @@
 	if (uname(&buf))
 		return;
 
-#if !defined(WITH_KERNEL_VERSION)
+#if defined(WITH_KERNEL_VERSION)
 	snprintf((char *)version,
 		VERSION_LEN, "%s %s", buf.release, buf.version);
 #else
diff --git a/lib/libf2fs_io.c b/lib/libf2fs_io.c
index dcedc17..320ee6c 100644
--- a/lib/libf2fs_io.c
+++ b/lib/libf2fs_io.c
@@ -507,6 +507,9 @@
 	int fd;
 	int err;
 
+	if (c.max_size < (offset + len))
+		c.max_size = offset + len;
+
 	if (c.sparse_mode)
 		return sparse_read_blk(offset / F2FS_BLKSIZE,
 					len / F2FS_BLKSIZE, buf);
@@ -548,6 +551,9 @@
 {
 	int fd;
 
+	if (c.max_size < (offset + len))
+		c.max_size = offset + len;
+
 	if (c.dry_run)
 		return 0;
 
@@ -590,6 +596,9 @@
 {
 	int fd;
 
+	if (c.max_size < (offset + len))
+		c.max_size = offset + len;
+
 	if (c.sparse_mode)
 		return sparse_write_zeroed_blk(offset / F2FS_BLKSIZE,
 						len / F2FS_BLKSIZE);
diff --git a/man/Makefile.am b/man/Makefile.am
index 1d16c6f..9363b82 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -1,3 +1,3 @@
 ## Makefile.am
 
-dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8 f2fs_io.8
+dist_man_MANS = mkfs.f2fs.8 fsck.f2fs.8 dump.f2fs.8 defrag.f2fs.8 resize.f2fs.8 sload.f2fs.8 f2fs_io.8 f2fslabel.8
diff --git a/man/f2fslabel.8 b/man/f2fslabel.8
new file mode 100644
index 0000000..848ed3b
--- /dev/null
+++ b/man/f2fslabel.8
@@ -0,0 +1,33 @@
+.\" Copyright (c) 2021 Samsung Electronics Co., Ltd.
+.\"
+.TH F2FSLABEL 8
+.SH NAME
+f2fslabel \- Change the label on an f2fs volume
+.SH SYNOPSIS
+.B f2fslabel
+.I device
+[
+.I volume-label
+]
+.SH DESCRIPTION
+.B f2fslabel
+will display or change the volume label on the f2fs located on
+.I device.
+.PP
+If the optional argument
+.I volume-label
+is present, then
+.B f2fslabel
+will set the volume label to be
+.IR volume-label .
+.PP
+Otherwise,
+.B f2fslabel
+will simply show the current label.
+.PP
+.SH AUTHOR
+.B f2fslabel
+was written by Dongwoo Lee (dwoo08.lee@samsung.com).
+.SH AVAILABILITY
+.B f2fslabel
+is available from git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git.
diff --git a/mkfs/f2fs_format.c b/mkfs/f2fs_format.c
index 3f1fa32..3565bd3 100644
--- a/mkfs/f2fs_format.c
+++ b/mkfs/f2fs_format.c
@@ -212,7 +212,7 @@
 	u_int64_t total_meta_zones, total_meta_segments;
 	u_int32_t sit_bitmap_size, max_sit_bitmap_size;
 	u_int32_t max_nat_bitmap_size, max_nat_segments;
-	u_int32_t total_zones;
+	u_int32_t total_zones, avail_zones;
 	enum quota_type qtype;
 	int i;
 
@@ -250,6 +250,9 @@
 		zone_size_bytes * zone_size_bytes -
 		(u_int64_t) c.start_sector * DEFAULT_SECTOR_SIZE;
 
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+		zone_align_start_offset = 8192;
+
 	if (c.start_sector % DEFAULT_SECTORS_PER_BLOCK) {
 		MSG(1, "\t%s: Align start sector number to the page unit\n",
 				c.zoned_mode ? "FAIL" : "WARN");
@@ -400,7 +403,10 @@
 			get_sb(segment_count_nat))) *
 			c.blks_per_seg;
 
-	blocks_for_ssa = total_valid_blks_available /
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+		blocks_for_ssa = 0;
+	else
+		blocks_for_ssa = total_valid_blks_available /
 				c.blks_per_seg + 1;
 
 	set_sb(segment_count_ssa, SEG_ALIGN(blocks_for_ssa));
@@ -457,7 +463,13 @@
 			(2 * (100 / c.overprovision + 1) + NR_CURSEG_TYPE) *
 			round_up(f2fs_get_usable_segments(sb), get_sb(section_count));
 
-	if (c.overprovision == 0 || c.total_segments < F2FS_MIN_SEGMENTS ||
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+		c.overprovision = 0;
+		c.reserved_segments = 0;
+	}
+	if ((!(c.feature & cpu_to_le32(F2FS_FEATURE_RO)) &&
+		c.overprovision == 0) ||
+		c.total_segments < F2FS_MIN_SEGMENTS ||
 		(c.devices[0].total_sectors *
 			c.sector_size < zone_align_start_offset) ||
 		(get_sb(segment_count_main) - NR_CURSEG_TYPE) <
@@ -503,13 +515,25 @@
 	if (c.feature & cpu_to_le32(F2FS_FEATURE_LOST_FOUND))
 		c.lpf_ino = c.next_free_nid++;
 
-	if (total_zones <= 6) {
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+		avail_zones = 2;
+	else
+		avail_zones = 6;
+
+	if (total_zones <= avail_zones) {
 		MSG(1, "\tError: %d zones: Need more zones "
 			"by shrinking zone size\n", total_zones);
 		return -1;
 	}
 
-	if (c.heap) {
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+		c.cur_seg[CURSEG_HOT_NODE] = 0;
+		c.cur_seg[CURSEG_WARM_NODE] = 0;
+		c.cur_seg[CURSEG_COLD_NODE] = 0;
+		c.cur_seg[CURSEG_HOT_DATA] = 1;
+		c.cur_seg[CURSEG_COLD_DATA] = 0;
+		c.cur_seg[CURSEG_WARM_DATA] = 0;
+	} else if (c.heap) {
 		c.cur_seg[CURSEG_HOT_NODE] =
 				last_section(last_zone(total_zones));
 		c.cur_seg[CURSEG_WARM_NODE] = prev_zone(CURSEG_HOT_NODE);
@@ -538,7 +562,8 @@
 	}
 
 	/* if there is redundancy, reassign it */
-	verify_cur_segs();
+	if (!(c.feature & cpu_to_le32(F2FS_FEATURE_RO)))
+		verify_cur_segs();
 
 	cure_extension_list();
 
@@ -723,7 +748,7 @@
 
 	if (f2fs_get_usable_segments(sb) <= get_cp(overprov_segment_count)) {
 		MSG(0, "\tError: Not enough segments to create F2FS Volume\n");
-		goto free_nat_bits;
+		goto free_cp_payload;
 	}
 	MSG(0, "Info: Overprovision ratio = %.3lf%%\n", c.overprovision);
 	MSG(0, "Info: Overprovision segments = %u (GC reserved = %u)\n",
@@ -731,9 +756,15 @@
 					c.reserved_segments);
 
 	/* main segments - reserved segments - (node + data segments) */
-	set_cp(free_segment_count, f2fs_get_usable_segments(sb) - 6);
-	set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+		set_cp(free_segment_count, f2fs_get_usable_segments(sb) - 2);
+		set_cp(user_block_count, ((get_cp(free_segment_count) + 2 -
 			get_cp(overprov_segment_count)) * c.blks_per_seg));
+	} else {
+		set_cp(free_segment_count, f2fs_get_usable_segments(sb) - 6);
+		set_cp(user_block_count, ((get_cp(free_segment_count) + 6 -
+			get_cp(overprov_segment_count)) * c.blks_per_seg));
+	}
 	/* cp page (2), data summaries (1), node summaries (3) */
 	set_cp(cp_pack_total_block_count, 6 + get_sb(cp_payload));
 	flags = CP_UMOUNT_FLAG | CP_COMPACT_SUM_FLAG;
@@ -847,8 +878,13 @@
 	sum_compact_p += SUM_JOURNAL_SIZE;
 
 	memset(sum, 0, sizeof(struct f2fs_summary_block));
+
 	/* inode sit for root */
-	journal->n_sits = cpu_to_le16(6);
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO))
+		journal->n_sits = cpu_to_le16(2);
+	else
+		journal->n_sits = cpu_to_le16(6);
+
 	journal->sit_j.entries[0].segno = cp->cur_node_segno[0];
 	journal->sit_j.entries[0].se.vblocks =
 				cpu_to_le16((CURSEG_HOT_NODE << 10) |
@@ -859,30 +895,43 @@
 	if (c.lpf_inum)
 		f2fs_set_bit(i, (char *)journal->sit_j.entries[0].se.valid_map);
 
-	journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
-	journal->sit_j.entries[1].se.vblocks =
-				cpu_to_le16((CURSEG_WARM_NODE << 10));
-	journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
-	journal->sit_j.entries[2].se.vblocks =
-				cpu_to_le16((CURSEG_COLD_NODE << 10));
+	if (c.feature & cpu_to_le32(F2FS_FEATURE_RO)) {
+		/* data sit for root */
+		journal->sit_j.entries[1].segno = cp->cur_data_segno[0];
+		journal->sit_j.entries[1].se.vblocks =
+					cpu_to_le16((CURSEG_HOT_DATA << 10) |
+							(1 + c.quota_dnum + c.lpf_dnum));
+		f2fs_set_bit(0, (char *)journal->sit_j.entries[1].se.valid_map);
+		for (i = 1; i <= c.quota_dnum; i++)
+			f2fs_set_bit(i, (char *)journal->sit_j.entries[1].se.valid_map);
+		if (c.lpf_dnum)
+			f2fs_set_bit(i, (char *)journal->sit_j.entries[1].se.valid_map);
+	} else {
+		journal->sit_j.entries[1].segno = cp->cur_node_segno[1];
+		journal->sit_j.entries[1].se.vblocks =
+					cpu_to_le16((CURSEG_WARM_NODE << 10));
+		journal->sit_j.entries[2].segno = cp->cur_node_segno[2];
+		journal->sit_j.entries[2].se.vblocks =
+					cpu_to_le16((CURSEG_COLD_NODE << 10));
 
-	/* data sit for root */
-	journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
-	journal->sit_j.entries[3].se.vblocks =
-				cpu_to_le16((CURSEG_HOT_DATA << 10) |
-						(1 + c.quota_dnum + c.lpf_dnum));
-	f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
-	for (i = 1; i <= c.quota_dnum; i++)
-		f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
-	if (c.lpf_dnum)
-		f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
+		/* data sit for root */
+		journal->sit_j.entries[3].segno = cp->cur_data_segno[0];
+		journal->sit_j.entries[3].se.vblocks =
+					cpu_to_le16((CURSEG_HOT_DATA << 10) |
+							(1 + c.quota_dnum + c.lpf_dnum));
+		f2fs_set_bit(0, (char *)journal->sit_j.entries[3].se.valid_map);
+		for (i = 1; i <= c.quota_dnum; i++)
+			f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
+		if (c.lpf_dnum)
+			f2fs_set_bit(i, (char *)journal->sit_j.entries[3].se.valid_map);
 
-	journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
-	journal->sit_j.entries[4].se.vblocks =
-				cpu_to_le16((CURSEG_WARM_DATA << 10));
-	journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
-	journal->sit_j.entries[5].se.vblocks =
-				cpu_to_le16((CURSEG_COLD_DATA << 10));
+		journal->sit_j.entries[4].segno = cp->cur_data_segno[1];
+		journal->sit_j.entries[4].se.vblocks =
+					cpu_to_le16((CURSEG_WARM_DATA << 10));
+		journal->sit_j.entries[5].segno = cp->cur_data_segno[2];
+		journal->sit_j.entries[5].se.vblocks =
+					cpu_to_le16((CURSEG_COLD_DATA << 10));
+	}
 
 	memcpy(sum_compact_p, &journal->n_sits, SUM_JOURNAL_SIZE);
 	sum_compact_p += SUM_JOURNAL_SIZE;
@@ -1090,7 +1139,7 @@
 	u_int64_t start_inode_pos = get_sb(main_blkaddr);
 	u_int64_t last_inode_pos;
 
-	if (c.zoned_mode)
+	if (c.zoned_mode || c.feature & cpu_to_le32(F2FS_FEATURE_RO))
 		return 0;
 
 	raw_node = calloc(sizeof(struct f2fs_node), 1);
diff --git a/tools/f2fs_io/f2fs_io.c b/tools/f2fs_io/f2fs_io.c
index fa7d3f5..aa1a7e4 100644
--- a/tools/f2fs_io/f2fs_io.c
+++ b/tools/f2fs_io/f2fs_io.c
@@ -213,7 +213,8 @@
 "  verity\n"							\
 "  casefold\n"							\
 "  compression\n"						\
-"  nocompression\n"
+"  nocompression\n"						\
+"  immutable\n"
 
 static void do_getflags(int argc, char **argv, const struct cmd_desc *cmd)
 {
@@ -271,6 +272,12 @@
 		printf("nocow(pinned)");
 		exist = 1;
 	}
+	if (flag & FS_IMMUTABLE_FL) {
+		if (exist)
+			printf(",");
+		printf("immutable");
+		exist = 1;
+	}
 	if (!exist)
 		printf("none");
 	printf("\n");
@@ -284,7 +291,8 @@
 "flag can be\n"							\
 "  casefold\n"							\
 "  compression\n"						\
-"  nocompression\n"
+"  nocompression\n"						\
+"  noimmutable\n"
 
 static void do_setflags(int argc, char **argv, const struct cmd_desc *cmd)
 {
@@ -310,6 +318,8 @@
 		flag |= FS_COMPR_FL;
 	else if (!strcmp(argv[1], "nocompression"))
 		flag |= FS_NOCOMP_FL;
+	else if (!strcmp(argv[1], "noimmutable"))
+		flag &= ~FS_IMMUTABLE_FL;
 
 	ret = ioctl(fd, F2FS_IOC_SETFLAGS, &flag);
 	printf("set a flag on %s ret=%d, flags=%s\n", argv[2], ret, argv[1]);
diff --git a/tools/f2fs_io/f2fs_io.h b/tools/f2fs_io/f2fs_io.h
index 3a0278f..cdaf00f 100644
--- a/tools/f2fs_io/f2fs_io.h
+++ b/tools/f2fs_io/f2fs_io.h
@@ -91,7 +91,7 @@
 #define F2FS_IOC_DECOMPRESS_FILE        _IO(F2FS_IOCTL_MAGIC, 23)
 #define F2FS_IOC_COMPRESS_FILE          _IO(F2FS_IOCTL_MAGIC, 24)
 
-#ifndef FS_IOC_GET_ENCRYPTION_POLICY
+#ifndef FSCRYPT_POLICY_V1
 #define FSCRYPT_POLICY_V1		0
 #define FSCRYPT_KEY_DESCRIPTOR_SIZE	8
 struct fscrypt_policy_v1 {
@@ -101,10 +101,12 @@
 	__u8 flags;
 	__u8 master_key_descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
 };
+#endif
+#ifndef FS_IOC_GET_ENCRYPTION_POLICY
 #define FS_IOC_GET_ENCRYPTION_POLICY		_IOW('f', 21, struct fscrypt_policy_v1)
 #endif
 
-#ifndef FS_IOC_GET_ENCRYPTION_POLICY_EX
+#ifndef FSCRYPT_POLICY_V2
 #define FSCRYPT_POLICY_V2		2
 #define FSCRYPT_KEY_IDENTIFIER_SIZE	16
 struct fscrypt_policy_v2 {
@@ -124,6 +126,8 @@
 		struct fscrypt_policy_v2 v2;
 	} policy; /* output */
 };
+#endif
+#ifndef FS_IOC_GET_ENCRYPTION_POLICY_EX
 #define FS_IOC_GET_ENCRYPTION_POLICY_EX		_IOWR('f', 22, __u8[9]) /* size + version */
 #endif