Add XFS bigtime support

Resolves: rhbz#1940165

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
This commit is contained in:
Javier Martinez Canillas 2021-04-14 12:16:57 +02:00
parent 294df22ef5
commit 95fb16271d
No known key found for this signature in database
GPG Key ID: C751E590D63F3D69
4 changed files with 501 additions and 1 deletions

View File

@ -0,0 +1,167 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Carlos Maiolino <cmaiolino@redhat.com>
Date: Wed, 14 Apr 2021 11:07:20 +0200
Subject: [PATCH] fs/xfs: Add bigtime support for xfs driver
XFS filesystem now supports bigtime feature, to overcome y2038 problem.
This patch makes grub able to support xfs filesystems with this feature
enabled.
xfs counter for bigtime enable timestamps starts on 0, which translates
to INT32_MIN (Dec 31 20:45:52 UTC 1901) in the legacy timestamps. The
conversion to unix timestamps is made before passing the value to
grub-core.
For this to work properly, grub requires to access flags2 field in the
xfs ondisk inode, so, the grub_xfs_inode structure has been updated to
the full ondisk inode size.
This patch is enough to make grub work properly with files with
timestamps up to INT32_MAX (y2038), any file with timestamps bigger than
this will overflow the counter, causing grub to show wrong timestamps
(not really much difference on current situation).
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
grub-core/fs/xfs.c | 69 ++++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 54 insertions(+), 15 deletions(-)
diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
index 43023e03fb3..2ce76ec70f9 100644
--- a/grub-core/fs/xfs.c
+++ b/grub-core/fs/xfs.c
@@ -75,10 +75,15 @@ GRUB_MOD_LICENSE ("GPLv3+");
XFS_SB_VERSION2_PROJID32BIT | \
XFS_SB_VERSION2_FTYPE)
+/* Inode flags2 flags */
+#define XFS_DIFLAG2_BIGTIME_BIT 3
+#define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT)
+
/* incompat feature flags */
-#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
-#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
-#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
+#define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */
+#define XFS_SB_FEAT_INCOMPAT_SPINODES (1 << 1) /* sparse inode chunks */
+#define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */
+#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
/*
* Directory entries with ftype are explicitly handled by GRUB code.
@@ -92,7 +97,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
#define XFS_SB_FEAT_INCOMPAT_SUPPORTED \
(XFS_SB_FEAT_INCOMPAT_FTYPE | \
XFS_SB_FEAT_INCOMPAT_SPINODES | \
- XFS_SB_FEAT_INCOMPAT_META_UUID)
+ XFS_SB_FEAT_INCOMPAT_META_UUID | \
+ XFS_SB_FEAT_INCOMPAT_BIGTIME)
struct grub_xfs_sblock
{
@@ -177,7 +183,7 @@ struct grub_xfs_btree_root
grub_uint64_t keys[1];
} GRUB_PACKED;
-struct grub_xfs_time
+struct grub_xfs_time_legacy
{
grub_uint32_t sec;
grub_uint32_t nanosec;
@@ -190,20 +196,23 @@ struct grub_xfs_inode
grub_uint8_t version;
grub_uint8_t format;
grub_uint8_t unused2[26];
- struct grub_xfs_time atime;
- struct grub_xfs_time mtime;
- struct grub_xfs_time ctime;
+ grub_uint64_t atime;
+ grub_uint64_t mtime;
+ grub_uint64_t ctime;
grub_uint64_t size;
grub_uint64_t nblocks;
grub_uint32_t extsize;
grub_uint32_t nextents;
grub_uint16_t unused3;
grub_uint8_t fork_offset;
- grub_uint8_t unused4[17];
+ grub_uint8_t unused4[37];
+ grub_uint64_t flags2;
+ grub_uint8_t unused5[48];
} GRUB_PACKED;
-#define XFS_V2_INODE_SIZE sizeof(struct grub_xfs_inode)
-#define XFS_V3_INODE_SIZE (XFS_V2_INODE_SIZE + 76)
+#define XFS_V3_INODE_SIZE sizeof(struct grub_xfs_inode)
+/* Size of struct grub_xfs_inode until fork_offset (included)*/
+#define XFS_V2_INODE_SIZE (XFS_V3_INODE_SIZE - 92)
struct grub_xfs_dirblock_tail
{
@@ -233,8 +242,6 @@ struct grub_xfs_data
static grub_dl_t my_mod;
-
-
static int grub_xfs_sb_hascrc(struct grub_xfs_data *data)
{
return (data->sblock.version & grub_cpu_to_be16_compile_time(XFS_SB_VERSION_NUMBITS)) ==
@@ -950,7 +957,6 @@ grub_xfs_mount (grub_disk_t disk)
return 0;
}
-
/* Context for grub_xfs_dir. */
struct grub_xfs_dir_ctx
{
@@ -958,6 +964,39 @@ struct grub_xfs_dir_ctx
void *hook_data;
};
+/* Bigtime inodes helpers */
+
+#define NSEC_PER_SEC 1000000000L
+#define XFS_BIGTIME_EPOCH_OFFSET (-(grub_int64_t)GRUB_INT32_MIN)
+
+static int grub_xfs_inode_has_bigtime(const struct grub_xfs_inode *inode)
+{
+ return inode->version >= 3 &&
+ (inode->flags2 & grub_cpu_to_be64_compile_time(XFS_DIFLAG2_BIGTIME));
+}
+
+static grub_int64_t
+grub_xfs_bigtime_to_unix(grub_uint64_t time)
+{
+ grub_uint64_t rem;
+ grub_int64_t nsec = NSEC_PER_SEC;
+ grub_int64_t seconds = grub_divmod64((grub_int64_t)time, nsec, &rem);
+
+ return seconds - XFS_BIGTIME_EPOCH_OFFSET;
+}
+
+static grub_int64_t
+grub_xfs_get_inode_time(struct grub_xfs_inode *inode)
+{
+ struct grub_xfs_time_legacy *lts;
+
+ if (grub_xfs_inode_has_bigtime(inode))
+ return grub_xfs_bigtime_to_unix(grub_be_to_cpu64(inode->mtime));
+
+ lts = (struct grub_xfs_time_legacy *)&inode->mtime;
+ return grub_be_to_cpu32(lts->sec);
+}
+
/* Helper for grub_xfs_dir. */
static int
grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
@@ -970,7 +1009,7 @@ grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
if (node->inode_read)
{
info.mtimeset = 1;
- info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
+ info.mtime = grub_xfs_get_inode_time(&node->inode);
}
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
grub_free (node);

View File

@ -0,0 +1,327 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Carlos Maiolino <cmaiolino@redhat.com>
Date: Wed, 14 Apr 2021 11:07:21 +0200
Subject: [PATCH] fs: Use 64bit type for filesystem timestamp
Some filesystems nowadays uses 64bit types for timestamps, so, update
grub_dirhook_info struct to use an int64 type to store mtime. This also
updates grub_unixtime2datetime() to receive a 64-bit timestamp
argument and do 64bit-safe divisions.
All the remaining conversion from 32 to 64 should be safe, as 32 to 64
attributions will be implicitly casted. The most crictical part in the
32 to 64bits conversion is on grub_unixtime2datetime() where it needs
to deal with the 64bit type, specially with division in x86_architectures,
so, for that, the grub_divmod64() helper has been used.
These changes enables grub to support dates beyond y2038.
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Carlos Maiolino <cmaiolino@redhat.com>
---
grub-core/fs/affs.c | 2 +-
grub-core/fs/ext2.c | 2 +-
grub-core/fs/fat.c | 4 ++--
grub-core/fs/hfs.c | 2 +-
grub-core/fs/hfsplus.c | 2 +-
grub-core/fs/iso9660.c | 6 +++---
grub-core/fs/nilfs2.c | 2 +-
grub-core/fs/squash4.c | 2 +-
grub-core/fs/ufs.c | 2 +-
grub-core/fs/zfs/zfs.c | 2 +-
grub-core/lib/datetime.c | 15 ++++++++++++---
grub-core/net/bootp.c | 2 +-
grub-core/normal/misc.c | 2 +-
grub-core/tests/sleep_test.c | 4 ++--
include/grub/datetime.h | 4 ++--
include/grub/fs.h | 4 ++--
16 files changed, 33 insertions(+), 24 deletions(-)
diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c
index 230e26af0f8..cafcd0fba91 100644
--- a/grub-core/fs/affs.c
+++ b/grub-core/fs/affs.c
@@ -641,7 +641,7 @@ grub_affs_label (grub_device_t device, char **label)
}
static grub_err_t
-grub_affs_mtime (grub_device_t device, grub_int32_t *t)
+grub_affs_mtime (grub_device_t device, grub_int64_t *t)
{
struct grub_affs_data *data;
grub_disk_t disk = device->disk;
diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
index 848bf939dba..e7dd78e6635 100644
--- a/grub-core/fs/ext2.c
+++ b/grub-core/fs/ext2.c
@@ -1055,7 +1055,7 @@ grub_ext2_uuid (grub_device_t device, char **uuid)
/* Get mtime. */
static grub_err_t
-grub_ext2_mtime (grub_device_t device, grub_int32_t *tm)
+grub_ext2_mtime (grub_device_t device, grub_int64_t *tm)
{
struct grub_ext2_data *data;
grub_disk_t disk = device->disk;
diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c
index 7f775a17038..dd82e4ee35d 100644
--- a/grub-core/fs/fat.c
+++ b/grub-core/fs/fat.c
@@ -737,7 +737,7 @@ grub_fat_iterate_dir_next (grub_fshelp_node_t node,
* https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification
*/
static int
-grub_exfat_timestamp (grub_uint32_t field, grub_uint8_t msec, grub_int32_t *nix) {
+grub_exfat_timestamp (grub_uint32_t field, grub_uint8_t msec, grub_int64_t *nix) {
struct grub_datetime datetime = {
.year = (field >> 25) + 1980,
.month = (field & 0x01E00000) >> 21,
@@ -891,7 +891,7 @@ grub_fat_iterate_dir_next (grub_fshelp_node_t node,
* https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-107.pdf
*/
static int
-grub_fat_timestamp (grub_uint16_t time, grub_uint16_t date, grub_int32_t *nix) {
+grub_fat_timestamp (grub_uint16_t time, grub_uint16_t date, grub_int64_t *nix) {
struct grub_datetime datetime = {
.year = (date >> 9) + 1980,
.month = (date & 0x01E0) >> 5,
diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c
index 9a5b7bbe906..f419965d154 100644
--- a/grub-core/fs/hfs.c
+++ b/grub-core/fs/hfs.c
@@ -1374,7 +1374,7 @@ grub_hfs_label (grub_device_t device, char **label)
}
static grub_err_t
-grub_hfs_mtime (grub_device_t device, grub_int32_t *tm)
+grub_hfs_mtime (grub_device_t device, grub_int64_t *tm)
{
struct grub_hfs_data *data;
diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c
index 2a69055c7ec..19c7b336798 100644
--- a/grub-core/fs/hfsplus.c
+++ b/grub-core/fs/hfsplus.c
@@ -1083,7 +1083,7 @@ grub_hfsplus_label (grub_device_t device, char **label)
/* Get mtime. */
static grub_err_t
-grub_hfsplus_mtime (grub_device_t device, grub_int32_t *tm)
+grub_hfsplus_mtime (grub_device_t device, grub_int64_t *tm)
{
struct grub_hfsplus_data *data;
grub_disk_t disk = device->disk;
diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c
index 5ec4433b8f8..ac011950a64 100644
--- a/grub-core/fs/iso9660.c
+++ b/grub-core/fs/iso9660.c
@@ -178,7 +178,7 @@ static grub_dl_t my_mod;
static grub_err_t
-iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
+iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int64_t *nix)
{
struct grub_datetime datetime;
@@ -206,7 +206,7 @@ iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
}
static int
-iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int32_t *nix)
+iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int64_t *nix)
{
struct grub_datetime datetime;
@@ -1107,7 +1107,7 @@ grub_iso9660_uuid (grub_device_t device, char **uuid)
/* Get writing time of filesystem. */
static grub_err_t
-grub_iso9660_mtime (grub_device_t device, grub_int32_t *timebuf)
+grub_iso9660_mtime (grub_device_t device, grub_int64_t *timebuf)
{
struct grub_iso9660_data *data;
grub_disk_t disk = device->disk;
diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c
index 9b76982b3b0..3c248a910b4 100644
--- a/grub-core/fs/nilfs2.c
+++ b/grub-core/fs/nilfs2.c
@@ -1186,7 +1186,7 @@ grub_nilfs2_uuid (grub_device_t device, char **uuid)
/* Get mtime. */
static grub_err_t
-grub_nilfs2_mtime (grub_device_t device, grub_int32_t * tm)
+grub_nilfs2_mtime (grub_device_t device, grub_int64_t * tm)
{
struct grub_nilfs2_data *data;
grub_disk_t disk = device->disk;
diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c
index a5f35c10e2f..6dd731e231e 100644
--- a/grub-core/fs/squash4.c
+++ b/grub-core/fs/squash4.c
@@ -1003,7 +1003,7 @@ grub_squash_close (grub_file_t file)
}
static grub_err_t
-grub_squash_mtime (grub_device_t dev, grub_int32_t *tm)
+grub_squash_mtime (grub_device_t dev, grub_int64_t *tm)
{
struct grub_squash_data *data = 0;
diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c
index fca46baa19d..34a698b71b8 100644
--- a/grub-core/fs/ufs.c
+++ b/grub-core/fs/ufs.c
@@ -837,7 +837,7 @@ grub_ufs_uuid (grub_device_t device, char **uuid)
/* Get mtime. */
static grub_err_t
-grub_ufs_mtime (grub_device_t device, grub_int32_t *tm)
+grub_ufs_mtime (grub_device_t device, grub_int64_t *tm)
{
struct grub_ufs_data *data = 0;
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index f9e755197c5..cf4d2ab189a 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -3771,7 +3771,7 @@ zfs_uuid (grub_device_t device, char **uuid)
}
static grub_err_t
-zfs_mtime (grub_device_t device, grub_int32_t *mt)
+zfs_mtime (grub_device_t device, grub_int64_t *mt)
{
struct grub_zfs_data *data;
grub_zfs_endian_t ub_endian = GRUB_ZFS_UNKNOWN_ENDIAN;
diff --git a/grub-core/lib/datetime.c b/grub-core/lib/datetime.c
index 95b8c9ff5e3..3e84fa1dbc4 100644
--- a/grub-core/lib/datetime.c
+++ b/grub-core/lib/datetime.c
@@ -19,6 +19,7 @@
#include <grub/datetime.h>
#include <grub/i18n.h>
+#include <grub/misc.h>
static const char *const grub_weekday_names[] =
{
@@ -60,9 +61,10 @@ grub_get_weekday_name (struct grub_datetime *datetime)
void
-grub_unixtime2datetime (grub_int32_t nix, struct grub_datetime *datetime)
+grub_unixtime2datetime (grub_int64_t nix, struct grub_datetime *datetime)
{
int i;
+ grub_uint64_t rem;
grub_uint8_t months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/* In the period of validity of unixtime all years divisible by 4
are bissextile*/
@@ -75,9 +77,16 @@ grub_unixtime2datetime (grub_int32_t nix, struct grub_datetime *datetime)
unsigned secs_in_day;
/* Transform C divisions and modulos to mathematical ones */
if (nix < 0)
- days_epoch = -(((unsigned) (SECPERDAY-nix-1)) / SECPERDAY);
+ /*
+ * the division here shouldn't be larger than INT_MAX,
+ * so, it's safe to store the result back in an int
+ */
+ days_epoch = -(grub_divmod64(((grub_int64_t)(SECPERDAY) - nix - 1),
+ SECPERDAY,
+ &rem));
else
- days_epoch = ((unsigned) nix) / SECPERDAY;
+ days_epoch = grub_divmod64(nix, SECPERDAY, &rem);
+
secs_in_day = nix - days_epoch * SECPERDAY;
days = days_epoch + 69 * DAYSPERYEAR + 17;
diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 8f2f55c0079..7baf3540c81 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -610,7 +610,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface)
grub_err_t err;
struct grub_net_bootp_packet *pack;
struct grub_datetime date;
- grub_int32_t t = 0;
+ grub_int64_t t = 0;
struct grub_net_buff *nb;
struct udphdr *udph;
grub_net_network_level_address_t target;
diff --git a/grub-core/normal/misc.c b/grub-core/normal/misc.c
index 8bb6da31fb3..f7e9e3ac4a1 100644
--- a/grub-core/normal/misc.c
+++ b/grub-core/normal/misc.c
@@ -136,7 +136,7 @@ grub_normal_print_device_info (const char *name)
}
if (fs->fs_mtime)
{
- grub_int32_t tm;
+ grub_int64_t tm;
struct grub_datetime datetime;
(fs->fs_mtime) (dev, &tm);
if (grub_errno == GRUB_ERR_NONE)
diff --git a/grub-core/tests/sleep_test.c b/grub-core/tests/sleep_test.c
index 3d11c717cb7..63f6713165f 100644
--- a/grub-core/tests/sleep_test.c
+++ b/grub-core/tests/sleep_test.c
@@ -32,7 +32,7 @@ static void
sleep_test (void)
{
struct grub_datetime st, en;
- grub_int32_t stu = 0, enu = 0;
+ grub_int64_t stu = 0, enu = 0;
int is_delayok;
grub_test_assert (!grub_get_datetime (&st), "Couldn't retrieve start time");
grub_millisleep (10000);
@@ -45,7 +45,7 @@ sleep_test (void)
if (enu - stu >= 15 && enu - stu <= 17)
is_delayok = 1;
#endif
- grub_test_assert (is_delayok, "Interval out of range: %d", enu-stu);
+ grub_test_assert (is_delayok, "Interval out of range: %lld", enu-stu);
}
diff --git a/include/grub/datetime.h b/include/grub/datetime.h
index fef281404d7..23ae0791ced 100644
--- a/include/grub/datetime.h
+++ b/include/grub/datetime.h
@@ -48,11 +48,11 @@ grub_err_t grub_set_datetime (struct grub_datetime *datetime);
int grub_get_weekday (struct grub_datetime *datetime);
const char *grub_get_weekday_name (struct grub_datetime *datetime);
-void grub_unixtime2datetime (grub_int32_t nix,
+void grub_unixtime2datetime (grub_int64_t nix,
struct grub_datetime *datetime);
static inline int
-grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int32_t *nix)
+grub_datetime2unixtime (const struct grub_datetime *datetime, grub_int64_t *nix)
{
grub_int32_t ret;
int y4, ay;
diff --git a/include/grub/fs.h b/include/grub/fs.h
index 302e48d4b50..026bc3bb861 100644
--- a/include/grub/fs.h
+++ b/include/grub/fs.h
@@ -39,7 +39,7 @@ struct grub_dirhook_info
unsigned mtimeset:1;
unsigned case_insensitive:1;
unsigned inodeset:1;
- grub_int32_t mtime;
+ grub_int64_t mtime;
grub_uint64_t inode;
};
@@ -81,7 +81,7 @@ struct grub_fs
grub_err_t (*fs_uuid) (grub_device_t device, char **uuid);
/* Get writing time of filesystem. */
- grub_err_t (*fs_mtime) (grub_device_t device, grub_int32_t *timebuf);
+ grub_err_t (*fs_mtime) (grub_device_t device, grub_int64_t *timebuf);
#ifdef GRUB_UTIL
/* Determine sectors available for embedding. */

View File

@ -194,3 +194,5 @@ Patch0193: 0193-appended-signatures-documentation.patch
Patch0194: 0194-ieee1275-enter-lockdown-based-on-ibm-secure-boot.patch
Patch0195: 0195-Revert-templates-Properly-disable-the-os-prober-by-d.patch
Patch0196: 0196-Revert-templates-Disable-the-os-prober-by-default.patch
Patch0197: 0197-fs-xfs-Add-bigtime-support-for-xfs-driver.patch
Patch0198: 0198-fs-Use-64bit-type-for-filesystem-timestamp.patch

View File

@ -14,7 +14,7 @@
Name: grub2
Epoch: 1
Version: 2.06~rc1
Release: 5%{?dist}
Release: 6%{?dist}
Summary: Bootloader with support for Linux, Multiboot and more
License: GPLv3+
URL: http://www.gnu.org/software/grub/
@ -555,6 +555,10 @@ mv ${EFI_HOME}/grub.cfg.stb ${EFI_HOME}/grub.cfg
%endif
%changelog
* Wed Apr 14 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-6
- Add XFS bigtime support (cmaiolino)
Resolves: rhbz#1940165
* Tue Apr 13 2021 Javier Martinez Canillas <javierm@redhat.com> - 2.06~rc1-5
- Use RHEL distro SBAT data also for CentOS Stream
Related: rhbz#1947696