diff mbox

[RFC,2/6] vfs: Add checks for filesystem timestamp limits

Message ID 1478099096-25637-3-git-send-email-deepa.kernel@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Deepa Dinamani Nov. 2, 2016, 3:04 p.m. UTC
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 Bergmann <arnd@arndb.de>
Signed-off-by: Deepa Dinamani <deepa.kernel@gmail.com>
---
 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 mbox

Patch

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(&sb->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	= "fs-timestamp-check-on",
+		.data		= &timestamp_check.check_on,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+	{
 		.procname	= "nr_open",
 		.data		= &sysctl_nr_open,
 		.maxlen		= sizeof(unsigned int),