| .. | .. |
|---|
| 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; |
|---|