1
0
Fork 0

Merge branch 'limits' of https://github.com/deepa-hub/vfs into y2038

The series is an update and a more complete version of the
previously posted series at
https://lore.kernel.org/linux-fsdevel/20180122020426.2988-1-deepa.kernel@gmail.com/

Thanks to Arnd Bergmann for doing a few preliminary reviews.
They helped me fix a few issues I had overlooked.

The limits (sometimes granularity also) for the filesystems updated here are according to the
following table:

File system   Time type                      Start year Expiration year Granularity
cramfs        fixed                          0          0
romfs         fixed                          0          0
pstore        ascii seconds (27 digit ascii) S64_MIN    S64_MAX         1
coda          INT64                          S64_MIN    S64_MAX         1
omfs          64-bit milliseconds            0          U64_MAX/ 1000   NSEC_PER_MSEC
befs          unsigned 48-bit seconds        0          0xffffffffffff  alloc_super
bfs           unsigned 32-bit seconds        0          U32_MAX         alloc_super
efs           unsigned 32-bit seconds        0          U32_MAX         alloc_super
ext2          signed 32-bit seconds          S32_MIN    S32_MAX         alloc_super
ext3          signed 32-bit seconds          S32_MIN    S32_MAX         alloc_super
ext4 (old)    signed 32-bit seconds          S32_MIN    S32_MAX         alloc_super
ext4 (extra)  34-bit seconds, 30-bit ns      S32_MIN    0x37fffffff     1
freevxfs      u32 secs/usecs                 0          U32_MAX         alloc_super
jffs2         unsigned 32-bit seconds        0          U32_MAX         alloc_super
jfs           unsigned 32-bit seconds/ns     0          U32_MAX         1
minix         unsigned 32-bit seconds        0          U32_MAX         alloc_super
qnx4          unsigned 32-bit seconds        0          U32_MAX         alloc_super
qnx6          unsigned 32-bit seconds        0          U32_MAX         alloc_super
reiserfs      unsigned 32-bit seconds        0          U32_MAX         alloc_super
squashfs      unsigned 32-bit seconds        0          U32_MAX         alloc_super
ufs1          signed 32-bit seconds          S32_MIN    S32_MAX         NSEC_PER_SEC
ufs2          signed 64-bit seconds/u32 ns   S64_MIN    S64_MAX         1
xfs           signed 32-bit seconds/ns       S32_MIN    S32_MAX         1
ceph          unsigned 32-bit second/ns      0          U32_MAX         1000
sysv          unsigned 32-bit seconds        0          U32_MAX         alloc_super
affs          u32 day, min, ticks            1978       u32_max days    NSEC_PER_SEC
nfsv2         unsigned 32-bit seconds/ns     0          U32_MAX         1
nfsv3         unsigned 32-bit seconds/ns     0          U32_MAX         1000
nfsv4         u64 seconds/u32 ns             S64_MIN    S64_MAX         1000
isofs         u8 year since 1900 (fixable)   1900       2155            alloc_super
hpfs          unsigned 32-bit seconds        1970       2106            alloc_super
fat           7-bit years, 2s resolution     1980       2107
cifs (smb)    7-bit years                    1980       2107
cifs (modern) 64-bit 100ns since 1601        1601       30828
9p (9P2000)   unsigned 32-bit seconds        1970       2106
9p (9P2000.L) signed 64-bit seconds, ns      1970       S64_MAX

Granularity column filled in by the alloc_super() in the above table indicates that
the granularity is NSEC_PER_SEC.
Note that anything not mentioned above still has the default limits
S64_MIN..S64_MAX.

The patches in the series are as structured below:
1. Add vfs support to maintain the limits per filesystem.
2. Add a new timestamp_truncate() api for clamping timestamps
   according to the filesystem limits.
3. Add a warning for mount syscall to indicate the impending
   expiry of timestamps.
4. Modify utimes to clamp the timestamps.
5. Fill in limits for filesystems.

A test for checking file system timestamp limits has been posted
at https://www.spinics.net/lists/fstests/msg12262.html

Changes since v8:
* Dropped orangefs patch because of ongoing discussion
* Dropped adfs patch because a conflict with Russell's ADFS series
Changes since v7:
* Dropped fat modifications from timespec_truncate patch
* Leverage timestamp_truncate function in utimes
* Added a fix for pstore ramoops timestamps
* Added ext4 warning for inodes without room for extended timestamps.
* Made mount warning more human readable
Changes since v6:
* No change in mount behavior because of expiry of timestamps.
* Included limits for more filesystems.
Changes since v5:
* Dropped y2038-specific changes
Changes since v4:
* Added documentation for boot param
Changes since v3:
* Remove redundant initializations in libfs.c
* Change early_param to __setup similar to other root mount options.
* Fix documentation warning
Changes since v2:
* Introduce early boot param override for checks.
* Drop afs patch for timestamp limits.
Changes since v1:
* return EROFS on mount errors
* fix mtime copy/paste error in utimes

* 'limits' of https://github.com/deepa-hub/vfs:
  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

Link: https://lore.kernel.org/lkml/20190730014924.2193-1-deepa.kernel@gmail.com/
Link: https://lore.kernel.org/lkml/20190830154744.4868-1-deepa.kernel@gmail.com/
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
alistair/sunxi64-5.4-dsi
Arnd Bergmann 2019-08-30 18:12:15 +02:00
commit 9d14545b05
47 changed files with 296 additions and 72 deletions

View 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)

View 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);

View 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;

View 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);

View 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;

View File

@ -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;

View 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);

View 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);

View 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)

View File

@ -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;
@ -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;

View 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;

View 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)

View 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;

View 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))

View 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)) {

View 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;

View File

@ -828,11 +828,15 @@ 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)); \
ext4_warning_inode(inode, "inode does not support timestamps beyond 2038"); \
} \
} while (0)
#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \
@ -1632,6 +1636,10 @@ static inline void ext4_clear_state_flags(struct ext4_inode_info *ei)
#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
*/

View File

@ -4035,8 +4035,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);

View File

@ -741,15 +741,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;

View File

@ -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.
@ -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;

View 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))) {

View 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)

View 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);

View 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);

View 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 .. */

View 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;

View 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;

View 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)

View 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);

View File

@ -2463,6 +2463,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
@ -2488,6 +2508,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;
}
@ -2528,6 +2551,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;
}
@ -2736,8 +2762,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;
}

View 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);

View 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;

View 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);

View 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;

View 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

View 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);

View 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

View 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

View 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;

View File

@ -257,6 +257,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;

View 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);

View 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;

View 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;

View 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;
}
/*

View File

@ -1663,6 +1663,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);

View File

@ -726,6 +726,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);
@ -1448,6 +1450,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;

View 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)