aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/misc.c
diff options
context:
space:
mode:
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{