@@ -75,6 +75,19 @@ 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 = 0,
+};
+
+static int __init setup_timestamp_check(char *str)
+{
+ timestamp_check.check_on = 1;
+ return 0;
+}
+
+early_param("fstimestampcheck", setup_timestamp_check);
+
static long get_nr_inodes(void)
{
int i;
@@ -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 *);
@@ -538,6 +538,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;
+
+ 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;
@@ -1245,6 +1245,13 @@ 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");
+ error = -EROFS;
+ goto out_sb;
+ }
+
up_write(&sb->s_umount);
free_secdata(secdata);
return root;
@@ -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,
@@ -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)
@@ -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
*/
@@ -1667,6 +1667,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),
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. A boot param supports initial override of these checks from the early boot without recompilation. Suggested-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com> --- fs/inode.c | 13 +++++++++++++ fs/internal.h | 2 ++ fs/namespace.c | 12 ++++++++++++ fs/super.c | 7 +++++++ include/linux/fs.h | 1 + include/linux/time64.h | 4 ++++ include/uapi/linux/fs.h | 6 +++++- kernel/sysctl.c | 7 +++++++ 8 files changed, 51 insertions(+), 1 deletion(-)