.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * linux/fs/fat/misc.c |
---|
3 | 4 | * |
---|
.. | .. |
---|
7 | 8 | */ |
---|
8 | 9 | |
---|
9 | 10 | #include "fat.h" |
---|
| 11 | +#include <linux/iversion.h> |
---|
10 | 12 | |
---|
11 | 13 | /* |
---|
12 | 14 | * fat_fs_error reports a file system problem that might indicate fa data |
---|
.. | .. |
---|
63 | 65 | struct buffer_head *bh; |
---|
64 | 66 | struct fat_boot_fsinfo *fsinfo; |
---|
65 | 67 | |
---|
66 | | - if (sbi->fat_bits != 32) |
---|
| 68 | + if (!is_fat32(sbi)) |
---|
67 | 69 | return 0; |
---|
68 | 70 | |
---|
69 | 71 | bh = sb_bread(sb, sbi->fsinfo_sector); |
---|
.. | .. |
---|
185 | 187 | 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, |
---|
186 | 188 | }; |
---|
187 | 189 | |
---|
| 190 | +static inline int fat_tz_offset(struct msdos_sb_info *sbi) |
---|
| 191 | +{ |
---|
| 192 | + return (sbi->options.tz_set ? |
---|
| 193 | + -sbi->options.time_offset : |
---|
| 194 | + sys_tz.tz_minuteswest) * SECS_PER_MIN; |
---|
| 195 | +} |
---|
| 196 | + |
---|
188 | 197 | /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ |
---|
189 | 198 | void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts, |
---|
190 | 199 | __le16 __time, __le16 __date, u8 time_cs) |
---|
.. | .. |
---|
210 | 219 | + days_in_year[month] + day |
---|
211 | 220 | + DAYS_DELTA) * SECS_PER_DAY; |
---|
212 | 221 | |
---|
213 | | - if (!sbi->options.tz_set) |
---|
214 | | - second += sys_tz.tz_minuteswest * SECS_PER_MIN; |
---|
215 | | - else |
---|
216 | | - second -= sbi->options.time_offset * SECS_PER_MIN; |
---|
| 222 | + second += fat_tz_offset(sbi); |
---|
217 | 223 | |
---|
218 | 224 | if (time_cs) { |
---|
219 | 225 | ts->tv_sec = second + (time_cs / 100); |
---|
.. | .. |
---|
229 | 235 | __le16 *time, __le16 *date, u8 *time_cs) |
---|
230 | 236 | { |
---|
231 | 237 | struct tm tm; |
---|
232 | | - time64_to_tm(ts->tv_sec, |
---|
233 | | - (sbi->options.tz_set ? sbi->options.time_offset : |
---|
234 | | - -sys_tz.tz_minuteswest) * SECS_PER_MIN, &tm); |
---|
| 238 | + time64_to_tm(ts->tv_sec, -fat_tz_offset(sbi), &tm); |
---|
235 | 239 | |
---|
236 | 240 | /* FAT can only support year between 1980 to 2107 */ |
---|
237 | 241 | if (tm.tm_year < 1980 - 1900) { |
---|
.. | .. |
---|
263 | 267 | } |
---|
264 | 268 | EXPORT_SYMBOL_GPL(fat_time_unix2fat); |
---|
265 | 269 | |
---|
| 270 | +static inline struct timespec64 fat_timespec64_trunc_2secs(struct timespec64 ts) |
---|
| 271 | +{ |
---|
| 272 | + return (struct timespec64){ ts.tv_sec & ~1ULL, 0 }; |
---|
| 273 | +} |
---|
| 274 | + |
---|
| 275 | +static inline struct timespec64 fat_timespec64_trunc_10ms(struct timespec64 ts) |
---|
| 276 | +{ |
---|
| 277 | + if (ts.tv_nsec) |
---|
| 278 | + ts.tv_nsec -= ts.tv_nsec % 10000000UL; |
---|
| 279 | + return ts; |
---|
| 280 | +} |
---|
| 281 | + |
---|
| 282 | +/* |
---|
| 283 | + * truncate the various times with appropriate granularity: |
---|
| 284 | + * root inode: |
---|
| 285 | + * all times always 0 |
---|
| 286 | + * all other inodes: |
---|
| 287 | + * mtime - 2 seconds |
---|
| 288 | + * ctime |
---|
| 289 | + * msdos - 2 seconds |
---|
| 290 | + * vfat - 10 milliseconds |
---|
| 291 | + * atime - 24 hours (00:00:00 in local timezone) |
---|
| 292 | + */ |
---|
| 293 | +int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags) |
---|
| 294 | +{ |
---|
| 295 | + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); |
---|
| 296 | + struct timespec64 ts; |
---|
| 297 | + |
---|
| 298 | + if (inode->i_ino == MSDOS_ROOT_INO) |
---|
| 299 | + return 0; |
---|
| 300 | + |
---|
| 301 | + if (now == NULL) { |
---|
| 302 | + now = &ts; |
---|
| 303 | + ts = current_time(inode); |
---|
| 304 | + } |
---|
| 305 | + |
---|
| 306 | + if (flags & S_ATIME) { |
---|
| 307 | + /* to localtime */ |
---|
| 308 | + time64_t seconds = now->tv_sec - fat_tz_offset(sbi); |
---|
| 309 | + s32 remainder; |
---|
| 310 | + |
---|
| 311 | + div_s64_rem(seconds, SECS_PER_DAY, &remainder); |
---|
| 312 | + /* to day boundary, and back to unix time */ |
---|
| 313 | + seconds = seconds + fat_tz_offset(sbi) - remainder; |
---|
| 314 | + |
---|
| 315 | + inode->i_atime = (struct timespec64){ seconds, 0 }; |
---|
| 316 | + } |
---|
| 317 | + if (flags & S_CTIME) { |
---|
| 318 | + if (sbi->options.isvfat) |
---|
| 319 | + inode->i_ctime = fat_timespec64_trunc_10ms(*now); |
---|
| 320 | + else |
---|
| 321 | + inode->i_ctime = fat_timespec64_trunc_2secs(*now); |
---|
| 322 | + } |
---|
| 323 | + if (flags & S_MTIME) |
---|
| 324 | + inode->i_mtime = fat_timespec64_trunc_2secs(*now); |
---|
| 325 | + |
---|
| 326 | + return 0; |
---|
| 327 | +} |
---|
| 328 | +EXPORT_SYMBOL_GPL(fat_truncate_time); |
---|
| 329 | + |
---|
| 330 | +int fat_update_time(struct inode *inode, struct timespec64 *now, int flags) |
---|
| 331 | +{ |
---|
| 332 | + int iflags = I_DIRTY_TIME; |
---|
| 333 | + bool dirty = false; |
---|
| 334 | + |
---|
| 335 | + if (inode->i_ino == MSDOS_ROOT_INO) |
---|
| 336 | + return 0; |
---|
| 337 | + |
---|
| 338 | + fat_truncate_time(inode, now, flags); |
---|
| 339 | + if (flags & S_VERSION) |
---|
| 340 | + dirty = inode_maybe_inc_iversion(inode, false); |
---|
| 341 | + if ((flags & (S_ATIME | S_CTIME | S_MTIME)) && |
---|
| 342 | + !(inode->i_sb->s_flags & SB_LAZYTIME)) |
---|
| 343 | + dirty = true; |
---|
| 344 | + |
---|
| 345 | + if (dirty) |
---|
| 346 | + iflags |= I_DIRTY_SYNC; |
---|
| 347 | + __mark_inode_dirty(inode, iflags); |
---|
| 348 | + return 0; |
---|
| 349 | +} |
---|
| 350 | +EXPORT_SYMBOL_GPL(fat_update_time); |
---|
| 351 | + |
---|
266 | 352 | int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) |
---|
267 | 353 | { |
---|
268 | 354 | int i, err = 0; |
---|