1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/fat/misc.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Written 1992,1993 by Werner Almesberger
61da177e4SLinus Torvalds * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
71da177e4SLinus Torvalds * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds
109e975daeSOGAWA Hirofumi #include "fat.h"
116bb885ecSFrank Sorenson #include <linux/iversion.h>
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds /*
1485c78591SDenis Karpov * fat_fs_error reports a file system problem that might indicate fa data
1585c78591SDenis Karpov * corruption/inconsistency. Depending on 'errors' mount option the
1685c78591SDenis Karpov * panic() is called, or error message is printed FAT and nothing is done,
1785c78591SDenis Karpov * or filesystem is remounted read-only (default behavior).
1885c78591SDenis Karpov * In case the file system is remounted read-only, it can be made writable
1985c78591SDenis Karpov * again by remounting it.
201da177e4SLinus Torvalds */
__fat_fs_error(struct super_block * sb,int report,const char * fmt,...)212c8a5ffbSAlexey Fisher void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
221da177e4SLinus Torvalds {
232c8a5ffbSAlexey Fisher struct fat_mount_options *opts = &MSDOS_SB(sb)->options;
241da177e4SLinus Torvalds va_list args;
252c8a5ffbSAlexey Fisher struct va_format vaf;
261da177e4SLinus Torvalds
27aaa04b48SOGAWA Hirofumi if (report) {
281da177e4SLinus Torvalds va_start(args, fmt);
292c8a5ffbSAlexey Fisher vaf.fmt = fmt;
302c8a5ffbSAlexey Fisher vaf.va = &args;
31e68e96d2SGu Zheng fat_msg(sb, KERN_ERR, "error, %pV", &vaf);
321da177e4SLinus Torvalds va_end(args);
33aaa04b48SOGAWA Hirofumi }
341da177e4SLinus Torvalds
3585c78591SDenis Karpov if (opts->errors == FAT_ERRORS_PANIC)
362c8a5ffbSAlexey Fisher panic("FAT-fs (%s): fs panic from previous error\n", sb->s_id);
37bc98a42cSDavid Howells else if (opts->errors == FAT_ERRORS_RO && !sb_rdonly(sb)) {
381751e8a6SLinus Torvalds sb->s_flags |= SB_RDONLY;
39e68e96d2SGu Zheng fat_msg(sb, KERN_ERR, "Filesystem has been set read-only");
401da177e4SLinus Torvalds }
411da177e4SLinus Torvalds }
42aaa04b48SOGAWA Hirofumi EXPORT_SYMBOL_GPL(__fat_fs_error);
431da177e4SLinus Torvalds
4481ac21d3SAlexey Fisher /**
45e057aaecSJonathan Lassoff * _fat_msg() - Print a preformatted FAT message based on a superblock.
46e057aaecSJonathan Lassoff * @sb: A pointer to a &struct super_block
47e057aaecSJonathan Lassoff * @level: A Kernel printk level constant
48e057aaecSJonathan Lassoff * @fmt: The printf-style format string to print.
49e057aaecSJonathan Lassoff *
50e057aaecSJonathan Lassoff * Everything that is not fat_fs_error() should be fat_msg().
51e057aaecSJonathan Lassoff *
52e057aaecSJonathan Lassoff * fat_msg() wraps _fat_msg() for printk indexing.
5381ac21d3SAlexey Fisher */
_fat_msg(struct super_block * sb,const char * level,const char * fmt,...)54e057aaecSJonathan Lassoff void _fat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
5581ac21d3SAlexey Fisher {
5681ac21d3SAlexey Fisher struct va_format vaf;
5781ac21d3SAlexey Fisher va_list args;
5881ac21d3SAlexey Fisher
5981ac21d3SAlexey Fisher va_start(args, fmt);
6081ac21d3SAlexey Fisher vaf.fmt = fmt;
6181ac21d3SAlexey Fisher vaf.va = &args;
62e057aaecSJonathan Lassoff _printk(FAT_PRINTK_PREFIX "%pV\n", level, sb->s_id, &vaf);
6381ac21d3SAlexey Fisher va_end(args);
6481ac21d3SAlexey Fisher }
6581ac21d3SAlexey Fisher
661da177e4SLinus Torvalds /* Flushes the number of free clusters on FAT32 */
671da177e4SLinus Torvalds /* XXX: Need to write one per FSINFO block. Currently only writes 1 */
fat_clusters_flush(struct super_block * sb)68ed248b29SOGAWA Hirofumi int fat_clusters_flush(struct super_block *sb)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds struct msdos_sb_info *sbi = MSDOS_SB(sb);
711da177e4SLinus Torvalds struct buffer_head *bh;
721da177e4SLinus Torvalds struct fat_boot_fsinfo *fsinfo;
731da177e4SLinus Torvalds
74306790f7SCarmeli Tamir if (!is_fat32(sbi))
75ed248b29SOGAWA Hirofumi return 0;
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds bh = sb_bread(sb, sbi->fsinfo_sector);
781da177e4SLinus Torvalds if (bh == NULL) {
79869f58c0SAlexey Fisher fat_msg(sb, KERN_ERR, "bread failed in fat_clusters_flush");
80ed248b29SOGAWA Hirofumi return -EIO;
811da177e4SLinus Torvalds }
821da177e4SLinus Torvalds
831da177e4SLinus Torvalds fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
841da177e4SLinus Torvalds /* Sanity check */
851da177e4SLinus Torvalds if (!IS_FSINFO(fsinfo)) {
86869f58c0SAlexey Fisher fat_msg(sb, KERN_ERR, "Invalid FSINFO signature: "
87869f58c0SAlexey Fisher "0x%08x, 0x%08x (sector = %lu)",
881da177e4SLinus Torvalds le32_to_cpu(fsinfo->signature1),
891da177e4SLinus Torvalds le32_to_cpu(fsinfo->signature2),
901da177e4SLinus Torvalds sbi->fsinfo_sector);
911da177e4SLinus Torvalds } else {
921da177e4SLinus Torvalds if (sbi->free_clusters != -1)
931da177e4SLinus Torvalds fsinfo->free_clusters = cpu_to_le32(sbi->free_clusters);
941da177e4SLinus Torvalds if (sbi->prev_free != -1)
951da177e4SLinus Torvalds fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
961da177e4SLinus Torvalds mark_buffer_dirty(bh);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds brelse(bh);
99ed248b29SOGAWA Hirofumi
100ed248b29SOGAWA Hirofumi return 0;
1011da177e4SLinus Torvalds }
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds /*
1041da177e4SLinus Torvalds * fat_chain_add() adds a new cluster to the chain of clusters represented
1051da177e4SLinus Torvalds * by inode.
1061da177e4SLinus Torvalds */
fat_chain_add(struct inode * inode,int new_dclus,int nr_cluster)1071da177e4SLinus Torvalds int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds struct super_block *sb = inode->i_sb;
1101da177e4SLinus Torvalds struct msdos_sb_info *sbi = MSDOS_SB(sb);
1111da177e4SLinus Torvalds int ret, new_fclus, last;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds /*
1141da177e4SLinus Torvalds * We must locate the last cluster of the file to add this new
1151da177e4SLinus Torvalds * one (new_dclus) to the end of the link list (the FAT).
1161da177e4SLinus Torvalds */
1171da177e4SLinus Torvalds last = new_fclus = 0;
1181da177e4SLinus Torvalds if (MSDOS_I(inode)->i_start) {
1191da177e4SLinus Torvalds int fclus, dclus;
1201da177e4SLinus Torvalds
1211da177e4SLinus Torvalds ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
1221da177e4SLinus Torvalds if (ret < 0)
1231da177e4SLinus Torvalds return ret;
1241da177e4SLinus Torvalds new_fclus = fclus + 1;
1251da177e4SLinus Torvalds last = dclus;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds
1281da177e4SLinus Torvalds /* add new one to the last of the cluster chain */
1291da177e4SLinus Torvalds if (last) {
1301da177e4SLinus Torvalds struct fat_entry fatent;
1311da177e4SLinus Torvalds
1321da177e4SLinus Torvalds fatent_init(&fatent);
1331da177e4SLinus Torvalds ret = fat_ent_read(inode, &fatent, last);
1341da177e4SLinus Torvalds if (ret >= 0) {
1351da177e4SLinus Torvalds int wait = inode_needs_sync(inode);
1361da177e4SLinus Torvalds ret = fat_ent_write(inode, &fatent, new_dclus, wait);
1371da177e4SLinus Torvalds fatent_brelse(&fatent);
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds if (ret < 0)
1401da177e4SLinus Torvalds return ret;
141c39540c6SRavishankar N /*
142c39540c6SRavishankar N * FIXME:Although we can add this cache, fat_cache_add() is
143c39540c6SRavishankar N * assuming to be called after linear search with fat_cache_id.
144c39540c6SRavishankar N */
1451da177e4SLinus Torvalds // fat_cache_add(inode, new_fclus, new_dclus);
1461da177e4SLinus Torvalds } else {
1471da177e4SLinus Torvalds MSDOS_I(inode)->i_start = new_dclus;
1481da177e4SLinus Torvalds MSDOS_I(inode)->i_logstart = new_dclus;
1491da177e4SLinus Torvalds /*
1502f3d675bSJan Kara * Since generic_write_sync() synchronizes regular files later,
1512f3d675bSJan Kara * we sync here only directories.
1521da177e4SLinus Torvalds */
1531da177e4SLinus Torvalds if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
1541da177e4SLinus Torvalds ret = fat_sync_inode(inode);
1551da177e4SLinus Torvalds if (ret)
1561da177e4SLinus Torvalds return ret;
1571da177e4SLinus Torvalds } else
1581da177e4SLinus Torvalds mark_inode_dirty(inode);
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
16185c78591SDenis Karpov fat_fs_error(sb, "clusters badly computed (%d != %llu)",
162c3302931SOGAWA Hirofumi new_fclus,
163c3302931SOGAWA Hirofumi (llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
1641da177e4SLinus Torvalds fat_cache_inval_inode(inode);
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds return 0;
1691da177e4SLinus Torvalds }
1701da177e4SLinus Torvalds
1717decd1cbSOGAWA Hirofumi /*
1727decd1cbSOGAWA Hirofumi * The epoch of FAT timestamp is 1980.
1737decd1cbSOGAWA Hirofumi * : bits : value
1747decd1cbSOGAWA Hirofumi * date: 0 - 4: day (1 - 31)
1757decd1cbSOGAWA Hirofumi * date: 5 - 8: month (1 - 12)
1767decd1cbSOGAWA Hirofumi * date: 9 - 15: year (0 - 127) from 1980
1777decd1cbSOGAWA Hirofumi * time: 0 - 4: sec (0 - 29) 2sec counts
1787decd1cbSOGAWA Hirofumi * time: 5 - 10: min (0 - 59)
1797decd1cbSOGAWA Hirofumi * time: 11 - 15: hour (0 - 23)
1807decd1cbSOGAWA Hirofumi */
1817decd1cbSOGAWA Hirofumi #define SECS_PER_MIN 60
1827decd1cbSOGAWA Hirofumi #define SECS_PER_HOUR (60 * 60)
1837decd1cbSOGAWA Hirofumi #define SECS_PER_DAY (SECS_PER_HOUR * 24)
1847decd1cbSOGAWA Hirofumi /* days between 1.1.70 and 1.1.80 (2 leap days) */
1857decd1cbSOGAWA Hirofumi #define DAYS_DELTA (365 * 10 + 2)
1867decd1cbSOGAWA Hirofumi /* 120 (2100 - 1980) isn't leap year */
1877decd1cbSOGAWA Hirofumi #define YEAR_2100 120
1887decd1cbSOGAWA Hirofumi #define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100)
1897decd1cbSOGAWA Hirofumi
1901da177e4SLinus Torvalds /* Linear day numbers of the respective 1sts in non-leap years. */
191f423420cSArnd Bergmann static long days_in_year[] = {
1921da177e4SLinus Torvalds /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
1937decd1cbSOGAWA Hirofumi 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
1941da177e4SLinus Torvalds };
1951da177e4SLinus Torvalds
fat_tz_offset(const struct msdos_sb_info * sbi)1964dcc3f96SChung-Chiang Cheng static inline int fat_tz_offset(const struct msdos_sb_info *sbi)
197d9f4d942SFrank Sorenson {
198d9f4d942SFrank Sorenson return (sbi->options.tz_set ?
199d9f4d942SFrank Sorenson -sbi->options.time_offset :
200d9f4d942SFrank Sorenson sys_tz.tz_minuteswest) * SECS_PER_MIN;
201d9f4d942SFrank Sorenson }
202d9f4d942SFrank Sorenson
2037decd1cbSOGAWA Hirofumi /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
fat_time_fat2unix(struct msdos_sb_info * sbi,struct timespec64 * ts,__le16 __time,__le16 __date,u8 time_cs)204f423420cSArnd Bergmann void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
2057decd1cbSOGAWA Hirofumi __le16 __time, __le16 __date, u8 time_cs)
2061da177e4SLinus Torvalds {
2077decd1cbSOGAWA Hirofumi u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date);
208f423420cSArnd Bergmann time64_t second;
209f423420cSArnd Bergmann long day, leap_day, month, year;
2101da177e4SLinus Torvalds
2111da177e4SLinus Torvalds year = date >> 9;
2127decd1cbSOGAWA Hirofumi month = max(1, (date >> 5) & 0xf);
2137decd1cbSOGAWA Hirofumi day = max(1, date & 0x1f) - 1;
2147decd1cbSOGAWA Hirofumi
2157decd1cbSOGAWA Hirofumi leap_day = (year + 3) / 4;
2167decd1cbSOGAWA Hirofumi if (year > YEAR_2100) /* 2100 isn't leap year */
2177decd1cbSOGAWA Hirofumi leap_day--;
2187decd1cbSOGAWA Hirofumi if (IS_LEAP_YEAR(year) && month > 2)
2197decd1cbSOGAWA Hirofumi leap_day++;
2207decd1cbSOGAWA Hirofumi
2217decd1cbSOGAWA Hirofumi second = (time & 0x1f) << 1;
2227decd1cbSOGAWA Hirofumi second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
2237decd1cbSOGAWA Hirofumi second += (time >> 11) * SECS_PER_HOUR;
224f423420cSArnd Bergmann second += (time64_t)(year * 365 + leap_day
2257decd1cbSOGAWA Hirofumi + days_in_year[month] + day
2267decd1cbSOGAWA Hirofumi + DAYS_DELTA) * SECS_PER_DAY;
2277decd1cbSOGAWA Hirofumi
228d9f4d942SFrank Sorenson second += fat_tz_offset(sbi);
2297decd1cbSOGAWA Hirofumi
2307decd1cbSOGAWA Hirofumi if (time_cs) {
2317decd1cbSOGAWA Hirofumi ts->tv_sec = second + (time_cs / 100);
2327decd1cbSOGAWA Hirofumi ts->tv_nsec = (time_cs % 100) * 10000000;
2337decd1cbSOGAWA Hirofumi } else {
2347decd1cbSOGAWA Hirofumi ts->tv_sec = second;
2357decd1cbSOGAWA Hirofumi ts->tv_nsec = 0;
2367decd1cbSOGAWA Hirofumi }
2371da177e4SLinus Torvalds }
2381da177e4SLinus Torvalds
239b0d4adafSDavid Gow /* Export fat_time_fat2unix() for the fat_test KUnit tests. */
240b0d4adafSDavid Gow EXPORT_SYMBOL_GPL(fat_time_fat2unix);
241b0d4adafSDavid Gow
2427decd1cbSOGAWA Hirofumi /* Convert linear UNIX date to a FAT time/date pair. */
fat_time_unix2fat(struct msdos_sb_info * sbi,struct timespec64 * ts,__le16 * time,__le16 * date,u8 * time_cs)243f423420cSArnd Bergmann void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
2447decd1cbSOGAWA Hirofumi __le16 *time, __le16 *date, u8 *time_cs)
2451da177e4SLinus Torvalds {
2461d81a181SZhaolei struct tm tm;
247d9f4d942SFrank Sorenson time64_to_tm(ts->tv_sec, -fat_tz_offset(sbi), &tm);
2481da177e4SLinus Torvalds
2491d81a181SZhaolei /* FAT can only support year between 1980 to 2107 */
2501d81a181SZhaolei if (tm.tm_year < 1980 - 1900) {
2517decd1cbSOGAWA Hirofumi *time = 0;
2527decd1cbSOGAWA Hirofumi *date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
2537decd1cbSOGAWA Hirofumi if (time_cs)
2547decd1cbSOGAWA Hirofumi *time_cs = 0;
2557decd1cbSOGAWA Hirofumi return;
2567decd1cbSOGAWA Hirofumi }
2571d81a181SZhaolei if (tm.tm_year > 2107 - 1900) {
2587decd1cbSOGAWA Hirofumi *time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
2597decd1cbSOGAWA Hirofumi *date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
2607decd1cbSOGAWA Hirofumi if (time_cs)
2617decd1cbSOGAWA Hirofumi *time_cs = 199;
2627decd1cbSOGAWA Hirofumi return;
2637decd1cbSOGAWA Hirofumi }
2641da177e4SLinus Torvalds
2651d81a181SZhaolei /* from 1900 -> from 1980 */
2661d81a181SZhaolei tm.tm_year -= 80;
2671d81a181SZhaolei /* 0~11 -> 1~12 */
2681d81a181SZhaolei tm.tm_mon++;
2691d81a181SZhaolei /* 0~59 -> 0~29(2sec counts) */
2701d81a181SZhaolei tm.tm_sec >>= 1;
2717decd1cbSOGAWA Hirofumi
2721d81a181SZhaolei *time = cpu_to_le16(tm.tm_hour << 11 | tm.tm_min << 5 | tm.tm_sec);
2731d81a181SZhaolei *date = cpu_to_le16(tm.tm_year << 9 | tm.tm_mon << 5 | tm.tm_mday);
2747decd1cbSOGAWA Hirofumi if (time_cs)
2757decd1cbSOGAWA Hirofumi *time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
2767decd1cbSOGAWA Hirofumi }
2777decd1cbSOGAWA Hirofumi EXPORT_SYMBOL_GPL(fat_time_unix2fat);
2781da177e4SLinus Torvalds
fat_timespec64_trunc_2secs(struct timespec64 ts)2796bb885ecSFrank Sorenson static inline struct timespec64 fat_timespec64_trunc_2secs(struct timespec64 ts)
2806bb885ecSFrank Sorenson {
2816bb885ecSFrank Sorenson return (struct timespec64){ ts.tv_sec & ~1ULL, 0 };
2826bb885ecSFrank Sorenson }
28397acf83dSDeepa Dinamani
2846bb885ecSFrank Sorenson /*
2854dcc3f96SChung-Chiang Cheng * truncate atime to 24 hour granularity (00:00:00 in local timezone)
2864dcc3f96SChung-Chiang Cheng */
fat_truncate_atime(const struct msdos_sb_info * sbi,const struct timespec64 * ts)2874dcc3f96SChung-Chiang Cheng struct timespec64 fat_truncate_atime(const struct msdos_sb_info *sbi,
2884dcc3f96SChung-Chiang Cheng const struct timespec64 *ts)
2894dcc3f96SChung-Chiang Cheng {
2904dcc3f96SChung-Chiang Cheng /* to localtime */
2914dcc3f96SChung-Chiang Cheng time64_t seconds = ts->tv_sec - fat_tz_offset(sbi);
2924dcc3f96SChung-Chiang Cheng s32 remainder;
2934dcc3f96SChung-Chiang Cheng
2944dcc3f96SChung-Chiang Cheng div_s64_rem(seconds, SECS_PER_DAY, &remainder);
2954dcc3f96SChung-Chiang Cheng /* to day boundary, and back to unix time */
2964dcc3f96SChung-Chiang Cheng seconds = seconds + fat_tz_offset(sbi) - remainder;
2974dcc3f96SChung-Chiang Cheng
2984dcc3f96SChung-Chiang Cheng return (struct timespec64){ seconds, 0 };
2994dcc3f96SChung-Chiang Cheng }
3004dcc3f96SChung-Chiang Cheng
3014dcc3f96SChung-Chiang Cheng /*
3024dcc3f96SChung-Chiang Cheng * truncate mtime to 2 second granularity
3034dcc3f96SChung-Chiang Cheng */
fat_truncate_mtime(const struct msdos_sb_info * sbi,const struct timespec64 * ts)3044dcc3f96SChung-Chiang Cheng struct timespec64 fat_truncate_mtime(const struct msdos_sb_info *sbi,
3054dcc3f96SChung-Chiang Cheng const struct timespec64 *ts)
3064dcc3f96SChung-Chiang Cheng {
3074dcc3f96SChung-Chiang Cheng return fat_timespec64_trunc_2secs(*ts);
3084dcc3f96SChung-Chiang Cheng }
3094dcc3f96SChung-Chiang Cheng
3104dcc3f96SChung-Chiang Cheng /*
3114dcc3f96SChung-Chiang Cheng * truncate the various times with appropriate granularity:
3124dcc3f96SChung-Chiang Cheng * all times in root node are always 0
3136bb885ecSFrank Sorenson */
fat_truncate_time(struct inode * inode,struct timespec64 * now,int flags)3146bb885ecSFrank Sorenson int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags)
3156bb885ecSFrank Sorenson {
3166bb885ecSFrank Sorenson struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
3176bb885ecSFrank Sorenson struct timespec64 ts;
3186bb885ecSFrank Sorenson
3196bb885ecSFrank Sorenson if (inode->i_ino == MSDOS_ROOT_INO)
3206bb885ecSFrank Sorenson return 0;
3216bb885ecSFrank Sorenson
3226bb885ecSFrank Sorenson if (now == NULL) {
3236bb885ecSFrank Sorenson now = &ts;
3246bb885ecSFrank Sorenson ts = current_time(inode);
3256bb885ecSFrank Sorenson }
3266bb885ecSFrank Sorenson
3274dcc3f96SChung-Chiang Cheng if (flags & S_ATIME)
3284dcc3f96SChung-Chiang Cheng inode->i_atime = fat_truncate_atime(sbi, now);
3290f9d1481SChung-Chiang Cheng /*
3300f9d1481SChung-Chiang Cheng * ctime and mtime share the same on-disk field, and should be
3310f9d1481SChung-Chiang Cheng * identical in memory. all mtime updates will be applied to ctime,
3320f9d1481SChung-Chiang Cheng * but ctime updates are ignored.
3330f9d1481SChung-Chiang Cheng */
3346bb885ecSFrank Sorenson if (flags & S_MTIME)
335ea60635eSJeff Layton inode->i_mtime = inode_set_ctime_to_ts(inode,
336ea60635eSJeff Layton fat_truncate_mtime(sbi, now));
3376bb885ecSFrank Sorenson
3386bb885ecSFrank Sorenson return 0;
3396bb885ecSFrank Sorenson }
3406bb885ecSFrank Sorenson EXPORT_SYMBOL_GPL(fat_truncate_time);
3416bb885ecSFrank Sorenson
fat_update_time(struct inode * inode,int flags)342*913e9928SJeff Layton int fat_update_time(struct inode *inode, int flags)
3436bb885ecSFrank Sorenson {
344ff4136e6SEric Biggers int dirty_flags = 0;
3456bb885ecSFrank Sorenson
3466bb885ecSFrank Sorenson if (inode->i_ino == MSDOS_ROOT_INO)
3476bb885ecSFrank Sorenson return 0;
3486bb885ecSFrank Sorenson
349ff4136e6SEric Biggers if (flags & (S_ATIME | S_CTIME | S_MTIME)) {
3506f4aaee3SJeff Layton fat_truncate_time(inode, NULL, flags);
351ff4136e6SEric Biggers if (inode->i_sb->s_flags & SB_LAZYTIME)
352ff4136e6SEric Biggers dirty_flags |= I_DIRTY_TIME;
353ff4136e6SEric Biggers else
354ff4136e6SEric Biggers dirty_flags |= I_DIRTY_SYNC;
355ff4136e6SEric Biggers }
3566bb885ecSFrank Sorenson
357ff4136e6SEric Biggers __mark_inode_dirty(inode, dirty_flags);
3586bb885ecSFrank Sorenson return 0;
3596bb885ecSFrank Sorenson }
3606bb885ecSFrank Sorenson EXPORT_SYMBOL_GPL(fat_update_time);
3616bb885ecSFrank Sorenson
fat_sync_bhs(struct buffer_head ** bhs,int nr_bhs)3621da177e4SLinus Torvalds int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
3631da177e4SLinus Torvalds {
3645b00226dSOGAWA Hirofumi int i, err = 0;
3651da177e4SLinus Torvalds
3669cb569d6SChristoph Hellwig for (i = 0; i < nr_bhs; i++)
3672a222ca9SMike Christie write_dirty_buffer(bhs[i], 0);
3689cb569d6SChristoph Hellwig
3691da177e4SLinus Torvalds for (i = 0; i < nr_bhs; i++) {
3701da177e4SLinus Torvalds wait_on_buffer(bhs[i]);
3710edd55faSChristoph Hellwig if (!err && !buffer_uptodate(bhs[i]))
3721da177e4SLinus Torvalds err = -EIO;
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds return err;
3751da177e4SLinus Torvalds }
376