1 | /* |
---|
2 | * Adaptation of NetBSD code for RTEMS by Victor V. Vengerov <vvv@oktet.ru> |
---|
3 | */ |
---|
4 | /* $NetBSD: msdosfs_conv.c,v 1.10 1994/12/27 18:36:24 mycroft Exp $ */ |
---|
5 | /* |
---|
6 | * Written by Paul Popelka (paulp@uts.amdahl.com) |
---|
7 | * |
---|
8 | * You can do anything you want with this software, just don't say you wrote |
---|
9 | * it, and don't remove this notice. |
---|
10 | * |
---|
11 | * This software is provided "as is". |
---|
12 | * |
---|
13 | * The author supplies this software to be publicly redistributed on the |
---|
14 | * understanding that the author is not responsible for the correct |
---|
15 | * functioning of this software in any circumstances and is not liable for |
---|
16 | * any damages caused by this software. |
---|
17 | * |
---|
18 | * October 1992 |
---|
19 | */ |
---|
20 | |
---|
21 | #if HAVE_CONFIG_H |
---|
22 | #include "config.h" |
---|
23 | #endif |
---|
24 | |
---|
25 | #include <rtems.h> |
---|
26 | #include "msdos.h" |
---|
27 | |
---|
28 | /* #define SECONDSPERDAY (24 * 60 * 60) */ |
---|
29 | #define SECONDSPERDAY ((uint32_t) 86400) |
---|
30 | |
---|
31 | /* |
---|
32 | * Days in each month in a regular year. |
---|
33 | */ |
---|
34 | static uint16_t regyear[] = { |
---|
35 | 31, 28, 31, 30, 31, 30, |
---|
36 | 31, 31, 30, 31, 30, 31 |
---|
37 | }; |
---|
38 | |
---|
39 | /* |
---|
40 | * Days in each month in a leap year. |
---|
41 | */ |
---|
42 | static uint16_t leapyear[] = { |
---|
43 | 31, 29, 31, 30, 31, 30, |
---|
44 | 31, 31, 30, 31, 30, 31 |
---|
45 | }; |
---|
46 | |
---|
47 | /* |
---|
48 | * Variables used to remember parts of the last time conversion. Maybe we |
---|
49 | * can avoid a full conversion. |
---|
50 | */ |
---|
51 | static uint32_t lasttime; |
---|
52 | static uint32_t lastday; |
---|
53 | static uint16_t lastddate; |
---|
54 | static uint16_t lastdtime; |
---|
55 | |
---|
56 | /* |
---|
57 | * Convert the unix version of time to dos's idea of time to be used in |
---|
58 | * file timestamps. The passed in unix time is assumed to be in GMT. |
---|
59 | */ |
---|
60 | void |
---|
61 | msdos_date_unix2dos(unsigned int t, unsigned short *ddp, |
---|
62 | unsigned short *dtp) |
---|
63 | { |
---|
64 | uint32_t days; |
---|
65 | uint32_t inc; |
---|
66 | uint32_t year; |
---|
67 | uint32_t month; |
---|
68 | uint16_t *months; |
---|
69 | |
---|
70 | /* |
---|
71 | * If the time from the last conversion is the same as now, then |
---|
72 | * skip the computations and use the saved result. |
---|
73 | */ |
---|
74 | if (lasttime != t) { |
---|
75 | lasttime = t; |
---|
76 | lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT) |
---|
77 | + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT) |
---|
78 | + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT); |
---|
79 | |
---|
80 | /* |
---|
81 | * If the number of days since 1970 is the same as the last |
---|
82 | * time we did the computation then skip all this leap year |
---|
83 | * and month stuff. |
---|
84 | */ |
---|
85 | days = t / (SECONDSPERDAY); |
---|
86 | if (days != lastday) { |
---|
87 | lastday = days; |
---|
88 | for (year = 1970;; year++) { |
---|
89 | inc = year & 0x03 ? 365 : 366; |
---|
90 | if (days < inc) |
---|
91 | break; |
---|
92 | days -= inc; |
---|
93 | } |
---|
94 | months = year & 0x03 ? regyear : leapyear; |
---|
95 | for (month = 0; month < 12; month++) { |
---|
96 | if (days < months[month]) |
---|
97 | break; |
---|
98 | days -= months[month]; |
---|
99 | } |
---|
100 | lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT) |
---|
101 | + ((month + 1) << MSDOS_DD_MONTH_SHIFT); |
---|
102 | /* |
---|
103 | * Remember dos's idea of time is relative to 1980. |
---|
104 | * unix's is relative to 1970. If somehow we get a |
---|
105 | * time before 1980 then don't give totally crazy |
---|
106 | * results. |
---|
107 | */ |
---|
108 | if (year > 1980) |
---|
109 | lastddate += (year - 1980) << |
---|
110 | MSDOS_DD_YEAR_SHIFT; |
---|
111 | } |
---|
112 | } |
---|
113 | *dtp = lastdtime; |
---|
114 | *ddp = lastddate; |
---|
115 | } |
---|
116 | |
---|
117 | /* |
---|
118 | * The number of days between Jan 1, 1970 and Jan 1, 1980. In that |
---|
119 | * interval there were 8 regular years and 2 leap years. |
---|
120 | */ |
---|
121 | /* #define DAYSTO1980 ((8 * 365) + (2 * 366)) */ |
---|
122 | #define DAYSTO1980 ((uint32_t) 3652) |
---|
123 | |
---|
124 | static uint16_t lastdosdate; |
---|
125 | static uint32_t lastseconds; |
---|
126 | |
---|
127 | /* |
---|
128 | * Convert from dos' idea of time to unix'. This will probably only be |
---|
129 | * called from the stat(), and fstat() system calls and so probably need |
---|
130 | * not be too efficient. |
---|
131 | */ |
---|
132 | unsigned int |
---|
133 | msdos_date_dos2unix(unsigned int dd, unsigned int dt) |
---|
134 | { |
---|
135 | uint32_t seconds; |
---|
136 | uint32_t m, month; |
---|
137 | uint32_t y, year; |
---|
138 | uint32_t days; |
---|
139 | uint16_t *months; |
---|
140 | |
---|
141 | seconds = ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT) |
---|
142 | + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60 |
---|
143 | + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600; |
---|
144 | /* |
---|
145 | * If the year, month, and day from the last conversion are the |
---|
146 | * same then use the saved value. |
---|
147 | */ |
---|
148 | if (lastdosdate != dd) { |
---|
149 | lastdosdate = dd; |
---|
150 | days = 0; |
---|
151 | year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT; |
---|
152 | for (y = 0; y < year; y++) |
---|
153 | days += y & 0x03 ? 365 : 366; |
---|
154 | months = year & 0x03 ? regyear : leapyear; |
---|
155 | /* |
---|
156 | * Prevent going from 0 to 0xffffffff in the following |
---|
157 | * loop. |
---|
158 | */ |
---|
159 | month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT; |
---|
160 | if (month == 0) { |
---|
161 | month = 1; |
---|
162 | } |
---|
163 | for (m = 0; m < month - 1; m++) |
---|
164 | days += months[m]; |
---|
165 | days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1; |
---|
166 | lastseconds = (days + DAYSTO1980) * SECONDSPERDAY; |
---|
167 | } |
---|
168 | return seconds + lastseconds; |
---|
169 | } |
---|
170 | |
---|
171 | static const uint8_t msdos_map[] = { |
---|
172 | 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ |
---|
173 | 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ |
---|
174 | 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ |
---|
175 | 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ |
---|
176 | 0, '!', 0, '#', '$', '%', '&', '\'', /* 20-27 */ |
---|
177 | '(', ')', 0, '+', 0, '-', 0, 0, /* 28-2f */ |
---|
178 | '0', '1', '2', '3', '4', '5', '6', '7', /* 30-37 */ |
---|
179 | '8', '9', 0, 0, 0, 0, 0, 0, /* 38-3f */ |
---|
180 | '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40-47 */ |
---|
181 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 48-4f */ |
---|
182 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 50-57 */ |
---|
183 | 'X', 'Y', 'Z', 0, 0, 0, '^', '_', /* 58-5f */ |
---|
184 | '`', 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 60-67 */ |
---|
185 | 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', /* 68-6f */ |
---|
186 | 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 70-77 */ |
---|
187 | 'X', 'Y', 'Z', '{', 0, '}', '~', 0, /* 78-7f */ |
---|
188 | 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ |
---|
189 | 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ |
---|
190 | 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ |
---|
191 | 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ |
---|
192 | 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ |
---|
193 | 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ |
---|
194 | 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ |
---|
195 | 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ |
---|
196 | 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ |
---|
197 | 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ |
---|
198 | 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ |
---|
199 | 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ |
---|
200 | 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ |
---|
201 | 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ |
---|
202 | 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ |
---|
203 | 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ |
---|
204 | #if OLD_TABLE |
---|
205 | /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
206 | /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
207 | /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
208 | /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
209 | /* 20 */ 0x00, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /* !"#$%&' */ |
---|
210 | /* 28 */ 0x28, 0x29, 0x00, 0x00, 0x00, 0x2D, 0x2E, 0x00, /* ()*+,-./ */ |
---|
211 | /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 01234567 */ |
---|
212 | /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */ |
---|
213 | /* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ |
---|
214 | /* 48 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* HIJKLMNO */ |
---|
215 | /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* PQRSTUVW */ |
---|
216 | /* 58 */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x00, 0x5E, 0x5F, /* XYZ[\]^_ */ |
---|
217 | /* 60 */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ |
---|
218 | /* 68 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* hijklmno */ |
---|
219 | /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* pqrstuvw */ |
---|
220 | /* 78 */ 0x58, 0x59, 0x5A, 0x5B, 0x7C, 0x00, 0x7E, 0x00, /* xyz{|}~ */ |
---|
221 | /* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
222 | /* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
223 | /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
224 | /* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
225 | /* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
226 | /* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
227 | /* B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
228 | /* B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
229 | /* C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
230 | /* C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
231 | /* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
232 | /* D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
233 | /* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
234 | /* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
235 | /* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
236 | /* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
237 | #endif |
---|
238 | }; |
---|
239 | /* |
---|
240 | * Convert a unix filename to a DOS filename. Return -1 if wrong name is |
---|
241 | * supplied. |
---|
242 | */ |
---|
243 | int |
---|
244 | msdos_filename_unix2dos(const char *un, int unlen, char *dn) |
---|
245 | { |
---|
246 | int i; |
---|
247 | uint8_t c; |
---|
248 | |
---|
249 | /* |
---|
250 | * Fill the dos filename string with blanks. These are DOS's pad |
---|
251 | * characters. |
---|
252 | */ |
---|
253 | for (i = 0; i <= 10; i++) |
---|
254 | dn[i] = ' '; |
---|
255 | |
---|
256 | /* |
---|
257 | * The filenames "." and ".." are handled specially, since they |
---|
258 | * don't follow dos filename rules. |
---|
259 | */ |
---|
260 | if (un[0] == '.' && unlen == 1) { |
---|
261 | dn[0] = '.'; |
---|
262 | return 0; |
---|
263 | } |
---|
264 | if (un[0] == '.' && un[1] == '.' && unlen == 2) { |
---|
265 | dn[0] = '.'; |
---|
266 | dn[1] = '.'; |
---|
267 | return 0; |
---|
268 | } |
---|
269 | |
---|
270 | /* |
---|
271 | * Remove any dots from the start of a file name. |
---|
272 | */ |
---|
273 | while (unlen && (*un == '.')) { |
---|
274 | un++; |
---|
275 | unlen--; |
---|
276 | } |
---|
277 | |
---|
278 | /* |
---|
279 | * Copy the unix filename into the dos filename string upto the end |
---|
280 | * of string, a '.', or 8 characters. Whichever happens first stops |
---|
281 | * us. This forms the name portion of the dos filename. Fold to |
---|
282 | * upper case. |
---|
283 | */ |
---|
284 | for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { |
---|
285 | if (msdos_map[c] == 0) |
---|
286 | break; |
---|
287 | dn[i] = msdos_map[c]; |
---|
288 | un++; |
---|
289 | unlen--; |
---|
290 | } |
---|
291 | |
---|
292 | /* |
---|
293 | * Strip any further characters up to a '.' or the end of the |
---|
294 | * string. |
---|
295 | */ |
---|
296 | while (unlen && (c = *un)) { |
---|
297 | un++; |
---|
298 | unlen--; |
---|
299 | /* Make sure we've skipped over the dot before stopping. */ |
---|
300 | if (c == '.') |
---|
301 | break; |
---|
302 | } |
---|
303 | |
---|
304 | /* |
---|
305 | * Copy in the extension part of the name, if any. Force to upper |
---|
306 | * case. Note that the extension is allowed to contain '.'s. |
---|
307 | * Filenames in this form are probably inaccessable under dos. |
---|
308 | */ |
---|
309 | for (i = 8; i <= 10 && unlen && (c = *un); i++) { |
---|
310 | if (msdos_map[c] == 0) |
---|
311 | break; |
---|
312 | dn[i] = msdos_map[c]; |
---|
313 | un++; |
---|
314 | unlen--; |
---|
315 | } |
---|
316 | return 0; |
---|
317 | } |
---|