]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/fat/misc.c
Merge tag 'edac_for_4.20_2' of git://git.kernel.org/pub/scm/linux/kernel/git/bp/bp
[linux.git] / fs / fat / misc.c
index 573836dcaefc4c1d2720ee213e5b90098a4acb87..fce0a76f3f1e4c2617934a93c027d0af0d074b45 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include "fat.h"
+#include <linux/iversion.h>
 
 /*
  * fat_fs_error reports a file system problem that might indicate fa data
@@ -185,6 +186,13 @@ static long days_in_year[] = {
        0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
 };
 
+static inline int fat_tz_offset(struct msdos_sb_info *sbi)
+{
+       return (sbi->options.tz_set ?
+              -sbi->options.time_offset :
+              sys_tz.tz_minuteswest) * SECS_PER_MIN;
+}
+
 /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
 void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
                       __le16 __time, __le16 __date, u8 time_cs)
@@ -210,10 +218,7 @@ void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
                   + days_in_year[month] + day
                   + DAYS_DELTA) * SECS_PER_DAY;
 
-       if (!sbi->options.tz_set)
-               second += sys_tz.tz_minuteswest * SECS_PER_MIN;
-       else
-               second -= sbi->options.time_offset * SECS_PER_MIN;
+       second += fat_tz_offset(sbi);
 
        if (time_cs) {
                ts->tv_sec = second + (time_cs / 100);
@@ -229,9 +234,7 @@ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
                       __le16 *time, __le16 *date, u8 *time_cs)
 {
        struct tm tm;
-       time64_to_tm(ts->tv_sec,
-                  (sbi->options.tz_set ? sbi->options.time_offset :
-                  -sys_tz.tz_minuteswest) * SECS_PER_MIN, &tm);
+       time64_to_tm(ts->tv_sec, -fat_tz_offset(sbi), &tm);
 
        /*  FAT can only support year between 1980 to 2107 */
        if (tm.tm_year < 1980 - 1900) {
@@ -263,6 +266,80 @@ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
 }
 EXPORT_SYMBOL_GPL(fat_time_unix2fat);
 
+static inline struct timespec64 fat_timespec64_trunc_2secs(struct timespec64 ts)
+{
+       return (struct timespec64){ ts.tv_sec & ~1ULL, 0 };
+}
+/*
+ * truncate the various times with appropriate granularity:
+ *   root inode:
+ *     all times always 0
+ *   all other inodes:
+ *     mtime - 2 seconds
+ *     ctime
+ *       msdos - 2 seconds
+ *       vfat  - 10 milliseconds
+ *     atime - 24 hours (00:00:00 in local timezone)
+ */
+int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags)
+{
+       struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
+       struct timespec64 ts;
+
+       if (inode->i_ino == MSDOS_ROOT_INO)
+               return 0;
+
+       if (now == NULL) {
+               now = &ts;
+               ts = current_time(inode);
+       }
+
+       if (flags & S_ATIME) {
+               /* to localtime */
+               time64_t seconds = now->tv_sec - fat_tz_offset(sbi);
+               s32 remainder;
+
+               div_s64_rem(seconds, SECS_PER_DAY, &remainder);
+               /* to day boundary, and back to unix time */
+               seconds = seconds + fat_tz_offset(sbi) - remainder;
+
+               inode->i_atime = (struct timespec64){ seconds, 0 };
+       }
+       if (flags & S_CTIME) {
+               if (sbi->options.isvfat)
+                       inode->i_ctime = timespec64_trunc(*now, 10000000);
+               else
+                       inode->i_ctime = fat_timespec64_trunc_2secs(*now);
+       }
+       if (flags & S_MTIME)
+               inode->i_mtime = fat_timespec64_trunc_2secs(*now);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fat_truncate_time);
+
+int fat_update_time(struct inode *inode, struct timespec64 *now, int flags)
+{
+       int iflags = I_DIRTY_TIME;
+       bool dirty = false;
+
+       if (inode->i_ino == MSDOS_ROOT_INO)
+               return 0;
+
+       fat_truncate_time(inode, now, flags);
+       if (flags & S_VERSION)
+               dirty = inode_maybe_inc_iversion(inode, false);
+       if ((flags & (S_ATIME | S_CTIME | S_MTIME)) &&
+           !(inode->i_sb->s_flags & SB_LAZYTIME))
+               dirty = true;
+
+       if (dirty)
+               iflags |= I_DIRTY_SYNC;
+       __mark_inode_dirty(inode, iflags);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fat_update_time);
+
 int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
 {
        int i, err = 0;