Skip to content

Commit

Permalink
Merge tag 'y2038-vfs' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/arnd/playground

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
  • Loading branch information
torvalds committed Sep 19, 2019
2 parents b41dae0 + cba465b commit cfb82e1
Show file tree
Hide file tree
Showing 47 changed files with 294 additions and 72 deletions.
6 changes: 5 additions & 1 deletion fs/9p/vfs_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion fs/affs/amigaffs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions fs/affs/amigaffs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions fs/affs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions fs/affs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
21 changes: 12 additions & 9 deletions fs/attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions fs/befs/linuxvfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions fs/bfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions fs/ceph/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 22 additions & 0 deletions fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@
#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;
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down
14 changes: 7 additions & 7 deletions fs/cifs/netmisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions fs/coda/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 6 additions & 6 deletions fs/configfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions fs/cramfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions fs/efs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
2 changes: 2 additions & 0 deletions fs/ext2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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
*/
Expand Down
17 changes: 15 additions & 2 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 12 additions & 9 deletions fs/f2fs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
12 changes: 12 additions & 0 deletions fs/fat/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@

#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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions fs/freevxfs/vxfs_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))) {
Expand Down
6 changes: 2 additions & 4 deletions fs/hpfs/hpfs_fn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions fs/hpfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading

0 comments on commit cfb82e1

Please sign in to comment.