Merge changes I47373995,I5bf9a003,Iff6f5f8a am: eb40ceb178 am: 76c28599af

Original change: https://android-review.googlesource.com/c/platform/external/erofs-utils/+/2039665

Change-Id: Iedebcaafa6c9b304d2afa35b3281fbcecaaf2cfe
diff --git a/dump/main.c b/dump/main.c
index bb1bd7f..72761bd 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -84,6 +84,7 @@
 
 static struct erofsdump_feature feature_lists[] = {
 	{ true, EROFS_FEATURE_COMPAT_SB_CHKSUM, "sb_csum" },
+	{ true, EROFS_FEATURE_COMPAT_MTIME, "mtime" },
 	{ false, EROFS_FEATURE_INCOMPAT_LZ4_0PADDING, "0padding" },
 	{ false, EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER, "big_pcluster" },
 	{ false, EROFS_FEATURE_INCOMPAT_CHUNKED_FILE, "chunked_file" },
@@ -488,7 +489,7 @@
 	}
 
 	strftime(timebuf, sizeof(timebuf),
-		 "%Y-%m-%d %H:%M:%S", localtime((time_t *)&inode.i_ctime));
+		 "%Y-%m-%d %H:%M:%S", localtime((time_t *)&inode.i_mtime));
 	access_mode = inode.i_mode & 0777;
 	for (i = 8; i >= 0; i--)
 		if (((access_mode >> i) & 1) == 0)
@@ -507,7 +508,7 @@
 	fprintf(stdout,	"Xattr size: %u\n", inode.xattr_isize);
 	fprintf(stdout, "Uid: %u   Gid: %u  ", inode.i_uid, inode.i_gid);
 	fprintf(stdout, "Access: %04o/%s\n", access_mode, access_mode_str);
-	fprintf(stdout, "Timestamp: %s.%09d\n", timebuf, inode.i_ctime_nsec);
+	fprintf(stdout, "Timestamp: %s.%09d\n", timebuf, inode.i_mtime_nsec);
 
 	if (!dumpcfg.show_extent)
 		return;
diff --git a/fsck/main.c b/fsck/main.c
index e669b44..0af15b4 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -229,14 +229,14 @@
 
 #ifdef HAVE_UTIMENSAT
 	if (utimensat(AT_FDCWD, path, (struct timespec []) {
-				[0] = { .tv_sec = inode->i_ctime,
-					.tv_nsec = inode->i_ctime_nsec },
-				[1] = { .tv_sec = inode->i_ctime,
-					.tv_nsec = inode->i_ctime_nsec },
+				[0] = { .tv_sec = inode->i_mtime,
+					.tv_nsec = inode->i_mtime_nsec },
+				[1] = { .tv_sec = inode->i_mtime,
+					.tv_nsec = inode->i_mtime_nsec },
 			}, AT_SYMLINK_NOFOLLOW) < 0)
 #else
-	if (utime(path, &((struct utimbuf){.actime = inode->i_ctime,
-					   .modtime = inode->i_ctime})) < 0)
+	if (utime(path, &((struct utimbuf){.actime = inode->i_mtime,
+					   .modtime = inode->i_mtime})) < 0)
 #endif
 		erofs_warn("failed to set times: %s", path);
 
diff --git a/fuse/main.c b/fuse/main.c
index 2549d8a..ae377ae 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -98,7 +98,7 @@
 	stbuf->st_gid = vi.i_gid;
 	if (S_ISBLK(vi.i_mode) || S_ISCHR(vi.i_mode))
 		stbuf->st_rdev = vi.u.i_rdev;
-	stbuf->st_ctime = vi.i_ctime;
+	stbuf->st_ctime = vi.i_mtime;
 	stbuf->st_mtime = stbuf->st_ctime;
 	stbuf->st_atime = stbuf->st_ctime;
 	return 0;
