1 /* $FreeBSD$ */ 2 /* $NetBSD: msdosfs_conv.c,v 1.25 1997/11/17 15:36:40 ws Exp $ */ 3 4 /*- 5 * SPDX-License-Identifier: BSD-4-Clause 6 * 7 * Copyright (C) 1995, 1997 Wolfgang Solfrank. 8 * Copyright (C) 1995, 1997 TooLs GmbH. 9 * All rights reserved. 10 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by TooLs GmbH. 23 * 4. The name of TooLs GmbH may not be used to endorse or promote products 24 * derived from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 32 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 33 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 */ 37 /*- 38 * Written by Paul Popelka (paulp@uts.amdahl.com) 39 * 40 * You can do anything you want with this software, just don't say you wrote 41 * it, and don't remove this notice. 42 * 43 * This software is provided "as is". 44 * 45 * The author supplies this software to be publicly redistributed on the 46 * understanding that the author is not responsible for the correct 47 * functioning of this software in any circumstances and is not liable for 48 * any damages caused by this software. 49 * 50 * October 1992 51 */ 52 53 #include <sys/param.h> 54 #include <sys/time.h> 55 #include <sys/kernel.h> /* defines tz */ 56 #include <sys/systm.h> 57 #include <machine/clock.h> 58 #include <sys/dirent.h> 59 #include <sys/iconv.h> 60 #include <sys/mount.h> 61 62 #include "bpb.h" 63 #include "msdosfsmount.h" 64 #include "direntry.h" 65 66 extern struct iconv_functions *msdos_iconv; 67 68 /* 69 * Total number of days that have passed for each month in a regular year. 70 */ 71 static u_short regyear[] = { 72 31, 59, 90, 120, 151, 181, 73 212, 243, 273, 304, 334, 365 74 }; 75 76 /* 77 * Total number of days that have passed for each month in a leap year. 78 */ 79 static u_short leapyear[] = { 80 31, 60, 91, 121, 152, 182, 81 213, 244, 274, 305, 335, 366 82 }; 83 84 /* 85 * Variables used to remember parts of the last time conversion. Maybe we 86 * can avoid a full conversion. 87 */ 88 static u_long lasttime; 89 static u_long lastday; 90 static u_short lastddate; 91 static u_short lastdtime; 92 93 /* 94 * Convert the unix version of time to dos's idea of time to be used in 95 * file timestamps. The passed in unix time is assumed to be in GMT. 96 */ 97 void 98 unix2dostime(struct timespec *tsp, uint16_t *ddp, uint16_t *dtp, 99 uint8_t *dhp) 100 { 101 u_long t; 102 u_long days; 103 u_long inc; 104 u_long year; 105 u_long month; 106 u_short *months; 107 108 /* 109 * If the time from the last conversion is the same as now, then 110 * skip the computations and use the saved result. 111 */ 112 t = tsp->tv_sec - (tz.tz_minuteswest * 60) 113 - (wall_cmos_clock ? adjkerntz : 0); 114 /* - daylight savings time correction */ 115 t &= ~1; 116 if (lasttime != t) { 117 lasttime = t; 118 lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) 119 + (((t / 60) % 60) << DT_MINUTES_SHIFT) 120 + (((t / 3600) % 24) << DT_HOURS_SHIFT); 121 122 /* 123 * If the number of days since 1970 is the same as the last 124 * time we did the computation then skip all this leap year 125 * and month stuff. 126 */ 127 days = t / (24 * 60 * 60); 128 if (days != lastday) { 129 lastday = days; 130 for (year = 1970;; year++) { 131 inc = year & 0x03 ? 365 : 366; 132 if (days < inc) 133 break; 134 days -= inc; 135 } 136 months = year & 0x03 ? regyear : leapyear; 137 for (month = 0; days >= months[month]; month++) 138 ; 139 if (month > 0) 140 days -= months[month - 1]; 141 lastddate = ((days + 1) << DD_DAY_SHIFT) 142 + ((month + 1) << DD_MONTH_SHIFT); 143 /* 144 * Remember dos's idea of time is relative to 1980. 145 * unix's is relative to 1970. If somehow we get a 146 * time before 1980 then don't give totally crazy 147 * results. 148 */ 149 if (year > 1980) 150 lastddate += (year - 1980) << DD_YEAR_SHIFT; 151 } 152 } 153 if (dtp) 154 *dtp = lastdtime; 155 if (dhp) 156 *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; 157 158 *ddp = lastddate; 159 } 160 161 /* 162 * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that 163 * interval there were 8 regular years and 2 leap years. 164 */ 165 #define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) 166 167 static u_short lastdosdate; 168 static u_long lastseconds; 169 170 /* 171 * Initialize the temporary concatenation buffer. 172 */ 173 void 174 mbnambuf_init(struct mbnambuf *nbp) 175 { 176 nbp->nb_len = 0; 177 nbp->nb_last_id = -1; 178 nbp->nb_buf[sizeof(nbp->nb_buf) - 1] = '\0'; 179 } 180 181 /* 182 * Fill out our concatenation buffer with the given substring, at the offset 183 * specified by its id. Since this function must be called with ids in 184 * descending order, we take advantage of the fact that ASCII substrings are 185 * exactly WIN_CHARS in length. For non-ASCII substrings, we shift all 186 * previous (i.e. higher id) substrings upwards to make room for this one. 187 * This only penalizes portions of substrings that contain more than 188 * WIN_CHARS bytes when they are first encountered. 189 */ 190 void 191 mbnambuf_write(struct mbnambuf *nbp, char *name, int id) 192 { 193 char *slot; 194 size_t count, newlen; 195 196 if (nbp->nb_len != 0 && id != nbp->nb_last_id - 1) { 197 kprintf("msdosfs: non-decreasing id: id %d, last id %d\n", 198 id, nbp->nb_last_id); 199 return; 200 } 201 /* Will store this substring in a WIN_CHARS-aligned slot. */ 202 slot = &nbp->nb_buf[id * WIN_CHARS]; 203 count = strlen(name); 204 newlen = nbp->nb_len + count; 205 if (newlen > WIN_MAXLEN || newlen > 127) { 206 kprintf("msdosfs: file name length %zu too large\n", newlen); 207 return; 208 } 209 /* Shift suffix upwards by the amount length exceeds WIN_CHARS. */ 210 if (count > WIN_CHARS && nbp->nb_len != 0) 211 bcopy(slot + WIN_CHARS, slot + count, nbp->nb_len); 212 /* Copy in the substring to its slot and update length so far. */ 213 bcopy(name, slot, count); 214 nbp->nb_len = newlen; 215 nbp->nb_last_id = id; 216 } 217 218 /* 219 * Take the completed string and use it to setup the struct dirent. 220 * Be sure to always nul-terminate the d_name and then copy the string 221 * from our buffer. Note that this function assumes the full string has 222 * been reassembled in the buffer. If it's called before all substrings 223 * have been written via mbnambuf_write(), the result will be incorrect. 224 */ 225 char * 226 mbnambuf_flush(struct mbnambuf *nbp, char *d_name, uint16_t *d_namlen) 227 { 228 #if 0 229 if (nbp->nb_len > 127) { 230 mbnambuf_init(nbp); 231 return (NULL); 232 } 233 #endif 234 bcopy(&nbp->nb_buf[0], d_name, nbp->nb_len); 235 d_name[nbp->nb_len] = '\0'; 236 *d_namlen = nbp->nb_len; 237 mbnambuf_init(nbp); 238 return (d_name); 239 } 240 241 /* 242 * Convert from dos' idea of time to unix'. This will probably only be 243 * called from the stat(), and fstat() system calls and so probably need 244 * not be too efficient. 245 */ 246 void 247 dos2unixtime(u_int dd, u_int dt, u_int dh, struct timespec *tsp) 248 { 249 u_long seconds; 250 u_long month; 251 u_long year; 252 u_long days; 253 u_short *months; 254 255 if (dd == 0) { 256 /* 257 * Uninitialized field, return the epoch. 258 */ 259 tsp->tv_sec = 0; 260 tsp->tv_nsec = 0; 261 return; 262 } 263 seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) 264 + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 265 + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 266 + dh / 100; 267 /* 268 * If the year, month, and day from the last conversion are the 269 * same then use the saved value. 270 */ 271 if (lastdosdate != dd) { 272 lastdosdate = dd; 273 days = 0; 274 year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; 275 days = year * 365; 276 days += year / 4 + 1; /* add in leap days */ 277 if ((year & 0x03) == 0) 278 days--; /* if year is a leap year */ 279 months = year & 0x03 ? regyear : leapyear; 280 month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; 281 if (month < 1 || month > 12) { 282 kprintf("dos2unixtime(): month value out of range (%ld)\n", 283 month); 284 month = 1; 285 } 286 if (month > 1) 287 days += months[month - 2]; 288 days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; 289 lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; 290 } 291 tsp->tv_sec = seconds + lastseconds + (tz.tz_minuteswest * 60) 292 + adjkerntz; 293 /* + daylight savings time correction */ 294 tsp->tv_nsec = (dh % 100) * 10000000; 295 } 296 297 /* 298 * 0 - character disallowed in long file name. 299 * 1 - character should be replaced by '_' in DOS file name, 300 * and generation number inserted. 301 * 2 - character ('.' and ' ') should be skipped in DOS file name, 302 * and generation number inserted. 303 */ 304 static const u_char 305 unix2dos[256] = { 306 /* iso8859-1 -> cp850 */ 307 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ 308 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ 309 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ 310 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ 311 2, 0x21, 0, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 312 0x28, 0x29, 0, 1, 1, 0x2d, 2, 0, /* 28-2f */ 313 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 314 0x38, 0x39, 0, 1, 0, 1, 0, 0, /* 38-3f */ 315 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ 316 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ 317 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ 318 0x58, 0x59, 0x5a, 1, 0, 1, 0x5e, 0x5f, /* 58-5f */ 319 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */ 320 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 68-6f */ 321 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */ 322 0x58, 0x59, 0x5a, 0x7b, 0, 0x7d, 0x7e, 0, /* 78-7f */ 323 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ 324 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ 325 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ 326 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ 327 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ 328 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ 329 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ 330 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ 331 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ 332 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ 333 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ 334 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ 335 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ 336 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ 337 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ 338 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ 339 }; 340 341 static const u_char 342 dos2unix[256] = { 343 /* cp850 -> iso8859-1 */ 344 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */ 345 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */ 346 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */ 347 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 18-1f */ 348 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 349 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 350 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 351 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 352 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ 353 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ 354 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ 355 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 356 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 357 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 358 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 359 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 360 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ 361 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */ 362 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ 363 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f, /* 98-9f */ 364 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */ 365 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */ 366 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, /* b0-b7 */ 367 0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f, /* b8-bf */ 368 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, /* c0-c7 */ 369 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4, /* c8-cf */ 370 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, /* d0-d7 */ 371 0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f, /* d8-df */ 372 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */ 373 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */ 374 0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */ 375 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0x3f, /* f8-ff */ 376 }; 377 378 static const u_char 379 u2l[256] = { 380 /* tolower */ 381 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 382 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 383 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ 384 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ 385 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 386 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 387 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 388 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 389 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ 390 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ 391 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ 392 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 393 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 394 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 395 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 396 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 397 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ 398 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ 399 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ 400 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ 401 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ 402 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ 403 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ 404 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ 405 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ 406 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ 407 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ 408 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ 409 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ 410 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ 411 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ 412 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ 413 }; 414 415 static const u_char 416 l2u[256] = { 417 /* toupper */ 418 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 419 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 420 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ 421 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ 422 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 423 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 424 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 425 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 426 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ 427 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ 428 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ 429 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 430 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 431 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 432 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 433 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 434 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ 435 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ 436 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ 437 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ 438 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ 439 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ 440 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ 441 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ 442 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ 443 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ 444 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ 445 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ 446 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ 447 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ 448 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ 449 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ 450 }; 451 452 /* 453 * Convert DOS char to Local char 454 */ 455 static uint16_t 456 dos2unixchr(const u_char **instr, size_t *ilen, int lower, 457 struct msdosfsmount *pmp) 458 { 459 u_char c; 460 char *outp, outbuf[3]; 461 uint16_t wc; 462 size_t len, olen; 463 464 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { 465 olen = len = 2; 466 outp = outbuf; 467 if (lower & (LCASE_BASE | LCASE_EXT)) 468 msdos_iconv->convchr_case(pmp->pm_d2u, 469 (const char **)instr, ilen, 470 &outp, &olen, KICONV_LOWER); 471 else 472 msdos_iconv->convchr(pmp->pm_d2u, (const char **)instr, 473 ilen, &outp, &olen); 474 len -= olen; 475 /* 476 * return '?' if failed to convert 477 */ 478 if (len == 0) { 479 (*ilen)--; 480 (*instr)++; 481 return ('?'); 482 } 483 wc = 0; 484 while(len--) 485 wc |= (*(outp - len - 1) & 0xff) << (len << 3); 486 return (wc); 487 } 488 (*ilen)--; 489 c = *(*instr)++; 490 c = dos2unix[c]; 491 if (lower & (LCASE_BASE | LCASE_EXT)) 492 c = u2l[c]; 493 return ((uint16_t)c); 494 } 495 496 /* 497 * DOS filenames are made of 2 parts, the name part and the extension part. 498 * The name part is 8 characters long and the extension part is 3 499 * characters long. They may contain trailing blanks if the name or 500 * extension are not long enough to fill their respective fields. 501 */ 502 503 /* 504 * Convert a DOS filename to a unix filename. And, return the number of 505 * characters in the resulting unix filename excluding the terminating 506 * null. 507 */ 508 int 509 dos2unixfn(u_char dn[11], u_char *un, int lower, struct msdosfsmount *pmp) 510 { 511 size_t i; 512 int thislong = 0; 513 u_char c; 514 515 /* 516 * If first char of the filename is SLOT_E5 (0x05), then the real 517 * first char of the filename should be 0xe5. But, they couldn't 518 * just have a 0xe5 mean 0xe5 because that is used to mean a freed 519 * directory slot. Another dos quirk. 520 */ 521 if (*dn == SLOT_E5) 522 *dn = 0xe5; 523 524 /* 525 * Copy the name portion into the unix filename string. 526 */ 527 for(i = 8; i > 0 && *dn != ' ';) { 528 c = dos2unixchr((const u_char **)(void *)&dn, 529 &i, lower & LCASE_BASE, pmp); 530 if (c & 0xff00) { 531 *un++ = c >> 8; 532 thislong++; 533 } 534 *un++ = c; 535 thislong++; 536 } 537 dn += i; 538 539 /* 540 * Now, if there is an extension then put in a period and copy in 541 * the extension. 542 */ 543 if (*dn != ' ') { 544 *un++ = '.'; 545 thislong++; 546 for (i = 3; i > 0 && *dn != ' ';) { 547 c = dos2unixchr((const u_char **)(void *)&dn, &i, 548 lower & LCASE_EXT, pmp); 549 if (c & 0xff00) { 550 *un++ = c >> 8; 551 thislong++; 552 } 553 *un++ = c; 554 thislong++; 555 } 556 } 557 *un++ = 0; 558 559 return (thislong); 560 } 561 562 /* 563 * Store an area with multi byte string instr, and reterns left 564 * byte of instr and moves pointer forward. The area's size is 565 * inlen or outlen. 566 */ 567 static int 568 mbsadjpos(const char **instr, size_t inlen, size_t outlen, int weight, int flag, 569 void *handle) 570 { 571 char *outp, outstr[outlen * weight + 1]; 572 573 bzero(outstr, outlen*weight+1); 574 if (flag & MSDOSFSMNT_KICONV && msdos_iconv) { 575 outp = outstr; 576 outlen *= weight; 577 msdos_iconv->conv(handle, instr, &inlen, &outp, &outlen); 578 return (inlen); 579 } 580 (*instr) += min(inlen, outlen); 581 return (inlen - min(inlen, outlen)); 582 } 583 584 /* 585 * Convert Local char to DOS char 586 */ 587 static uint16_t 588 unix2doschr(const u_char **instr, size_t *ilen, struct msdosfsmount *pmp) 589 { 590 u_char c; 591 char *up, *outp, unicode[3], outbuf[3]; 592 uint16_t wc; 593 size_t len, ucslen, unixlen, olen; 594 595 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { 596 /* 597 * to hide an invisible character, using a unicode filter 598 */ 599 ucslen = 2; 600 len = *ilen; 601 up = unicode; 602 msdos_iconv->convchr(pmp->pm_u2w, (const char **)instr, 603 ilen, &up, &ucslen); 604 unixlen = len - *ilen; 605 606 /* 607 * cannot be converted 608 */ 609 if (unixlen == 0) { 610 (*ilen)--; 611 (*instr)++; 612 return (0); 613 } 614 /* 615 * return magic number for ascii char 616 */ 617 if (unixlen == 1) { 618 c = *(*instr -1); 619 if (! (c & 0x80)) { 620 c = unix2dos[c]; 621 if (c <= 2) 622 return (c); 623 } 624 } 625 /* 626 * now convert using libiconv 627 */ 628 *instr -= unixlen; 629 *ilen = len; 630 olen = len = 2; 631 outp = outbuf; 632 msdos_iconv->convchr_case(pmp->pm_u2d, (const char **)instr, 633 ilen, &outp, &olen, 634 KICONV_FROM_UPPER); 635 len -= olen; 636 /* 637 * cannot be converted, but has unicode char, should return magic number 638 */ 639 if (len == 0) { 640 (*ilen) -= unixlen; 641 (*instr) += unixlen; 642 return (1); 643 } 644 wc = 0; 645 while(len--) 646 wc |= (*(outp - len - 1) & 0xff) << (len << 3); 647 return (wc); 648 } 649 (*ilen)--; 650 c = *(*instr)++; 651 c = l2u[c]; 652 c = unix2dos[c]; 653 return ((uint16_t)c); 654 } 655 656 /* 657 * Convert a unix filename to a DOS filename according to Win95 rules. 658 * If applicable and gen is not 0, it is inserted into the converted 659 * filename as a generation number. 660 * Returns 661 * 0 if name couldn't be converted 662 * 1 if the converted name is the same as the original 663 * (no long filename entry necessary for Win95) 664 * 2 if conversion was successful 665 * 3 if conversion was successful and generation number was inserted 666 */ 667 int 668 unix2dosfn(const u_char *un, u_char dn[12], size_t unlen, u_int gen, 669 struct msdosfsmount *pmp) 670 { 671 ssize_t i, j; 672 int l; 673 int conv = 1; 674 const u_char *cp, *dp, *dp1; 675 u_char gentext[6], *wcp; 676 uint16_t c; 677 678 /* 679 * Fill the dos filename string with blanks. These are DOS's pad 680 * characters. 681 */ 682 for (i = 0; i < 11; i++) 683 dn[i] = ' '; 684 dn[11] = 0; 685 686 /* 687 * The filenames "." and ".." are handled specially, since they 688 * don't follow dos filename rules. 689 */ 690 if (un[0] == '.' && unlen == 1) { 691 dn[0] = '.'; 692 return gen <= 1; 693 } 694 if (un[0] == '.' && un[1] == '.' && unlen == 2) { 695 dn[0] = '.'; 696 dn[1] = '.'; 697 return gen <= 1; 698 } 699 700 /* 701 * Filenames with only blanks and dots are not allowed! 702 */ 703 for (cp = un, i = unlen; --i >= 0; cp++) 704 if (*cp != ' ' && *cp != '.') 705 break; 706 if (i < 0) 707 return 0; 708 709 /* 710 * Filenames with some characters are not allowed! 711 */ 712 for (cp = un, i = unlen; i > 0;) 713 if (unix2doschr(&cp, (size_t *)&i, pmp) == 0) 714 return 0; 715 716 /* 717 * Now find the extension 718 * Note: dot as first char doesn't start extension 719 * and trailing dots and blanks are ignored 720 * Note(2003/7): It seems recent Windows has 721 * defferent rule than this code, that Windows 722 * ignores all dots before extension, and use all 723 * chars as filename except for dots. 724 */ 725 dp = dp1 = NULL; 726 for (cp = un + 1, i = unlen - 1; --i >= 0;) { 727 switch (*cp++) { 728 case '.': 729 if (!dp1) 730 dp1 = cp; 731 break; 732 case ' ': 733 break; 734 default: 735 if (dp1) 736 dp = dp1; 737 dp1 = NULL; 738 break; 739 } 740 } 741 742 /* 743 * Now convert it (this part is for extension). 744 * As Windows XP do, if it's not ascii char, 745 * this function should return 2 or 3, so that checkng out Unicode name. 746 */ 747 if (dp) { 748 if (dp1) 749 l = dp1 - dp; 750 else 751 l = unlen - (dp - un); 752 for (cp = dp, i = l, j = 8; i > 0 && j < 11; j++) { 753 c = unix2doschr(&cp, (size_t *)&i, pmp); 754 if (c & 0xff00) { 755 dn[j] = c >> 8; 756 if (++j < 11) { 757 dn[j] = c; 758 if (conv != 3) 759 conv = 2; 760 continue; 761 } else { 762 conv = 3; 763 dn[j-1] = ' '; 764 break; 765 } 766 } else { 767 dn[j] = c; 768 } 769 if (((dn[j] & 0x80) || *(cp - 1) != dn[j]) && conv != 3) 770 conv = 2; 771 if (dn[j] == 1) { 772 conv = 3; 773 dn[j] = '_'; 774 } 775 if (dn[j] == 2) { 776 conv = 3; 777 dn[j--] = ' '; 778 } 779 } 780 if (i > 0) 781 conv = 3; 782 dp--; 783 } else { 784 for (dp = cp; *--dp == ' ' || *dp == '.';); 785 dp++; 786 } 787 788 /* 789 * Now convert the rest of the name 790 */ 791 for (i = dp - un, j = 0; un < dp && j < 8; j++) { 792 c = unix2doschr(&un, &i, pmp); 793 if (c & 0xff00) { 794 dn[j] = c >> 8; 795 if (++j < 8) { 796 dn[j] = c; 797 if (conv != 3) 798 conv = 2; 799 continue; 800 } else { 801 conv = 3; 802 dn[j-1] = ' '; 803 break; 804 } 805 } else { 806 dn[j] = c; 807 } 808 if (((dn[j] & 0x80) || *(un - 1) != dn[j]) && conv != 3) 809 conv = 2; 810 if (dn[j] == 1) { 811 conv = 3; 812 dn[j] = '_'; 813 } 814 if (dn[j] == 2) { 815 conv = 3; 816 dn[j--] = ' '; 817 } 818 } 819 if (un < dp) 820 conv = 3; 821 /* 822 * If we didn't have any chars in filename, 823 * generate a default 824 */ 825 if (!j) 826 dn[0] = '_'; 827 828 /* 829 * If there wasn't any char dropped, 830 * there is no place for generation numbers 831 */ 832 if (conv != 3) { 833 if (gen > 1) 834 conv = 0; 835 goto done; 836 } 837 838 /* 839 * Now insert the generation number into the filename part 840 */ 841 if (gen == 0) 842 goto done; 843 for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) 844 *--wcp = gen % 10 + '0'; 845 if (gen) { 846 conv = 0; 847 goto done; 848 } 849 for (i = 8; dn[--i] == ' ';); 850 i++; 851 if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) 852 i = 8 - (gentext + sizeof(gentext) - wcp + 1); 853 /* 854 * Correct posision to where insert the generation number 855 */ 856 cp = dn; 857 i -= mbsadjpos((const char**)&cp, i, unlen, 1, pmp->pm_flags, 858 pmp->pm_d2u); 859 dn[i++] = '~'; 860 while (wcp < gentext + sizeof(gentext)) 861 dn[i++] = *wcp++; 862 863 /* 864 * Tail of the filename should be space 865 */ 866 while (i < 8) 867 dn[i++] = ' '; 868 conv = 3; 869 870 done: 871 /* 872 * The first character cannot be E5, 873 * because that means a deleted entry 874 */ 875 if (dn[0] == 0xe5) 876 dn[0] = SLOT_E5; 877 878 return conv; 879 } 880 881 /* 882 * Convert Local char to Windows char 883 */ 884 static uint16_t 885 unix2winchr(const u_char **instr, size_t *ilen, int lower, 886 struct msdosfsmount *pmp) 887 { 888 u_char *outp, outbuf[3]; 889 uint16_t wc; 890 size_t olen; 891 892 if (*ilen == 0) 893 return (0); 894 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { 895 outp = outbuf; 896 olen = 2; 897 if (lower & (LCASE_BASE | LCASE_EXT)) 898 msdos_iconv->convchr_case(pmp->pm_u2w, 899 (const char **)instr, 900 ilen, (char **)&outp, &olen, 901 KICONV_FROM_LOWER); 902 else 903 msdos_iconv->convchr(pmp->pm_u2w, (const char **)instr, 904 ilen, (char **)&outp, &olen); 905 /* 906 * return '0' if end of filename 907 */ 908 if (olen == 2) 909 return (0); 910 wc = (outbuf[0]<<8) | outbuf[1]; 911 return (wc); 912 } 913 (*ilen)--; 914 wc = (*instr)[0]; 915 if (lower & (LCASE_BASE | LCASE_EXT)) 916 wc = u2l[wc]; 917 (*instr)++; 918 return (wc); 919 } 920 921 /* 922 * Create a Win95 long name directory entry 923 * Note: assumes that the filename is valid, 924 * i.e. doesn't consist solely of blanks and dots 925 */ 926 int 927 unix2winfn(const u_char *un, size_t unlen, struct winentry *wep, int cnt, 928 int chksum, struct msdosfsmount *pmp) 929 { 930 uint8_t *wcp; 931 int i, end; 932 uint16_t code; 933 934 /* 935 * Drop trailing blanks and dots 936 */ 937 unlen = winLenFixup(un, unlen); 938 939 /* 940 * Cut *un for this slot 941 */ 942 unlen = mbsadjpos((const char **)&un, unlen, (cnt - 1) * WIN_CHARS, 2, 943 pmp->pm_flags, pmp->pm_u2w); 944 945 /* 946 * Initialize winentry to some useful default 947 */ 948 for (wcp = (uint8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); 949 wep->weCnt = cnt; 950 wep->weAttributes = ATTR_WIN95; 951 wep->weReserved1 = 0; 952 wep->weChksum = chksum; 953 wep->weReserved2 = 0; 954 955 /* 956 * Now convert the filename parts 957 */ 958 end = 0; 959 for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0 && !end;) { 960 code = unix2winchr(&un, &unlen, 0, pmp); 961 *wcp++ = code; 962 *wcp++ = code >> 8; 963 if (!code) 964 end = WIN_LAST; 965 } 966 for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0 && !end;) { 967 code = unix2winchr(&un, &unlen, 0, pmp); 968 *wcp++ = code; 969 *wcp++ = code >> 8; 970 if (!code) 971 end = WIN_LAST; 972 } 973 for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0 && !end;) { 974 code = unix2winchr(&un, &unlen, 0, pmp); 975 *wcp++ = code; 976 *wcp++ = code >> 8; 977 if (!code) 978 end = WIN_LAST; 979 } 980 if (*un == '\0') 981 end = WIN_LAST; 982 wep->weCnt |= end; 983 return !end; 984 } 985 986 static __inline uint8_t 987 find_lcode(uint16_t code, uint16_t *u2w) 988 { 989 int i; 990 991 for (i = 0; i < 128; i++) 992 if (u2w[i] == code) 993 return (i | 0x80); 994 return '?'; 995 } 996 997 /* 998 * Compare our filename to the one in the Win95 entry 999 * Returns the checksum or -1 if no match 1000 */ 1001 int 1002 winChkName(struct mbnambuf *nbp, const u_char *un, size_t unlen, int chksum, 1003 struct msdosfsmount *pmp) 1004 { 1005 size_t len; 1006 uint16_t c1, c2; 1007 u_char *np; 1008 uint16_t d_namlen; 1009 char d_name[127]; 1010 1011 bzero(d_name, 127); 1012 /* 1013 * We already have winentry in *nbp. 1014 */ 1015 if (!mbnambuf_flush(nbp, d_name, &d_namlen) || d_namlen == 0) 1016 return -1; 1017 mprintf("winChkName(): un=%s:%zd,d_name=%s:%d\n", 1018 un, unlen, d_name, d_namlen); 1019 /* 1020 * Compare the name parts 1021 */ 1022 len = d_namlen; 1023 if (unlen != len) 1024 return -2; 1025 1026 for (np = d_name; unlen > 0 && len > 0;) { 1027 /* 1028 * Comparison must be case insensitive, because FAT disallows 1029 * to look up or create files in case sensitive even when 1030 * it's a long file name. 1031 */ 1032 c1 = unix2winchr((const u_char **)(void *)&np, 1033 &len, LCASE_BASE, pmp); 1034 c2 = unix2winchr(&un, &unlen, LCASE_BASE, pmp); 1035 if (c1 != c2) 1036 return -2; 1037 } 1038 return chksum; 1039 } 1040 1041 /* 1042 * Convert Windows char to Local char 1043 */ 1044 static uint16_t 1045 win2unixchr(uint16_t wc, struct msdosfsmount *pmp) 1046 { 1047 u_char *inp, *outp, inbuf[3], outbuf[3]; 1048 size_t ilen, olen, len; 1049 1050 if (wc == 0) 1051 return (0); 1052 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { 1053 inbuf[0] = (u_char)(wc>>8); 1054 inbuf[1] = (u_char)wc; 1055 inbuf[2] = '\0'; 1056 ilen = olen = len = 2; 1057 inp = inbuf; 1058 outp = outbuf; 1059 msdos_iconv->convchr(pmp->pm_w2u, (const char **)&inp, &ilen, 1060 (char **)&outp, &olen); 1061 len -= olen; 1062 1063 /* 1064 * return '?' if failed to convert 1065 */ 1066 if (len == 0) { 1067 wc = '?'; 1068 return (wc); 1069 } 1070 wc = 0; 1071 while(len--) 1072 wc |= (*(outp - len - 1) & 0xff) << (len << 3); 1073 return (wc); 1074 } 1075 if (wc & 0xff00) 1076 wc = '?'; 1077 return (wc); 1078 } 1079 1080 /* 1081 * Convert Win95 filename to dirbuf. 1082 * Returns the checksum or -1 if impossible 1083 */ 1084 int 1085 win2unixfn(struct mbnambuf *nbp, struct winentry *wep, int chksum, 1086 struct msdosfsmount *pmp) 1087 { 1088 uint8_t *cp; 1089 uint8_t *np, name[WIN_CHARS * 2 + 1]; 1090 uint16_t code; 1091 int i; 1092 1093 if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) 1094 || !(wep->weCnt&WIN_CNT)) 1095 return -1; 1096 1097 /* 1098 * First compare checksums 1099 */ 1100 if (wep->weCnt&WIN_LAST) { 1101 chksum = wep->weChksum; 1102 } else if (chksum != wep->weChksum) 1103 chksum = -1; 1104 if (chksum == -1) 1105 return -1; 1106 1107 /* 1108 * Convert the name parts 1109 */ 1110 np = name; 1111 for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { 1112 code = (cp[1] << 8) | cp[0]; 1113 switch (code) { 1114 case 0: 1115 *np = '\0'; 1116 mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); 1117 return chksum; 1118 case '/': 1119 *np = '\0'; 1120 return -1; 1121 default: 1122 code = win2unixchr(code, pmp); 1123 if (code & 0xff00) 1124 *np++ = code >> 8; 1125 *np++ = code; 1126 break; 1127 } 1128 cp += 2; 1129 } 1130 for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { 1131 code = (cp[1] << 8) | cp[0]; 1132 switch (code) { 1133 case 0: 1134 *np = '\0'; 1135 mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); 1136 return chksum; 1137 case '/': 1138 *np = '\0'; 1139 return -1; 1140 default: 1141 code = win2unixchr(code, pmp); 1142 if (code & 0xff00) 1143 *np++ = code >> 8; 1144 *np++ = code; 1145 break; 1146 } 1147 cp += 2; 1148 } 1149 for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { 1150 code = (cp[1] << 8) | cp[0]; 1151 switch (code) { 1152 case 0: 1153 *np = '\0'; 1154 mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); 1155 return chksum; 1156 case '/': 1157 *np = '\0'; 1158 return -1; 1159 default: 1160 code = win2unixchr(code, pmp); 1161 if (code & 0xff00) 1162 *np++ = code >> 8; 1163 *np++ = code; 1164 break; 1165 } 1166 cp += 2; 1167 } 1168 *np = '\0'; 1169 mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); 1170 return chksum; 1171 } 1172 1173 /* 1174 * Compute the unrolled checksum of a DOS filename for Win95 LFN use. 1175 */ 1176 uint8_t 1177 winChksum(uint8_t *name) 1178 { 1179 int i; 1180 uint8_t s; 1181 1182 for (s = 0, i = 11; --i >= 0; s += *name++) 1183 s = (s << 7)|(s >> 1); 1184 return (s); 1185 } 1186 1187 /* 1188 * Determine the number of slots necessary for Win95 names 1189 */ 1190 int 1191 winSlotCnt(const u_char *un, size_t unlen, struct msdosfsmount *pmp) 1192 { 1193 size_t wlen; 1194 char wn[WIN_MAXLEN * 2 + 1], *wnp; 1195 1196 unlen = winLenFixup(un, unlen); 1197 1198 if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { 1199 wlen = WIN_MAXLEN * 2; 1200 wnp = wn; 1201 msdos_iconv->conv(pmp->pm_u2w, (const char **)&un, &unlen, &wnp, 1202 &wlen); 1203 if (unlen > 0) 1204 return 0; 1205 return howmany(WIN_MAXLEN - wlen/2, WIN_CHARS); 1206 } 1207 1208 if (unlen > WIN_MAXLEN) 1209 return 0; 1210 return howmany(unlen, WIN_CHARS); 1211 } 1212 1213 /* 1214 * Determine the number of bytes necessary for Win95 names 1215 */ 1216 size_t 1217 winLenFixup(const u_char *un, size_t unlen) 1218 { 1219 for (un += unlen; unlen > 0; unlen--) 1220 if (*--un != ' ' && *un != '.') 1221 break; 1222 return unlen; 1223 } 1224