Merge tag 'y2038-vfs' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Sep 2019 16:42:37 +0000 (09:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 19 Sep 2019 16:42:37 +0000 (09:42 -0700)
Pull y2038 vfs updates from Arnd Bergmann:
 "Add inode timestamp clamping.

  This series from Deepa Dinamani adds a per-superblock minimum/maximum
  timestamp limit for a file system, and clamps timestamps as they are
  written, to avoid random behavior from integer overflow as well as
  having different time stamps on disk vs in memory.

  At mount time, a warning is now printed for any file system that can
  represent current timestamps but not future timestamps more than 30
  years into the future, similar to the arbitrary 30 year limit that was
  added to settimeofday().

  This was picked as a compromise to warn users to migrate to other file
  systems (e.g. ext4 instead of ext3) when they need the file system to
  survive beyond 2038 (or similar limits in other file systems), but not
  get in the way of normal usage"

* tag 'y2038-vfs' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/playground:
  ext4: Reduce ext4 timestamp warnings
  isofs: Initialize filesystem timestamp ranges
  pstore: fs superblock limits
  fs: omfs: Initialize filesystem timestamp ranges
  fs: hpfs: Initialize filesystem timestamp ranges
  fs: ceph: Initialize filesystem timestamp ranges
  fs: sysv: Initialize filesystem timestamp ranges
  fs: affs: Initialize filesystem timestamp ranges
  fs: fat: Initialize filesystem timestamp ranges
  fs: cifs: Initialize filesystem timestamp ranges
  fs: nfs: Initialize filesystem timestamp ranges
  ext4: Initialize timestamps limits
  9p: Fill min and max timestamps in sb
  fs: Fill in max and min timestamps in superblock
  utimes: Clamp the timestamps before update
  mount: Add mount warning for impending timestamp expiry
  timestamp_truncate: Replace users of timespec64_trunc
  vfs: Add timestamp_truncate() api
  vfs: Add file timestamp range support

47 files changed:
fs/9p/vfs_super.c
fs/affs/amigaffs.c
fs/affs/amigaffs.h
fs/affs/inode.c
fs/affs/super.c
fs/attr.c
fs/befs/linuxvfs.c
fs/bfs/inode.c
fs/ceph/super.c
fs/cifs/cifsfs.c
fs/cifs/netmisc.c
fs/coda/inode.c
fs/configfs/inode.c
fs/cramfs/inode.c
fs/efs/super.c
fs/ext2/super.c
fs/ext4/ext4.h
fs/ext4/super.c
fs/f2fs/file.c
fs/fat/inode.c
fs/freevxfs/vxfs_super.c
fs/hpfs/hpfs_fn.h
fs/hpfs/super.c
fs/inode.c
fs/isofs/inode.c
fs/jffs2/fs.c
fs/jfs/super.c
fs/kernfs/inode.c
fs/minix/inode.c
fs/namespace.c
fs/nfs/super.c
fs/ntfs/inode.c
fs/omfs/inode.c
fs/pstore/ram.c
fs/qnx4/inode.c
fs/qnx6/inode.c
fs/reiserfs/super.c
fs/romfs/super.c
fs/squashfs/super.c
fs/super.c
fs/sysv/super.c
fs/ubifs/file.c
fs/ufs/super.c
fs/utimes.c
fs/xfs/xfs_super.c
include/linux/fs.h
include/linux/time64.h

index 08112fbcaeced97c724f994d17558b531ec9997c..ca243e658d7186251c2b622c799476c277835c75 100644 (file)
@@ -69,8 +69,12 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
        if (v9fs_proto_dotl(v9ses)) {
                sb->s_op = &v9fs_super_ops_dotl;
                sb->s_xattr = v9fs_xattr_handlers;
-       } else
+       } else {
                sb->s_op = &v9fs_super_ops;
+               sb->s_time_max = U32_MAX;
+       }
+
+       sb->s_time_min = 0;
 
        ret = super_setup_bdi(sb);
        if (ret)
index 14a6c1b90c9fb8e0652ad848fa46536cfd96484b..f708c45d5f664d84475f6a42214d0732c43325b9 100644 (file)
@@ -375,7 +375,7 @@ affs_secs_to_datestamp(time64_t secs, struct affs_date *ds)
        u32      minute;
        s32      rem;
 
-       secs -= sys_tz.tz_minuteswest * 60 + ((8 * 365 + 2) * 24 * 60 * 60);
+       secs -= sys_tz.tz_minuteswest * 60 + AFFS_EPOCH_DELTA;
        if (secs < 0)
                secs = 0;
        days    = div_s64_rem(secs, 86400, &rem);
index f9bef905665927551acd4034370a902e6441981b..81fb396d4dfa37fe7f4296e50751eb5ad1b797df 100644 (file)
@@ -32,6 +32,9 @@
 
 #define AFFS_ROOT_BMAPS                25
 
+/* Seconds since Amiga epoch of 1978/01/01 to UNIX */
+#define AFFS_EPOCH_DELTA ((8 * 365 + 2) * 86400LL)
+
 struct affs_date {
        __be32 days;
        __be32 mins;
index 73598bff8506d45dbfcdfa2804bdbe393fe32149..a346cf7659f19804db57d64d6702c5d2130b7230 100644 (file)
@@ -150,10 +150,10 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino)
        }
 
        inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec
-                      = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) +
+                      = (be32_to_cpu(tail->change.days) * 86400LL +
                         be32_to_cpu(tail->change.mins) * 60 +
                         be32_to_cpu(tail->change.ticks) / 50 +
-                        ((8 * 365 + 2) * 24 * 60 * 60)) +
+                        AFFS_EPOCH_DELTA) +
                         sys_tz.tz_minuteswest * 60;
        inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0;
        affs_brelse(bh);
