vfs: Add timestamp range check support
Subsystems (4)
cover 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(-)
Signature · 12 lines
2.7.4 Cc: [email protected] Cc: "Theodore Ts'o" <[email protected]> Cc: Andreas Dilger <[email protected]> Cc: [email protected] _______________________________________________ Y2038 mailing list [email protected] https://lists.linaro.org/mailman/listinfo/y2038
RFC 1/6 vfs: Add file timestamp range support
Add fields to the superblock to track the min and max timestamps supported by filesystems.
Initially, when a superblock is allocated, initialize it to the max and min values the fields can hold. Individual filesystems override these to match their actual limits.
Pseudo filesystems are assumed to always support the min and max allowable values for the fields.
Note that the time ranges are save in type time64_t rather than time_t.
This is required because if we save ranges in time_t then we would not be able to save timestamp ranges for files that support timestamps beyond y2038.
--- fs/libfs.c | 4 ++++ fs/super.c | 2 ++ include/linux/fs.h | 3 +++ include/linux/time64.h | 2 ++ 4 files changed, 11 insertions(+)
fs/libfs.c +4 -0
@@ -256,6 +256,8 @@ struct dentry *mount_pseudo_xattr(struct file_system_type *fs_type, char *name, s->s_op = ops ? ops : &simple_super_operations; s->s_xattr = xattr; s->s_time_gran =1 ; + s->s_time_min =TIME64_MIN ; + s->s_time_max =TIME64_MAX ; root =new_inode (s);if (!root) goto Enomem;
@@ -515,6 +517,8 @@ int simple_fill_super(struct super_block *s, unsigned long magic, s->s_magic = magic; s->s_op = &simple_super_operations; s->s_time_gran =1 ; + s->s_time_min =TIME64_MIN ; + s->s_time_max =TIME64_MAX ; inode =new_inode (s);if (!inode)
fs/super.c +2 -0
@@ -248,6 +248,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 ;
include/linux/fs.h +3 -0
@@ -1380,6 +1380,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;/* * The next field is for VFS *only*. No filesystems have any business
include/linux/time64.h +2 -0
@@ -38,6 +38,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)
Signature · 1 line
2.7.4
RFC 2/6 vfs: Add checks for filesystem timestamp limits
In reply to Deepa DinamaniAllow 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.
--- 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(-)
fs/inode.c +5 -0
@@ -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;
fs/internal.h +2 -0
@@ -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 *);
fs/namespace.c +12 -0
@@ -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;
fs/super.c +6 -0
@@ -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 (&sb->s_umount);free_secdata (secdata);return root;
include/linux/fs.h +1 -0
@@ -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,
include/linux/time64.h +4 -0
@@ -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)
include/uapi/linux/fs.h +5 -1
@@ -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 */
kernel/sysctl.c +7 -0
@@ -1681,6 +1681,13 @@ static struct ctl_table fs_table[] = { .proc_handler = proc_doulongvec_minmax, }, { + .procname = "fs-timestamp-check-on", + .data = ×tamp_check.check_on, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { .procname = "nr_open", .data = &sysctl_nr_open, .maxlen = sizeof(unsigned int),
Signature · 1 line
2.7.4
RFC 3/6 afs: Add time limits in the super block
In reply to Deepa DinamaniNote that all the filesystems that have such simple limits will be initialized in the same patch.
--- fs/afs/super.c | 2 ++ 1 file changed, 2 insertions(+)
fs/afs/super.c +2 -0
@@ -321,6 +321,8 @@ static int afs_fill_super(struct super_block *sb, sb->s_op = &afs_super_ops; sb->s_bdi = &as->volume->bdi;strlcpy (sb->s_id, as->volume->vlocation->vldb.name,sizeof (sb->s_id)); + sb->s_time_max =Y2106_EXPIRY_TIMESTAMP ; + sb->s_time_min =0 ;/* allocate the root inode and dentry */ fid.vid = as->volume->vid;
Signature · 1 line
2.7.4
RFC 4/6 ext4: Initialize timestamps limits
In reply to Deepa Dinamaniext4 has different overflow limits for max filesystem timestamps based on the extra bytes available.
--- fs/ext4/ext4.h | 4 ++++ fs/ext4/super.c | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-)
fs/ext4/ext4.h +4 -0
@@ -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 */
fs/ext4/super.c +6 -1
@@ -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);
Signature · 6 lines
2.7.4 _______________________________________________ Y2038 mailing list [email protected] https://lists.linaro.org/mailman/listinfo/y2038
RFC 5/6 vfs: Add timestamp_truncate() api
In reply to Deepa Dinamanitimespec_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().
--- fs/inode.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-)
fs/inode.c +31 -1
@@ -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);
Signature · 1 line
2.7.4
RFC 6/6 utimes: Clamp the timestamps before update
In reply to Deepa DinamaniPOSIX.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().
--- fs/utimes.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-)
fs/utimes.c +13 -4
@@ -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 ; }/*
Signature · 1 line
2.7.4
Quoted 9 lines · Deepa Dinamani
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
Quoted 7 lines
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.
Signature · 2 lines
Dave Chinner [email protected]
Quoted 18 lines · Dave Chinner
Quoted 9 lines · Deepa Dinamani
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/551Quoted 7 lines
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.
Quoted 5 lines
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?
Seconded. :)
I guess the only way to tell if a mountpoint can do 64 bit times is to try it and see what happens? Unless you enable the procfs thing that prints to dmesg. Evidently turning on the knob won't cause complaints if there's already a mounted fs that didn't have 64-bit time support. I'd go look at the testcases to corroborate this, but I don't know that there are any?
(It was a big help to write a big pile of tests for adding reflink to XFS. It helped us find some btrfs reflink bugs too.)
--D
Quoted 11 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Theodore Ts'o
maintainerQuoted 6 lines · Dave Chinner
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?
In order to have automated regression tests which are file system independent, we need a way to query what are the timestamps that a particular mounted file systme supports. One approach would be to use fsinfo, which David Howells had been working on, but which has been bike-shedded to death for the last n years, and I'd hate to block this patch series behind a proposed new fsinfo(2) system call. Alternatively, we can just create a specialized ioctl to return that information which is non-ideal in other dimensions.
The last option, which is admittedly ugly, would be to create an shell function which knows how to figure out the max_timestamp and min_timestamp by using the file system name and querying the superblock using dumpe2fs, xfs_db, etc.
I'd argue for the last option because once we do get a programmtic way to get the information via a system call such as fsinfo(2), we can convert xfstests to use it, where as if we add an ioctl to return this information, we'll have to support the ioctl forever.
Does this make sense? Any objections?
Cheers,
- Ted
_______________________________________________
Y2038 mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/y2038
Quoted 11 lines · Theodore Ts'o
Quoted 6 lines · Dave Chinner
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?
In order to have automated regression tests which are file system independent, we need a way to query what are the timestamps that a particular mounted file systme supports.
We don't need that - we simply code it directly into the test infrastructure, like we've done for things like the maximum number of ACLs a filesystem supports (common/attr::_acl_get_max()).
Quoted 4 lines
The last option, which is admittedly ugly, would be to create an shell function which knows how to figure out the
max_timestampandmin_timestampby using the file system name and querying the superblock using dumpe2fs,xfs_db, etc.
Yup, precisely that. We shouldn't trust the kernel to tell us the correct thing to enable the test that tells us that thing is working correctly or not...
Quoted 4 lines
I'd argue for the last option because once we do get a programmtic way to get the information via a system call such as fsinfo(2), we can convert xfstests to use it, where as if we add an ioctl to return this information, we'll have to support the ioctl forever.
We have to support kernels that won't ever have something like fsinfo, so it has to be done the "ugly way".
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] _______________________________________________ Y2038 mailing list [email protected] https://lists.linaro.org/mailman/listinfo/y2038
Quoted 11 lines · Theodore Ts'o
Quoted 6 lines · Dave Chinner
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?
Quoted 6 lines
In order to have automated regression tests which are file system independent, we need a way to query what are the timestamps that a particular mounted file systme supports. One approach would be to use fsinfo, which David Howells had been working on, but which has been bike-shedded to death for the last n years, and I'd hate to block this patch series behind a proposed new fsinfo(2) system call.
I wish we could just get the fsinfo and statx calls landed, but I agree it would be a DOS to block any other patches waiting for that to land...
or maybe we _should_ block other patches behind that patch, and force it to be landed... :-)
Quoted 8 lines
Alternatively, we can just create a specialized ioctl to return that information which is non-ideal in other dimensions.
The last option, which is admittedly ugly, would be to create an shell function which knows how to figure out the
max_timestampandmin_timestampby using the file system name and querying the superblock using dumpe2fs,xfs_db, etc.
Quoted 6 lines
I'd argue for the last option because once we do get a programmtic way to get the information via a system call such as fsinfo(2), we can convert xfstests to use it, where as if we add an ioctl to return this information, we'll have to support the ioctl forever.
Does this make sense? Any objections?
Realistically, there are only a handful of filesystems being tested by xfstests, and it is simple enough to hard-code these limits into the test script for ext4, xfs, btrfs, etc. since the limits will not be changing very often (and it is noteworthy when they do). If and when there is an interface to query these values from the kernel, it may still make sense to keep the hard-coded limits to verify the syscall against...
Cheers, Andreas
I will post xfs tests that validate mount and range checking. I will keep the policy same as what the RFC suggests for now.
Clamping can be verified once vfs is transitioned to using time64_t.
Thanks,
Deepa
Quoted 4 lines · CET Deepa Dinamani
I will post xfs tests that validate mount and range checking. I will keep the policy same as what the RFC suggests for now.
Clamping can be verified once vfs is transitioned to using
time64_t.
Won't it already work as expected on 64-bit architectures as they have a 64-bit time_t?
Arnd
_______________________________________________
Y2038 mailing list
[email protected]
https://lists.linaro.org/mailman/listinfo/y2038
Quoted 8 lines · Arnd Bergmann
Quoted 4 lines · CET Deepa Dinamani
I will post xfs tests that validate mount and range checking. I will keep the policy same as what the RFC suggests for now.
Clamping can be verified once vfs is transitioned to using
time64_t.Won't it already work as expected on 64-bit architectures as they have a 64-bit
time_t?
Yes, on 64 bit architectures, it should work fine.
32 bit machines will have wrong clamped timestamps though for some filesystems.
I can post a test for clamping that only works on 64 bit machines.
Thanks,
-Deepa