diff options
author | OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | 2008-11-06 15:53:47 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-11-06 18:41:20 -0500 |
commit | 7decd1cb0305b97243f283fa7f4baf5fe613edeb (patch) | |
tree | d17b291fd622c8ea45fd0ac8346f3c82c13759b6 | |
parent | 9e975dae2970d22557662761c8505ce9fd165684 (diff) |
fat: Fix and cleanup timestamp conversion
This cleans date_dos2unix()/fat_date_unix2dos() up. New code should be
much more readable.
And this fixes those old functions. Those doesn't handle 2100
correctly. 2100 isn't leap year, but old one handles it as leap year.
Also, with this, centi sec is handled and is fixed.
Signed-off-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/fat/dir.c | 6 | ||||
-rw-r--r-- | fs/fat/fat.h | 7 | ||||
-rw-r--r-- | fs/fat/inode.c | 34 | ||||
-rw-r--r-- | fs/fat/misc.c | 148 | ||||
-rw-r--r-- | fs/fat/namei_msdos.c | 2 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 5 |
6 files changed, 130 insertions, 72 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 08b23ad25f1c..a601c6d45bc0 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -1089,6 +1089,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1089 | struct msdos_dir_entry *de; | 1089 | struct msdos_dir_entry *de; |
1090 | sector_t blknr; | 1090 | sector_t blknr; |
1091 | __le16 date, time; | 1091 | __le16 date, time; |
1092 | u8 time_cs; | ||
1092 | int err, cluster; | 1093 | int err, cluster; |
1093 | 1094 | ||
1094 | err = fat_alloc_clusters(dir, &cluster, 1); | 1095 | err = fat_alloc_clusters(dir, &cluster, 1); |
@@ -1102,7 +1103,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1102 | goto error_free; | 1103 | goto error_free; |
1103 | } | 1104 | } |
1104 | 1105 | ||
1105 | fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); | 1106 | fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); |
1106 | 1107 | ||
1107 | de = (struct msdos_dir_entry *)bhs[0]->b_data; | 1108 | de = (struct msdos_dir_entry *)bhs[0]->b_data; |
1108 | /* filling the new directory slots ("." and ".." entries) */ | 1109 | /* filling the new directory slots ("." and ".." entries) */ |
@@ -1112,13 +1113,14 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1112 | de[0].lcase = de[1].lcase = 0; | 1113 | de[0].lcase = de[1].lcase = 0; |
1113 | de[0].time = de[1].time = time; | 1114 | de[0].time = de[1].time = time; |
1114 | de[0].date = de[1].date = date; | 1115 | de[0].date = de[1].date = date; |
1115 | de[0].ctime_cs = de[1].ctime_cs = 0; | ||
1116 | if (sbi->options.isvfat) { | 1116 | if (sbi->options.isvfat) { |
1117 | /* extra timestamps */ | 1117 | /* extra timestamps */ |
1118 | de[0].ctime = de[1].ctime = time; | 1118 | de[0].ctime = de[1].ctime = time; |
1119 | de[0].ctime_cs = de[1].ctime_cs = time_cs; | ||
1119 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; | 1120 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; |
1120 | } else { | 1121 | } else { |
1121 | de[0].ctime = de[1].ctime = 0; | 1122 | de[0].ctime = de[1].ctime = 0; |
1123 | de[0].ctime_cs = de[1].ctime_cs = 0; | ||
1122 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; | 1124 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; |
1123 | } | 1125 | } |
1124 | de[0].start = cpu_to_le16(cluster); | 1126 | de[0].start = cpu_to_le16(cluster); |
diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 51f1c42ca5e3..a2a570f81719 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h | |||
@@ -263,9 +263,10 @@ extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, | |||
263 | extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); | 263 | extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); |
264 | extern void fat_clusters_flush(struct super_block *sb); | 264 | extern void fat_clusters_flush(struct super_block *sb); |
265 | extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); | 265 | extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); |
266 | extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc); | 266 | extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, |
267 | extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, | 267 | __le16 __time, __le16 __date, u8 time_cs); |
268 | int tz_utc); | 268 | extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, |
269 | __le16 *time, __le16 *date, u8 *time_cs); | ||
269 | extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); | 270 | extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); |
270 | 271 | ||
271 | int fat_cache_init(void); | 272 | int fat_cache_init(void); |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 3921de2013a4..079d9d5e0d36 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c | |||
@@ -381,22 +381,12 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de) | |||
381 | MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; | 381 | MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; |
382 | inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) | 382 | inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) |
383 | & ~((loff_t)sbi->cluster_size - 1)) >> 9; | 383 | & ~((loff_t)sbi->cluster_size - 1)) >> 9; |
384 | inode->i_mtime.tv_sec = | 384 | |
385 | date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), | 385 | fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0); |
386 | sbi->options.tz_utc); | ||
387 | inode->i_mtime.tv_nsec = 0; | ||
388 | if (sbi->options.isvfat) { | 386 | if (sbi->options.isvfat) { |
389 | int secs = de->ctime_cs / 100; | 387 | fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime, |
390 | int csecs = de->ctime_cs % 100; | 388 | de->cdate, de->ctime_cs); |
391 | inode->i_ctime.tv_sec = | 389 | fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0); |
392 | date_dos2unix(le16_to_cpu(de->ctime), | ||
393 | le16_to_cpu(de->cdate), | ||
394 | sbi->options.tz_utc) + secs; | ||
395 | inode->i_ctime.tv_nsec = csecs * 10000000; | ||
396 | inode->i_atime.tv_sec = | ||
397 | date_dos2unix(0, le16_to_cpu(de->adate), | ||
398 | sbi->options.tz_utc); | ||
399 | inode->i_atime.tv_nsec = 0; | ||
400 | } else | 390 | } else |
401 | inode->i_ctime = inode->i_atime = inode->i_mtime; | 391 | inode->i_ctime = inode->i_atime = inode->i_mtime; |
402 | 392 | ||
@@ -591,16 +581,14 @@ retry: | |||
591 | raw_entry->attr = fat_attr(inode); | 581 | raw_entry->attr = fat_attr(inode); |
592 | raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); | 582 | raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); |
593 | raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); | 583 | raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); |
594 | fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, | 584 | fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time, |
595 | &raw_entry->date, sbi->options.tz_utc); | 585 | &raw_entry->date, NULL); |
596 | if (sbi->options.isvfat) { | 586 | if (sbi->options.isvfat) { |
597 | __le16 atime; | 587 | __le16 atime; |
598 | fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, | 588 | fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime, |
599 | &raw_entry->cdate, sbi->options.tz_utc); | 589 | &raw_entry->cdate, &raw_entry->ctime_cs); |
600 | fat_date_unix2dos(inode->i_atime.tv_sec, &atime, | 590 | fat_time_unix2fat(sbi, &inode->i_atime, &atime, |
601 | &raw_entry->adate, sbi->options.tz_utc); | 591 | &raw_entry->adate, NULL); |
602 | raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 + | ||
603 | inode->i_ctime.tv_nsec / 10000000; | ||
604 | } | 592 | } |
605 | spin_unlock(&sbi->inode_hash_lock); | 593 | spin_unlock(&sbi->inode_hash_lock); |
606 | mark_buffer_dirty(bh); | 594 | mark_buffer_dirty(bh); |
diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 91ad9be18ff9..a191e79e66a9 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c | |||
@@ -135,65 +135,131 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) | |||
135 | 135 | ||
136 | extern struct timezone sys_tz; | 136 | extern struct timezone sys_tz; |
137 | 137 | ||
138 | /* | ||
139 | * The epoch of FAT timestamp is 1980. | ||
140 | * : bits : value | ||
141 | * date: 0 - 4: day (1 - 31) | ||
142 | * date: 5 - 8: month (1 - 12) | ||
143 | * date: 9 - 15: year (0 - 127) from 1980 | ||
144 | * time: 0 - 4: sec (0 - 29) 2sec counts | ||
145 | * time: 5 - 10: min (0 - 59) | ||
146 | * time: 11 - 15: hour (0 - 23) | ||
147 | */ | ||
148 | #define SECS_PER_MIN 60 | ||
149 | #define SECS_PER_HOUR (60 * 60) | ||
150 | #define SECS_PER_DAY (SECS_PER_HOUR * 24) | ||
151 | #define UNIX_SECS_1980 315532800L | ||
152 | #if BITS_PER_LONG == 64 | ||
153 | #define UNIX_SECS_2108 4354819200L | ||
154 | #endif | ||
155 | /* days between 1.1.70 and 1.1.80 (2 leap days) */ | ||
156 | #define DAYS_DELTA (365 * 10 + 2) | ||
157 | /* 120 (2100 - 1980) isn't leap year */ | ||
158 | #define YEAR_2100 120 | ||
159 | #define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100) | ||
160 | |||
138 | /* Linear day numbers of the respective 1sts in non-leap years. */ | 161 | /* Linear day numbers of the respective 1sts in non-leap years. */ |
139 | static int day_n[] = { | 162 | static time_t days_in_year[] = { |
140 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ | 163 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ |
141 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 | 164 | 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, |
142 | }; | 165 | }; |
143 | 166 | ||
144 | /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ | 167 | /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ |
145 | int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) | 168 | void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, |
169 | __le16 __time, __le16 __date, u8 time_cs) | ||
146 | { | 170 | { |
147 | int month, year, secs; | 171 | u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date); |
172 | time_t second, day, leap_day, month, year; | ||
148 | 173 | ||
149 | /* | 174 | year = date >> 9; |
150 | * first subtract and mask after that... Otherwise, if | 175 | month = max(1, (date >> 5) & 0xf); |
151 | * date == 0, bad things happen | 176 | day = max(1, date & 0x1f) - 1; |
152 | */ | 177 | |
153 | month = ((date >> 5) - 1) & 15; | 178 | leap_day = (year + 3) / 4; |
154 | year = date >> 9; | 179 | if (year > YEAR_2100) /* 2100 isn't leap year */ |
155 | secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* | 180 | leap_day--; |
156 | ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && | 181 | if (IS_LEAP_YEAR(year) && month > 2) |
157 | month < 2 ? 1 : 0)+3653); | 182 | leap_day++; |
158 | /* days since 1.1.70 plus 80's leap day */ | 183 | |
159 | if (!tz_utc) | 184 | second = (time & 0x1f) << 1; |
160 | secs += sys_tz.tz_minuteswest*60; | 185 | second += ((time >> 5) & 0x3f) * SECS_PER_MIN; |
161 | return secs; | 186 | second += (time >> 11) * SECS_PER_HOUR; |
187 | second += (year * 365 + leap_day | ||
188 | + days_in_year[month] + day | ||
189 | + DAYS_DELTA) * SECS_PER_DAY; | ||
190 | |||
191 | if (!sbi->options.tz_utc) | ||
192 | second += sys_tz.tz_minuteswest * SECS_PER_MIN; | ||
193 | |||
194 | if (time_cs) { | ||
195 | ts->tv_sec = second + (time_cs / 100); | ||
196 | ts->tv_nsec = (time_cs % 100) * 10000000; | ||
197 | } else { | ||
198 | ts->tv_sec = second; | ||
199 | ts->tv_nsec = 0; | ||
200 | } | ||
162 | } | 201 | } |
163 | 202 | ||
164 | /* Convert linear UNIX date to a MS-DOS time/date pair. */ | 203 | /* Convert linear UNIX date to a FAT time/date pair. */ |
165 | void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) | 204 | void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, |
205 | __le16 *time, __le16 *date, u8 *time_cs) | ||
166 | { | 206 | { |
167 | int day, year, nl_day, month; | 207 | time_t second = ts->tv_sec; |
208 | time_t day, leap_day, month, year; | ||
168 | 209 | ||
169 | if (!tz_utc) | 210 | if (!sbi->options.tz_utc) |
170 | unix_date -= sys_tz.tz_minuteswest*60; | 211 | second -= sys_tz.tz_minuteswest * SECS_PER_MIN; |
171 | 212 | ||
172 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ | 213 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ |
173 | if (unix_date < 315532800) | 214 | if (second < UNIX_SECS_1980) { |
174 | unix_date = 315532800; | 215 | *time = 0; |
175 | 216 | *date = cpu_to_le16((0 << 9) | (1 << 5) | 1); | |
176 | *time = cpu_to_le16((unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ | 217 | if (time_cs) |
177 | (((unix_date/3600) % 24) << 11)); | 218 | *time_cs = 0; |
178 | day = unix_date/86400-3652; | 219 | return; |
179 | year = day/365; | 220 | } |
180 | if ((year+3)/4+365*year > day) | 221 | #if BITS_PER_LONG == 64 |
222 | if (second >= UNIX_SECS_2108) { | ||
223 | *time = cpu_to_le16((23 << 11) | (59 << 5) | 29); | ||
224 | *date = cpu_to_le16((127 << 9) | (12 << 5) | 31); | ||
225 | if (time_cs) | ||
226 | *time_cs = 199; | ||
227 | return; | ||
228 | } | ||
229 | #endif | ||
230 | |||
231 | day = second / SECS_PER_DAY - DAYS_DELTA; | ||
232 | year = day / 365; | ||
233 | leap_day = (year + 3) / 4; | ||
234 | if (year > YEAR_2100) /* 2100 isn't leap year */ | ||
235 | leap_day--; | ||
236 | if (year * 365 + leap_day > day) | ||
181 | year--; | 237 | year--; |
182 | day -= (year+3)/4+365*year; | 238 | leap_day = (year + 3) / 4; |
183 | if (day == 59 && !(year & 3)) { | 239 | if (year > YEAR_2100) /* 2100 isn't leap year */ |
184 | nl_day = day; | 240 | leap_day--; |
241 | day -= year * 365 + leap_day; | ||
242 | |||
243 | if (IS_LEAP_YEAR(year) && day == days_in_year[3]) { | ||
185 | month = 2; | 244 | month = 2; |
186 | } else { | 245 | } else { |
187 | nl_day = (year & 3) || day <= 59 ? day : day-1; | 246 | if (IS_LEAP_YEAR(year) && day > days_in_year[3]) |
188 | for (month = 0; month < 12; month++) { | 247 | day--; |
189 | if (day_n[month] > nl_day) | 248 | for (month = 1; month < 12; month++) { |
249 | if (days_in_year[month + 1] > day) | ||
190 | break; | 250 | break; |
191 | } | 251 | } |
192 | } | 252 | } |
193 | *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9)); | 253 | day -= days_in_year[month]; |
194 | } | ||
195 | 254 | ||
196 | EXPORT_SYMBOL_GPL(fat_date_unix2dos); | 255 | *time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11 |
256 | | ((second / SECS_PER_MIN) % 60) << 5 | ||
257 | | (second % SECS_PER_MIN) >> 1); | ||
258 | *date = cpu_to_le16((year << 9) | (month << 5) | (day + 1)); | ||
259 | if (time_cs) | ||
260 | *time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000; | ||
261 | } | ||
262 | EXPORT_SYMBOL_GPL(fat_time_unix2fat); | ||
197 | 263 | ||
198 | int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) | 264 | int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) |
199 | { | 265 | { |
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index c0a4d5cd99b2..e92e8158ebaf 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c | |||
@@ -247,7 +247,7 @@ static int msdos_add_entry(struct inode *dir, const unsigned char *name, | |||
247 | if (is_hid) | 247 | if (is_hid) |
248 | de.attr |= ATTR_HIDDEN; | 248 | de.attr |= ATTR_HIDDEN; |
249 | de.lcase = 0; | 249 | de.lcase = 0; |
250 | fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); | 250 | fat_time_unix2fat(sbi, ts, &time, &date, NULL); |
251 | de.cdate = de.adate = 0; | 251 | de.cdate = de.adate = 0; |
252 | de.ctime = 0; | 252 | de.ctime = 0; |
253 | de.ctime_cs = 0; | 253 | de.ctime_cs = 0; |
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index facf3bf0211a..1536bc3ca0f0 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c | |||
@@ -568,6 +568,7 @@ static int vfat_build_slots(struct inode *dir, const unsigned char *name, | |||
568 | unsigned char msdos_name[MSDOS_NAME]; | 568 | unsigned char msdos_name[MSDOS_NAME]; |
569 | wchar_t *uname; | 569 | wchar_t *uname; |
570 | __le16 time, date; | 570 | __le16 time, date; |
571 | u8 time_cs; | ||
571 | int err, ulen, usize, i; | 572 | int err, ulen, usize, i; |
572 | loff_t offset; | 573 | loff_t offset; |
573 | 574 | ||
@@ -620,10 +621,10 @@ shortname: | |||
620 | memcpy(de->name, msdos_name, MSDOS_NAME); | 621 | memcpy(de->name, msdos_name, MSDOS_NAME); |
621 | de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; | 622 | de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; |
622 | de->lcase = lcase; | 623 | de->lcase = lcase; |
623 | fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); | 624 | fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); |
624 | de->time = de->ctime = time; | 625 | de->time = de->ctime = time; |
625 | de->date = de->cdate = de->adate = date; | 626 | de->date = de->cdate = de->adate = date; |
626 | de->ctime_cs = 0; | 627 | de->ctime_cs = time_cs; |
627 | de->start = cpu_to_le16(cluster); | 628 | de->start = cpu_to_le16(cluster); |
628 | de->starthi = cpu_to_le16(cluster >> 16); | 629 | de->starthi = cpu_to_le16(cluster >> 16); |
629 | de->size = 0; | 630 | de->size = 0; |