aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>2008-11-06 15:53:47 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-11-06 18:41:20 -0500
commit7decd1cb0305b97243f283fa7f4baf5fe613edeb (patch)
treed17b291fd622c8ea45fd0ac8346f3c82c13759b6 /fs
parent9e975dae2970d22557662761c8505ce9fd165684 (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>
Diffstat (limited to 'fs')
-rw-r--r--fs/fat/dir.c6
-rw-r--r--fs/fat/fat.h7
-rw-r--r--fs/fat/inode.c34
-rw-r--r--fs/fat/misc.c148
-rw-r--r--fs/fat/namei_msdos.c2
-rw-r--r--fs/fat/namei_vfat.c5
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,
263extern void fat_fs_panic(struct super_block *s, const char *fmt, ...); 263extern void fat_fs_panic(struct super_block *s, const char *fmt, ...);
264extern void fat_clusters_flush(struct super_block *sb); 264extern void fat_clusters_flush(struct super_block *sb);
265extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); 265extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster);
266extern int date_dos2unix(unsigned short time, unsigned short date, int tz_utc); 266extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts,
267extern void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, 267 __le16 __time, __le16 __date, u8 time_cs);
268 int tz_utc); 268extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts,
269 __le16 *time, __le16 *date, u8 *time_cs);
269extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); 270extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs);
270 271
271int fat_cache_init(void); 272int 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
136extern struct timezone sys_tz; 136extern 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. */
139static int day_n[] = { 162static 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). */
145int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) 168void 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. */
165void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) 204void 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
196EXPORT_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}
262EXPORT_SYMBOL_GPL(fat_time_unix2fat);
197 263
198int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) 264int 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;