index e7d036efbaa1ec474c2825427926834895b3f2e2..cc463ae47c12a0bc7cc1c6e444103a8950154b8c 100644 (file)
@@ -355,6 +355,10 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_op                = &affs_sops;
        sb->s_flags |= SB_NODIRATIME;
 
+       sb->s_time_gran = NSEC_PER_SEC;
+       sb->s_time_min = sys_tz.tz_minuteswest * 60 + AFFS_EPOCH_DELTA;
+       sb->s_time_max = 86400LL * U32_MAX + 86400 + sb->s_time_min;
+
        sbi = kzalloc(sizeof(struct affs_sb_info), GFP_KERNEL);
        if (!sbi)
                return -ENOMEM;
index d22e8187477fa77350c003d73a446284c02383bb..df28035aa23ef74020a58c3ac826a5c8bf66c333 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -183,15 +183,18 @@ void setattr_copy(struct inode *inode, const struct iattr *attr)
                inode->i_uid = attr->ia_uid;
        if (ia_valid & ATTR_GID)
                inode->i_gid = attr->ia_gid;
-       if (ia_valid & ATTR_ATIME)
-               inode->i_atime = timespec64_trunc(attr->ia_atime,
-                                                 inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_MTIME)
-               inode->i_mtime = timespec64_trunc(attr->ia_mtime,
-                                                 inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_CTIME)
-               inode->i_ctime = timespec64_trunc(attr->ia_ctime,
-                                                 inode->i_sb->s_time_gran);
+       if (ia_valid & ATTR_ATIME) {
+               inode->i_atime = timestamp_truncate(attr->ia_atime,
+                                                 inode);
+       }
+       if (ia_valid & ATTR_MTIME) {
+               inode->i_mtime = timestamp_truncate(attr->ia_mtime,
+                                                 inode);
+       }
+       if (ia_valid & ATTR_CTIME) {
+               inode->i_ctime = timestamp_truncate(attr->ia_ctime,
+                                                 inode);
+       }
        if (ia_valid & ATTR_MODE) {
                umode_t mode = attr->ia_mode;
 
index 462d096ff3e976c2dfaa9c6846fc72189c98a197..64cdf4d8e424513607a7401672b3b0d92d0813c0 100644 (file)
@@ -893,6 +893,8 @@ befs_fill_super(struct super_block *sb, void *data, int silent)
        sb_set_blocksize(sb, (ulong) befs_sb->block_size);
        sb->s_op = &befs_sops;
        sb->s_export_op = &befs_export_operations;
+       sb->s_time_min = 0;
+       sb->s_time_max = 0xffffffffffffll;
        root = befs_iget(sb, iaddr2blockno(sb, &(befs_sb->root_dir)));
        if (IS_ERR(root)) {
                ret = PTR_ERR(root);
index 5e97bed073d7f00f506e6c39c355dfc86982a913..f8ce1368218b2333106f460d6a021b2d5eb61ae6 100644 (file)
@@ -324,6 +324,8 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent)
                return -ENOMEM;
        mutex_init(&info->bfs_lock);
        s->s_fs_info = info;
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
 
        sb_set_blocksize(s, BFS_BSIZE);
 
index ab4868c7308ecc9011fd76bf06307244658f2580..377fafc76f200526f9bac4ca7a7468fe004ef0c4 100644 (file)
@@ -979,6 +979,8 @@ static int ceph_set_super(struct super_block *s, void *data)
        s->s_export_op = &ceph_export_ops;
 
        s->s_time_gran = 1;
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
 
        ret = set_anon_super(s, NULL);  /* what is that second arg for? */
        if (ret != 0)
index 4e2f74894e9b3c34eece9d115e6001cb7f96a5e6..e8afff702bb8e60bba9c43fbc593fd6f9e0a1b74 100644 (file)
 #include "dfs_cache.h"
 #endif
 
+/*
+ * DOS dates from 1980/1/1 through 2107/12/31
+ * Protocol specifications indicate the range should be to 119, which
+ * limits maximum year to 2099. But this range has not been checked.
+ */
+#define SMB_DATE_MAX (127<<9 | 12<<5 | 31)
+#define SMB_DATE_MIN (0<<9 | 1<<5 | 1)
+#define SMB_TIME_MAX (23<<11 | 59<<5 | 29)
+
 int cifsFYI = 0;
 bool traceSMB;
 bool enable_oplocks = true;
@@ -142,6 +151,7 @@ cifs_read_super(struct super_block *sb)
        struct inode *inode;
        struct cifs_sb_info *cifs_sb;
        struct cifs_tcon *tcon;
+       struct timespec64 ts;
        int rc = 0;
 
        cifs_sb = CIFS_SB(sb);
@@ -161,6 +171,18 @@ cifs_read_super(struct super_block *sb)
        /* BB FIXME fix time_gran to be larger for LANMAN sessions */
        sb->s_time_gran = 100;
 
+       if (tcon->unix_ext) {
+               ts = cifs_NTtimeToUnix(0);
+               sb->s_time_min = ts.tv_sec;
+               ts = cifs_NTtimeToUnix(cpu_to_le64(S64_MAX));
+               sb->s_time_max = ts.tv_sec;
+       } else {
+               ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MIN), 0, 0);
+               sb->s_time_min = ts.tv_sec;
+               ts = cnvrtDosUnixTm(cpu_to_le16(SMB_DATE_MAX), cpu_to_le16(SMB_TIME_MAX), 0);
+               sb->s_time_max = ts.tv_sec;
+       }
+
        sb->s_magic = CIFS_MAGIC_NUMBER;
        sb->s_op = &cifs_super_ops;
        sb->s_xattr = cifs_xattr_handlers;
