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 | * Days in each month in a regular year. |
---|
29 | */ |
---|
30 | static u_short regyear[] = { |
---|
31 | 31, 28, 31, 30, 31, 30, |
---|
32 | 31, 31, 30, 31, 30, 31 |
---|
33 | }; |
---|
34 | |
---|
35 | /* |
---|
36 | * Days in each month in a leap year. |
---|
37 | */ |
---|
38 | static u_short leapyear[] = { |
---|
39 | 31, 29, 31, 30, 31, 30, |
---|
40 | 31, 31, 30, 31, 30, 31 |
---|
41 | }; |
---|
42 | |
---|
43 | /* |
---|
44 | * Variables used to remember parts of the last time conversion. Maybe we |
---|
45 | * can avoid a full conversion. |
---|
46 | */ |
---|
47 | static u_long lasttime; |
---|
48 | static u_long lastday; |
---|
49 | static u_short lastddate; |
---|
50 | static u_short lastdtime; |
---|
51 | |
---|
52 | /* |
---|
53 | * Convert the unix version of time to dos's idea of time to be used in |
---|
54 | * file timestamps. The passed in unix time is assumed to be in GMT. |
---|
55 | */ |
---|
56 | void |
---|
57 | msdos_date_unix2dos(unsigned int t, unsigned short *ddp, |
---|
58 | unsigned short *dtp) |
---|
59 | { |
---|
60 | u_long days; |
---|
61 | u_long inc; |
---|
62 | u_long year; |
---|
63 | u_long month; |
---|
64 | u_short *months; |
---|
65 | |
---|
66 | /* |
---|
67 | * If the time from the last conversion is the same as now, then |
---|
68 | * skip the computations and use the saved result. |
---|
69 | */ |
---|
70 | if (lasttime != t) { |
---|
71 | lasttime = t; |
---|
72 | lastdtime = (((t % 60) >> 1) << MSDOS_DT_2SECONDS_SHIFT) |
---|
73 | + (((t / 60) % 60) << MSDOS_DT_MINUTES_SHIFT) |
---|
74 | + (((t / 3600) % 24) << MSDOS_DT_HOURS_SHIFT); |
---|
75 | |
---|
76 | /* |
---|
77 | * If the number of days since 1970 is the same as the last |
---|
78 | * time we did the computation then skip all this leap year |
---|
79 | * and month stuff. |
---|
80 | */ |
---|
81 | days = t / (24 * 60 * 60); |
---|
82 | if (days != lastday) { |
---|
83 | lastday = days; |
---|
84 | for (year = 1970;; year++) { |
---|
85 | inc = year & 0x03 ? 365 : 366; |
---|
86 | if (days < inc) |
---|
87 | break; |
---|
88 | days -= inc; |
---|
89 | } |
---|
90 | months = year & 0x03 ? regyear : leapyear; |
---|
91 | for (month = 0; month < 12; month++) { |
---|
92 | if (days < months[month]) |
---|
93 | break; |
---|
94 | days -= months[month]; |
---|
95 | } |
---|
96 | lastddate = ((days + 1) << MSDOS_DD_DAY_SHIFT) |
---|
97 | + ((month + 1) << MSDOS_DD_MONTH_SHIFT); |
---|
98 | /* |
---|
99 | * Remember dos's idea of time is relative to 1980. |
---|
100 | * unix's is relative to 1970. If somehow we get a |
---|
101 | * time before 1980 then don't give totally crazy |
---|
102 | * results. |
---|
103 | */ |
---|
104 | if (year > 1980) |
---|
105 | lastddate += (year - 1980) << |
---|
106 | MSDOS_DD_YEAR_SHIFT; |
---|
107 | } |
---|
108 | } |
---|
109 | *dtp = lastdtime; |
---|
110 | *ddp = lastddate; |
---|
111 | } |
---|
112 | |
---|
113 | /* |
---|
114 | * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that |
---|
115 | * interval there were 8 regular years and 2 leap years. |
---|
116 | */ |
---|
117 | #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) |
---|
118 | |
---|
119 | static u_short lastdosdate; |
---|
120 | static u_long lastseconds; |
---|
121 | |
---|
122 | /* |
---|
123 | * Convert from dos' idea of time to unix'. This will probably only be |
---|
124 | * called from the stat(), and fstat() system calls and so probably need |
---|
125 | * not be too efficient. |
---|
126 | */ |
---|
127 | unsigned int |
---|
128 | msdos_date_dos2unix(unsigned int dd, unsigned int dt) |
---|
129 | { |
---|
130 | u_long seconds; |
---|
131 | u_long m, month; |
---|
132 | u_long y, year; |
---|
133 | u_long days; |
---|
134 | u_short *months; |
---|
135 | |
---|
136 | seconds = ((dt & MSDOS_DT_2SECONDS_MASK) >> MSDOS_DT_2SECONDS_SHIFT) |
---|
137 | + ((dt & MSDOS_DT_MINUTES_MASK) >> MSDOS_DT_MINUTES_SHIFT) * 60 |
---|
138 | + ((dt & MSDOS_DT_HOURS_MASK) >> MSDOS_DT_HOURS_SHIFT) * 3600; |
---|
139 | /* |
---|
140 | * If the year, month, and day from the last conversion are the |
---|
141 | * same then use the saved value. |
---|
142 | */ |
---|
143 | if (lastdosdate != dd) { |
---|
144 | lastdosdate = dd; |
---|
145 | days = 0; |
---|
146 | year = (dd & MSDOS_DD_YEAR_MASK) >> MSDOS_DD_YEAR_SHIFT; |
---|
147 | for (y = 0; y < year; y++) |
---|
148 | days += y & 0x03 ? 365 : 366; |
---|
149 | months = year & 0x03 ? regyear : leapyear; |
---|
150 | /* |
---|
151 | * Prevent going from 0 to 0xffffffff in the following |
---|
152 | * loop. |
---|
153 | */ |
---|
154 | month = (dd & MSDOS_DD_MONTH_MASK) >> MSDOS_DD_MONTH_SHIFT; |
---|
155 | if (month == 0) { |
---|
156 | month = 1; |
---|
157 | } |
---|
158 | for (m = 0; m < month - 1; m++) |
---|
159 | days += months[m]; |
---|
160 | days += ((dd & MSDOS_DD_DAY_MASK) >> MSDOS_DD_DAY_SHIFT) - 1; |
---|
161 | lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; |
---|
162 | } |
---|
163 | return seconds + lastseconds; |
---|
164 | } |
---|
165 | |
---|
166 | static const u_char msdos_map[] = { |
---|
167 | /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
168 | /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
169 | /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
170 | /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
171 | /* 20 */ 0x00, 0x21, 0x00, 0x23, 0x24, 0x25, 0x26, 0x27, /* !"#$%&' */ |
---|
172 | /* 28 */ 0x28, 0x29, 0x00, 0x00, 0x00, 0x2D, 0x2E, 0x00, /* ()*+,-./ */ |
---|
173 | /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 01234567 */ |
---|
174 | /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */ |
---|
175 | /* 40 */ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ |
---|
176 | /* 48 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* HIJKLMNO */ |
---|
177 | /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* PQRSTUVW */ |
---|
178 | /* 58 */ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x00, 0x5E, 0x5F, /* XYZ[\]^_ */ |
---|
179 | /* 60 */ 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ |
---|
180 | /* 68 */ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, /* hijklmno */ |
---|
181 | /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* pqrstuvw */ |
---|
182 | /* 78 */ 0x58, 0x59, 0x5A, 0x5B, 0x7C, 0x00, 0x7E, 0x00, /* xyz{|}~ */ |
---|
183 | /* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
184 | /* 88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
185 | /* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
186 | /* 98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
187 | /* A0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
188 | /* A8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
189 | /* B0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
190 | /* B8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
191 | /* C0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
192 | /* C8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
193 | /* D0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
194 | /* D8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
195 | /* E0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
196 | /* E8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
197 | /* F0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
198 | /* F8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
---|
199 | }; |
---|
200 | /* |
---|
201 | * Convert a unix filename to a DOS filename. Return -1 if wrong name is |
---|
202 | * supplied. |
---|
203 | */ |
---|
204 | int |
---|
205 | msdos_filename_unix2dos(char *un, int unlen, char *dn) |
---|
206 | { |
---|
207 | int i; |
---|
208 | u_char c; |
---|
209 | |
---|
210 | /* |
---|
211 | * Fill the dos filename string with blanks. These are DOS's pad |
---|
212 | * characters. |
---|
213 | */ |
---|
214 | for (i = 0; i <= 10; i++) |
---|
215 | dn[i] = ' '; |
---|
216 | |
---|
217 | /* |
---|
218 | * The filenames "." and ".." are handled specially, since they |
---|
219 | * don't follow dos filename rules. |
---|
220 | */ |
---|
221 | if (un[0] == '.' && unlen == 1) { |
---|
222 | dn[0] = '.'; |
---|
223 | return 0; |
---|
224 | } |
---|
225 | if (un[0] == '.' && un[1] == '.' && unlen == 2) { |
---|
226 | dn[0] = '.'; |
---|
227 | dn[1] = '.'; |
---|
228 | return 0; |
---|
229 | } |
---|
230 | |
---|
231 | /* |
---|
232 | * Copy the unix filename into the dos filename string upto the end |
---|
233 | * of string, a '.', or 8 characters. Whichever happens first stops |
---|
234 | * us. This forms the name portion of the dos filename. Fold to |
---|
235 | * upper case. |
---|
236 | */ |
---|
237 | for (i = 0; i <= 7 && unlen && (c = *un) && c != '.'; i++) { |
---|
238 | if ((dn[i] = msdos_map[c]) == 0) |
---|
239 | return -1; |
---|
240 | un++; |
---|
241 | unlen--; |
---|
242 | } |
---|
243 | |
---|
244 | /* |
---|
245 | * Strip any further characters up to a '.' or the end of the |
---|
246 | * string. |
---|
247 | */ |
---|
248 | while (unlen && (c = *un)) { |
---|
249 | un++; |
---|
250 | unlen--; |
---|
251 | /* Make sure we've skipped over the dot before stopping. */ |
---|
252 | if (c == '.') |
---|
253 | break; |
---|
254 | } |
---|
255 | |
---|
256 | /* |
---|
257 | * Copy in the extension part of the name, if any. Force to upper |
---|
258 | * case. Note that the extension is allowed to contain '.'s. |
---|
259 | * Filenames in this form are probably inaccessable under dos. |
---|
260 | */ |
---|
261 | for (i = 8; i <= 10 && unlen && (c = *un); i++) { |
---|
262 | if ((dn[i] = msdos_map[c]) == 0) |
---|
263 | return -1; |
---|
264 | un++; |
---|
265 | unlen--; |
---|
266 | } |
---|
267 | return 0; |
---|
268 | } |
---|