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:
+13
-1
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user