diff --git a/include/erofs/config.h b/include/erofs/config.h
index cb064b6..0a1b18b 100644
--- a/include/erofs/config.h
+++ b/include/erofs/config.h
@@ -43,6 +43,7 @@
 	char c_timeinherit;
 	char c_chunkbits;
 	bool c_noinline_data;
+	bool c_ignore_mtime;
 
 #ifdef HAVE_LIBSELINUX
 	struct selabel_handle *sehnd;
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 947304f..56627e9 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -154,8 +154,8 @@
 	u64 i_ino[2];
 	u32 i_uid;
 	u32 i_gid;
-	u64 i_ctime;
-	u32 i_ctime_nsec;
+	u64 i_mtime;
+	u32 i_mtime_nsec;
 	u32 i_nlink;
 
 	union {
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index 9a91877..7956a62 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -13,6 +13,7 @@
 #define EROFS_SUPER_OFFSET      1024
 
 #define EROFS_FEATURE_COMPAT_SB_CHKSUM		0x00000001
+#define EROFS_FEATURE_COMPAT_MTIME		0x00000002
 
 /*
  * Any bits that aren't in EROFS_ALL_FEATURE_INCOMPAT should
@@ -183,8 +184,8 @@
 
 	__le32 i_uid;
 	__le32 i_gid;
-	__le64 i_ctime;
-	__le32 i_ctime_nsec;
+	__le64 i_mtime;
+	__le32 i_mtime_nsec;
 	__le32 i_nlink;
 	__u8   i_reserved2[16];
 };
diff --git a/lib/config.c b/lib/config.c
index f1c8edf..cc15e57 100644
--- a/lib/config.c
+++ b/lib/config.c
@@ -20,6 +20,7 @@
 	cfg.c_dbg_lvl  = EROFS_WARN;
 	cfg.c_version  = PACKAGE_VERSION;
 	cfg.c_dry_run  = false;
+	cfg.c_ignore_mtime = false;
 	cfg.c_compr_level_master = -1;
 	cfg.c_force_inodeversion = 0;
 	cfg.c_inline_xattr_tolerance = 2;
diff --git a/lib/inode.c b/lib/inode.c
index 461c797..77ea8bf 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -477,8 +477,8 @@
 		u.die.i_uid = cpu_to_le32(inode->i_uid);
 		u.die.i_gid = cpu_to_le32(inode->i_gid);
 
-		u.die.i_ctime = cpu_to_le64(inode->i_ctime);
-		u.die.i_ctime_nsec = cpu_to_le32(inode->i_ctime_nsec);
+		u.die.i_mtime = cpu_to_le64(inode->i_mtime);
+		u.die.i_mtime_nsec = cpu_to_le32(inode->i_mtime_nsec);
 
 		switch (inode->i_mode & S_IFMT) {
 		case S_IFCHR:
@@ -730,6 +730,10 @@
 		return true;
 	if (inode->i_nlink > USHRT_MAX)
 		return true;
+	if ((inode->i_mtime != sbi.build_time ||
+	     inode->i_mtime_nsec != sbi.build_time_nsec) &&
+	    !cfg.c_ignore_mtime)
+		return true;
 	return false;
 }
 
@@ -806,16 +810,16 @@
 	inode->i_mode = st->st_mode;
 	inode->i_uid = cfg.c_uid == -1 ? st->st_uid : cfg.c_uid;
 	inode->i_gid = cfg.c_gid == -1 ? st->st_gid : cfg.c_gid;
-	inode->i_ctime = st->st_ctime;
-	inode->i_ctime_nsec = ST_CTIM_NSEC(st);
+	inode->i_mtime = st->st_mtime;
+	inode->i_mtime_nsec = ST_MTIM_NSEC(st);
 
 	switch (cfg.c_timeinherit) {
 	case TIMESTAMP_CLAMPING:
-		if (st->st_ctime < sbi.build_time)
+		if (inode->i_mtime < sbi.build_time)
 			break;
 	case TIMESTAMP_FIXED:
-		inode->i_ctime = sbi.build_time;
-		inode->i_ctime_nsec = sbi.build_time_nsec;
+		inode->i_mtime = sbi.build_time;
+		inode->i_mtime_nsec = sbi.build_time_nsec;
 	default:
 		break;
 	}
diff --git a/lib/namei.c b/lib/namei.c
index 7377e74..2c8891a 100644
--- a/lib/namei.c
+++ b/lib/namei.c
@@ -79,8 +79,8 @@
 		vi->i_gid = le32_to_cpu(die->i_gid);
 		vi->i_nlink = le32_to_cpu(die->i_nlink);
 
-		vi->i_ctime = le64_to_cpu(die->i_ctime);
-		vi->i_ctime_nsec = le64_to_cpu(die->i_ctime_nsec);
+		vi->i_mtime = le64_to_cpu(die->i_mtime);
+		vi->i_mtime_nsec = le64_to_cpu(die->i_mtime_nsec);
 		vi->i_size = le64_to_cpu(die->i_size);
 		if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
 			/* fill chunked inode summary info */
@@ -114,8 +114,8 @@
 		vi->i_gid = le16_to_cpu(dic->i_gid);
 		vi->i_nlink = le16_to_cpu(dic->i_nlink);
 
-		vi->i_ctime = sbi.build_time;
-		vi->i_ctime_nsec = sbi.build_time_nsec;
+		vi->i_mtime = sbi.build_time;
+		vi->i_mtime_nsec = sbi.build_time_nsec;
 
 		vi->i_size = le32_to_cpu(dic->i_size);
 		if (vi->datalayout == EROFS_INODE_CHUNK_BASED)
diff --git a/man/mkfs.erofs.1 b/man/mkfs.erofs.1
index 9c7788e..d61e33e 100644
--- a/man/mkfs.erofs.1
+++ b/man/mkfs.erofs.1
@@ -112,6 +112,11 @@
 .B \-\-help
 Display this help and exit.
 .TP
+.B "\-\-ignore-mtime"
+File modification time is ignored whenever it would cause \fBmkfs.erofs\fR to
+use extended inodes over compact inodes. When not using a fixed timestamp, this
+can reduce total metadata size.
+.TP
 .BI "\-\-max-extent-bytes " #
 Specify maximum decompressed extent size # in bytes.
 .SH AUTHOR
diff --git a/mkfs/main.c b/mkfs/main.c
index 3f34450..b62a8aa 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -49,6 +49,7 @@
 	{"chunksize", required_argument, NULL, 11},
 	{"quiet", no_argument, 0, 12},
 	{"blobdev", required_argument, NULL, 13},
+	{"ignore-mtime", no_argument, NULL, 14},
 #ifdef WITH_ANDROID
 	{"mount-point", required_argument, NULL, 512},
 	{"product-out", required_argument, NULL, 513},
@@ -96,6 +97,7 @@
 	      " --force-uid=#         set all file uids to # (# = UID)\n"
 	      " --force-gid=#         set all file gids to # (# = GID)\n"
 	      " --help                display this help and exit\n"
+	      " --ignore-mtime        use build time instead of strict per-file modification time\n"
 	      " --max-extent-bytes=#  set maximum decompressed extent size # in bytes\n"
 	      " --quiet               quiet execution (do not write anything to standard output.)\n"
 #ifndef NDEBUG
@@ -366,6 +368,9 @@
 		case 13:
 			cfg.c_blobdev_path = optarg;
 			break;
+		case 14:
+			cfg.c_ignore_mtime = true;
+			break;
 		case 1:
 			usage();
 			exit(0);
@@ -511,7 +516,8 @@
 {
 	cfg.c_legacy_compress = false;
 	sbi.feature_incompat = EROFS_FEATURE_INCOMPAT_LZ4_0PADDING;
-	sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM;
+	sbi.feature_compat = EROFS_FEATURE_COMPAT_SB_CHKSUM |
+			     EROFS_FEATURE_COMPAT_MTIME;
 
 	/* generate a default uuid first */
 #ifdef HAVE_LIBUUID