index ed92958e842d3502b4d54817796c0f4a81d54c38..49c17ee182544d6f986334379d76976b6f3ef3b3 100644 (file)
@@ -949,8 +949,8 @@ static const int total_days_of_prev_months[] = {
 struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
 {
        struct timespec64 ts;
-       time64_t sec;
-       int min, days, month, year;
+       time64_t sec, days;
+       int min, day, month, year;
        u16 date = le16_to_cpu(le_date);
        u16 time = le16_to_cpu(le_time);
        SMB_TIME *st = (SMB_TIME *)&time;
@@ -966,15 +966,15 @@ struct timespec64 cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)
        sec += 60 * 60 * st->Hours;
        if (st->Hours > 24)
                cifs_dbg(VFS, "illegal hours %d\n", st->Hours);
-       days = sd->Day;
+       day = sd->Day;
        month = sd->Month;
-       if (days < 1 || days > 31 || month < 1 || month > 12) {
-               cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, days);
-               days = clamp(days, 1, 31);
+       if (day < 1 || day > 31 || month < 1 || month > 12) {
+               cifs_dbg(VFS, "illegal date, month %d day: %d\n", month, day);
+               day = clamp(day, 1, 31);
                month = clamp(month, 1, 12);
        }
        month -= 1;
-       days += total_days_of_prev_months[month];
+       days = day + total_days_of_prev_months[month];
        days += 3652; /* account for difference in days between 1980 and 1970 */
        year = sd->Year;
        days += year * 365;
index 321f56e487cbe575904ded73ba1780fed6804804..b1c70e2b9b1e6e7bdebd993708931e4ec22ace74 100644 (file)
@@ -188,6 +188,9 @@ static int coda_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_magic = CODA_SUPER_MAGIC;
        sb->s_op = &coda_super_operations;
        sb->s_d_op = &coda_dentry_operations;
+       sb->s_time_gran = 1;
+       sb->s_time_min = S64_MIN;
+       sb->s_time_max = S64_MAX;
 
        error = super_setup_bdi(sb);
        if (error)
index ab0284321912a3784cd847e4da3e2184bb921440..884dcf06cfbec85e486327d4cde9d6cfcdebabb3 100644 (file)
@@ -76,14 +76,14 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr)
        if (ia_valid & ATTR_GID)
                sd_iattr->ia_gid = iattr->ia_gid;
        if (ia_valid & ATTR_ATIME)
-               sd_iattr->ia_atime = timespec64_trunc(iattr->ia_atime,
-                                                     inode->i_sb->s_time_gran);
+               sd_iattr->ia_atime = timestamp_truncate(iattr->ia_atime,
+                                                     inode);
        if (ia_valid & ATTR_MTIME)
-               sd_iattr->ia_mtime = timespec64_trunc(iattr->ia_mtime,
-                                                     inode->i_sb->s_time_gran);
+               sd_iattr->ia_mtime = timestamp_truncate(iattr->ia_mtime,
+                                                     inode);
        if (ia_valid & ATTR_CTIME)
-               sd_iattr->ia_ctime = timespec64_trunc(iattr->ia_ctime,
-                                                     inode->i_sb->s_time_gran);
+               sd_iattr->ia_ctime = timestamp_truncate(iattr->ia_ctime,
+                                                     inode);
        if (ia_valid & ATTR_MODE) {
                umode_t mode = iattr->ia_mode;
 
index 9352487bd0fc660f99f67407e323724cf2ff4b76..4d1d8b7761ed0f06622981bbe2dd66e5f37ce079 100644 (file)
@@ -597,6 +597,8 @@ static int cramfs_finalize_super(struct super_block *sb,
 
        /* Set it all up.. */
        sb->s_flags |= SB_RDONLY;
+       sb->s_time_min = 0;
+       sb->s_time_max = 0;
        sb->s_op = &cramfs_ops;
        root = get_cramfs_inode(sb, cramfs_root, 0);
        if (IS_ERR(root))
index 867fc24dee207e0ec0a4c8eecd5c80132962565f..4a6ebff2af76f37678e0d1bae5bff8cf9787d6c4 100644 (file)
@@ -257,6 +257,8 @@ static int efs_fill_super(struct super_block *s, void *d, int silent)
        if (!sb)
                return -ENOMEM;
        s->s_fs_info = sb;
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
  
        s->s_magic              = EFS_SUPER_MAGIC;
        if (!sb_set_blocksize(s, EFS_BLOCKSIZE)) {
index 44eb6e7eb492f2fc302cbe0652fd83269e7d2950..baa36c6fb71e44405f8ac23edc39918c65eb75f7 100644 (file)
@@ -1002,6 +1002,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);
        sb->s_max_links = EXT2_LINK_MAX;
+       sb->s_time_min = S32_MIN;
+       sb->s_time_max = S32_MAX;
 
        if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
                sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
index 9c7f4036021b436d47be4589ff943b44216f425c..42c6e4a5e673f7c2de8f1d7c182cf3979a78c007 100644 (file)
@@ -832,11 +832,13 @@ static inline void ext4_decode_extra_time(struct timespec64 *time,
 
 #define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode)                          \
 do {                                                                           \
-       (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);                \
        if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra))     {\
+               (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec);        \
                (raw_inode)->xtime ## _extra =                                  \
                                ext4_encode_extra_time(&(inode)->xtime);        \
                }                                                               \
+       else    \
+               (raw_inode)->xtime = cpu_to_le32(clamp_t(int32_t, (inode)->xtime.tv_sec, S32_MIN, S32_MAX));    \
 } while (0)
 
 #define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode)                               \
@@ -1643,6 +1645,10 @@ static inline bool ext4_verity_in_progress(struct inode *inode)
 
 #define EXT4_GOOD_OLD_INODE_SIZE 128
 
+#define EXT4_EXTRA_TIMESTAMP_MAX       (((s64)1 << 34) - 1  + S32_MIN)
+#define EXT4_NON_EXTRA_TIMESTAMP_MAX   S32_MAX
+#define EXT4_TIMESTAMP_MIN             S32_MIN
+
 /*
  * Feature set definitions
  */
index 27cd622676e717c597d0f0d161b7f41d5bb82a51..3db5f17228b7691f9208252e937c16349d486e12 100644 (file)
@@ -4039,8 +4039,21 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                               sbi->s_inode_size);
                        goto failed_mount;
                }
