The branch main has been updated by mckusick:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=553ef188f7ecc23a384bd7ef1f3d5015fb8661da

commit 553ef188f7ecc23a384bd7ef1f3d5015fb8661da
Author:     Kirk McKusick <[email protected]>
AuthorDate: 2026-06-01 23:48:21 +0000
Commit:     Kirk McKusick <[email protected]>
CommitDate: 2026-06-01 23:48:21 +0000

    Avoid incorrect UFS1 timestamp corrections when system clock fails at boot.
    
    Git 1111a44301da - main - Defer the January 19, 2038 date limit in
    UFS1 file systems to February 7, 2106 - did so by changing the UFS1
    32-bit signed timestamps to unsigned. With this change, time stamps
    from before January 1, 1970 went from being negative numbers to
    large positive numbers implying times in the future. When such a
    time stamp is encountered when an inode is read into memory or when
    it is encountered by fsck, its timestamp is replaced with the
    kernel's current time.
    
    Andre Albsmeier reported that he had a machine reboot after a power
    failure and the battery that maintained its real-time clock had
    died. The result was that the system booted with the time set to
    five years earlier (absent a real-time clock value, the boot ROM
    used the time that the boot ROM had last been updated). The net
    result was that fsck reset the time stamps of all files newer than
    five years old to the five year old time.
    
    Andres's original request was for a flag in the file system superblock
    to say that there are no timestamps from before 1970 in the file
    system, so there shouldn't be anything to fix because of the signed
    to unsigned switch. But this assumes that no one every does an rsync
    or extracts a tar file or restores a dump that introduces an incorrect
    time stamp on their system. So this approach was not taken.
    
    This change compares the system's version of the current time to
    the last modification time in the file system superblock. If the
    current time is earlier than that time then use the last modification
    time in the superblock as the value for the current time. There
    should be no files in the file system with times newer than the
    last modification time in the superblock.
    
    The superblock time stamp is updated in the in-memory superblock
    every time any change is made to anything in the file system. The
    superblock is written to the disk every 30 seconds, so it may be
    off by up to 30 seconds plus the time it sits in the disk cache
    waiting to be written if the system has an unclean shutdown (such
    as a power failure). Thus, the worst case scenario with this change
    is that files written in the last 30 seconds plus disk cache delay
    time before the crash may have their times adjusted back by up to
    30 seconds plus the disk cache delay time.
    
    Requested by: Andre Albsmeier
    Approved by:  kib
    Reviewed by:  kib, imp, Andre Albsmeier
    MFC-after:    1 week
    Differential Revision: https://reviews.freebsd.org/D57371
---
 lib/libufs/inode.c       | 17 +++++++++--------
 sbin/fsck_ffs/inode.c    |  3 ++-
 sys/ufs/ffs/ffs_vfsops.c |  7 ++++---
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/lib/libufs/inode.c b/lib/libufs/inode.c
index bc4d99c66203..a93e1b085342 100644
--- a/lib/libufs/inode.c
+++ b/lib/libufs/inode.c
@@ -57,7 +57,8 @@ getinode(struct uufsd *disk, union dinodep *dp, ino_t inum)
        ino_t min, max;
        caddr_t inoblock;
        struct fs *fs;
-       struct timespec now;
+       struct timespec time;
+       time_t now;
 
        ERROR(disk, NULL);
 
@@ -70,10 +71,11 @@ getinode(struct uufsd *disk, union dinodep *dp, ino_t inum)
        min = disk->d_inomin;
        max = disk->d_inomax;
 
-       if (clock_gettime(CLOCK_REALTIME_FAST, &now) != 0) {
-               ERROR(disk, "cannot get current time of day");
-               return (-1);
-       }
+       if (clock_gettime(CLOCK_REALTIME_FAST, &time) == 0 &&
+           time.tv_sec > fs->fs_time)
+               now = time.tv_sec;
+       else
+               now = fs->fs_time;
        if (inum >= min && inum < max)
                goto gotit;
        bread(disk, fsbtodb(fs, ino_to_fsba(fs, inum)), inoblock,
@@ -83,7 +85,7 @@ getinode(struct uufsd *disk, union dinodep *dp, ino_t inum)
 gotit: switch (disk->d_ufs) {
        case 1:
                disk->d_dp.dp1 = &((struct ufs1_dinode *)inoblock)[inum - min];
-               if (ffs_oldfscompat_inode_read(fs, disk->d_dp, now.tv_sec))
+               if (ffs_oldfscompat_inode_read(fs, disk->d_dp, now))
                        putinode(disk);
                if (dp != NULL)
                        *dp = disk->d_dp;
@@ -93,8 +95,7 @@ gotit:        switch (disk->d_ufs) {
                if (dp != NULL)
                        *dp = disk->d_dp;
                if (ffs_verify_dinode_ckhash(fs, disk->d_dp.dp2) == 0) {
-                       if (ffs_oldfscompat_inode_read(fs, disk->d_dp,
-                           now.tv_sec))
+                       if (ffs_oldfscompat_inode_read(fs, disk->d_dp, now))
                                putinode(disk);
                        return (0);
                }
diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c
index b30e3aa5068b..f8e32bf4b157 100644
--- a/sbin/fsck_ffs/inode.c
+++ b/sbin/fsck_ffs/inode.c
@@ -647,7 +647,8 @@ setinodebuf(int cg, ino_t inosused)
         * If for some reason getting the time fails, we will use
         * the last time that the superblock was updated.
         */
-       if (clock_gettime(CLOCK_REALTIME_FAST, &time) == 0)
+       if (clock_gettime(CLOCK_REALTIME_FAST, &time) == 0 &&
+           time.tv_sec > sblock.fs_time)
                now = time.tv_sec;
        else
                now = sblock.fs_time;
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 8ac2075f8db8..11afa99bea76 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -189,8 +189,10 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struct fs 
*fs, ino_t ino)
 {
        struct ufs1_dinode *dip1;
        struct ufs2_dinode *dip2;
+       time_t now;
        int error;
 
+       now = time_second > fs->fs_time ? time_second : fs->fs_time;
        if (I_IS_UFS1(ip)) {
                dip1 = ip->i_din1;
                *dip1 =
@@ -203,7 +205,7 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struct fs 
*fs, ino_t ino)
                ip->i_gen = dip1->di_gen;
                ip->i_uid = dip1->di_uid;
                ip->i_gid = dip1->di_gid;
-               if (ffs_oldfscompat_inode_read(fs, ip->i_dp, time_second) &&
+               if (ffs_oldfscompat_inode_read(fs, ip->i_dp, now) &&
                    fs->fs_ronly == 0)
                        UFS_INODE_SET_FLAG(ip, IN_MODIFIED);
                return (0);
@@ -225,8 +227,7 @@ ffs_load_inode(struct buf *bp, struct inode *ip, struct fs 
*fs, ino_t ino)
        ip->i_gen = dip2->di_gen;
        ip->i_uid = dip2->di_uid;
        ip->i_gid = dip2->di_gid;
-       if (ffs_oldfscompat_inode_read(fs, ip->i_dp, time_second) &&
-           fs->fs_ronly == 0)
+       if (ffs_oldfscompat_inode_read(fs, ip->i_dp, now) && fs->fs_ronly == 0)
                UFS_INODE_SET_FLAG(ip, IN_MODIFIED);
        return (0);
 }

Reply via email to