aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/misc.c
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/fat/misc.c
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/fat/misc.c')
-rw-r--r--fs/fat/misc.c148
1 files changed, 107 insertions, 41 deletions
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{