-               if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE)
-                       sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2);
+               /*
+                * i_atime_extra is the last extra field available for [acm]times in
+                * struct ext4_inode. Checking for that field should suffice to ensure
+                * we have extra space for all three.
+                */
+               if (sbi->s_inode_size >= offsetof(struct ext4_inode, i_atime_extra) +
+                       sizeof(((struct ext4_inode *)0)->i_atime_extra)) {
+                       sb->s_time_gran = 1;
+                       sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX;
+               } else {
+                       sb->s_time_gran = NSEC_PER_SEC;
+                       sb->s_time_max = EXT4_NON_EXTRA_TIMESTAMP_MAX;
+               }
+
+               sb->s_time_min = EXT4_TIMESTAMP_MIN;
        }
 
        sbi->s_desc_size = le16_to_cpu(es->s_desc_size);
index 39fffc19e00c910a2b6218d14fd213aa75c0db47..56efde9d36596bd3c9e5bdb328abe6a47df61180 100644 (file)
@@ -745,15 +745,18 @@ static void __setattr_copy(struct inode *inode, const struct iattr *attr)
                inode->i_uid = attr->ia_uid;
        if (ia_valid & ATTR_GID)
                inode->i_gid = attr->ia_gid;
-       if (ia_valid & ATTR_ATIME)
-               inode->i_atime = timespec64_trunc(attr->ia_atime,
-                                                 inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_MTIME)
-               inode->i_mtime = timespec64_trunc(attr->ia_mtime,
-                                                 inode->i_sb->s_time_gran);
-       if (ia_valid & ATTR_CTIME)
-               inode->i_ctime = timespec64_trunc(attr->ia_ctime,
-                                                 inode->i_sb->s_time_gran);
+       if (ia_valid & ATTR_ATIME) {
+               inode->i_atime = timestamp_truncate(attr->ia_atime,
+                                                 inode);
+       }
+       if (ia_valid & ATTR_MTIME) {
+               inode->i_mtime = timestamp_truncate(attr->ia_mtime,
+                                                 inode);
+       }
+       if (ia_valid & ATTR_CTIME) {
+               inode->i_ctime = timestamp_truncate(attr->ia_ctime,
+                                                 inode);
+       }
        if (ia_valid & ATTR_MODE) {
                umode_t mode = attr->ia_mode;
 
index 05689198f5aff712b771916b52893ac9a0196959..5f04c5c810fb67276a92a705bb4ccf5ed6f89da5 100644 (file)
 
 #define KB_IN_SECTORS 2
 
+/* DOS dates from 1980/1/1 through 2107/12/31 */
+#define FAT_DATE_MIN (0<<9 | 1<<5 | 1)
+#define FAT_DATE_MAX (127<<9 | 12<<5 | 31)
+#define FAT_TIME_MAX (23<<11 | 59<<5 | 29)
+
 /*
  * A deserialized copy of the on-disk structure laid out in struct
  * fat_boot_sector.
@@ -1605,6 +1610,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
        int debug;
        long error;
        char buf[50];
+       struct timespec64 ts;
 
        /*
         * GFP_KERNEL is ok here, because while we do hold the
@@ -1698,6 +1704,12 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat,
        sbi->free_clus_valid = 0;
        sbi->prev_free = FAT_START_ENT;
        sb->s_maxbytes = 0xffffffff;
+       fat_time_fat2unix(sbi, &ts, 0, cpu_to_le16(FAT_DATE_MIN), 0);
+       sb->s_time_min = ts.tv_sec;
+
+       fat_time_fat2unix(sbi, &ts, cpu_to_le16(FAT_TIME_MAX),
+                         cpu_to_le16(FAT_DATE_MAX), 0);
+       sb->s_time_max = ts.tv_sec;
 
        if (!sbi->fat_length && bpb.fat32_length) {
                struct fat_boot_fsinfo *fsinfo;
index a89f68c3cbed7b279702fa58876c5200cabc2d72..578a5062706ee9db9aaa43005276ccefd0680cbc 100644 (file)
@@ -229,6 +229,8 @@ static int vxfs_fill_super(struct super_block *sbp, void *dp, int silent)
 
        sbp->s_op = &vxfs_super_ops;
        sbp->s_fs_info = infp;
+       sbp->s_time_min = 0;
+       sbp->s_time_max = U32_MAX;
 
        if (!vxfs_try_sb_magic(sbp, silent, 1,
                        (__force __fs32)cpu_to_le32(VXFS_SUPER_MAGIC))) {
index ab2e7cc2ff33295c749e7bd2df4a098e6d3fc850..1cca83218fb58892f7a939aeea4cab70abd58f55 100644 (file)
@@ -334,7 +334,7 @@ long hpfs_ioctl(struct file *file, unsigned cmd, unsigned long arg);
  * local time (HPFS) to GMT (Unix)
  */
 
-static inline time64_t local_to_gmt(struct super_block *s, time32_t t)
+static inline time64_t local_to_gmt(struct super_block *s, time64_t t)
 {
        extern struct timezone sys_tz;
        return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift;
@@ -343,9 +343,7 @@ static inline time64_t local_to_gmt(struct super_block *s, time32_t t)
 static inline time32_t gmt_to_local(struct super_block *s, time64_t t)
 {
        extern struct timezone sys_tz;
-       t = t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift;
-
-       return clamp_t(time64_t, t, 0, U32_MAX);
+       return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift;
 }
 
 static inline time32_t local_get_seconds(struct super_block *s)
index 9db6d84f0d62c578b6033f8fa91ce64926913ecc..0a677a9aaf340c2661293798c16fede4d8021d88 100644 (file)
@@ -614,6 +614,8 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent)
        s->s_magic = HPFS_SUPER_MAGIC;
        s->s_op = &hpfs_sops;
        s->s_d_op = &hpfs_dentry_operations;
+       s->s_time_min =  local_to_gmt(s, 0);
+       s->s_time_max =  local_to_gmt(s, U32_MAX);
 
        sbi->sb_root = le32_to_cpu(superblock->root);
        sbi->sb_fs_size = le32_to_cpu(superblock->n_sectors);
index 0f1e3b563c47209cd9a09dd1591f6b08d0bd43d4..64bf28cf05cde859ab0b72abd4a30cbdc7e3912b 100644 (file)
@@ -2166,6 +2166,37 @@ struct timespec64 timespec64_trunc(struct timespec64 t, unsigned gran)
 }
 EXPORT_SYMBOL(timespec64_trunc);
 
