1
0
mirror of https://git.FreeBSD.org/src.git synced 2026-06-02 11:24:32 +00:00

Defer the January 19, 2038 date limit in UFS1 filesystems to February 7, 2106

UFS1 uses a signed 32-bit value for its times. Zero is
January 1, 1970 UTC. Negative values of 32-bit time predate
January 1, 1970 back to December 13, 1901. The maximum positive
value for 32-bit time is on January 19, 2038 (my 84th birthday).
On that date, time will go negative and start registering from
December 13, 1901. Note that this issue only affects UFS1 filesystems
since UFS2 has 64-bit times. This fix changes UFS1 times from
signed to unsigned 32-bit values. With this change it will no longer
be possible to represent time from before January 1, 1970, but it
will accurately track time until February 7, 2106. Hopefully there
will not be any FreeBSD systems using UFS1 still in existence by
that time (and by then I will have been dead long enough that no-one
will know at whom to yell :-).

It is possible that some existing UFS1 systems will have set times
predating January 1, 1970. With this commit they will appear as
later than the current time. This commit checks inode times when
they are read into memory and if they are greater than the current
time resets them to the current time. By default this reset happens
silently, but setting the sysctl vfs.ffs.prttimechgs=1 will cause
console messages to be printed whenever a future time is changed.

