The current_fs_time function is not y2038 safe because of the use of struct timespec.
The macros CURRENT_TIME and CURRENT_TIME_SEC do not represent file system times correctly as they cannnot perform range checks or truncations. These are also not y2038 safe.
Provide a new set of SYSTEM_TIME macros which will return time in timespec or timespec64 based on CONFIG_FS_USES_64_BIT_TIME. These are meant to be used only within file systems because of being tied to the above config. Once the config is enabled, the timespec version of it can be deleted and the 64 bit time version can be used elsewhere also.
Add struct timespec64 version for current_fs_time(). Current version of current_fs_time() can be dropped after enabling CONFIG_FS_USES_64BIT_TIME.
Provide an alternative to timespec_trunc(): fs_time_trunc(). This function takes super block as an argument in addition to timestamp so that it can include range and precision checks. Additionally, the function uses y2038 safe timespec64 instead of timespec for timestamp representation.
Add function: current_fs_time_sec() to obtain only the seconds portion of the current time(Equivalent to CURRENT_TIME_SEC). This function has two versions selected by the config CONFIG_FS_USES_64BIT_TIME. The 32 bit version support can be dropped after the above config is enabled globally.
All calls to timespec_trunc() will be eventually replaced by fs_time_trunc(). At which point, timespec_trunc() can be deleted.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/fs.h | 5 +++- include/linux/time64.h | 8 +++++++ kernel/time/time.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h index 3b018c6..3ca1768 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1424,7 +1424,10 @@ struct super_block { struct list_head s_inodes; /* all inodes */ };
-extern struct timespec current_fs_time(struct super_block *sb); +extern struct inode_timespec current_fs_time(struct super_block *sb); +extern struct inode_timespec current_fs_time_sec(struct super_block *sb); +extern struct inode_timespec +fs_time_trunc(struct inode_timespec ts, struct super_block *sb);
/* * Snapshotting support. diff --git a/include/linux/time64.h b/include/linux/time64.h index 43ad63b..a348612 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -51,6 +51,11 @@ struct inode_time { #define inode_timespec_compare timespec64_compare #define inode_timespec_equal timespec64_equal
+#define SYSTEM_TIME (current_kernel_time64()) +#define SYSTEM_TIME_SEC \ + ((struct timespec64) { ktime_get_real_seconds(), 0 }) + + #else #define inode_time timespec
@@ -59,6 +64,9 @@ struct inode_time { #define inode_timespec_compare timespec_compare #define inode_timespec_equal timespec_equal
+#define SYSTEM_TIME (current_kernel_time()) +#define SYSTEM_TIME_SEC ((struct timespec) { get_seconds(), 0 }) + #endif
/* Parameters used to convert the timespec values: */ diff --git a/kernel/time/time.c b/kernel/time/time.c index 86751c6..3b8ba4a 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -237,13 +237,42 @@ SYSCALL_DEFINE1(adjtimex, struct timex __user *, txc_p) * Return the current time truncated to the time granularity supported by * the fs. */ +#ifdef CONFIG_FS_USES_64BIT_TIME +struct timespec64 current_fs_time(struct super_block *sb) +{ + struct timespec64 now = current_kernel_time64(); + + return fs_time_trunc(now, sb); +} +EXPORT_SYMBOL(current_fs_time); + +struct timespec64 current_fs_time_sec(struct super_block *sb) +{ + struct timespec64 ts = {ktime_get_real_seconds(), 0}; + + /* TODO: Add range check for time. */ + return ts; +} +EXPORT_SYMBOL(current_fs_time_sec); +#else struct timespec current_fs_time(struct super_block *sb) { struct timespec now = current_kernel_time(); - return timespec_trunc(now, sb->s_time_gran); + + return fs_time_trunc(now, sb); } EXPORT_SYMBOL(current_fs_time);
+struct timespec current_fs_time_sec(struct super_block *sb) +{ + struct timespec ts = { get_seconds(), 0 }; + + /* TODO: Add range check for time. */ + return ts; +} +EXPORT_SYMBOL(current_fs_time_sec); +#endif + /* * Convert jiffies to milliseconds and back. * @@ -286,6 +315,36 @@ unsigned int jiffies_to_usecs(const unsigned long j) } EXPORT_SYMBOL(jiffies_to_usecs);
+/* + * fs_time_trunc - Truncate inode_timespec to a granularity + * @t: inode_timespec + * @sb: Super block. + * + * Truncate a timespec to a granularity. Always rounds down. Granularity + * must * not be 0 nor greater than a second (NSEC_PER_SEC, or 10^9 ns). + */ + +struct inode_timespec +fs_time_trunc(struct inode_timespec t, struct super_block *sb) +{ + u32 gran = sb->s_time_gran; + + /* TODO: Add range check for time. */ + + /* 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(fs_time_trunc); + /** * timespec_trunc - Truncate timespec to a granularity * @t: Timespec @@ -293,6 +352,9 @@ EXPORT_SYMBOL(jiffies_to_usecs); * * 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). + * + * This function is deprecated and should no longer be used for filesystems. + * fs_time_trunc should be used instead. */ struct timespec timespec_trunc(struct timespec t, unsigned gran) { @@ -308,6 +370,7 @@ struct timespec timespec_trunc(struct timespec t, unsigned gran) } return t; } + EXPORT_SYMBOL(timespec_trunc);
/*