+/**
+ * timestamp_truncate - Truncate timespec to a granularity
+ * @t: Timespec
+ * @inode: inode being updated
+ *
+ * Truncate a timespec to the granularity supported by the fs
+ * containing the inode. Always rounds down. gran must
+ * not be 0 nor greater than a second (NSEC_PER_SEC, or 10^9 ns).
+ */
+struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       unsigned int gran = sb->s_time_gran;
+
+       t.tv_sec = clamp(t.tv_sec, sb->s_time_min, sb->s_time_max);
+       if (unlikely(t.tv_sec == sb->s_time_max || t.tv_sec == sb->s_time_min))
+               t.tv_nsec = 0;
+
+       /* Avoid division in the common cases 1 ns and 1 s. */
+       if (gran == 1)
+               ; /* nothing */
+       else if (gran == NSEC_PER_SEC)
+               t.tv_nsec = 0;
+       else if (gran > 1 && gran < NSEC_PER_SEC)
+               t.tv_nsec -= t.tv_nsec % gran;
+       else
+               WARN(1, "invalid file time granularity: %u", gran);
+       return t;
+}
+EXPORT_SYMBOL(timestamp_truncate);
+
 /**
  * current_time - Return FS time
  * @inode: inode.
@@ -2187,7 +2218,7 @@ struct timespec64 current_time(struct inode *inode)
                return now;
        }
 
-       return timespec64_trunc(now, inode->i_sb->s_time_gran);
+       return timestamp_truncate(now, inode);
 }
 EXPORT_SYMBOL(current_time);
 
index 9e30d8703735cfeca35a4746ee3b18814f31a61f..62c0462dc89f3e52d88edb162717d80091c239a5 100644 (file)
@@ -30,6 +30,9 @@
 #include "isofs.h"
 #include "zisofs.h"
 
+/* max tz offset is 13 hours */
+#define MAX_TZ_OFFSET (52*15*60)
+
 #define BEQUIET
 
 static int isofs_hashi(const struct dentry *parent, struct qstr *qstr);
@@ -801,6 +804,10 @@ root_found:
         */
        s->s_maxbytes = 0x80000000000LL;
 
+       /* ECMA-119 timestamp from 1900/1/1 with tz offset */
+       s->s_time_min = mktime64(1900, 1, 1, 0, 0, 0) - MAX_TZ_OFFSET;
+       s->s_time_max = mktime64(U8_MAX+1900, 12, 31, 23, 59, 59) + MAX_TZ_OFFSET;
+
        /* Set this for reference. Its not currently used except on write
           which we don't have .. */
 
index 8a20ddd25f2da4b2b2cf6ad229097682dad006f0..d0b59d03a7a992f6370e598ec69671fbaa1a43bb 100644 (file)
@@ -590,6 +590,9 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_blocksize = PAGE_SIZE;
        sb->s_blocksize_bits = PAGE_SHIFT;
        sb->s_magic = JFFS2_SUPER_MAGIC;
+       sb->s_time_min = 0;
+       sb->s_time_max = U32_MAX;
+
        if (!sb_rdonly(sb))
                jffs2_start_garbage_collect_thread(c);
        return 0;
index f4e10cb9f734a3f1de1bd47a08340247bd273ad1..b2dc4d1f9dcc559117e0a5bcdd38676e873d3e53 100644 (file)
@@ -503,6 +503,8 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_fs_info = sbi;
        sb->s_max_links = JFS_LINK_MAX;
+       sb->s_time_min = 0;
+       sb->s_time_max = U32_MAX;
        sbi->sb = sb;
        sbi->uid = INVALID_UID;
        sbi->gid = INVALID_GID;
index f3f3984cce80b048317c3942669c20e3e064cd2e..f3eaa8869f42ff12d037893b78d64535c1c7b6fa 100644 (file)
@@ -158,12 +158,11 @@ static inline void set_default_inode_attr(struct inode *inode, umode_t mode)
 static inline void set_inode_attr(struct inode *inode,
                                  struct kernfs_iattrs *attrs)
 {
-       struct super_block *sb = inode->i_sb;
        inode->i_uid = attrs->ia_uid;
        inode->i_gid = attrs->ia_gid;
-       inode->i_atime = timespec64_trunc(attrs->ia_atime, sb->s_time_gran);
-       inode->i_mtime = timespec64_trunc(attrs->ia_mtime, sb->s_time_gran);
-       inode->i_ctime = timespec64_trunc(attrs->ia_ctime, sb->s_time_gran);
+       inode->i_atime = timestamp_truncate(attrs->ia_atime, inode);
+       inode->i_mtime = timestamp_truncate(attrs->ia_mtime, inode);
+       inode->i_ctime = timestamp_truncate(attrs->ia_ctime, inode);
 }
 
 static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
