Blame SOURCES/xfsprogs-5.10.0-xfs-widen-ondisk-inode-timestamps-to-deal-with-y2038.patch

0bf83d
From e7e3beb95efd751f227a0ced4c83fc5b88582e2e Mon Sep 17 00:00:00 2001
0bf83d
From: "Darrick J. Wong" <darrick.wong@oracle.com>
0bf83d
Date: Wed, 11 Nov 2020 20:08:14 -0500
0bf83d
Subject: [PATCH] xfs: widen ondisk inode timestamps to deal with y2038+
0bf83d
0bf83d
Source kernel commit: f93e5436f0ee5a85eaa3a86d2614d215873fb18b
0bf83d
0bf83d
Redesign the ondisk inode timestamps to be a simple unsigned 64-bit
0bf83d
counter of nanoseconds since 14 Dec 1901 (i.e. the minimum time in the
0bf83d
32-bit unix time epoch).  This enables us to handle dates up to 2486,
0bf83d
which solves the y2038 problem.
0bf83d
0bf83d
sandeen: update xfs_flags2diflags2() as well, to match
0bf83d
0bf83d
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
0bf83d
Reviewed-by: Christoph Hellwig <hch@lst.de>
0bf83d
Reviewed-by: Gao Xiang <hsiangkao@redhat.com>
0bf83d
Reviewed-by: Dave Chinner <dchinner@redhat.com>
0bf83d
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
0bf83d
---
0bf83d
0bf83d
NOTE: xfs_trans_inode.c was not brought over in this version, so code
0bf83d
landed in trans.c
0bf83d
0bf83d
We also do not have the pre-computed geometry, so that needs to be
0bf83d
explicitly added to libxfs_ialloc rather than inheriting from igeo.
0bf83d
0bf83d
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
0bf83d
index ddd48be..25f2eac 100644
0bf83d
--- a/include/xfs_inode.h
0bf83d
+++ b/include/xfs_inode.h
0bf83d
@@ -146,6 +146,11 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
0bf83d
 	return ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
0bf83d
 }
0bf83d
 
