Add the fat-specific inode_operation ->update_time() and fat_truncate_time() function to truncate the inode timestamps with the appropriate granularity. Signed-off-by: Frank Sorenson <sorenson@xxxxxxxxxx> --- fs/fat/fat.h | 2 ++ fs/fat/file.c | 1 + fs/fat/misc.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fat/namei_msdos.c | 1 + fs/fat/namei_vfat.c | 1 + 5 files changed, 67 insertions(+) diff --git a/fs/fat/fat.h b/fs/fat/fat.h index c40f7e69f078..6201c6bdb5f9 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -428,6 +428,8 @@ extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts, __le16 __time, __le16 __date, u8 time_cs); extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts, __le16 *time, __le16 *date, u8 *time_cs); +extern int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags); +extern int fat_update_time(struct inode *inode, struct timespec64 *now, int flags); extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); int fat_cache_init(void); diff --git a/fs/fat/file.c b/fs/fat/file.c index 4f3d72fb1e60..19b6b0566411 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -552,4 +552,5 @@ EXPORT_SYMBOL_GPL(fat_setattr); const struct inode_operations fat_file_inode_operations = { .setattr = fat_setattr, .getattr = fat_getattr, + .update_time = fat_update_time, }; diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 58580c7e558e..42c4fb063287 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -255,6 +255,68 @@ void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts, } EXPORT_SYMBOL_GPL(fat_time_unix2fat); +/* + * 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) + */ +noinline 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) { + int offset = fat_tz_offset(sbi); + long seconds; + + seconds = now->tv_sec; + seconds -= (now->tv_sec - offset) % 86400; + + inode->i_atime = (struct timespec64){ seconds, 0 }; + } + if (flags & S_CTIME) { + if (sbi->options.isvfat) + inode->i_ctime = *now; + else + inode->i_ctime = (struct timespec64){ now->tv_sec & ~1, 0 }; + } + if (flags & S_MTIME) + inode->i_mtime = (struct timespec64){ now->tv_sec & ~1, 0 }; + 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; + + if (inode->i_ino == MSDOS_ROOT_INO) + return 0; + fat_truncate_time(inode, now, flags); + + if ((flags & (S_ATIME | S_CTIME | S_MTIME)) && + !(inode->i_sb->s_flags & SB_LAZYTIME)) + 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; diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index efb8c40c9d27..effbdd5fbf6e 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -637,6 +637,7 @@ static const struct inode_operations msdos_dir_inode_operations = { .rename = msdos_rename, .setattr = fat_setattr, .getattr = fat_getattr, + .update_time = fat_update_time, }; static void setup(struct super_block *sb) diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 82cd1e69cbdf..1daa57cf4bf3 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -1032,6 +1032,7 @@ static const struct inode_operations vfat_dir_inode_operations = { .rename = vfat_rename, .setattr = fat_setattr, .getattr = fat_getattr, + .update_time = fat_update_time, }; static void setup(struct super_block *sb) -- 2.13.6