index f96073f2543297ed94883eacdd2835f869d14480..7cb5fd38eb149ffebd76aa68e61990cbf52820c8 100644 (file)
@@ -277,6 +277,8 @@ static int minix_fill_super(struct super_block *s, void *data, int silent)
 
        /* set up enough so that it can read an inode */
        s->s_op = &minix_sops;
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
        root_inode = minix_iget(s, MINIX_ROOT_INO);
        if (IS_ERR(root_inode)) {
                ret = PTR_ERR(root_inode);
index 227f7b34303465a2621497524dece91038267b23..93c043245c463eb77c4c48316e8ba8ae80f47830 100644 (file)
@@ -2466,6 +2466,26 @@ static void set_mount_attributes(struct mount *mnt, unsigned int mnt_flags)
        unlock_mount_hash();
 }
 
+static void mnt_warn_timestamp_expiry(struct path *mountpoint, struct vfsmount *mnt)
+{
+       struct super_block *sb = mnt->mnt_sb;
+
+       if (!__mnt_is_readonly(mnt) &&
+          (ktime_get_real_seconds() + TIME_UPTIME_SEC_MAX > sb->s_time_max)) {
+               char *buf = (char *)__get_free_page(GFP_KERNEL);
+               char *mntpath = buf ? d_path(mountpoint, buf, PAGE_SIZE) : ERR_PTR(-ENOMEM);
+               struct tm tm;
+
+               time64_to_tm(sb->s_time_max, 0, &tm);
+
+               pr_warn("Mounted %s file system at %s supports timestamps until %04ld (0x%llx)\n",
+                       sb->s_type->name, mntpath,
+                       tm.tm_year+1900, (unsigned long long)sb->s_time_max);
+
+               free_page((unsigned long)buf);
+       }
+}
+
 /*
  * Handle reconfiguration of the mountpoint only without alteration of the
  * superblock it refers to.  This is triggered by specifying MS_REMOUNT|MS_BIND
@@ -2491,6 +2511,9 @@ static int do_reconfigure_mnt(struct path *path, unsigned int mnt_flags)
        if (ret == 0)
                set_mount_attributes(mnt, mnt_flags);
        up_write(&sb->s_umount);
+
+       mnt_warn_timestamp_expiry(path, &mnt->mnt);
+
        return ret;
 }
 
@@ -2531,6 +2554,9 @@ static int do_remount(struct path *path, int ms_flags, int sb_flags,
                }
                up_write(&sb->s_umount);
        }
+
+       mnt_warn_timestamp_expiry(path, &mnt->mnt);
+
        put_fs_context(fc);
        return err;
 }
@@ -2739,8 +2765,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
                return PTR_ERR(mnt);
 
        error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags);
-       if (error < 0)
+       if (error < 0) {
                mntput(mnt);
+               return error;
+       }
+
+       mnt_warn_timestamp_expiry(mountpoint, mnt);
+
        return error;
 }
 
index 703f595dce90c1bfb8dfad1ca589221cc7f6f0b5..19a76cfa8b1fcf9c2cd976c43ba8823110115562 100644 (file)
@@ -2382,6 +2382,15 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
                sb->s_flags |= SB_POSIXACL;
                sb->s_time_gran = 1;
                sb->s_export_op = &nfs_export_ops;
+       } else
+               sb->s_time_gran = 1000;
+
+       if (server->nfs_client->rpc_ops->version != 4) {
+               sb->s_time_min = 0;
+               sb->s_time_max = U32_MAX;
+       } else {
+               sb->s_time_min = S64_MIN;
+               sb->s_time_max = S64_MAX;
        }
 
        nfs_initialise_sb(sb);
@@ -2402,7 +2411,6 @@ static void nfs_clone_super(struct super_block *sb,
        sb->s_maxbytes = old_sb->s_maxbytes;
        sb->s_xattr = old_sb->s_xattr;
        sb->s_op = old_sb->s_op;
-       sb->s_time_gran = 1;
        sb->s_export_op = old_sb->s_export_op;
 
        if (server->nfs_client->rpc_ops->version != 2) {
@@ -2410,6 +2418,16 @@ static void nfs_clone_super(struct super_block *sb,
                 * so ourselves when necessary.
                 */
                sb->s_flags |= SB_POSIXACL;
+               sb->s_time_gran = 1;
+       } else
+               sb->s_time_gran = 1000;
+
+       if (server->nfs_client->rpc_ops->version != 4) {
+               sb->s_time_min = 0;
+               sb->s_time_max = U32_MAX;
+       } else {
+               sb->s_time_min = S64_MIN;
+               sb->s_time_max = S64_MAX;
        }
 
        nfs_initialise_sb(sb);
index 8baa34baf548bd4ca908f184455b4a8946dd3d46..6c7388430ad3a9595578f28b08a34746aced4c85 100644 (file)
@@ -2899,15 +2899,18 @@ int ntfs_setattr(struct dentry *dentry, struct iattr *attr)
                        ia_valid |= ATTR_MTIME | ATTR_CTIME;
                }
        }
-       if (ia_valid & ATTR_ATIME)
-               vi->i_atime = timespec64_trunc(attr->ia_atime,
-                                              vi->i_sb->s_time_gran);
-       if (ia_valid & ATTR_MTIME)
-               vi->i_mtime = timespec64_trunc(attr->ia_mtime,
-                                              vi->i_sb->s_time_gran);
-       if (ia_valid & ATTR_CTIME)
-               vi->i_ctime = timespec64_trunc(attr->ia_ctime,
-                                              vi->i_sb->s_time_gran);
+       if (ia_valid & ATTR_ATIME) {
+               vi->i_atime = timestamp_truncate(attr->ia_atime,
+                                              vi);
+       }
+       if (ia_valid & ATTR_MTIME) {
+               vi->i_mtime = timestamp_truncate(attr->ia_mtime,
+                                              vi);
+       }
+       if (ia_valid & ATTR_CTIME) {
+               vi->i_ctime = timestamp_truncate(attr->ia_ctime,
+                                              vi);
+       }
        mark_inode_dirty(vi);
 out:
        return err;
index 08226a835ec3334fc5f09dba90a74a5e1a953362..b76ec6b88ded5c8c0717817bd77ade59991f8fbd 100644 (file)
@@ -478,6 +478,10 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_maxbytes = 0xffffffff;
 
