From 3fbb6b784acb4f308e2bc93dbc57761e8b6d9e80 Mon Sep 17 00:00:00 2001 From: Yubo Feng Date: Fri, 29 Apr 2022 14:38:02 -0700 Subject: fatfs: remove redundant judgment iput() has already judged the incoming parameter, so there is no need to repeat outside. Link: https://lkml.kernel.org/r/1648265418-76563-1-git-send-email-fengyubo3@huawei.com Signed-off-by: Yubo Feng Reported-by: Hulk Robot Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/inode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/inode.c b/fs/fat/inode.c index bf6051bdf1d1..cb698a827c9a 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1889,10 +1889,8 @@ out_invalid: fat_msg(sb, KERN_INFO, "Can't find a valid FAT filesystem"); out_fail: - if (fsinfo_inode) - iput(fsinfo_inode); - if (fat_inode) - iput(fat_inode); + iput(fsinfo_inode); + iput(fat_inode); unload_nls(sbi->nls_io); unload_nls(sbi->nls_disk); fat_reset_iocharset(&sbi->options); -- cgit v1.2.3 From e057aaec34ae7534ac8f5cc4f880aa7de8402852 Mon Sep 17 00:00:00 2001 From: Jonathan Lassoff Date: Fri, 29 Apr 2022 14:38:02 -0700 Subject: fatfs: add FAT messages to printk index In order for end users to quickly react to new issues that come up in production, it is proving useful to leverage the printk indexing system. This printk index enables kernel developers to use calls to printk() with changeable ad-hoc format strings (as they always have; no change of expectations), while enabling end users to examine format strings to detect changes. Since end users are using regular expressions to match messages printed through printk(), being able to detect changes in chosen format strings from release to release provides a useful signal to review printk()-matching regular expressions for any necessary updates. So that detailed FAT messages are captured by this printk index, this patch wraps fat_msg with a macro. [akpm@linux-foundation.org: coding-style cleanups] Link: https://lkml.kernel.org/r/8aaa2dd7995e820292bb40d2120ab69756662c65.1648688136.git.jof@thejof.com Signed-off-by: Jonathan Lassoff Acked-by: OGAWA Hirofumi Reviewed-by: Petr Mladek Tested-by: Petr Mladek Signed-off-by: Andrew Morton --- fs/fat/fat.h | 9 ++++++++- fs/fat/misc.c | 14 ++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 02d4d4234956..2cf85a6e0d99 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -433,8 +433,15 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...); __fat_fs_error(sb, 1, fmt , ## args) #define fat_fs_error_ratelimit(sb, fmt, args...) \ __fat_fs_error(sb, __ratelimit(&MSDOS_SB(sb)->ratelimit), fmt , ## args) + +#define FAT_PRINTK_PREFIX "%sFAT-fs (%s): " +#define fat_msg(sb, level, fmt, args...) \ +do { \ + printk_index_subsys_emit(FAT_PRINTK_PREFIX, level, fmt, ##args);\ + _fat_msg(sb, level, fmt, ##args); \ +} while (0) __printf(3, 4) __cold -void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...); +void _fat_msg(struct super_block *sb, const char *level, const char *fmt, ...); #define fat_msg_ratelimit(sb, level, fmt, args...) \ do { \ if (__ratelimit(&MSDOS_SB(sb)->ratelimit)) \ diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 91ca3c304211..855477d89f41 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -42,10 +42,16 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...) EXPORT_SYMBOL_GPL(__fat_fs_error); /** - * fat_msg() - print preformated FAT specific messages. Every thing what is - * not fat_fs_error() should be fat_msg(). + * _fat_msg() - Print a preformatted FAT message based on a superblock. + * @sb: A pointer to a &struct super_block + * @level: A Kernel printk level constant + * @fmt: The printf-style format string to print. + * + * Everything that is not fat_fs_error() should be fat_msg(). + * + * fat_msg() wraps _fat_msg() for printk indexing. */ -void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...) +void _fat_msg(struct super_block *sb, const char *level, const char *fmt, ...) { struct va_format vaf; va_list args; @@ -53,7 +59,7 @@ void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...) va_start(args, fmt); vaf.fmt = fmt; vaf.va = &args; - printk("%sFAT-fs (%s): %pV\n", level, sb->s_id, &vaf); + _printk(FAT_PRINTK_PREFIX "%pV\n", level, sb->s_id, &vaf); va_end(args); } -- cgit v1.2.3 From 183c3237c928109d2008c0456dff508baf692b20 Mon Sep 17 00:00:00 2001 From: OGAWA Hirofumi Date: Fri, 29 Apr 2022 14:38:02 -0700 Subject: fat: add ratelimit to fat*_ent_bread() fat*_ent_bread() can be the cause of too many report on I/O error path. So use fat_msg_ratelimit() instead. Link: https://lkml.kernel.org/r/87bkxogfeq.fsf@mail.parknet.co.jp Signed-off-by: OGAWA Hirofumi Reported-by: qianfan Tested-by: qianfan Signed-off-by: Andrew Morton --- fs/fat/fatent.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 978ac6751aeb..1db348f8f887 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -94,7 +94,8 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent, err_brelse: brelse(bhs[0]); err: - fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", (llu)blocknr); + fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)", + (llu)blocknr); return -EIO; } @@ -107,8 +108,8 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, fatent->fat_inode = MSDOS_SB(sb)->fat_inode; fatent->bhs[0] = sb_bread(sb, blocknr); if (!fatent->bhs[0]) { - fat_msg(sb, KERN_ERR, "FAT read failed (blocknr %llu)", - (llu)blocknr); + fat_msg_ratelimit(sb, KERN_ERR, "FAT read failed (blocknr %llu)", + (llu)blocknr); return -EIO; } fatent->nr_bhs = 1; -- cgit v1.2.3 From 4dcc3f96e7439f9a3a6e47d7fc147aad1338ddc4 Mon Sep 17 00:00:00 2001 From: Chung-Chiang Cheng Date: Tue, 3 May 2022 23:25:33 +0800 Subject: fat: split fat_truncate_time() into separate functions Separate fat_truncate_time() to each timestamps for later creation time work. This patch does not introduce any functional changes, it's merely refactoring change. Link: https://lkml.kernel.org/r/20220503152536.2503003-1-cccheng@synology.com Signed-off-by: Chung-Chiang Cheng Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/fat.h | 6 +++++ fs/fat/misc.c | 74 +++++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 27 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 2cf85a6e0d99..b7f60b366030 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -453,6 +453,12 @@ 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 struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts); +extern struct timespec64 fat_truncate_crtime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts); +extern struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts); extern int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags); extern int fat_update_time(struct inode *inode, struct timespec64 *now, diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 855477d89f41..80c6f8b3dc75 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -193,7 +193,7 @@ 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) +static inline int fat_tz_offset(const struct msdos_sb_info *sbi) { return (sbi->options.tz_set ? -sbi->options.time_offset : @@ -288,16 +288,49 @@ static inline struct timespec64 fat_timespec64_trunc_10ms(struct timespec64 ts) return ts; } +/* + * truncate atime to 24 hour granularity (00:00:00 in local timezone) + */ +struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts) +{ + /* to localtime */ + time64_t seconds = ts->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; + + return (struct timespec64){ seconds, 0 }; +} + +/* + * truncate creation time with appropriate granularity: + * msdos - 2 seconds + * vfat - 10 milliseconds + */ +struct timespec64 fat_truncate_crtime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts) +{ + if (sbi->options.isvfat) + return fat_timespec64_trunc_10ms(*ts); + else + return fat_timespec64_trunc_2secs(*ts); +} + +/* + * truncate mtime to 2 second granularity + */ +struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi, + const struct timespec64 *ts) +{ + return fat_timespec64_trunc_2secs(*ts); +} + /* * 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) + * all times in root node are always 0 */ int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags) { @@ -312,25 +345,12 @@ int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags) 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 = fat_timespec64_trunc_10ms(*now); - else - inode->i_ctime = fat_timespec64_trunc_2secs(*now); - } + if (flags & S_ATIME) + inode->i_atime = fat_truncate_atime(sbi, now); + if (flags & S_CTIME) + inode->i_ctime = fat_truncate_crtime(sbi, now); if (flags & S_MTIME) - inode->i_mtime = fat_timespec64_trunc_2secs(*now); + inode->i_mtime = fat_truncate_mtime(sbi, now); return 0; } -- cgit v1.2.3 From 0f9d148167c53a7029aba29cdc45072027033b72 Mon Sep 17 00:00:00 2001 From: Chung-Chiang Cheng Date: Tue, 3 May 2022 23:25:34 +0800 Subject: fat: ignore ctime updates, and keep ctime identical to mtime in memory FAT supports creation time but not change time, and there was no corresponding timestamp for creation time in previous VFS. The original implementation took the compromise of saving the in-memory change time into the on-disk creation time field, but this would lead to compatibility issues with non-linux systems. To address this issue, this patch changes the behavior of ctime. It will no longer be loaded and stored from the creation time on disk. Instead of that, it'll be consistent with the in-memory mtime and share the same on-disk field. All updates to mtime will also be applied to ctime in memory, while all updates to ctime will be ignored. Link: https://lkml.kernel.org/r/20220503152536.2503003-2-cccheng@synology.com Signed-off-by: Chung-Chiang Cheng Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/inode.c | 11 ++++------- fs/fat/misc.c | 9 ++++++--- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/inode.c b/fs/fat/inode.c index cb698a827c9a..d0371913f496 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -567,12 +567,11 @@ int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) & ~((loff_t)sbi->cluster_size - 1)) >> 9; fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0); - if (sbi->options.isvfat) { - fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime, - de->cdate, de->ctime_cs); + inode->i_ctime = inode->i_mtime; + if (sbi->options.isvfat) fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0); - } else - fat_truncate_time(inode, &inode->i_mtime, S_ATIME|S_CTIME); + else + inode->i_atime = fat_truncate_atime(sbi, &inode->i_mtime); return 0; } @@ -888,8 +887,6 @@ retry: &raw_entry->date, NULL); if (sbi->options.isvfat) { __le16 atime; - fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime, - &raw_entry->cdate, &raw_entry->ctime_cs); fat_time_unix2fat(sbi, &inode->i_atime, &atime, &raw_entry->adate, NULL); } diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 80c6f8b3dc75..8ebe49e315ab 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -347,10 +347,13 @@ int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags) if (flags & S_ATIME) inode->i_atime = fat_truncate_atime(sbi, now); - if (flags & S_CTIME) - inode->i_ctime = fat_truncate_crtime(sbi, now); + /* + * ctime and mtime share the same on-disk field, and should be + * identical in memory. all mtime updates will be applied to ctime, + * but ctime updates are ignored. + */ if (flags & S_MTIME) - inode->i_mtime = fat_truncate_mtime(sbi, now); + inode->i_mtime = inode->i_ctime = fat_truncate_mtime(sbi, now); return 0; } -- cgit v1.2.3 From 30abce053f811f52688a5b739c3e4ba98d34070d Mon Sep 17 00:00:00 2001 From: Chung-Chiang Cheng Date: Tue, 3 May 2022 23:25:35 +0800 Subject: fat: report creation time in statx creation time is no longer mixed with change time. Add an in-memory field for it, and report it in statx if supported. Link: https://lkml.kernel.org/r/20220503152536.2503003-3-cccheng@synology.com Signed-off-by: Chung-Chiang Cheng Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/fat.h | 1 + fs/fat/file.c | 14 +++++++++++--- fs/fat/inode.c | 10 ++++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/fat.h b/fs/fat/fat.h index b7f60b366030..6ed05ac0e694 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -126,6 +126,7 @@ struct msdos_inode_info { struct hlist_node i_fat_hash; /* hash by i_location */ struct hlist_node i_dir_hash; /* hash by i_logstart */ struct rw_semaphore truncate_lock; /* protect bmap against truncate */ + struct timespec64 i_crtime; /* File creation (birth) time */ struct inode vfs_inode; }; diff --git a/fs/fat/file.c b/fs/fat/file.c index a5a309fcc7fa..8f5218450a3a 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -399,13 +399,21 @@ int fat_getattr(struct user_namespace *mnt_userns, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct inode *inode = d_inode(path->dentry); + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); + generic_fillattr(mnt_userns, inode, stat); - stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size; + stat->blksize = sbi->cluster_size; - if (MSDOS_SB(inode->i_sb)->options.nfs == FAT_NFS_NOSTALE_RO) { + if (sbi->options.nfs == FAT_NFS_NOSTALE_RO) { /* Use i_pos for ino. This is used as fileid of nfs. */ - stat->ino = fat_i_pos_read(MSDOS_SB(inode->i_sb), inode); + stat->ino = fat_i_pos_read(sbi, inode); } + + if (sbi->options.isvfat && request_mask & STATX_BTIME) { + stat->result_mask |= STATX_BTIME; + stat->btime = MSDOS_I(inode)->i_crtime; + } + return 0; } EXPORT_SYMBOL_GPL(fat_getattr); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index d0371913f496..32e721b88a49 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -568,9 +568,11 @@ int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0); inode->i_ctime = inode->i_mtime; - if (sbi->options.isvfat) + if (sbi->options.isvfat) { fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0); - else + fat_time_fat2unix(sbi, &MSDOS_I(inode)->i_crtime, de->ctime, + de->cdate, de->ctime_cs); + } else inode->i_atime = fat_truncate_atime(sbi, &inode->i_mtime); return 0; @@ -756,6 +758,8 @@ static struct inode *fat_alloc_inode(struct super_block *sb) ei->i_logstart = 0; ei->i_attrs = 0; ei->i_pos = 0; + ei->i_crtime.tv_sec = 0; + ei->i_crtime.tv_nsec = 0; return &ei->vfs_inode; } @@ -889,6 +893,8 @@ retry: __le16 atime; fat_time_unix2fat(sbi, &inode->i_atime, &atime, &raw_entry->adate, NULL); + fat_time_unix2fat(sbi, &MSDOS_I(inode)->i_crtime, &raw_entry->ctime, + &raw_entry->cdate, &raw_entry->ctime_cs); } spin_unlock(&sbi->inode_hash_lock); mark_buffer_dirty(bh); -- cgit v1.2.3 From 1213375077451337c2381a8b5b88502a3fc394a2 Mon Sep 17 00:00:00 2001 From: Chung-Chiang Cheng Date: Tue, 3 May 2022 23:25:36 +0800 Subject: fat: remove time truncations in vfat_create/vfat_mkdir All the timestamps in vfat_create() and vfat_mkdir() come from fat_time_fat2unix() which ensures time granularity. We don't need to truncate them to fit FAT's format. Moreover, fat_truncate_crtime() and fat_timespec64_trunc_10ms() are also removed because there is no caller anymore. Link: https://lkml.kernel.org/r/20220503152536.2503003-4-cccheng@synology.com Signed-off-by: Chung-Chiang Cheng Acked-by: OGAWA Hirofumi Signed-off-by: Andrew Morton --- fs/fat/fat.h | 2 -- fs/fat/misc.c | 21 --------------------- fs/fat/namei_vfat.c | 4 ---- 3 files changed, 27 deletions(-) (limited to 'fs/fat') diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 6ed05ac0e694..a415c02ede39 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -456,8 +456,6 @@ extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts, __le16 *time, __le16 *date, u8 *time_cs); extern struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, const struct timespec64 *ts); -extern struct timespec64 fat_truncate_crtime(const struct msdos_sb_info *sbi, - const struct timespec64 *ts); extern struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi, const struct timespec64 *ts); extern int fat_truncate_time(struct inode *inode, struct timespec64 *now, diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 8ebe49e315ab..7e5d6ae305f2 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -281,13 +281,6 @@ static inline struct timespec64 fat_timespec64_trunc_2secs(struct timespec64 ts) return (struct timespec64){ ts.tv_sec & ~1ULL, 0 }; } -static inline struct timespec64 fat_timespec64_trunc_10ms(struct timespec64 ts) -{ - if (ts.tv_nsec) - ts.tv_nsec -= ts.tv_nsec % 10000000UL; - return ts; -} - /* * truncate atime to 24 hour granularity (00:00:00 in local timezone) */ @@ -305,20 +298,6 @@ struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi, return (struct timespec64){ seconds, 0 }; } -/* - * truncate creation time with appropriate granularity: - * msdos - 2 seconds - * vfat - 10 milliseconds - */ -struct timespec64 fat_truncate_crtime(const struct msdos_sb_info *sbi, - const struct timespec64 *ts) -{ - if (sbi->options.isvfat) - return fat_timespec64_trunc_10ms(*ts); - else - return fat_timespec64_trunc_2secs(*ts); -} - /* * truncate mtime to 2 second granularity */ diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index 5369d82e0bfb..c573314806cf 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -780,8 +780,6 @@ static int vfat_create(struct user_namespace *mnt_userns, struct inode *dir, goto out; } inode_inc_iversion(inode); - fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME); - /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); out: @@ -878,8 +876,6 @@ static int vfat_mkdir(struct user_namespace *mnt_userns, struct inode *dir, } inode_inc_iversion(inode); set_nlink(inode, 2); - fat_truncate_time(inode, &ts, S_ATIME|S_CTIME|S_MTIME); - /* timestamp is already written, so mark_inode_dirty() is unneeded. */ d_instantiate(dentry, inode); -- cgit v1.2.3