[c125425] | 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 | |
---|
[ffc144e] | 21 | #if HAVE_CONFIG_H |
---|
| 22 | #include "config.h" |
---|
| 23 | #endif |
---|
| 24 | |
---|
[c125425] | 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 | } |
---|