+       sb->s_time_gran = NSEC_PER_MSEC;
+       sb->s_time_min = 0;
+       sb->s_time_max = U64_MAX / MSEC_PER_SEC;
+
        sb_set_blocksize(sb, 0x200);
 
        bh = sb_bread(sb, 0);
index 2bb3468fc93aadc8a4e88e9f2aa911210eea7891..8caff834f002668eb79d84d2adf8c108dbc2c640 100644 (file)
@@ -144,6 +144,7 @@ static int ramoops_read_kmsg_hdr(char *buffer, struct timespec64 *time,
        if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lld.%lu-%c\n%n",
                   (time64_t *)&time->tv_sec, &time->tv_nsec, &data_type,
                   &header_length) == 3) {
+               time->tv_nsec *= 1000;
                if (data_type == 'C')
                        *compressed = true;
                else
@@ -151,6 +152,7 @@ static int ramoops_read_kmsg_hdr(char *buffer, struct timespec64 *time,
        } else if (sscanf(buffer, RAMOOPS_KERNMSG_HDR "%lld.%lu\n%n",
                          (time64_t *)&time->tv_sec, &time->tv_nsec,
                          &header_length) == 2) {
+               time->tv_nsec *= 1000;
                *compressed = false;
        } else {
                time->tv_sec = 0;
index 922d083bbc7c236e39be5991d88c04753e85dd64..e8da1cde87b9be255025cc138ab45853b0209867 100644 (file)
@@ -201,6 +201,8 @@ static int qnx4_fill_super(struct super_block *s, void *data, int silent)
        s->s_op = &qnx4_sops;
        s->s_magic = QNX4_SUPER_MAGIC;
        s->s_flags |= SB_RDONLY;        /* Yup, read-only yet */
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
 
        /* Check the superblock signature. Since the qnx4 code is
           dangerous, we should leave as quickly as possible
index 0f8b0ff1ba4327b3bbd2cc8792b5a71a1500ff91..345db56c98fd7dbf31e3be56bc96c1ac66d8e849 100644 (file)
@@ -429,6 +429,8 @@ mmi_success:
        s->s_op = &qnx6_sops;
        s->s_magic = QNX6_SUPER_MAGIC;
        s->s_flags |= SB_RDONLY;        /* Yup, read-only yet */
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
 
        /* ease the later tree level calculations */
        sbi = QNX6_SB(s);
index ab028ea0e5616a9de27a7fe9cfb1744d9dc98d1e..d69b4ac0ae2f3e3ce3d3a61a09cd3041ba72c9b9 100644 (file)
@@ -1976,6 +1976,9 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent)
                goto error_unlocked;
        }
 
