Blame view

fs/fat/misc.c 9.38 KB
457c89965   Thomas Gleixner   treewide: Add SPD...
1
  // SPDX-License-Identifier: GPL-2.0-only
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
  /*
   *  linux/fs/fat/misc.c
   *
   *  Written 1992,1993 by Werner Almesberger
   *  22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980
   *		 and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru)
   */
9e975dae2   OGAWA Hirofumi   fat: split includ...
9
  #include "fat.h"
6bb885ecd   Frank Sorenson   fat: add function...
10
  #include <linux/iversion.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
  
  /*
85c785919   Denis Karpov   FAT: add 'errors'...
13
14
15
16
17
18
   * fat_fs_error reports a file system problem that might indicate fa data
   * corruption/inconsistency. Depending on 'errors' mount option the
   * panic() is called, or error message is printed FAT and nothing is done,
   * or filesystem is remounted read-only (default behavior).
   * In case the file system is remounted read-only, it can be made writable
   * again by remounting it.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
   */
2c8a5ffb9   Alexey Fisher   fat: Convert fat_...
20
  void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
  {
2c8a5ffb9   Alexey Fisher   fat: Convert fat_...
22
  	struct fat_mount_options *opts = &MSDOS_SB(sb)->options;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
  	va_list args;
2c8a5ffb9   Alexey Fisher   fat: Convert fat_...
24
  	struct va_format vaf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25

aaa04b487   OGAWA Hirofumi   fatfs: ratelimit ...
26
  	if (report) {
aaa04b487   OGAWA Hirofumi   fatfs: ratelimit ...
27
  		va_start(args, fmt);
2c8a5ffb9   Alexey Fisher   fat: Convert fat_...
28
29
  		vaf.fmt = fmt;
  		vaf.va = &args;
e68e96d2a   Gu Zheng   fs/fat: use fat_m...
30
  		fat_msg(sb, KERN_ERR, "error, %pV", &vaf);
aaa04b487   OGAWA Hirofumi   fatfs: ratelimit ...
31
  		va_end(args);
aaa04b487   OGAWA Hirofumi   fatfs: ratelimit ...
32
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33

85c785919   Denis Karpov   FAT: add 'errors'...
34
  	if (opts->errors == FAT_ERRORS_PANIC)
2c8a5ffb9   Alexey Fisher   fat: Convert fat_...
35
36
  		panic("FAT-fs (%s): fs panic from previous error
  ", sb->s_id);
bc98a42c1   David Howells   VFS: Convert sb->...
37
  	else if (opts->errors == FAT_ERRORS_RO && !sb_rdonly(sb)) {
1751e8a6c   Linus Torvalds   Rename superblock...
38
  		sb->s_flags |= SB_RDONLY;
e68e96d2a   Gu Zheng   fs/fat: use fat_m...
39
  		fat_msg(sb, KERN_ERR, "Filesystem has been set read-only");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
  	}
  }
aaa04b487   OGAWA Hirofumi   fatfs: ratelimit ...
42
  EXPORT_SYMBOL_GPL(__fat_fs_error);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

81ac21d34   Alexey Fisher   fat: Add fat_msg(...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  /**
   * fat_msg() - print preformated FAT specific messages. Every thing what is
   * not fat_fs_error() should be fat_msg().
   */
  void fat_msg(struct super_block *sb, const char *level, const char *fmt, ...)
  {
  	struct va_format vaf;
  	va_list args;
  
  	va_start(args, fmt);
  	vaf.fmt = fmt;
  	vaf.va = &args;
  	printk("%sFAT-fs (%s): %pV
  ", level, sb->s_id, &vaf);
  	va_end(args);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60
61
  /* Flushes the number of free clusters on FAT32 */
  /* XXX: Need to write one per FSINFO block.  Currently only writes 1 */
ed248b290   OGAWA Hirofumi   fat: Check s_dirt...
62
  int fat_clusters_flush(struct super_block *sb)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
66
  {
  	struct msdos_sb_info *sbi = MSDOS_SB(sb);
  	struct buffer_head *bh;
  	struct fat_boot_fsinfo *fsinfo;
306790f75   Carmeli Tamir   fat: new inline f...
67
  	if (!is_fat32(sbi))
ed248b290   OGAWA Hirofumi   fat: Check s_dirt...
68
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
  
  	bh = sb_bread(sb, sbi->fsinfo_sector);
  	if (bh == NULL) {
869f58c0c   Alexey Fisher   fat: Replace all ...
72
  		fat_msg(sb, KERN_ERR, "bread failed in fat_clusters_flush");
ed248b290   OGAWA Hirofumi   fat: Check s_dirt...
73
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
75
76
77
78
  	}
  
  	fsinfo = (struct fat_boot_fsinfo *)bh->b_data;
  	/* Sanity check */
  	if (!IS_FSINFO(fsinfo)) {
869f58c0c   Alexey Fisher   fat: Replace all ...
79
80
  		fat_msg(sb, KERN_ERR, "Invalid FSINFO signature: "
  		       "0x%08x, 0x%08x (sector = %lu)",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
81
82
83
84
85
86
87
88
89
  		       le32_to_cpu(fsinfo->signature1),
  		       le32_to_cpu(fsinfo->signature2),
  		       sbi->fsinfo_sector);
  	} else {
  		if (sbi->free_clusters != -1)
  			fsinfo->free_clusters = cpu_to_le32(sbi->free_clusters);
  		if (sbi->prev_free != -1)
  			fsinfo->next_cluster = cpu_to_le32(sbi->prev_free);
  		mark_buffer_dirty(bh);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
91
  	}
  	brelse(bh);
ed248b290   OGAWA Hirofumi   fat: Check s_dirt...
92
93
  
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  }
  
  /*
   * fat_chain_add() adds a new cluster to the chain of clusters represented
   * by inode.
   */
  int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
  {
  	struct super_block *sb = inode->i_sb;
  	struct msdos_sb_info *sbi = MSDOS_SB(sb);
  	int ret, new_fclus, last;
  
  	/*
  	 * We must locate the last cluster of the file to add this new
  	 * one (new_dclus) to the end of the link list (the FAT).
  	 */
  	last = new_fclus = 0;
  	if (MSDOS_I(inode)->i_start) {
  		int fclus, dclus;
  
  		ret = fat_get_cluster(inode, FAT_ENT_EOF, &fclus, &dclus);
  		if (ret < 0)
  			return ret;
  		new_fclus = fclus + 1;
  		last = dclus;
  	}
  
  	/* add new one to the last of the cluster chain */
  	if (last) {
  		struct fat_entry fatent;
  
  		fatent_init(&fatent);
  		ret = fat_ent_read(inode, &fatent, last);
  		if (ret >= 0) {
  			int wait = inode_needs_sync(inode);
  			ret = fat_ent_write(inode, &fatent, new_dclus, wait);
  			fatent_brelse(&fatent);
  		}
  		if (ret < 0)
  			return ret;
c39540c6d   Ravishankar N   fat: fix incorrec...
134
135
136
137
  		/*
  		 * FIXME:Although we can add this cache, fat_cache_add() is
  		 * assuming to be called after linear search with fat_cache_id.
  		 */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
140
141
142
  //		fat_cache_add(inode, new_fclus, new_dclus);
  	} else {
  		MSDOS_I(inode)->i_start = new_dclus;
  		MSDOS_I(inode)->i_logstart = new_dclus;
  		/*
2f3d675bc   Jan Kara   fat: Opencode syn...
143
144
  		 * Since generic_write_sync() synchronizes regular files later,
  		 * we sync here only directories.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
147
148
149
150
151
152
153
  		 */
  		if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
  			ret = fat_sync_inode(inode);
  			if (ret)
  				return ret;
  		} else
  			mark_inode_dirty(inode);
  	}
  	if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) {
85c785919   Denis Karpov   FAT: add 'errors'...
154
  		fat_fs_error(sb, "clusters badly computed (%d != %llu)",
c3302931d   OGAWA Hirofumi   fat: i_blocks war...
155
156
  			     new_fclus,
  			     (llu)(inode->i_blocks >> (sbi->cluster_bits - 9)));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
  		fat_cache_inval_inode(inode);
  	}
  	inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9);
  
  	return 0;
  }
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
163
164
165
166
167
168
169
170
171
172
173
174
175
  /*
   * The epoch of FAT timestamp is 1980.
   *     :  bits :     value
   * date:  0 -  4: day	(1 -  31)
   * date:  5 -  8: month	(1 -  12)
   * date:  9 - 15: year	(0 - 127) from 1980
   * time:  0 -  4: sec	(0 -  29) 2sec counts
   * time:  5 - 10: min	(0 -  59)
   * time: 11 - 15: hour	(0 -  23)
   */
  #define SECS_PER_MIN	60
  #define SECS_PER_HOUR	(60 * 60)
  #define SECS_PER_DAY	(SECS_PER_HOUR * 24)
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
176
177
178
179
180
  /* days between 1.1.70 and 1.1.80 (2 leap days) */
  #define DAYS_DELTA	(365 * 10 + 2)
  /* 120 (2100 - 1980) isn't leap year */
  #define YEAR_2100	120
  #define IS_LEAP_YEAR(y)	(!((y) & 3) && (y) != YEAR_2100)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  /* Linear day numbers of the respective 1sts in non-leap years. */
f423420c2   Arnd Bergmann   fat: propagate 64...
182
  static long days_in_year[] = {
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
183
184
  	/* Jan  Feb  Mar  Apr  May  Jun  Jul  Aug  Sep  Oct  Nov  Dec */
  	0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185
  };
d9f4d9426   Frank Sorenson   fat: create a fun...
186
187
188
189
190
191
  static inline int fat_tz_offset(struct msdos_sb_info *sbi)
  {
  	return (sbi->options.tz_set ?
  	       -sbi->options.time_offset :
  	       sys_tz.tz_minuteswest) * SECS_PER_MIN;
  }
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
192
  /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */
f423420c2   Arnd Bergmann   fat: propagate 64...
193
  void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec64 *ts,
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
194
  		       __le16 __time, __le16 __date, u8 time_cs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  {
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
196
  	u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date);
f423420c2   Arnd Bergmann   fat: propagate 64...
197
198
  	time64_t second;
  	long day, leap_day, month, year;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199

7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
200
201
202
203
204
205
206
207
208
209
210
211
212
  	year  = date >> 9;
  	month = max(1, (date >> 5) & 0xf);
  	day   = max(1, date & 0x1f) - 1;
  
  	leap_day = (year + 3) / 4;
  	if (year > YEAR_2100)		/* 2100 isn't leap year */
  		leap_day--;
  	if (IS_LEAP_YEAR(year) && month > 2)
  		leap_day++;
  
  	second =  (time & 0x1f) << 1;
  	second += ((time >> 5) & 0x3f) * SECS_PER_MIN;
  	second += (time >> 11) * SECS_PER_HOUR;
f423420c2   Arnd Bergmann   fat: propagate 64...
213
  	second += (time64_t)(year * 365 + leap_day
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
214
215
  		   + days_in_year[month] + day
  		   + DAYS_DELTA) * SECS_PER_DAY;
d9f4d9426   Frank Sorenson   fat: create a fun...
216
  	second += fat_tz_offset(sbi);
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
217
218
219
220
221
222
223
224
  
  	if (time_cs) {
  		ts->tv_sec = second + (time_cs / 100);
  		ts->tv_nsec = (time_cs % 100) * 10000000;
  	} else {
  		ts->tv_sec = second;
  		ts->tv_nsec = 0;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
225
  }
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
226
  /* Convert linear UNIX date to a FAT time/date pair. */
f423420c2   Arnd Bergmann   fat: propagate 64...
227
  void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec64 *ts,
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
228
  		       __le16 *time, __le16 *date, u8 *time_cs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  {
1d81a181e   Zhaolei   fatfs: use common...
230
  	struct tm tm;
d9f4d9426   Frank Sorenson   fat: create a fun...
231
  	time64_to_tm(ts->tv_sec, -fat_tz_offset(sbi), &tm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232

1d81a181e   Zhaolei   fatfs: use common...
233
234
  	/*  FAT can only support year between 1980 to 2107 */
  	if (tm.tm_year < 1980 - 1900) {
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
235
236
237
238
239
240
  		*time = 0;
  		*date = cpu_to_le16((0 << 9) | (1 << 5) | 1);
  		if (time_cs)
  			*time_cs = 0;
  		return;
  	}
1d81a181e   Zhaolei   fatfs: use common...
241
  	if (tm.tm_year > 2107 - 1900) {
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
242
243
244
245
246
247
  		*time = cpu_to_le16((23 << 11) | (59 << 5) | 29);
  		*date = cpu_to_le16((127 << 9) | (12 << 5) | 31);
  		if (time_cs)
  			*time_cs = 199;
  		return;
  	}
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
248

1d81a181e   Zhaolei   fatfs: use common...
249
250
251
252
253
254
  	/* from 1900 -> from 1980 */
  	tm.tm_year -= 80;
  	/* 0~11 -> 1~12 */
  	tm.tm_mon++;
  	/* 0~59 -> 0~29(2sec counts) */
  	tm.tm_sec >>= 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255

1d81a181e   Zhaolei   fatfs: use common...
256
257
  	*time = cpu_to_le16(tm.tm_hour << 11 | tm.tm_min << 5 | tm.tm_sec);
  	*date = cpu_to_le16(tm.tm_year << 9 | tm.tm_mon << 5 | tm.tm_mday);
7decd1cb0   OGAWA Hirofumi   fat: Fix and clea...
258
259
260
261
  	if (time_cs)
  		*time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000;
  }
  EXPORT_SYMBOL_GPL(fat_time_unix2fat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262

6bb885ecd   Frank Sorenson   fat: add function...
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  static inline struct timespec64 fat_timespec64_trunc_2secs(struct timespec64 ts)
  {
  	return (struct timespec64){ ts.tv_sec & ~1ULL, 0 };
  }
  /*
   * 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)
   */
  int fat_truncate_time(struct inode *inode, struct timespec64 *now, int flags)
  {
  	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
  	struct timespec64 ts;
  
  	if (inode->i_ino == MSDOS_ROOT_INO)
  		return 0;
  
  	if (now == NULL) {
  		now = &ts;
  		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 = timespec64_trunc(*now, 10000000);
  		else
  			inode->i_ctime = fat_timespec64_trunc_2secs(*now);
  	}
  	if (flags & S_MTIME)
  		inode->i_mtime = fat_timespec64_trunc_2secs(*now);
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(fat_truncate_time);
  
  int fat_update_time(struct inode *inode, struct timespec64 *now, int flags)
  {
  	int iflags = I_DIRTY_TIME;
  	bool dirty = false;
  
  	if (inode->i_ino == MSDOS_ROOT_INO)
  		return 0;
  
  	fat_truncate_time(inode, now, flags);
  	if (flags & S_VERSION)
  		dirty = inode_maybe_inc_iversion(inode, false);
  	if ((flags & (S_ATIME | S_CTIME | S_MTIME)) &&
  	    !(inode->i_sb->s_flags & SB_LAZYTIME))
  		dirty = true;
  
  	if (dirty)
  		iflags |= I_DIRTY_SYNC;
  	__mark_inode_dirty(inode, iflags);
  	return 0;
  }
  EXPORT_SYMBOL_GPL(fat_update_time);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
  int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs)
  {
5b00226d4   OGAWA Hirofumi   [PATCH] fat: Repl...
338
  	int i, err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339

9cb569d60   Christoph Hellwig   remove SWRITE* I/...
340
  	for (i = 0; i < nr_bhs; i++)
2a222ca99   Mike Christie   fs: have submit_b...
341
  		write_dirty_buffer(bhs[i], 0);
9cb569d60   Christoph Hellwig   remove SWRITE* I/...
342

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343
344
  	for (i = 0; i < nr_bhs; i++) {
  		wait_on_buffer(bhs[i]);
0edd55fae   Christoph Hellwig   block: remove the...
345
  		if (!err && !buffer_uptodate(bhs[i]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
  			err = -EIO;
  	}
  	return err;
  }