0bf83d
+static inline bool xfs_inode_has_bigtime(struct xfs_inode *ip)
0bf83d
+{
0bf83d
+	return ip->i_d.di_flags2 & XFS_DIFLAG2_BIGTIME;
0bf83d
+}
0bf83d
+
0bf83d
 typedef struct cred {
0bf83d
 	uid_t	cr_uid;
0bf83d
 	gid_t	cr_gid;
0bf83d
diff --git a/libxfs/trans.c b/libxfs/trans.c
0bf83d
index db90624..54e4dd6 100644
0bf83d
--- a/libxfs/trans.c
0bf83d
+++ b/libxfs/trans.c
0bf83d
@@ -415,6 +415,17 @@ xfs_trans_log_inode(
0bf83d
 	tp->t_flags |= XFS_TRANS_DIRTY;
0bf83d
 	set_bit(XFS_LI_DIRTY, &ip->i_itemp->ili_item.li_flags);
0bf83d
 
0bf83d
+	/*
0bf83d
+	 * If we're updating the inode core or the timestamps and it's possible
0bf83d
+	 * to upgrade this inode to bigtime format, do so now.
0bf83d
+	 */
0bf83d
+	if ((flags & (XFS_ILOG_CORE | XFS_ILOG_TIMESTAMP)) &&
0bf83d
+	    xfs_sb_version_hasbigtime(&ip->i_mount->m_sb) &&
0bf83d
+	    !xfs_inode_has_bigtime(ip)) {
0bf83d
+		ip->i_d.di_flags2 |= XFS_DIFLAG2_BIGTIME;
0bf83d
+		flags |= XFS_ILOG_CORE;
0bf83d
+	}
0bf83d
+
0bf83d
 	/*
0bf83d
 	 * Always OR in the bits from the ili_last_fields field.
0bf83d
 	 * This is to coordinate with the xfs_iflush() and xfs_iflush_done()
0bf83d
diff --git a/libxfs/util.c b/libxfs/util.c
0bf83d
index 9383bb8..7a8729f 100644
0bf83d
--- a/libxfs/util.c
0bf83d
+++ b/libxfs/util.c
0bf83d
@@ -222,7 +222,8 @@ xfs_flags2diflags2(
0bf83d
 	unsigned int		xflags)
0bf83d
 {
0bf83d
 	uint64_t		di_flags2 =
0bf83d
-		(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
0bf83d
+		(ip->i_d.di_flags2 & (XFS_DIFLAG2_REFLINK |
0bf83d
+				      XFS_DIFLAG2_BIGTIME));
0bf83d
 
0bf83d
 	if (xflags & FS_XFLAG_DAX)
0bf83d
 		di_flags2 |= XFS_DIFLAG2_DAX;
0bf83d
@@ -317,8 +318,14 @@ libxfs_ialloc(
0bf83d
 		ASSERT(ip->i_d.di_ino == ino);
0bf83d
 		ASSERT(uuid_equal(&ip->i_d.di_uuid, &mp->m_sb.sb_meta_uuid));
0bf83d
 		VFS_I(ip)->i_version = 1;
0bf83d
-		ip->i_d.di_flags2 = pip ? 0 : xfs_flags2diflags2(ip,
0bf83d
-				fsx->fsx_xflags);
0bf83d
+		if (pip) {
0bf83d
+			ip->i_d.di_flags2 = 0;
0bf83d
+			if (xfs_sb_version_hasbigtime(&ip->i_mount->m_sb))
0bf83d
+				ip->i_d.di_flags2 |= XFS_DIFLAG2_BIGTIME;
0bf83d
+		} else {
0bf83d
+			ip->i_d.di_flags2 = xfs_flags2diflags2(ip, fsx->fsx_xflags);
0bf83d
+		}
0bf83d
+
0bf83d
 		ip->i_d.di_crtime.tv_sec = (int32_t)VFS_I(ip)->i_mtime.tv_sec;
0bf83d
 		ip->i_d.di_crtime.tv_nsec = (int32_t)VFS_I(ip)->i_mtime.tv_nsec;
0bf83d
 		ip->i_d.di_cowextsize = pip ? 0 : fsx->fsx_cowextsize;
0bf83d
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
0bf83d
index 371f5cd..b1f6219 100644
0bf83d
--- a/libxfs/xfs_format.h
0bf83d
+++ b/libxfs/xfs_format.h
0bf83d
@@ -466,6 +466,7 @@ xfs_sb_has_ro_compat_feature(
0bf83d
 #define XFS_SB_FEAT_INCOMPAT_FTYPE	(1 << 0)	/* filetype in dirent */
0bf83d
 #define XFS_SB_FEAT_INCOMPAT_SPINODES	(1 << 1)	/* sparse inode chunks */
0bf83d
 #define XFS_SB_FEAT_INCOMPAT_META_UUID	(1 << 2)	/* metadata UUID */
0bf83d
+#define XFS_SB_FEAT_INCOMPAT_BIGTIME	(1 << 3)	/* large timestamps */
0bf83d
 #define XFS_SB_FEAT_INCOMPAT_ALL \
0bf83d
 		(XFS_SB_FEAT_INCOMPAT_FTYPE|	\
0bf83d
 		 XFS_SB_FEAT_INCOMPAT_SPINODES|	\
0bf83d
@@ -580,6 +581,12 @@ xfs_is_quota_inode(struct xfs_sb *sbp, xfs_ino_t ino)
0bf83d
 #define	XFS_FSB_TO_DADDR(mp,fsbno)	XFS_AGB_TO_DADDR(mp, \
0bf83d
 			XFS_FSB_TO_AGNO(mp,fsbno), XFS_FSB_TO_AGBNO(mp,fsbno))
0bf83d
 
0bf83d
+static inline bool xfs_sb_version_hasbigtime(struct xfs_sb *sbp)
0bf83d
+{
0bf83d
+	return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
0bf83d
+		(sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_BIGTIME);
0bf83d
+}
0bf83d
+
0bf83d
 /*
0bf83d
  * File system sector to basic block conversions.
0bf83d
  */
0bf83d
@@ -849,6 +856,13 @@ typedef struct xfs_agfl {
0bf83d
  * Therefore, the ondisk min and max defined here can be used directly to
0bf83d
  * constrain the incore timestamps on a Unix system.  Note that we actually
0bf83d
  * encode a __be64 value on disk.
0bf83d
+ *
0bf83d
+ * When the bigtime feature is enabled, ondisk inode timestamps become an
0bf83d
+ * unsigned 64-bit nanoseconds counter.  This means that the bigtime inode
0bf83d
+ * timestamp epoch is the start of the classic timestamp range, which is
0bf83d
+ * Dec 31 20:45:52 UTC 1901.  Because the epochs are not the same, callers
0bf83d
+ * /must/ use the bigtime conversion functions when encoding and decoding raw
0bf83d
+ * timestamps.
0bf83d
  */
0bf83d
 typedef __be64 xfs_timestamp_t;
0bf83d
 
0bf83d
@@ -870,6 +884,50 @@ struct xfs_legacy_timestamp {
0bf83d
  */
0bf83d
 #define XFS_LEGACY_TIME_MAX	((int64_t)S32_MAX)
0bf83d
 
0bf83d
+/*
0bf83d
+ * Smallest possible ondisk seconds value with bigtime timestamps.  This
0bf83d
+ * corresponds (after conversion to a Unix timestamp) with the traditional
0bf83d
+ * minimum timestamp of Dec 13 20:45:52 UTC 1901.
0bf83d
+ */
0bf83d
+#define XFS_BIGTIME_TIME_MIN	((int64_t)0)
0bf83d
+
0bf83d
+/*
0bf83d
+ * Largest supported ondisk seconds value with bigtime timestamps.  This
0bf83d
+ * corresponds (after conversion to a Unix timestamp) with an incore timestamp
0bf83d
+ * of Jul  2 20:20:24 UTC 2486.
0bf83d
+ *
0bf83d
+ * We round down the ondisk limit so that the bigtime quota and inode max
0bf83d
+ * timestamps will be the same.
0bf83d
+ */
0bf83d
+#define XFS_BIGTIME_TIME_MAX	((int64_t)((-1ULL / NSEC_PER_SEC) & ~0x3ULL))
0bf83d
+
0bf83d
+/*
0bf83d
+ * Bigtime epoch is set exactly to the minimum time value that a traditional
0bf83d
+ * 32-bit timestamp can represent when using the Unix epoch as a reference.
0bf83d
+ * Hence the Unix epoch is at a fixed offset into the supported bigtime
0bf83d
+ * timestamp range.
0bf83d
+ *
0bf83d
+ * The bigtime epoch also matches the minimum value an on-disk 32-bit XFS
0bf83d
+ * timestamp can represent so we will not lose any fidelity in converting
0bf83d
+ * to/from unix and bigtime timestamps.
0bf83d
+ *
0bf83d
+ * The following conversion factor converts a seconds counter from the Unix
0bf83d
+ * epoch to the bigtime epoch.
0bf83d
+ */
0bf83d
+#define XFS_BIGTIME_EPOCH_OFFSET	(-(int64_t)S32_MIN)
0bf83d
+
0bf83d
+/* Convert a timestamp from the Unix epoch to the bigtime epoch. */
0bf83d
+static inline uint64_t xfs_unix_to_bigtime(time64_t unix_seconds)
0bf83d
+{
0bf83d
+	return (uint64_t)unix_seconds + XFS_BIGTIME_EPOCH_OFFSET;
0bf83d
+}
0bf83d
+
0bf83d
+/* Convert a timestamp from the bigtime epoch to the Unix epoch. */
0bf83d
+static inline time64_t xfs_bigtime_to_unix(uint64_t ondisk_seconds)
0bf83d
+{
0bf83d
+	return (time64_t)ondisk_seconds - XFS_BIGTIME_EPOCH_OFFSET;
0bf83d
+}
0bf83d
+
0bf83d
 /*
0bf83d
  * On-disk inode structure.
0bf83d
  *
0bf83d
@@ -1096,12 +1154,22 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
0bf83d
 #define XFS_DIFLAG2_DAX_BIT	0	/* use DAX for this inode */
0bf83d
 #define XFS_DIFLAG2_REFLINK_BIT	1	/* file's blocks may be shared */
0bf83d
 #define XFS_DIFLAG2_COWEXTSIZE_BIT   2  /* copy on write extent size hint */
0bf83d
+#define XFS_DIFLAG2_BIGTIME_BIT	3	/* big timestamps */
0bf83d
+
0bf83d
 #define XFS_DIFLAG2_DAX		(1 << XFS_DIFLAG2_DAX_BIT)
0bf83d
 #define XFS_DIFLAG2_REFLINK     (1 << XFS_DIFLAG2_REFLINK_BIT)
0bf83d
 #define XFS_DIFLAG2_COWEXTSIZE  (1 << XFS_DIFLAG2_COWEXTSIZE_BIT)
0bf83d
+#define XFS_DIFLAG2_BIGTIME	(1 << XFS_DIFLAG2_BIGTIME_BIT)
0bf83d
 
0bf83d
 #define XFS_DIFLAG2_ANY \
0bf83d
-	(XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)
0bf83d
+	(XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \
0bf83d
+	 XFS_DIFLAG2_BIGTIME)
0bf83d
+
0bf83d
+static inline bool xfs_dinode_has_bigtime(const struct xfs_dinode *dip)
0bf83d
+{
0bf83d
+	return dip->di_version >= 3 &&
0bf83d
+	       (dip->di_flags2 & cpu_to_be64(XFS_DIFLAG2_BIGTIME));
0bf83d
+}
0bf83d
 
0bf83d
 /*
0bf83d
  * Inode number format:
0bf83d
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
0bf83d
index 4fa9852..714dba1 100644
0bf83d
--- a/libxfs/xfs_fs.h
0bf83d
+++ b/libxfs/xfs_fs.h
0bf83d
@@ -231,6 +231,7 @@ typedef struct xfs_fsop_resblks {
0bf83d
 #define XFS_FSOP_GEOM_FLAGS_SPINODES	0x40000	/* sparse inode chunks	*/
0bf83d
 #define XFS_FSOP_GEOM_FLAGS_RMAPBT	0x80000	/* reverse mapping btree */
0bf83d
 #define XFS_FSOP_GEOM_FLAGS_REFLINK	0x100000 /* files can share blocks */
0bf83d
+#define XFS_FSOP_GEOM_FLAGS_BIGTIME	0x200000 /* 64-bit nsec timestamps */
0bf83d
 
0bf83d
 /*
0bf83d
  * Minimum and maximum sizes need for growth checks.
0bf83d
diff --git a/libxfs/xfs_inode_buf.c b/libxfs/xfs_inode_buf.c
0bf83d
index d8831a1..8cd16bf 100644
0bf83d
--- a/libxfs/xfs_inode_buf.c
0bf83d
+++ b/libxfs/xfs_inode_buf.c
0bf83d
@@ -195,14 +195,29 @@ xfs_imap_to_bp(
0bf83d
 	return 0;
0bf83d
 }
0bf83d
 
0bf83d
+static inline struct timespec64 xfs_inode_decode_bigtime(uint64_t ts)
0bf83d
+{
0bf83d
+	struct timespec64	tv;
0bf83d
+	uint32_t		n;
0bf83d
+
0bf83d
+	tv.tv_sec = xfs_bigtime_to_unix(div_u64_rem(ts, NSEC_PER_SEC, &n);;
0bf83d
+	tv.tv_nsec = n;
0bf83d
+
0bf83d
+	return tv;
0bf83d
+}
0bf83d
+
0bf83d
 /* Convert an ondisk timestamp to an incore timestamp. */
0bf83d
 struct timespec64
0bf83d
 xfs_inode_from_disk_ts(
0bf83d
+	struct xfs_dinode		*dip,
0bf83d
 	const xfs_timestamp_t		ts)
0bf83d
 {
0bf83d
 	struct timespec64		tv;
0bf83d
 	struct xfs_legacy_timestamp	*lts;
0bf83d
 
0bf83d
+	if (xfs_dinode_has_bigtime(dip))
0bf83d
+		return xfs_inode_decode_bigtime(be64_to_cpu(ts));
0bf83d
+
0bf83d
 	lts = (struct xfs_legacy_timestamp *)&ts;
0bf83d
 	tv.tv_sec = (int)be32_to_cpu(lts->t_sec);
0bf83d
 	tv.tv_nsec = (int)be32_to_cpu(lts->t_nsec);
0bf83d
@@ -246,9 +261,9 @@ xfs_inode_from_disk(
0bf83d
 	 * a time before epoch is converted to a time long after epoch
0bf83d
 	 * on 64 bit systems.
0bf83d
 	 */
0bf83d
-	inode->i_atime = xfs_inode_from_disk_ts(from->di_atime);
0bf83d
-	inode->i_mtime = xfs_inode_from_disk_ts(from->di_mtime);
0bf83d
-	inode->i_ctime = xfs_inode_from_disk_ts(from->di_ctime);
0bf83d
+	inode->i_atime = xfs_inode_from_disk_ts(from, from->di_atime);
0bf83d
+	inode->i_mtime = xfs_inode_from_disk_ts(from, from->di_mtime);
0bf83d
+	inode->i_ctime = xfs_inode_from_disk_ts(from, from->di_ctime);
0bf83d
 
0bf83d
 	inode->i_generation = be32_to_cpu(from->di_gen);
0bf83d
 	inode->i_mode = be16_to_cpu(from->di_mode);
0bf83d
@@ -267,7 +282,7 @@ xfs_inode_from_disk(
0bf83d
 	if (to->di_version == 3) {
0bf83d
 		inode_set_iversion_queried(inode,
0bf83d
 					   be64_to_cpu(from->di_changecount));
0bf83d
-		to->di_crtime = xfs_inode_from_disk_ts(from->di_crtime);
0bf83d
+		to->di_crtime = xfs_inode_from_disk_ts(from, from->di_crtime);
0bf83d
 		to->di_flags2 = be64_to_cpu(from->di_flags2);
0bf83d
 		to->di_cowextsize = be32_to_cpu(from->di_cowextsize);
0bf83d
 	}
0bf83d
@@ -276,11 +291,15 @@ xfs_inode_from_disk(
0bf83d
 /* Convert an incore timestamp to an ondisk timestamp. */
0bf83d
 static inline xfs_timestamp_t
0bf83d
 xfs_inode_to_disk_ts(
0bf83d
+	struct xfs_inode		*ip,
0bf83d
 	const struct timespec64		tv)
0bf83d
 {
0bf83d
 	struct xfs_legacy_timestamp	*lts;
0bf83d
 	xfs_timestamp_t			ts;
0bf83d
 
0bf83d
+	if (xfs_inode_has_bigtime(ip))
0bf83d
+		return cpu_to_be64(xfs_inode_encode_bigtime(tv));
0bf83d
+
0bf83d
 	lts = (struct xfs_legacy_timestamp *)&ts;
0bf83d
 	lts->t_sec = cpu_to_be32(tv.tv_sec);
0bf83d
 	lts->t_nsec = cpu_to_be32(tv.tv_nsec);
0bf83d
@@ -308,9 +327,9 @@ xfs_inode_to_disk(
0bf83d
 	to->di_projid_hi = cpu_to_be16(from->di_projid_hi);
0bf83d
 
0bf83d
 	memset(to->di_pad, 0, sizeof(to->di_pad));
0bf83d
-	to->di_atime = xfs_inode_to_disk_ts(inode->i_atime);
0bf83d
-	to->di_mtime = xfs_inode_to_disk_ts(inode->i_mtime);
0bf83d
-	to->di_ctime = xfs_inode_to_disk_ts(inode->i_ctime);
0bf83d
+	to->di_atime = xfs_inode_to_disk_ts(ip, inode->i_atime);
0bf83d
+	to->di_mtime = xfs_inode_to_disk_ts(ip, inode->i_mtime);
0bf83d
+	to->di_ctime = xfs_inode_to_disk_ts(ip, inode->i_ctime);
0bf83d
 	to->di_nlink = cpu_to_be32(inode->i_nlink);
0bf83d
 	to->di_gen = cpu_to_be32(inode->i_generation);
0bf83d
 	to->di_mode = cpu_to_be16(inode->i_mode);
0bf83d
@@ -328,7 +347,7 @@ xfs_inode_to_disk(
0bf83d
 
0bf83d
 	if (from->di_version == 3) {
0bf83d
 		to->di_changecount = cpu_to_be64(inode_peek_iversion(inode));
0bf83d
-		to->di_crtime = xfs_inode_to_disk_ts(from->di_crtime);
0bf83d
+		to->di_crtime = xfs_inode_to_disk_ts(ip, from->di_crtime);
0bf83d
 		to->di_flags2 = cpu_to_be64(from->di_flags2);
0bf83d
 		to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
0bf83d
 		to->di_ino = cpu_to_be64(ip->i_ino);
0bf83d
@@ -547,6 +566,11 @@ xfs_dinode_verify(
0bf83d
 	if (fa)
0bf83d
 		return fa;
0bf83d
 
0bf83d
+	/* bigtime iflag can only happen on bigtime filesystems */
0bf83d
+	if (xfs_dinode_has_bigtime(dip) &&
0bf83d
+	    !xfs_sb_version_hasbigtime(&mp->m_sb))
0bf83d
+		return __this_address;
0bf83d
+
0bf83d
 	return NULL;
0bf83d
 }
0bf83d
 
0bf83d
diff --git a/libxfs/xfs_inode_buf.h b/libxfs/xfs_inode_buf.h
0bf83d
index 6147f42..2b91e60 100644
0bf83d
--- a/libxfs/xfs_inode_buf.h
0bf83d
+++ b/libxfs/xfs_inode_buf.h
0bf83d
@@ -40,6 +40,11 @@ struct xfs_icdinode {
0bf83d
 	struct timespec64 di_crtime;	/* time created */
0bf83d
 };
0bf83d
 
0bf83d
+static inline bool xfs_icdinode_has_bigtime(const struct xfs_icdinode *icd)
0bf83d
+{
0bf83d
+	return icd->di_flags2 & XFS_DIFLAG2_BIGTIME;
0bf83d
+}
0bf83d
+
0bf83d
 /*
0bf83d
  * Inode location information.  Stored in the inode and passed to
0bf83d
  * xfs_imap_to_bp() to get a buffer and dinode for a given inode.
0bf83d
@@ -76,6 +81,12 @@ xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,
0bf83d
 		uint32_t cowextsize, uint16_t mode, uint16_t flags,
0bf83d
 		uint64_t flags2);
0bf83d
 
0bf83d
-struct timespec64 xfs_inode_from_disk_ts(const xfs_timestamp_t ts);
0bf83d
+static inline uint64_t xfs_inode_encode_bigtime(struct timespec64 tv)
0bf83d
+{
0bf83d
+	return xfs_unix_to_bigtime(tv.tv_sec) * NSEC_PER_SEC + tv.tv_nsec;
0bf83d
+}
0bf83d
+
0bf83d
+struct timespec64 xfs_inode_from_disk_ts(struct xfs_dinode *dip,
0bf83d
+		const xfs_timestamp_t ts);
0bf83d
 
0bf83d
 #endif	/* __XFS_INODE_BUF_H__ */
0bf83d
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
0bf83d
index cee77a6..d11545b 100644
0bf83d
--- a/libxfs/xfs_sb.c
0bf83d
+++ b/libxfs/xfs_sb.c
0bf83d
@@ -1124,6 +1124,8 @@ xfs_fs_geometry(
0bf83d
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_RMAPBT;
0bf83d
 	if (xfs_sb_version_hasreflink(sbp))
0bf83d
 		geo->flags |= XFS_FSOP_GEOM_FLAGS_REFLINK;
0bf83d
+	if (xfs_sb_version_hasbigtime(sbp))
0bf83d
+		geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
0bf83d
 	if (xfs_sb_version_hassector(sbp))
0bf83d
 		geo->logsectsize = sbp->sb_logsectsize;
0bf83d
 	else