+       s->s_time_min = 0;
+       s->s_time_max = U32_MAX;
+
        rs = SB_DISK_SUPER_BLOCK(s);
        /*
         * Let's do basic sanity check to verify that underlying device is not
index 7d580f7c3f1d01e6e149c6d56576ef32f20cdeee..a42c0e3079dc0705c7ac49de4fbc88ab74313417 100644 (file)
@@ -478,6 +478,8 @@ static int romfs_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_maxbytes = 0xFFFFFFFF;
        sb->s_magic = ROMFS_MAGIC;
        sb->s_flags |= SB_RDONLY | SB_NOATIME;
+       sb->s_time_min = 0;
+       sb->s_time_max = 0;
        sb->s_op = &romfs_super_ops;
 
 #ifdef CONFIG_ROMFS_ON_MTD
index effa638d6d85b8fe1331f85605cc2fe753ad3517..a9e9837617a9fd7399e5e21d9c1dca9d77c8b005 100644 (file)
@@ -183,6 +183,8 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
                (u64) le64_to_cpu(sblk->id_table_start));
 
        sb->s_maxbytes = MAX_LFS_FILESIZE;
+       sb->s_time_min = 0;
+       sb->s_time_max = U32_MAX;
        sb->s_flags |= SB_RDONLY;
        sb->s_op = &squashfs_super_ops;
 
index 9459ba75a32e3687902384896f506eee2d5ac8b5..2d679db9e8c7b64783f34e32e299b8e2d74ed2af 100644 (file)
@@ -258,6 +258,8 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
        s->s_maxbytes = MAX_NON_LFS;
        s->s_op = &default_op;
        s->s_time_gran = 1000000000;
+       s->s_time_min = TIME64_MIN;
+       s->s_time_max = TIME64_MAX;
        s->cleancache_poolid = CLEANCACHE_NO_POOL;
 
        s->s_shrink.seeks = DEFAULT_SEEKS;
index d788b1daa7eba0c5eac82425af4e8f999f766e63..cc8e2ed155c84e700ab1191efc563ada2266a65e 100644 (file)
@@ -368,7 +368,8 @@ static int sysv_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_block_base = 0;
        mutex_init(&sbi->s_lock);
        sb->s_fs_info = sbi;
-
+       sb->s_time_min = 0;
+       sb->s_time_max = U32_MAX;
        sb_set_blocksize(sb, BLOCK_SIZE);
 
        for (i = 0; i < ARRAY_SIZE(flavours) && !size; i++) {
@@ -487,6 +488,8 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_type = FSTYPE_V7;
        mutex_init(&sbi->s_lock);
        sb->s_fs_info = sbi;
+       sb->s_time_min = 0;
+       sb->s_time_max = U32_MAX;
        
        sb_set_blocksize(sb, 512);
 
index 400970d740bb8b628b979f515711df963bf85448..cd52585c8f4fe16f474771e6a822d9ec1a60f23a 100644 (file)
@@ -1078,15 +1078,18 @@ static void do_attr_changes(struct inode *inode, const struct iattr *attr)
                inode->i_uid = attr->ia_uid;
        if (attr->ia_valid & ATTR_GID)
                inode->i_gid = attr->ia_gid;
-       if (attr->ia_valid & ATTR_ATIME)
-               inode->i_atime = timespec64_trunc(attr->ia_atime,
-                                                 inode->i_sb->s_time_gran);
-       if (attr->ia_valid & ATTR_MTIME)
-               inode->i_mtime = timespec64_trunc(attr->ia_mtime,
-                                                 inode->i_sb->s_time_gran);
-       if (attr->ia_valid & ATTR_CTIME)
-               inode->i_ctime = timespec64_trunc(attr->ia_ctime,
-                                                 inode->i_sb->s_time_gran);
+       if (attr->ia_valid & ATTR_ATIME) {
+               inode->i_atime = timestamp_truncate(attr->ia_atime,
+                                                 inode);
+       }
+       if (attr->ia_valid & ATTR_MTIME) {
+               inode->i_mtime = timestamp_truncate(attr->ia_mtime,
+                                                 inode);
+       }
+       if (attr->ia_valid & ATTR_CTIME) {
+               inode->i_ctime = timestamp_truncate(attr->ia_ctime,
+                                                 inode);
+       }
        if (attr->ia_valid & ATTR_MODE) {
                umode_t mode = attr->ia_mode;
 
index 4ed0dca52ec861893f637fd5f529b03474a78da0..1da0be667409b2cdc302e2cb2b9291bad2842ba6 100644 (file)
@@ -843,6 +843,10 @@ static int ufs_fill_super(struct super_block *sb, void *data, int silent)
 
        sb->s_maxbytes = MAX_LFS_FILESIZE;
 
+       sb->s_time_gran = NSEC_PER_SEC;
+       sb->s_time_min = S32_MIN;
+       sb->s_time_max = S32_MAX;
+
        switch (sbi->s_mount_opt & UFS_MOUNT_UFSTYPE) {
        case UFS_MOUNT_UFSTYPE_44BSD:
                UFSD("ufstype=44bsd\n");
@@ -861,6 +865,9 @@ static int ufs_fill_super(struct super_block *sb, void *data, int silent)
                uspi->s_fshift = 9;
                uspi->s_sbsize = super_block_size = 1536;
                uspi->s_sbbase =  0;
+               sb->s_time_gran = 1;
+               sb->s_time_min = S64_MIN;
+               sb->s_time_max = S64_MAX;
                flags |= UFS_TYPE_UFS2 | UFS_DE_44BSD | UFS_UID_44BSD | UFS_ST_44BSD | UFS_CG_44BSD;
                break;
                
index 350c9c16ace1a2e8b247211e18ac731df538b7f1..1ba3f78838704a84e063a624d6cc3497bd2b1b35 100644 (file)
@@ -36,16 +36,14 @@ static int utimes_common(const struct path *path, struct timespec64 *times)
                if (times[0].tv_nsec == UTIME_OMIT)
                        newattrs.ia_valid &= ~ATTR_ATIME;
                else if (times[0].tv_nsec != UTIME_NOW) {
-                       newattrs.ia_atime.tv_sec = times[0].tv_sec;
-                       newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
+                       newattrs.ia_atime = timestamp_truncate(times[0], inode);
                        newattrs.ia_valid |= ATTR_ATIME_SET;
                }
 
                if (times[1].tv_nsec == UTIME_OMIT)
                        newattrs.ia_valid &= ~ATTR_MTIME;
                else if (times[1].tv_nsec != UTIME_NOW) {
-                       newattrs.ia_mtime.tv_sec = times[1].tv_sec;
-                       newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
+                       newattrs.ia_mtime = timestamp_truncate(times[1], inode);
                        newattrs.ia_valid |= ATTR_MTIME_SET;
                }
                /*
index 391b4748cae3462bab567f55ff0ab69ec6257f4e..8d1df9f8be071db3be45ac169e9382f740ffed7e 100644 (file)
@@ -1664,6 +1664,8 @@ xfs_fs_fill_super(
        sb->s_maxbytes = xfs_max_file_offset(sb->s_blocksize_bits);
        sb->s_max_links = XFS_MAXLINK;
        sb->s_time_gran = 1;
+       sb->s_time_min = S32_MIN;
+       sb->s_time_max = S32_MAX;
        sb->s_iflags |= SB_I_CGROUPWB;
 
        set_posix_acl_flag(sb);
index ffe35d97afcb4971e7efa86fdac6e1675d100631..866268c2c6e3a0e127cf87b1276a4ebadf52c37f 100644 (file)
@@ -732,6 +732,8 @@ struct inode {
        void                    *i_private; /* fs or device private pointer */
 } __randomize_layout;
 
+struct timespec64 timestamp_truncate(struct timespec64 t, struct inode *inode);
+
 static inline unsigned int i_blocksize(const struct inode *node)
 {
        return (1 << node->i_blkbits);
@@ -1458,6 +1460,9 @@ struct super_block {
 
        /* Granularity of c/m/atime in ns (cannot be worse than a second) */
        u32                     s_time_gran;
+       /* Time limits for c/m/atime in seconds */
+       time64_t                   s_time_min;
+       time64_t                   s_time_max;
 #ifdef CONFIG_FSNOTIFY
        __u32                   s_fsnotify_mask;
        struct fsnotify_mark_connector __rcu    *s_fsnotify_marks;
index a620ee610b9f3e07ae76fc4e8d28563258f5c0df..19125489ae948b3ee5efc929561118db867d85fc 100644 (file)
@@ -30,6 +30,8 @@ struct itimerspec64 {
 
 /* Located here for timespec[64]_valid_strict */
 #define TIME64_MAX                     ((s64)~((u64)1 << 63))
+#define TIME64_MIN                     (-TIME64_MAX - 1)
+
 #define KTIME_MAX                      ((s64)~((u64)1 << 63))
 #define KTIME_SEC_MAX                  (KTIME_MAX / NSEC_PER_SEC)