xref: /openbmc/linux/fs/fat/misc.c (revision 913e9928)
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