|
@@ -7,6 +7,7 @@
|
|
|
*/
|
|
|
|
|
|
#include "fat.h"
|
|
|
+#include <linux/iversion.h>
|
|
|
|
|
|
/*
|
|
|
* fat_fs_error reports a file system problem that might indicate fa data
|
|
@@ -265,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;
|