diff --git a/lib/libufs/inode.c b/lib/libufs/inode.c --- a/lib/libufs/inode.c +++ b/lib/libufs/inode.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -53,6 +54,7 @@ ino_t min, max; caddr_t inoblock; struct fs *fs; + struct timespec now; ERROR(disk, NULL); @@ -65,6 +67,10 @@ 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 @@ 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 @@ 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: diff --git a/lib/libufs/libufs.h b/lib/libufs/libufs.h --- a/lib/libufs/libufs.h +++ b/lib/libufs/libufs.h @@ -29,6 +29,7 @@ #ifndef __LIBUFS_H__ #define __LIBUFS_H__ +#include /* * Various disk controllers require their buffers to be aligned to the size @@ -120,6 +121,7 @@ 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); diff --git a/sbin/fsck_ffs/inode.c b/sbin/fsck_ffs/inode.c --- a/sbin/fsck_ffs/inode.c +++ b/sbin/fsck_ffs/inode.c @@ -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 @@ 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 @@ 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 @@ inodirty(ip); } } + if (ffs_oldfscompat_inode_read(&sblock, dpp, now)) + inodirty(ip); } /* @@ -519,6 +527,7 @@ 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 @@ 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 @@ 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 @@ 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; diff --git a/share/man/man4/ffs.4 b/share/man/man4/ffs.4 --- a/share/man/man4/ffs.4 +++ b/share/man/man4/ffs.4 @@ -285,6 +285,11 @@ 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 .El .Sh HISTORY The diff --git a/sys/ufs/ffs/ffs_extern.h b/sys/ufs/ffs/ffs_extern.h --- a/sys/ufs/ffs/ffs_extern.h +++ b/sys/ufs/ffs/ffs_extern.h @@ -83,6 +83,7 @@ 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)); diff --git a/sys/ufs/ffs/ffs_subr.c b/sys/ufs/ffs/ffs_subr.c --- a/sys/ufs/ffs/ffs_subr.c +++ b/sys/ufs/ffs/ffs_subr.c @@ -34,6 +34,7 @@ #include #ifndef _KERNEL +#include #include #include #include @@ -59,6 +60,7 @@ #include #include #include +#include #include #include @@ -395,6 +397,63 @@ } } +/* + * 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. */ diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -203,6 +203,9 @@ 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 @@ 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); } diff --git a/sys/ufs/ufs/dinode.h b/sys/ufs/ufs/dinode.h --- a/sys/ufs/ufs/dinode.h +++ b/sys/ufs/ufs/dinode.h @@ -185,11 +185,11 @@ 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 {