Reviewed-by: kib
Tested-by:   Peter Holm
MFC-after:   1 week
Differential Revision: https://reviews.freebsd.org/D48472
This commit is contained in:
Kirk McKusick
2025-01-30 17:27:59 -08:00
parent a69ac6ee79
commit 1111a44301
8 changed files with 112 additions and 4 deletions
+13 -1
View File
@@ -43,6 +43,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <libufs.h>
@@ -53,6 +54,7 @@ getinode(struct uufsd *disk, union dinodep *dp, ino_t inum)
ino_t min, max;
caddr_t inoblock;
struct fs *fs;
struct timespec now;
ERROR(disk, NULL);
@@ -65,6 +67,10 @@ 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 (inum >= min && inum < max)
goto gotit;
bread(disk, fsbtodb(fs, ino_to_fsba(fs, inum)), inoblock,
@@ -74,6 +80,8 @@ 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))
putinode(disk);
if (dp != NULL)
*dp = disk->d_dp;
return (0);
@@ -81,8 +89,12 @@ gotit: switch (disk->d_ufs) {
disk->d_dp.dp2 = &((struct ufs2_dinode *)inoblock)[inum - min];
if (dp != NULL)
*dp = disk->d_dp;
if (ffs_verify_dinode_ckhash(fs, disk->d_dp.dp2) == 0)
if (ffs_verify_dinode_ckhash(fs, disk->d_dp.dp2) == 0) {
if (ffs_oldfscompat_inode_read(fs, disk->d_dp,
now.tv_sec))
putinode(disk);
return (0);
}
ERROR(disk, "check-hash failed for inode read from disk");
return (-1);
default:
+2
View File
@@ -29,6 +29,7 @@
#ifndef __LIBUFS_H__
#define __LIBUFS_H__
#include <stdbool.h>
/*
* Various disk controllers require their buffers to be aligned to the size
@@ -120,6 +121,7 @@ void ffs_clusteracct(struct fs *, struct cg *, ufs1_daddr_t, int);
void ffs_fragacct(struct fs *, int, int32_t [], int);
int ffs_isblock(struct fs *, u_char *, ufs1_daddr_t);
int ffs_isfreeblock(struct fs *, u_char *, ufs1_daddr_t);
bool ffs_oldfscompat_inode_read(struct fs *, union dinodep, time_t);
int ffs_sbsearch(void *, struct fs **, int, char *,
int (*)(void *, off_t, void **, int));
void ffs_setblock(struct fs *, u_char *, ufs1_daddr_t);
+24
View File
@@ -46,6 +46,7 @@
#include "fsck.h"
struct bufarea *icachebp; /* inode cache buffer */
static time_t now; /* current time of day */
static int iblock(struct inodesc *, off_t isize, int type);
static ufs2_daddr_t indir_blkatoff(ufs2_daddr_t, ino_t, ufs_lbn_t, ufs_lbn_t,
@@ -429,6 +430,7 @@ void
ginode(ino_t inumber, struct inode *ip)
{
ufs2_daddr_t iblk;
union dinodep dpp;
struct ufs2_dinode *dp;
if (inumber < UFS_ROOTINO || inumber >= maxino)
@@ -466,10 +468,14 @@ ginode(ino_t inumber, struct inode *ip)
if (sblock.fs_magic == FS_UFS1_MAGIC) {
ip->i_dp = (union dinode *)
&ip->i_bp->b_un.b_dinode1[inumber - ip->i_bp->b_index];
dpp.dp1 = (struct ufs1_dinode *)ip->i_dp;
if (ffs_oldfscompat_inode_read(&sblock, dpp, now))
inodirty(ip);
return;
}
ip->i_dp = (union dinode *)
&ip->i_bp->b_un.b_dinode2[inumber - ip->i_bp->b_index];
dpp.dp2 = dp = (struct ufs2_dinode *)ip->i_dp;
/* Do not check hash of inodes being created */
if (dp->di_mode != 0 && ffs_verify_dinode_ckhash(&sblock, dp)) {
pwarn("INODE CHECK-HASH FAILED");
@@ -481,6 +487,8 @@ ginode(ino_t inumber, struct inode *ip)
inodirty(ip);
}
}
if (ffs_oldfscompat_inode_read(&sblock, dpp, now))
inodirty(ip);
}
/*
@@ -519,6 +527,7 @@ getnextinode(ino_t inumber, int rebuiltcg)
mode_t mode;
ufs2_daddr_t ndb, blk;
union dinode *dp;
union dinodep dpp;
struct inode ip;
static caddr_t nextinop;
@@ -551,8 +560,10 @@ getnextinode(ino_t inumber, int rebuiltcg)
dp = (union dinode *)nextinop;
if (sblock.fs_magic == FS_UFS1_MAGIC) {
nextinop += sizeof(struct ufs1_dinode);
dpp.dp1 = (struct ufs1_dinode *)dp;
} else {
nextinop += sizeof(struct ufs2_dinode);
dpp.dp2 = (struct ufs2_dinode *)dp;
}
if ((ckhashadd & CK_INODE) != 0) {
ffs_update_dinode_ckhash(&sblock, (struct ufs2_dinode *)dp);
@@ -572,6 +583,8 @@ getnextinode(ino_t inumber, int rebuiltcg)
dirty(&inobuf);
}
}
if (ffs_oldfscompat_inode_read(&sblock, dpp, now))
dirty(&inobuf);
if (rebuiltcg && (char *)dp == inobuf.b_un.b_buf) {
/*
* Try to determine if we have reached the end of the
@@ -625,8 +638,19 @@ getnextinode(ino_t inumber, int rebuiltcg)
void
setinodebuf(int cg, ino_t inosused)
{
struct timespec time;
ino_t inum;
/*
* Get the current value of the present time.
* This will happen before each cylinder group is scanned.
* 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)
now = time.tv_sec;
else
now = sblock.fs_time;
inum = cg * sblock.fs_ipg;
lastvalidinum = inum + inosused - 1;
nextinum = inum;
+4
View File
@@ -285,6 +285,10 @@ upon reallocating file system blocks to be contiguous.
Enable support for the rearrangement of blocks
to be contiguous.
.Pq Default: 1 .
.It Va vfs.ffs.prttimechgs
Print a console message when timestamps for UFS1 filesystems are found
to be in the future and are changed to be the present time.
.Pq Default: 0 .
.El
.Sh HISTORY
The
+1
View File
@@ -83,6 +83,7 @@ int ffs_inotovp(struct mount *, ino_t, uint64_t, int, struct vnode **,
int ffs_isblock(struct fs *, uint8_t *, ufs1_daddr_t);
int ffs_isfreeblock(struct fs *, uint8_t *, ufs1_daddr_t);
void ffs_oldfscompat_write(struct fs *);
bool ffs_oldfscompat_inode_read(struct fs *, union dinodep, time_t);
int ffs_own_mount(const struct mount *mp);
int ffs_sbsearch(void *, struct fs **, int, struct malloc_type *,
int (*)(void *, off_t, void **, int));
+59
View File
@@ -34,6 +34,7 @@
#include <sys/limits.h>
#ifndef _KERNEL
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -59,6 +60,7 @@ struct malloc_type;
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/ucred.h>
#include <sys/sysctl.h>
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
@@ -395,6 +397,63 @@ ffs_oldfscompat_write(struct fs *fs)
}
}
/*
* Sanity checks for loading old filesystem inodes.
*
* XXX - Parts get retired eventually.
* Unfortunately new bits get added.
*/
static int prttimechgs = 0;
#ifdef _KERNEL
SYSCTL_DECL(_vfs_ffs);
SYSCTL_INT(_vfs_ffs, OID_AUTO, prttimechgs, CTLFLAG_RWTUN, &prttimechgs, 0,
"print UFS1 time changes made to inodes");
#endif /* _KERNEL */
bool
ffs_oldfscompat_inode_read(struct fs *fs, union dinodep dp, time_t now)
{
bool change;
change = false;
switch (fs->fs_magic) {
case FS_UFS2_MAGIC:
/* No changes for now */
break;
case FS_UFS1_MAGIC:
/*
* With the change to unsigned time values in UFS1, times set
* before Jan 1, 1970 will appear to be in the future. Check
* for future times and set them to be the current time.
*/
if (dp.dp1->di_ctime > now) {
if (prttimechgs)
printf("ctime %ud changed to %ld\n",
dp.dp1->di_ctime, (long)now);
dp.dp1->di_ctime = now;
change = true;
}
if (dp.dp1->di_mtime > now) {
if (prttimechgs)
printf("mtime %ud changed to %ld\n",
dp.dp1->di_mtime, (long)now);
dp.dp1->di_mtime = now;
dp.dp1->di_ctime = now;
change = true;
}
if (dp.dp1->di_atime > now) {
if (prttimechgs)
printf("atime %ud changed to %ld\n",
dp.dp1->di_atime, (long)now);
dp.dp1->di_atime = now;
dp.dp1->di_ctime = now;
change = true;
}
break;
}
return (change);
}
/*
* Verify the filesystem values.
*/
+6
View File
@@ -203,6 +203,9 @@ 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) &&
fs->fs_ronly == 0)
UFS_INODE_SET_FLAG(ip, IN_MODIFIED);
return (0);
}
dip2 = ((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ino));
@@ -222,6 +225,9 @@ 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)
UFS_INODE_SET_FLAG(ip, IN_MODIFIED);
return (0);
}
+3 -3
View File
@@ -185,11 +185,11 @@ struct ufs1_dinode {
uint32_t di_dirdepth; /* 4: IFDIR: depth from root dir */
};
uint64_t di_size; /* 8: File byte count. */
int32_t di_atime; /* 16: Last access time. */
uint32_t di_atime; /* 16: Last access time. */
int32_t di_atimensec; /* 20: Last access time. */
int32_t di_mtime; /* 24: Last modified time. */
uint32_t di_mtime; /* 24: Last modified time. */
int32_t di_mtimensec; /* 28: Last modified time. */
int32_t di_ctime; /* 32: Last inode change time. */
uint32_t di_ctime; /* 32: Last inode change time. */
int32_t di_ctimensec; /* 36: Last inode change time. */
union {
struct {