Re: [Y2038] [RFC 0/6] vfs: Add timestamp range check support
On Wed, Nov 02, 2016 at 08:04:50AM -0700, Deepa Dinamani wrote: > The series is aimed at adding timestamp checking and policy > related to it to vfs. > > The series was developed with discussions and guidance from > Arnd Bergmann. > > The original idea for the series was the discussion: > https://lkml.org/lkml/2014/5/30/551 > > Patches 5 and 6 can be merged only after vfs is transitioned > to use 64 bit timestamps as noted in the respective commit > texts. > > The series only includes adding range limits to filesystems: > ext4 and afs as examples to keep the series simple. > Every filesystem will be updated to add these limits. We're going to need regression tests for this to ensure that it works properly and that we don't inadvertantly break it in future. Can you write some xfstests that exercise this functionality and validate that the mount behaviour, clamping and range limiting is working as intended? Cheers, Dave. -- Dave Chinner da...@fromorbit.com ___ Y2038 mailing list Y2038@lists.linaro.org https://lists.linaro.org/mailman/listinfo/y2038
[Y2038] [RFC 2/6] vfs: Add checks for filesystem timestamp limits
Allow read only mounts for filesystems that do not have maximum timestamps beyond the y2038 expiry timestamp. Also, allow a sysctl override to all such filesystems to be mounted with write permissions. Alternatively, a mount option can be created to allow or disallow range check based clamps and the least max timestamp supported. If we take the sysctl approach, then the plan is to also add a boot param to support initial override of these checks without recompilation. Suggested-by: Arnd BergmannSigned-off-by: Deepa Dinamani --- fs/inode.c | 5 + fs/internal.h | 2 ++ fs/namespace.c | 12 fs/super.c | 6 ++ include/linux/fs.h | 1 + include/linux/time64.h | 4 include/uapi/linux/fs.h | 6 +- kernel/sysctl.c | 7 +++ 8 files changed, 42 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 88110fd..7b2b78d 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -75,6 +75,11 @@ static DEFINE_PER_CPU(unsigned long, nr_unused); static struct kmem_cache *inode_cachep __read_mostly; +struct vfs_max_timestamp_check timestamp_check = { + .timestamp_supported = Y2038_EXPIRY_TIMESTAMP, + .check_on = 1, +}; + static long get_nr_inodes(void) { int i; diff --git a/fs/internal.h b/fs/internal.h index f4da334..5a144a8 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -67,6 +67,8 @@ extern int finish_automount(struct vfsmount *, struct path *); extern int sb_prepare_remount_readonly(struct super_block *); +extern bool sb_file_times_updatable(struct super_block *sb); + extern void __init mnt_init(void); extern int __mnt_want_write(struct vfsmount *); diff --git a/fs/namespace.c b/fs/namespace.c index e6c234b..b784b95 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -542,6 +542,18 @@ static void __mnt_unmake_readonly(struct mount *mnt) unlock_mount_hash(); } +bool sb_file_times_updatable(struct super_block *sb) +{ + + if (!timestamp_check.check_on) + return true; + + else if (sb->s_time_max > timestamp_check.timestamp_supported) + return true; + + return false; +} + int sb_prepare_remount_readonly(struct super_block *sb) { struct mount *mnt; diff --git a/fs/super.c b/fs/super.c index 27c973e..5073d70 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1199,6 +1199,12 @@ mount_fs(struct file_system_type *type, int flags, const char *name, void *data) WARN((sb->s_maxbytes < 0), "%s set sb->s_maxbytes to " "negative value (%lld)\n", type->name, sb->s_maxbytes); + if (!(sb->s_flags & MS_RDONLY) && !sb_file_times_updatable(sb)) { + WARN(1, "File times cannot be updated on the filesystem.\n"); + WARN(1, "Retry mounting the filesystem readonly.\n"); + goto out_sb; + } + up_write(>s_umount); free_secdata(secdata); return root; diff --git a/include/linux/fs.h b/include/linux/fs.h index 6d1346b..a079393 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -68,6 +68,7 @@ extern struct inodes_stat_t inodes_stat; extern int leases_enable, lease_break_time; extern int sysctl_protected_symlinks; extern int sysctl_protected_hardlinks; +extern struct vfs_max_timestamp_check timestamp_check; struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, diff --git a/include/linux/time64.h b/include/linux/time64.h index 25433b18..906e0b3 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -43,6 +43,10 @@ struct itimerspec64 { #define KTIME_MAX ((s64)~((u64)1 << 63)) #define KTIME_SEC_MAX (KTIME_MAX / NSEC_PER_SEC) +/* Timestamps on boundary */ +#define Y2038_EXPIRY_TIMESTAMP S32_MAX /* 2147483647 */ +#define Y2106_EXPIRY_TIMESTAMP U32_MAX /* 4294967295 */ + #if __BITS_PER_LONG == 64 static inline struct timespec timespec64_to_timespec(const struct timespec64 ts64) diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index acb2b61..60482b1 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -91,6 +91,11 @@ struct files_stat_struct { unsigned long max_files;/* tunable */ }; +struct vfs_max_timestamp_check { + time64_t timestamp_supported; + int check_on; +}; + struct inodes_stat_t { long nr_inodes; long nr_unused; @@ -100,7 +105,6 @@ struct inodes_stat_t { #define NR_FILE 8192 /* this can well be larger on a larger system */ - /* * These are the fs-independent mount-flags: up to 32 flags are supported */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 706309f..e65e6b9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1681,6 +1681,13 @@ static struct ctl_table fs_table[] = { .proc_handler = proc_doulongvec_minmax, }, { + .procname
[Y2038] [RFC 6/6] utimes: Clamp the timestamps before update
POSIX.1 section for futimens, utimensat and utimes says: The file's relevant timestamp shall be set to the greatest value supported by the file system that is not greater than the specified time. Clamp the timestamps accordingly before assignment. Note that clamp_t macro is used for clamping here as vfs is not yet using struct timespec64 internally. This is for compilation purposes only. The actual patch can only be merged only after vfs is transitioned to use timespec64 for correct operation of clamp macro. At which point, clamp_t() will be replaced by clamp(). Signed-off-by: Deepa Dinamani--- fs/utimes.c | 17 + 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/fs/utimes.c b/fs/utimes.c index 22307cd..186e12b 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -53,6 +53,7 @@ static int utimes_common(struct path *path, struct timespec *times) int error; struct iattr newattrs; struct inode *inode = path->dentry->d_inode; + struct super_block *sb = inode->i_sb; struct inode *delegated_inode = NULL; error = mnt_want_write(path->mnt); @@ -68,16 +69,24 @@ static int utimes_common(struct path *path, struct timespec *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.tv_sec = + clamp_t(time64_t, times[0].tv_sec, sb->s_time_min, sb->s_time_max); + if (times[0].tv_sec >= sb->s_time_max) + newattrs.ia_atime.tv_nsec = 0; + else + newattrs.ia_atime.tv_nsec = times[0].tv_nsec; 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_atime.tv_sec = + clamp_t(time64_t, times[0].tv_sec, sb->s_time_min, sb->s_time_max); + if (times[0].tv_sec >= sb->s_time_max) + newattrs.ia_atime.tv_nsec = 0; + else + newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } /* -- 2.7.4 ___ Y2038 mailing list Y2038@lists.linaro.org https://lists.linaro.org/mailman/listinfo/y2038
[Y2038] [RFC 5/6] vfs: Add timestamp_truncate() api
timespec_trunc() function is used to truncate a filesystem timestamp to the right granularity. But, the function does not clamp tv_sec part of the timestamps according to the filesystem timestamp limits. Also, timespec_trunc() is exclusively used for filesystem timestamps. Move the api to be part of vfs. The replacement api: timestamp_truncate() also alters the signature of the function to accommodate filesystem timestamp clamping according to flesystem limits. Note that clamp_t macro is used for clamping here as vfs is not yet using struct timespec64 internally. This is only for compilation purposes. The actual patch can only be merged after the vfs is transitioned to use timespec64 for correct operation of clamp macro. At which point, clamp_t() will be replaced by clamp(). Signed-off-by: Deepa Dinamani--- fs/inode.c | 32 +++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 7b2b78d..f9285f2 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2106,6 +2106,36 @@ void inode_nohighmem(struct inode *inode) EXPORT_SYMBOL(inode_nohighmem); /** + * fs_timespec_trunc - Truncate timespec to a granularity + * @t: Timespec + * @gran: Granularity in ns. + * + * Truncate a timespec to a granularity. Always rounds down. gran must + * not be 0 nor greater than a second (NSEC_PER_SEC, or 10^9 ns). + */ +struct timespec timestamp_truncate(struct timespec t, struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + unsigned int gran = sb->s_time_gran; + + t.tv_sec = clamp_t(time64_t, t.tv_sec, sb->s_time_min, sb->s_time_max); + + /* 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, "illegal file time granularity: %u", gran); + } + return t; +} +EXPORT_SYMBOL(timestamp_truncate); + + +/** * current_time - Return FS time * @inode: inode. * @@ -2124,6 +2154,6 @@ struct timespec current_time(struct inode *inode) return now; } - return timespec_trunc(now, inode->i_sb->s_time_gran); + return timestamp_truncate(now, inode); } EXPORT_SYMBOL(current_time); -- 2.7.4 ___ Y2038 mailing list Y2038@lists.linaro.org https://lists.linaro.org/mailman/listinfo/y2038
[Y2038] [RFC 4/6] ext4: Initialize timestamps limits
ext4 has different overflow limits for max filesystem timestamps based on the extra bytes available. Signed-off-by: Deepa DinamaniCc: "Theodore Ts'o" Cc: Andreas Dilger Cc: linux-e...@vger.kernel.org --- fs/ext4/ext4.h | 4 fs/ext4/super.c | 7 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6789379..fca339a 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1635,6 +1635,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 Y2038_EXPIRY_TIMESTAMP +#define EXT4_TIMESTAMP_MIN S32_MIN + /* * Feature set definitions */ diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ab00bff..ebd039d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3633,8 +3633,13 @@ 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) + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2); + sb->s_time_max = EXT4_EXTRA_TIMESTAMP_MAX; + } else + 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); -- 2.7.4 ___ Y2038 mailing list Y2038@lists.linaro.org https://lists.linaro.org/mailman/listinfo/y2038
[Y2038] [RFC 0/6] vfs: Add timestamp range check support
The series is aimed at adding timestamp checking and policy related to it to vfs. The series was developed with discussions and guidance from Arnd Bergmann. The original idea for the series was the discussion: https://lkml.org/lkml/2014/5/30/551 Patches 5 and 6 can be merged only after vfs is transitioned to use 64 bit timestamps as noted in the respective commit texts. The series only includes adding range limits to filesystems: ext4 and afs as examples to keep the series simple. Every filesystem will be updated to add these limits. There is an ext4 current_time() api replacement patch that the series depends on: https://lkml.org/lkml/2016/6/9/38 . This needs reposting to the mailing list. The branch for the tree along with dependency can be found at https://github.com/deepa-hub/vfs.git refs/heads/vfs_timestamp_policy Deepa Dinamani (6): vfs: Add file timestamp range support vfs: Add checks for filesystem timestamp limits afs: Add time limits in the super block ext4: Initialize timestamps limits vfs: Add timestamp_truncate() api utimes: Clamp the timestamps before update fs/afs/super.c | 2 ++ fs/ext4/ext4.h | 4 fs/ext4/super.c | 7 ++- fs/inode.c | 37 - fs/internal.h | 2 ++ fs/libfs.c | 4 fs/namespace.c | 12 fs/super.c | 8 fs/utimes.c | 17 + include/linux/fs.h | 4 include/linux/time64.h | 6 ++ include/uapi/linux/fs.h | 6 +- kernel/sysctl.c | 7 +++ 13 files changed, 109 insertions(+), 7 deletions(-) -- 2.7.4 Cc: linux-...@lists.infradead.org Cc: "Theodore Ts'o"Cc: Andreas Dilger Cc: linux-e...@vger.kernel.org ___ Y2038 mailing list Y2038@lists.linaro.org https://lists.linaro.org/mailman/listinfo/y2038