msdosfs_conv.c revision 143435
150477Speter/* $FreeBSD: head/sys/fs/msdosfs/msdosfs_conv.c 143435 2005-03-11 23:27:45Z njl $ */ 233548Sjkh/* $NetBSD: msdosfs_conv.c,v 1.25 1997/11/17 15:36:40 ws Exp $ */ 32893Sdfr 433548Sjkh/*- 533548Sjkh * Copyright (C) 1995, 1997 Wolfgang Solfrank. 633548Sjkh * Copyright (C) 1995, 1997 TooLs GmbH. 733548Sjkh * All rights reserved. 833548Sjkh * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 933548Sjkh * 1033548Sjkh * Redistribution and use in source and binary forms, with or without 1133548Sjkh * modification, are permitted provided that the following conditions 1233548Sjkh * are met: 1333548Sjkh * 1. Redistributions of source code must retain the above copyright 1433548Sjkh * notice, this list of conditions and the following disclaimer. 1533548Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1633548Sjkh * notice, this list of conditions and the following disclaimer in the 1733548Sjkh * documentation and/or other materials provided with the distribution. 1833548Sjkh * 3. All advertising materials mentioning features or use of this software 1933548Sjkh * must display the following acknowledgement: 2033548Sjkh * This product includes software developed by TooLs GmbH. 2133548Sjkh * 4. The name of TooLs GmbH may not be used to endorse or promote products 2233548Sjkh * derived from this software without specific prior written permission. 2333548Sjkh * 2433548Sjkh * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2533548Sjkh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2633548Sjkh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2733548Sjkh * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2833548Sjkh * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2933548Sjkh * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 3033548Sjkh * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3133548Sjkh * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3233548Sjkh * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3333548Sjkh * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3433548Sjkh */ 35139776Simp/*- 362893Sdfr * Written by Paul Popelka (paulp@uts.amdahl.com) 378876Srgrimes * 382893Sdfr * You can do anything you want with this software, just don't say you wrote 392893Sdfr * it, and don't remove this notice. 408876Srgrimes * 412893Sdfr * This software is provided "as is". 428876Srgrimes * 432893Sdfr * The author supplies this software to be publicly redistributed on the 442893Sdfr * understanding that the author is not responsible for the correct 452893Sdfr * functioning of this software in any circumstances and is not liable for 462893Sdfr * any damages caused by this software. 478876Srgrimes * 482893Sdfr * October 1992 492893Sdfr */ 502893Sdfr 512893Sdfr/* 522893Sdfr * System include files. 532893Sdfr */ 542893Sdfr#include <sys/param.h> 552893Sdfr#include <sys/time.h> 562893Sdfr#include <sys/kernel.h> /* defines tz */ 5733548Sjkh#include <sys/systm.h> 587465Sache#include <machine/clock.h> 5933548Sjkh#include <sys/dirent.h> 60120492Sfjoe#include <sys/iconv.h> 61120492Sfjoe#include <sys/mount.h> 62120492Sfjoe#include <sys/malloc.h> 632893Sdfr 64120492Sfjoeextern struct iconv_functions *msdosfs_iconv; 65120492Sfjoe 662893Sdfr/* 672893Sdfr * MSDOSFS include files. 682893Sdfr */ 69120492Sfjoe#include <fs/msdosfs/bpb.h> 70120492Sfjoe#include <fs/msdosfs/msdosfsmount.h> 7177162Sru#include <fs/msdosfs/direntry.h> 722893Sdfr 732893Sdfr/* 747465Sache * Total number of days that have passed for each month in a regular year. 752893Sdfr */ 7612144Sphkstatic u_short regyear[] = { 777465Sache 31, 59, 90, 120, 151, 181, 787465Sache 212, 243, 273, 304, 334, 365 792893Sdfr}; 802893Sdfr 812893Sdfr/* 827465Sache * Total number of days that have passed for each month in a leap year. 832893Sdfr */ 8412144Sphkstatic u_short leapyear[] = { 857465Sache 31, 60, 91, 121, 152, 182, 867465Sache 213, 244, 274, 305, 335, 366 872893Sdfr}; 882893Sdfr 892893Sdfr/* 902893Sdfr * Variables used to remember parts of the last time conversion. Maybe we 912893Sdfr * can avoid a full conversion. 922893Sdfr */ 9333181Seivindstatic u_long lasttime; 9433181Seivindstatic u_long lastday; 9533181Seivindstatic u_short lastddate; 9633181Seivindstatic u_short lastdtime; 972893Sdfr 98125992Stjrstatic int mbsadjpos(const char **, size_t, size_t, int, int, void *handle); 99120492Sfjoestatic u_int16_t dos2unixchr(const u_char **, size_t *, int, struct msdosfsmount *); 100120492Sfjoestatic u_int16_t unix2doschr(const u_char **, size_t *, struct msdosfsmount *); 101120492Sfjoestatic u_int16_t win2unixchr(u_int16_t, struct msdosfsmount *); 102120492Sfjoestatic u_int16_t unix2winchr(const u_char **, size_t *, int, struct msdosfsmount *); 10333747Sache 104143435Snjlstatic char *nambuf_ptr; 105143435Snjlstatic size_t nambuf_len; 106143435Snjlstatic int nambuf_max_id; 107120492Sfjoe 1082893Sdfr/* 1092893Sdfr * Convert the unix version of time to dos's idea of time to be used in 1102893Sdfr * file timestamps. The passed in unix time is assumed to be in GMT. 1112893Sdfr */ 1122893Sdfrvoid 11333548Sjkhunix2dostime(tsp, ddp, dtp, dhp) 1142893Sdfr struct timespec *tsp; 11533548Sjkh u_int16_t *ddp; 11633548Sjkh u_int16_t *dtp; 11733548Sjkh u_int8_t *dhp; 1182893Sdfr{ 1192893Sdfr u_long t; 1202893Sdfr u_long days; 1212893Sdfr u_long inc; 1222893Sdfr u_long year; 1232893Sdfr u_long month; 1242893Sdfr u_short *months; 1252893Sdfr 1262893Sdfr /* 1272893Sdfr * If the time from the last conversion is the same as now, then 1282893Sdfr * skip the computations and use the saved result. 1292893Sdfr */ 130110299Sphk t = tsp->tv_sec - (tz_minuteswest * 60) 13115055Sache - (wall_cmos_clock ? adjkerntz : 0); 13215053Sache /* - daylight savings time correction */ 13333548Sjkh t &= ~1; 1342893Sdfr if (lasttime != t) { 1352893Sdfr lasttime = t; 13633548Sjkh lastdtime = (((t / 2) % 30) << DT_2SECONDS_SHIFT) 1372893Sdfr + (((t / 60) % 60) << DT_MINUTES_SHIFT) 1382893Sdfr + (((t / 3600) % 24) << DT_HOURS_SHIFT); 1392893Sdfr 1402893Sdfr /* 1412893Sdfr * If the number of days since 1970 is the same as the last 1422893Sdfr * time we did the computation then skip all this leap year 1432893Sdfr * and month stuff. 1442893Sdfr */ 1452893Sdfr days = t / (24 * 60 * 60); 1462893Sdfr if (days != lastday) { 1472893Sdfr lastday = days; 1482893Sdfr for (year = 1970;; year++) { 1492893Sdfr inc = year & 0x03 ? 365 : 366; 1502893Sdfr if (days < inc) 1512893Sdfr break; 1522893Sdfr days -= inc; 1532893Sdfr } 1542893Sdfr months = year & 0x03 ? regyear : leapyear; 15520138Sbde for (month = 0; days >= months[month]; month++) 1567465Sache ; 1577465Sache if (month > 0) 1587465Sache days -= months[month - 1]; 1592893Sdfr lastddate = ((days + 1) << DD_DAY_SHIFT) 1602893Sdfr + ((month + 1) << DD_MONTH_SHIFT); 1612893Sdfr /* 1622893Sdfr * Remember dos's idea of time is relative to 1980. 1632893Sdfr * unix's is relative to 1970. If somehow we get a 1642893Sdfr * time before 1980 then don't give totally crazy 1652893Sdfr * results. 1662893Sdfr */ 1672893Sdfr if (year > 1980) 1682893Sdfr lastddate += (year - 1980) << DD_YEAR_SHIFT; 1692893Sdfr } 1702893Sdfr } 17133548Sjkh if (dtp) 17233548Sjkh *dtp = lastdtime; 17333548Sjkh if (dhp) 17433548Sjkh *dhp = (tsp->tv_sec & 1) * 100 + tsp->tv_nsec / 10000000; 17533548Sjkh 1762893Sdfr *ddp = lastddate; 1772893Sdfr} 1782893Sdfr 1792893Sdfr/* 1802893Sdfr * The number of seconds between Jan 1, 1970 and Jan 1, 1980. In that 1812893Sdfr * interval there were 8 regular years and 2 leap years. 1822893Sdfr */ 1832893Sdfr#define SECONDSTO1980 (((8 * 365) + (2 * 366)) * (24 * 60 * 60)) 1842893Sdfr 18533181Seivindstatic u_short lastdosdate; 18633181Seivindstatic u_long lastseconds; 1872893Sdfr 1882893Sdfr/* 1892893Sdfr * Convert from dos' idea of time to unix'. This will probably only be 1902893Sdfr * called from the stat(), and fstat() system calls and so probably need 1912893Sdfr * not be too efficient. 1922893Sdfr */ 1932893Sdfrvoid 19433548Sjkhdos2unixtime(dd, dt, dh, tsp) 19533548Sjkh u_int dd; 19633548Sjkh u_int dt; 19733548Sjkh u_int dh; 1982893Sdfr struct timespec *tsp; 1992893Sdfr{ 2002893Sdfr u_long seconds; 20111921Sphk u_long month; 20211921Sphk u_long year; 2032893Sdfr u_long days; 2042893Sdfr u_short *months; 2052893Sdfr 20633548Sjkh if (dd == 0) { 20733548Sjkh /* 20833548Sjkh * Uninitialized field, return the epoch. 20933548Sjkh */ 21033548Sjkh tsp->tv_sec = 0; 21133548Sjkh tsp->tv_nsec = 0; 21233548Sjkh return; 21333548Sjkh } 2145083Sbde seconds = (((dt & DT_2SECONDS_MASK) >> DT_2SECONDS_SHIFT) << 1) 2152893Sdfr + ((dt & DT_MINUTES_MASK) >> DT_MINUTES_SHIFT) * 60 21633548Sjkh + ((dt & DT_HOURS_MASK) >> DT_HOURS_SHIFT) * 3600 21733548Sjkh + dh / 100; 2182893Sdfr /* 2192893Sdfr * If the year, month, and day from the last conversion are the 2202893Sdfr * same then use the saved value. 2212893Sdfr */ 2222893Sdfr if (lastdosdate != dd) { 2232893Sdfr lastdosdate = dd; 2242893Sdfr days = 0; 2252893Sdfr year = (dd & DD_YEAR_MASK) >> DD_YEAR_SHIFT; 2267465Sache days = year * 365; 2277465Sache days += year / 4 + 1; /* add in leap days */ 2287465Sache if ((year & 0x03) == 0) 2297465Sache days--; /* if year is a leap year */ 2302893Sdfr months = year & 0x03 ? regyear : leapyear; 2312893Sdfr month = (dd & DD_MONTH_MASK) >> DD_MONTH_SHIFT; 2327465Sache if (month < 1 || month > 12) { 23333548Sjkh printf("dos2unixtime(): month value out of range (%ld)\n", 2343152Sphk month); 2352893Sdfr month = 1; 2362893Sdfr } 2377465Sache if (month > 1) 2387465Sache days += months[month - 2]; 2392893Sdfr days += ((dd & DD_DAY_MASK) >> DD_DAY_SHIFT) - 1; 2402893Sdfr lastseconds = (days * 24 * 60 * 60) + SECONDSTO1980; 2412893Sdfr } 242110299Sphk tsp->tv_sec = seconds + lastseconds + (tz_minuteswest * 60) 24315055Sache + adjkerntz; 24415053Sache /* + daylight savings time correction */ 24533548Sjkh tsp->tv_nsec = (dh % 100) * 10000000; 2462893Sdfr} 2472893Sdfr 24833872Smsmith/* 24933872Smsmith * 0 - character disallowed in long file name. 25033872Smsmith * 1 - character should be replaced by '_' in DOS file name, 25133872Smsmith * and generation number inserted. 25233872Smsmith * 2 - character ('.' and ' ') should be skipped in DOS file name, 25333872Smsmith * and generation number inserted. 25433872Smsmith */ 25533548Sjkhstatic u_char 25633548Sjkhunix2dos[256] = { 257120492Sfjoe/* iso8859-1 -> cp850 */ 25833548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ 25933548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ 26033548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ 26133548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 18-1f */ 26233872Smsmith 2, 0x21, 0, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 26333872Smsmith 0x28, 0x29, 0, 1, 1, 0x2d, 2, 0, /* 28-2f */ 26433548Sjkh 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 26533872Smsmith 0x38, 0x39, 0, 1, 0, 1, 0, 0, /* 38-3f */ 26633548Sjkh 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ 26733548Sjkh 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ 26833548Sjkh 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ 26933872Smsmith 0x58, 0x59, 0x5a, 1, 0, 1, 0x5e, 0x5f, /* 58-5f */ 27033548Sjkh 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 60-67 */ 27133548Sjkh 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 68-6f */ 27233548Sjkh 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 70-77 */ 27333548Sjkh 0x58, 0x59, 0x5a, 0x7b, 0, 0x7d, 0x7e, 0, /* 78-7f */ 27433548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 80-87 */ 27533548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 88-8f */ 27633548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 90-97 */ 27733548Sjkh 0, 0, 0, 0, 0, 0, 0, 0, /* 98-9f */ 27833548Sjkh 0, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, /* a0-a7 */ 27933548Sjkh 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, /* a8-af */ 28033548Sjkh 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, /* b0-b7 */ 28133548Sjkh 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, /* b8-bf */ 28233548Sjkh 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* c0-c7 */ 28333548Sjkh 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* c8-cf */ 28433548Sjkh 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, /* d0-d7 */ 28533548Sjkh 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, /* d8-df */ 28633548Sjkh 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, /* e0-e7 */ 28733548Sjkh 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, /* e8-ef */ 28833548Sjkh 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0xf6, /* f0-f7 */ 28933548Sjkh 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0x98, /* f8-ff */ 29033548Sjkh}; 2912893Sdfr 29233548Sjkhstatic u_char 29333548Sjkhdos2unix[256] = { 294120492Sfjoe/* cp850 -> iso8859-1 */ 29533548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */ 29633548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */ 29733548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */ 29833548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 18-1f */ 29933548Sjkh 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 30033548Sjkh 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 30133548Sjkh 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 30233548Sjkh 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 30333548Sjkh 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 40-47 */ 30433548Sjkh 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 48-4f */ 30533548Sjkh 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 50-57 */ 30633548Sjkh 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 30733548Sjkh 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 30833548Sjkh 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 30933548Sjkh 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 31033548Sjkh 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 31133548Sjkh 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7, /* 80-87 */ 31233548Sjkh 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5, /* 88-8f */ 31333548Sjkh 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9, /* 90-97 */ 31433548Sjkh 0xff, 0xd6, 0xdc, 0xf8, 0xa3, 0xd8, 0xd7, 0x3f, /* 98-9f */ 31533548Sjkh 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba, /* a0-a7 */ 31633548Sjkh 0xbf, 0xae, 0xac, 0xbd, 0xbc, 0xa1, 0xab, 0xbb, /* a8-af */ 31733548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xc1, 0xc2, 0xc0, /* b0-b7 */ 31833548Sjkh 0xa9, 0x3f, 0x3f, 0x3f, 0x3f, 0xa2, 0xa5, 0x3f, /* b8-bf */ 31933548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xe3, 0xc3, /* c0-c7 */ 32033548Sjkh 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0xa4, /* c8-cf */ 32133548Sjkh 0xf0, 0xd0, 0xca, 0xcb, 0xc8, 0x3f, 0xcd, 0xce, /* d0-d7 */ 32233548Sjkh 0xcf, 0x3f, 0x3f, 0x3f, 0x3f, 0xa6, 0xcc, 0x3f, /* d8-df */ 32333548Sjkh 0xd3, 0xdf, 0xd4, 0xd2, 0xf5, 0xd5, 0xb5, 0xfe, /* e0-e7 */ 32433548Sjkh 0xde, 0xda, 0xdb, 0xd9, 0xfd, 0xdd, 0xaf, 0x3f, /* e8-ef */ 32533548Sjkh 0xad, 0xb1, 0x3f, 0xbe, 0xb6, 0xa7, 0xf7, 0xb8, /* f0-f7 */ 32633548Sjkh 0xb0, 0xa8, 0xb7, 0xb9, 0xb3, 0xb2, 0x3f, 0x3f, /* f8-ff */ 32733548Sjkh}; 32833548Sjkh 32933548Sjkhstatic u_char 33033548Sjkhu2l[256] = { 331120492Sfjoe/* tolower */ 33233548Sjkh 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 33333548Sjkh 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 33433548Sjkh 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ 33533548Sjkh 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ 33633548Sjkh 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 33733548Sjkh 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 33833548Sjkh 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 33933548Sjkh 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 34033548Sjkh 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ 34133548Sjkh 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ 34233548Sjkh 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ 34333548Sjkh 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 34433548Sjkh 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 34533548Sjkh 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 34633548Sjkh 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 34733548Sjkh 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 34833548Sjkh 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ 34933548Sjkh 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ 35033548Sjkh 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ 35133548Sjkh 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ 35233548Sjkh 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ 35333548Sjkh 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ 35433548Sjkh 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ 35533548Sjkh 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ 35633548Sjkh 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ 35733548Sjkh 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ 35833548Sjkh 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ 35933548Sjkh 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ 36033548Sjkh 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ 36133548Sjkh 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ 36233548Sjkh 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ 36333548Sjkh 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ 36433548Sjkh}; 36533548Sjkh 36633768Sachestatic u_char 36733768Sachel2u[256] = { 368120492Sfjoe/* toupper */ 36933768Sache 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 37033768Sache 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 37133768Sache 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ 37233768Sache 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 18-1f */ 37333768Sache 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 20-27 */ 37433768Sache 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 28-2f */ 37533768Sache 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 30-37 */ 37633768Sache 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 38-3f */ 37733768Sache 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 40-47 */ 37833768Sache 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 48-4f */ 37933768Sache 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 50-57 */ 38033768Sache 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 58-5f */ 38133768Sache 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 60-67 */ 38233768Sache 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 68-6f */ 38333768Sache 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 70-77 */ 38433768Sache 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 78-7f */ 38533768Sache 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 80-87 */ 38633768Sache 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 88-8f */ 38733768Sache 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 90-97 */ 38833768Sache 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 98-9f */ 38933768Sache 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a0-a7 */ 39033768Sache 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* a8-af */ 39133768Sache 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b0-b7 */ 39233768Sache 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* b8-bf */ 39333768Sache 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* c0-c7 */ 39433768Sache 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* c8-cf */ 39533768Sache 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* d0-d7 */ 39633768Sache 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* d8-df */ 39733768Sache 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e0-e7 */ 39833768Sache 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* e8-ef */ 39933768Sache 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f0-f7 */ 40033768Sache 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ 40133768Sache}; 40233768Sache 4032893Sdfr/* 4042893Sdfr * DOS filenames are made of 2 parts, the name part and the extension part. 4052893Sdfr * The name part is 8 characters long and the extension part is 3 4062893Sdfr * characters long. They may contain trailing blanks if the name or 4072893Sdfr * extension are not long enough to fill their respective fields. 4082893Sdfr */ 4092893Sdfr 4102893Sdfr/* 4112893Sdfr * Convert a DOS filename to a unix filename. And, return the number of 4122893Sdfr * characters in the resulting unix filename excluding the terminating 4132893Sdfr * null. 4142893Sdfr */ 4152893Sdfrint 416120492Sfjoedos2unixfn(dn, un, lower, pmp) 4172893Sdfr u_char dn[11]; 4182893Sdfr u_char *un; 41933791Sache int lower; 420120492Sfjoe struct msdosfsmount *pmp; 4212893Sdfr{ 422125992Stjr size_t i; 423120492Sfjoe int thislong = 0; 424120492Sfjoe u_int16_t c; 4252893Sdfr 4262893Sdfr /* 42733548Sjkh * If first char of the filename is SLOT_E5 (0x05), then the real 42833548Sjkh * first char of the filename should be 0xe5. But, they couldn't 42933548Sjkh * just have a 0xe5 mean 0xe5 because that is used to mean a freed 43033548Sjkh * directory slot. Another dos quirk. 4312893Sdfr */ 43233548Sjkh if (*dn == SLOT_E5) 433120492Sfjoe *dn = 0xe5; 4342893Sdfr 4352893Sdfr /* 43633548Sjkh * Copy the name portion into the unix filename string. 4372893Sdfr */ 438120492Sfjoe for (i = 8; i > 0 && *dn != ' ';) { 439125992Stjr c = dos2unixchr((const u_char **)&dn, &i, lower, pmp); 440120492Sfjoe if (c & 0xff00) { 441120492Sfjoe *un++ = c >> 8; 442120492Sfjoe thislong++; 443120492Sfjoe } 444120492Sfjoe *un++ = c; 4452893Sdfr thislong++; 4462893Sdfr } 447120492Sfjoe dn += i; 4482893Sdfr 4492893Sdfr /* 4502893Sdfr * Now, if there is an extension then put in a period and copy in 4512893Sdfr * the extension. 4522893Sdfr */ 45333548Sjkh if (*dn != ' ') { 4542893Sdfr *un++ = '.'; 4552893Sdfr thislong++; 456120492Sfjoe for (i = 3; i > 0 && *dn != ' ';) { 457125992Stjr c = dos2unixchr((const u_char **)&dn, &i, lower, pmp); 458120492Sfjoe if (c & 0xff00) { 459120492Sfjoe *un++ = c >> 8; 460120492Sfjoe thislong++; 461120492Sfjoe } 462120492Sfjoe *un++ = c; 4632893Sdfr thislong++; 4642893Sdfr } 4652893Sdfr } 4662893Sdfr *un++ = 0; 4672893Sdfr 46833548Sjkh return (thislong); 4692893Sdfr} 4702893Sdfr 4712893Sdfr/* 47233548Sjkh * Convert a unix filename to a DOS filename according to Win95 rules. 47333548Sjkh * If applicable and gen is not 0, it is inserted into the converted 47433548Sjkh * filename as a generation number. 47533548Sjkh * Returns 47633548Sjkh * 0 if name couldn't be converted 47733548Sjkh * 1 if the converted name is the same as the original 47833548Sjkh * (no long filename entry necessary for Win95) 47933548Sjkh * 2 if conversion was successful 48033548Sjkh * 3 if conversion was successful and generation number was inserted 4812893Sdfr */ 48233548Sjkhint 483120492Sfjoeunix2dosfn(un, dn, unlen, gen, pmp) 48433548Sjkh const u_char *un; 48533548Sjkh u_char dn[12]; 486125992Stjr size_t unlen; 48733548Sjkh u_int gen; 488120492Sfjoe struct msdosfsmount *pmp; 4892893Sdfr{ 490125992Stjr ssize_t i, j; 491125992Stjr int l; 49233548Sjkh int conv = 1; 49333548Sjkh const u_char *cp, *dp, *dp1; 49433548Sjkh u_char gentext[6], *wcp; 495120492Sfjoe u_int16_t c; 4962893Sdfr 4972893Sdfr /* 4982893Sdfr * Fill the dos filename string with blanks. These are DOS's pad 4992893Sdfr * characters. 5002893Sdfr */ 50133548Sjkh for (i = 0; i < 11; i++) 5022893Sdfr dn[i] = ' '; 50333548Sjkh dn[11] = 0; 5042893Sdfr 5052893Sdfr /* 5062893Sdfr * The filenames "." and ".." are handled specially, since they 5072893Sdfr * don't follow dos filename rules. 5082893Sdfr */ 5092893Sdfr if (un[0] == '.' && unlen == 1) { 5102893Sdfr dn[0] = '.'; 51133548Sjkh return gen <= 1; 5122893Sdfr } 5132893Sdfr if (un[0] == '.' && un[1] == '.' && unlen == 2) { 5142893Sdfr dn[0] = '.'; 5152893Sdfr dn[1] = '.'; 51633548Sjkh return gen <= 1; 5172893Sdfr } 5182893Sdfr 5192893Sdfr /* 52033548Sjkh * Filenames with only blanks and dots are not allowed! 5212893Sdfr */ 52233548Sjkh for (cp = un, i = unlen; --i >= 0; cp++) 52333548Sjkh if (*cp != ' ' && *cp != '.') 52433548Sjkh break; 52533548Sjkh if (i < 0) 52633548Sjkh return 0; 52733548Sjkh 52833872Smsmith 52933548Sjkh /* 53033872Smsmith * Filenames with some characters are not allowed! 53133872Smsmith */ 532120492Sfjoe for (cp = un, i = unlen; i > 0;) 533120492Sfjoe if (unix2doschr(&cp, (size_t *)&i, pmp) == 0) 53433872Smsmith return 0; 53533872Smsmith 53633872Smsmith /* 53733548Sjkh * Now find the extension 53833548Sjkh * Note: dot as first char doesn't start extension 53933548Sjkh * and trailing dots and blanks are ignored 540120492Sfjoe * Note(2003/7): It seems recent Windows has 541120492Sfjoe * defferent rule than this code, that Windows 542120492Sfjoe * ignores all dots before extension, and use all 543120492Sfjoe * chars as filename except for dots. 54433548Sjkh */ 54533548Sjkh dp = dp1 = 0; 54633548Sjkh for (cp = un + 1, i = unlen - 1; --i >= 0;) { 54733548Sjkh switch (*cp++) { 54833548Sjkh case '.': 54933548Sjkh if (!dp1) 55033548Sjkh dp1 = cp; 55133548Sjkh break; 55233548Sjkh case ' ': 55333548Sjkh break; 55433548Sjkh default: 55533548Sjkh if (dp1) 55633548Sjkh dp = dp1; 55733548Sjkh dp1 = 0; 55833548Sjkh break; 55933548Sjkh } 5602893Sdfr } 5612893Sdfr 5622893Sdfr /* 563120492Sfjoe * Now convert it (this part is for extension) 5642893Sdfr */ 56533548Sjkh if (dp) { 56633548Sjkh if (dp1) 56733548Sjkh l = dp1 - dp; 56833548Sjkh else 56933548Sjkh l = unlen - (dp - un); 570120492Sfjoe for (cp = dp, i = l, j = 8; i > 0 && j < 11; j++) { 571120492Sfjoe c = unix2doschr(&cp, (size_t *)&i, pmp); 572120492Sfjoe if (c & 0xff00) { 573120492Sfjoe dn[j] = c >> 8; 574120492Sfjoe if (++j < 11) { 575120492Sfjoe dn[j] = c; 576120492Sfjoe continue; 577120492Sfjoe } else { 578120492Sfjoe conv = 3; 579120492Sfjoe dn[j-1] = ' '; 580120492Sfjoe break; 581120492Sfjoe } 582120492Sfjoe } else { 583120492Sfjoe dn[j] = c; 584120492Sfjoe } 585120492Sfjoe if (*(cp - 1) != dn[j] && conv != 3) 58633548Sjkh conv = 2; 58733872Smsmith if (dn[j] == 1) { 58833548Sjkh conv = 3; 58933872Smsmith dn[j] = '_'; 59033872Smsmith } 59133872Smsmith if (dn[j] == 2) { 59233872Smsmith conv = 3; 59333548Sjkh dn[j--] = ' '; 59433548Sjkh } 59533548Sjkh } 596120492Sfjoe if (i > 0) 59733548Sjkh conv = 3; 59833548Sjkh dp--; 59933548Sjkh } else { 60033548Sjkh for (dp = cp; *--dp == ' ' || *dp == '.';); 60133548Sjkh dp++; 60233548Sjkh } 60333548Sjkh 60433548Sjkh /* 60533548Sjkh * Now convert the rest of the name 60633548Sjkh */ 607120492Sfjoe for (i = dp - un, j = 0; un < dp && j < 8; j++) { 608125992Stjr c = unix2doschr(&un, &i, pmp); 609120492Sfjoe if (c & 0xff00) { 610120492Sfjoe dn[j] = c >> 8; 611120492Sfjoe if (++j < 8) { 612120492Sfjoe dn[j] = c; 613120492Sfjoe continue; 614120492Sfjoe } else { 615120492Sfjoe conv = 3; 616120492Sfjoe dn[j-1] = ' '; 617120492Sfjoe break; 618120492Sfjoe } 619120492Sfjoe } else { 620120492Sfjoe dn[j] = c; 621120492Sfjoe } 622120492Sfjoe if (*(un - 1) != dn[j] && conv != 3) 62333548Sjkh conv = 2; 62433872Smsmith if (dn[j] == 1) { 62533548Sjkh conv = 3; 62633872Smsmith dn[j] = '_'; 62733872Smsmith } 62833872Smsmith if (dn[j] == 2) { 62933872Smsmith conv = 3; 63033548Sjkh dn[j--] = ' '; 63133548Sjkh } 63233548Sjkh } 63333548Sjkh if (un < dp) 63433548Sjkh conv = 3; 63533548Sjkh /* 63633548Sjkh * If we didn't have any chars in filename, 63733548Sjkh * generate a default 63833548Sjkh */ 63933548Sjkh if (!j) 64033548Sjkh dn[0] = '_'; 64133548Sjkh 64233548Sjkh /* 64333548Sjkh * If there wasn't any char dropped, 64433548Sjkh * there is no place for generation numbers 6452893Sdfr */ 64633548Sjkh if (conv != 3) { 64733548Sjkh if (gen > 1) 648120492Sfjoe conv = 0; 649120492Sfjoe goto done; 6502893Sdfr } 6512893Sdfr 6522893Sdfr /* 65333548Sjkh * Now insert the generation number into the filename part 6542893Sdfr */ 65536133Sdt if (gen == 0) 656120492Sfjoe goto done; 65733548Sjkh for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) 65833548Sjkh *--wcp = gen % 10 + '0'; 659120492Sfjoe if (gen) { 660120492Sfjoe conv = 0; 661120492Sfjoe goto done; 662120492Sfjoe } 66333548Sjkh for (i = 8; dn[--i] == ' ';); 66433548Sjkh i++; 66533548Sjkh if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) 66633548Sjkh i = 8 - (gentext + sizeof(gentext) - wcp + 1); 667120492Sfjoe /* 668120492Sfjoe * Correct posision to where insert the generation number 669120492Sfjoe */ 670120492Sfjoe cp = dn; 671120492Sfjoe i -= mbsadjpos((const char**)&cp, i, unlen, 1, pmp->pm_flags, pmp->pm_d2u); 672120492Sfjoe 67333548Sjkh dn[i++] = '~'; 67433548Sjkh while (wcp < gentext + sizeof(gentext)) 67533548Sjkh dn[i++] = *wcp++; 676120492Sfjoe 677120492Sfjoe /* 678120492Sfjoe * Tail of the filename should be space 679120492Sfjoe */ 680120492Sfjoe while (i < 8) 681120492Sfjoe dn[i++] = ' '; 682120492Sfjoe conv = 3; 683120492Sfjoe 684120492Sfjoedone: 685120492Sfjoe /* 686120492Sfjoe * The first character cannot be E5, 687120492Sfjoe * because that means a deleted entry 688120492Sfjoe */ 689120492Sfjoe if (dn[0] == 0xe5) 690120492Sfjoe dn[0] = SLOT_E5; 691120492Sfjoe 692120492Sfjoe return conv; 69333548Sjkh} 69433548Sjkh 69533548Sjkh/* 69633548Sjkh * Create a Win95 long name directory entry 69733548Sjkh * Note: assumes that the filename is valid, 69833548Sjkh * i.e. doesn't consist solely of blanks and dots 69933548Sjkh */ 70033548Sjkhint 701120492Sfjoeunix2winfn(un, unlen, wep, cnt, chksum, pmp) 70233548Sjkh const u_char *un; 703125992Stjr size_t unlen; 70433548Sjkh struct winentry *wep; 70533548Sjkh int cnt; 70633548Sjkh int chksum; 707120492Sfjoe struct msdosfsmount *pmp; 70833548Sjkh{ 70933548Sjkh u_int8_t *wcp; 710120492Sfjoe int i, end; 71133747Sache u_int16_t code; 71233548Sjkh 71333548Sjkh /* 71433548Sjkh * Drop trailing blanks and dots 71533548Sjkh */ 716120492Sfjoe unlen = winLenFixup(un, unlen); 71733548Sjkh 718120492Sfjoe /* 719120492Sfjoe * Cut *un for this slot 720120492Sfjoe */ 721120492Sfjoe unlen = mbsadjpos((const char **)&un, unlen, (cnt - 1) * WIN_CHARS, 2, 722120492Sfjoe pmp->pm_flags, pmp->pm_u2w); 72333548Sjkh 72433548Sjkh /* 72533548Sjkh * Initialize winentry to some useful default 72633548Sjkh */ 72733548Sjkh for (wcp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); 72833548Sjkh wep->weCnt = cnt; 72933548Sjkh wep->weAttributes = ATTR_WIN95; 73033548Sjkh wep->weReserved1 = 0; 73133548Sjkh wep->weChksum = chksum; 73233548Sjkh wep->weReserved2 = 0; 73333548Sjkh 73433548Sjkh /* 73533548Sjkh * Now convert the filename parts 73633548Sjkh */ 737120492Sfjoe end = 0; 738120492Sfjoe for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0 && !end;) { 739125992Stjr code = unix2winchr(&un, &unlen, 0, pmp); 740120492Sfjoe *wcp++ = code; 741120492Sfjoe *wcp++ = code >> 8; 742120492Sfjoe if (!code) 743120492Sfjoe end = WIN_LAST; 7442893Sdfr } 745120492Sfjoe for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0 && !end;) { 746125992Stjr code = unix2winchr(&un, &unlen, 0, pmp); 747120492Sfjoe *wcp++ = code; 748120492Sfjoe *wcp++ = code >> 8; 749120492Sfjoe if (!code) 750120492Sfjoe end = WIN_LAST; 75133548Sjkh } 752120492Sfjoe for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0 && !end;) { 753125992Stjr code = unix2winchr(&un, &unlen, 0, pmp); 754120492Sfjoe *wcp++ = code; 755120492Sfjoe *wcp++ = code >> 8; 756120492Sfjoe if (!code) 757120492Sfjoe end = WIN_LAST; 75833548Sjkh } 759120492Sfjoe if (*un == '\0') 760120492Sfjoe end = WIN_LAST; 761120492Sfjoe wep->weCnt |= end; 762120492Sfjoe return !end; 7632893Sdfr} 7642893Sdfr 7652893Sdfr/* 76633548Sjkh * Compare our filename to the one in the Win95 entry 76733548Sjkh * Returns the checksum or -1 if no match 7682893Sdfr */ 76933548Sjkhint 770120492SfjoewinChkName(un, unlen, chksum, pmp) 77133548Sjkh const u_char *un; 772125992Stjr size_t unlen; 77333548Sjkh int chksum; 774120492Sfjoe struct msdosfsmount *pmp; 77533548Sjkh{ 776125992Stjr size_t len; 777120492Sfjoe u_int16_t c1, c2; 778120492Sfjoe u_char *np; 779120492Sfjoe struct dirent dirbuf; 78033548Sjkh 78133548Sjkh /* 782120492Sfjoe * We alread have winentry in mbnambuf 78333548Sjkh */ 784120492Sfjoe if (!mbnambuf_flush(&dirbuf) || !dirbuf.d_namlen) 78533548Sjkh return -1; 78633548Sjkh 787120492Sfjoe#ifdef MSDOSFS_DEBUG 788120492Sfjoe printf("winChkName(): un=%s:%d,d_name=%s:%d\n", un, unlen, 789120492Sfjoe dirbuf.d_name, 790120492Sfjoe dirbuf.d_namlen); 791120492Sfjoe#endif 792106110Ssemenu 793106110Ssemenu /* 794120492Sfjoe * Compare the name parts 795106110Ssemenu */ 796120492Sfjoe len = dirbuf.d_namlen; 797120492Sfjoe if (unlen != len) 798120492Sfjoe return -2; 79933548Sjkh 800120492Sfjoe for (np = dirbuf.d_name; unlen > 0 && len > 0;) { 801120492Sfjoe /* 802123293Sfjoe * Comparison must be case insensitive, because FAT disallows 803123293Sfjoe * to look up or create files in case sensitive even when 804123293Sfjoe * it's a long file name. 805120492Sfjoe */ 806125992Stjr c1 = unix2winchr((const u_char **)&np, &len, LCASE_BASE, pmp); 807125992Stjr c2 = unix2winchr(&un, &unlen, LCASE_BASE, pmp); 80833760Sache if (c1 != c2) 809120492Sfjoe return -2; 81033548Sjkh } 81133548Sjkh return chksum; 81233548Sjkh} 81333548Sjkh 81433548Sjkh/* 81533548Sjkh * Convert Win95 filename to dirbuf. 81633548Sjkh * Returns the checksum or -1 if impossible 81733548Sjkh */ 81833548Sjkhint 819120492Sfjoewin2unixfn(wep, chksum, pmp) 82033548Sjkh struct winentry *wep; 82133548Sjkh int chksum; 822120492Sfjoe struct msdosfsmount *pmp; 82333548Sjkh{ 82433548Sjkh u_int8_t *cp; 825120492Sfjoe u_int8_t *np, name[WIN_CHARS * 2 + 1]; 82633744Sache u_int16_t code; 82733548Sjkh int i; 82833548Sjkh 82933548Sjkh if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) 83033548Sjkh || !(wep->weCnt&WIN_CNT)) 83133548Sjkh return -1; 83233548Sjkh 83333548Sjkh /* 83433548Sjkh * First compare checksums 83533548Sjkh */ 83633548Sjkh if (wep->weCnt&WIN_LAST) { 83733548Sjkh chksum = wep->weChksum; 83833548Sjkh } else if (chksum != wep->weChksum) 83933548Sjkh chksum = -1; 84033548Sjkh if (chksum == -1) 84133548Sjkh return -1; 84233548Sjkh 84333548Sjkh /* 84433548Sjkh * Convert the name parts 84533548Sjkh */ 846120492Sfjoe np = name; 84733548Sjkh for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { 84833744Sache code = (cp[1] << 8) | cp[0]; 84933744Sache switch (code) { 85033548Sjkh case 0: 85133744Sache *np = '\0'; 852120492Sfjoe mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 1); 85333548Sjkh return chksum; 85433548Sjkh case '/': 85533744Sache *np = '\0'; 85633548Sjkh return -1; 85733744Sache default: 858120492Sfjoe code = win2unixchr(code, pmp); 859120492Sfjoe if (code & 0xff00) 860120492Sfjoe *np++ = code >> 8; 86133744Sache *np++ = code; 86233744Sache break; 86333548Sjkh } 86433744Sache cp += 2; 86533548Sjkh } 86633548Sjkh for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { 86733744Sache code = (cp[1] << 8) | cp[0]; 86833744Sache switch (code) { 86933548Sjkh case 0: 87033744Sache *np = '\0'; 871120492Sfjoe mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 1); 87233548Sjkh return chksum; 87333548Sjkh case '/': 87433744Sache *np = '\0'; 87533548Sjkh return -1; 87633744Sache default: 877120492Sfjoe code = win2unixchr(code, pmp); 878120492Sfjoe if (code & 0xff00) 879120492Sfjoe *np++ = code >> 8; 88033744Sache *np++ = code; 88133744Sache break; 88233548Sjkh } 88333744Sache cp += 2; 88433548Sjkh } 88533548Sjkh for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { 88633744Sache code = (cp[1] << 8) | cp[0]; 88733744Sache switch (code) { 88833548Sjkh case 0: 88933744Sache *np = '\0'; 890120492Sfjoe mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 1); 89133548Sjkh return chksum; 89233548Sjkh case '/': 89333744Sache *np = '\0'; 89433548Sjkh return -1; 89533744Sache default: 896120492Sfjoe code = win2unixchr(code, pmp); 897120492Sfjoe if (code & 0xff00) 898120492Sfjoe *np++ = code >> 8; 89933744Sache *np++ = code; 90033744Sache break; 90133548Sjkh } 90233744Sache cp += 2; 90333548Sjkh } 904120492Sfjoe *np = '\0'; 905120492Sfjoe mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 1); 90633548Sjkh return chksum; 90733548Sjkh} 90833548Sjkh 90933548Sjkh/* 910141497Snjl * Compute the unrolled checksum of a DOS filename for Win95 LFN use. 91133548Sjkh */ 91233548Sjkhu_int8_t 91333548SjkhwinChksum(name) 91433548Sjkh u_int8_t *name; 91533548Sjkh{ 91633548Sjkh u_int8_t s; 91733548Sjkh 918141497Snjl s = name[0]; 919141497Snjl s = ((s << 7) | (s >> 1)) + name[1]; 920141497Snjl s = ((s << 7) | (s >> 1)) + name[2]; 921141497Snjl s = ((s << 7) | (s >> 1)) + name[3]; 922141497Snjl s = ((s << 7) | (s >> 1)) + name[4]; 923141497Snjl s = ((s << 7) | (s >> 1)) + name[5]; 924141497Snjl s = ((s << 7) | (s >> 1)) + name[6]; 925141497Snjl s = ((s << 7) | (s >> 1)) + name[7]; 926141497Snjl s = ((s << 7) | (s >> 1)) + name[8]; 927141497Snjl s = ((s << 7) | (s >> 1)) + name[9]; 928141497Snjl s = ((s << 7) | (s >> 1)) + name[10]; 929141497Snjl 930141497Snjl return (s); 93133548Sjkh} 93233548Sjkh 93333548Sjkh/* 93433548Sjkh * Determine the number of slots necessary for Win95 names 93533548Sjkh */ 93633548Sjkhint 937120492SfjoewinSlotCnt(un, unlen, pmp) 93833548Sjkh const u_char *un; 939125992Stjr size_t unlen; 940120492Sfjoe struct msdosfsmount *pmp; 94133548Sjkh{ 942120492Sfjoe size_t wlen; 943120492Sfjoe char wn[WIN_MAXLEN * 2 + 1], *wnp; 944120492Sfjoe 94533848Smsmith unlen = winLenFixup(un, unlen); 946120492Sfjoe 947120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 948120492Sfjoe wlen = WIN_MAXLEN * 2; 949120492Sfjoe wnp = wn; 950125992Stjr msdosfs_iconv->conv(pmp->pm_u2w, (const char **)&un, &unlen, &wnp, &wlen); 951120492Sfjoe if (unlen > 0) 952120492Sfjoe return 0; 953120492Sfjoe return howmany(WIN_MAXLEN - wlen/2, WIN_CHARS); 954120492Sfjoe } 955120492Sfjoe 95633548Sjkh if (unlen > WIN_MAXLEN) 95733548Sjkh return 0; 95833548Sjkh return howmany(unlen, WIN_CHARS); 95933548Sjkh} 96033848Smsmith 96133848Smsmith/* 96233848Smsmith * Determine the number of bytes neccesary for Win95 names 96333848Smsmith */ 964125992Stjrsize_t 96533848SmsmithwinLenFixup(un, unlen) 96633848Smsmith const u_char* un; 967125992Stjr size_t unlen; 96833848Smsmith{ 96933848Smsmith for (un += unlen; unlen > 0; unlen--) 97033848Smsmith if (*--un != ' ' && *un != '.') 97133848Smsmith break; 97233848Smsmith return unlen; 97333848Smsmith} 974120492Sfjoe 975120492Sfjoe/* 976120492Sfjoe * Store an area with multi byte string instr, and reterns left 977120492Sfjoe * byte of instr and moves pointer forward. The area's size is 978120492Sfjoe * inlen or outlen. 979120492Sfjoe */ 980120492Sfjoestatic int 981125992Stjrmbsadjpos(const char **instr, size_t inlen, size_t outlen, int weight, int flag, void *handle) 982120492Sfjoe{ 983120492Sfjoe char *outp, outstr[outlen * weight + 1]; 984120492Sfjoe 985120492Sfjoe if (flag & MSDOSFSMNT_KICONV && msdosfs_iconv) { 986120492Sfjoe outp = outstr; 987120492Sfjoe outlen *= weight; 988125992Stjr msdosfs_iconv->conv(handle, instr, &inlen, &outp, &outlen); 989120492Sfjoe return (inlen); 990120492Sfjoe } 991120492Sfjoe 992120492Sfjoe (*instr) += min(inlen, outlen); 993120492Sfjoe return (inlen - min(inlen, outlen)); 994120492Sfjoe} 995120492Sfjoe 996120492Sfjoe/* 997120492Sfjoe * Convert DOS char to Local char 998120492Sfjoe */ 999120492Sfjoestatic u_int16_t 1000120492Sfjoedos2unixchr(const u_char **instr, size_t *ilen, int lower, struct msdosfsmount *pmp) 1001120492Sfjoe{ 1002120492Sfjoe u_char c; 1003120492Sfjoe char *outp, outbuf[3]; 1004120492Sfjoe u_int16_t wc; 1005120492Sfjoe size_t len, olen; 1006120492Sfjoe 1007120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 1008120492Sfjoe olen = len = 2; 1009120492Sfjoe outp = outbuf; 1010120492Sfjoe 1011120492Sfjoe if (lower & (LCASE_BASE | LCASE_EXT)) 1012120492Sfjoe msdosfs_iconv->convchr_case(pmp->pm_d2u, (const char **)instr, 1013120492Sfjoe ilen, &outp, &olen, KICONV_LOWER); 1014120492Sfjoe else 1015120492Sfjoe msdosfs_iconv->convchr(pmp->pm_d2u, (const char **)instr, 1016120492Sfjoe ilen, &outp, &olen); 1017120492Sfjoe len -= olen; 1018120492Sfjoe 1019120492Sfjoe /* 1020120492Sfjoe * return '?' if failed to convert 1021120492Sfjoe */ 1022120492Sfjoe if (len == 0) { 1023120492Sfjoe (*ilen)--; 1024120492Sfjoe (*instr)++; 1025120492Sfjoe return ('?'); 1026120492Sfjoe } 1027120492Sfjoe 1028120492Sfjoe wc = 0; 1029120492Sfjoe while(len--) 1030120492Sfjoe wc |= (*(outp - len - 1) & 0xff) << (len << 3); 1031120492Sfjoe return (wc); 1032120492Sfjoe } 1033120492Sfjoe 1034120492Sfjoe (*ilen)--; 1035120492Sfjoe c = *(*instr)++; 1036120492Sfjoe c = dos2unix[c]; 1037120492Sfjoe if (lower & (LCASE_BASE | LCASE_EXT)) 1038120492Sfjoe c = u2l[c]; 1039120492Sfjoe return ((u_int16_t)c); 1040120492Sfjoe} 1041120492Sfjoe 1042120492Sfjoe/* 1043120492Sfjoe * Convert Local char to DOS char 1044120492Sfjoe */ 1045120492Sfjoestatic u_int16_t 1046120492Sfjoeunix2doschr(const u_char **instr, size_t *ilen, struct msdosfsmount *pmp) 1047120492Sfjoe{ 1048120492Sfjoe u_char c; 1049120492Sfjoe char *up, *outp, unicode[3], outbuf[3]; 1050120492Sfjoe u_int16_t wc; 1051120492Sfjoe size_t len, ulen, olen; 1052120492Sfjoe 1053120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 1054120492Sfjoe /* 1055120492Sfjoe * to hide an invisible character, using a unicode filter 1056120492Sfjoe */ 1057120492Sfjoe ulen = 2; 1058120492Sfjoe len = *ilen; 1059120492Sfjoe up = unicode; 1060120492Sfjoe msdosfs_iconv->convchr(pmp->pm_u2w, (const char **)instr, 1061120492Sfjoe ilen, &up, &ulen); 1062120492Sfjoe 1063120492Sfjoe /* 1064120492Sfjoe * cannot be converted 1065120492Sfjoe */ 1066120492Sfjoe if (ulen == 2) { 1067120492Sfjoe (*ilen)--; 1068120492Sfjoe (*instr)++; 1069120492Sfjoe return (0); 1070120492Sfjoe } 1071120492Sfjoe 1072120492Sfjoe /* 1073120492Sfjoe * return magic number for ascii char 1074120492Sfjoe */ 1075120492Sfjoe if ((len - *ilen) == 1) { 1076120492Sfjoe c = *(*instr -1); 1077120492Sfjoe if (! (c & 0x80)) { 1078120492Sfjoe c = unix2dos[c]; 1079120492Sfjoe if (c <= 2) 1080120492Sfjoe return (c); 1081120492Sfjoe } 1082120492Sfjoe } 1083120492Sfjoe 1084120492Sfjoe /* 1085120492Sfjoe * now convert using libiconv 1086120492Sfjoe */ 1087120492Sfjoe *instr -= len - *ilen; 1088120492Sfjoe *ilen = (int)len; 1089120492Sfjoe 1090120492Sfjoe olen = len = 2; 1091120492Sfjoe outp = outbuf; 1092120492Sfjoe msdosfs_iconv->convchr_case(pmp->pm_u2d, (const char **)instr, 1093120492Sfjoe ilen, &outp, &olen, KICONV_FROM_UPPER); 1094120492Sfjoe len -= olen; 1095120492Sfjoe wc = 0; 1096120492Sfjoe while(len--) 1097120492Sfjoe wc |= (*(outp - len - 1) & 0xff) << (len << 3); 1098120492Sfjoe return (wc); 1099120492Sfjoe } 1100120492Sfjoe 1101120492Sfjoe (*ilen)--; 1102120492Sfjoe c = *(*instr)++; 1103120492Sfjoe c = l2u[c]; 1104120492Sfjoe c = unix2dos[c]; 1105120492Sfjoe return ((u_int16_t)c); 1106120492Sfjoe} 1107120492Sfjoe 1108120492Sfjoe/* 1109120492Sfjoe * Convert Windows char to Local char 1110120492Sfjoe */ 1111120492Sfjoestatic u_int16_t 1112120492Sfjoewin2unixchr(u_int16_t wc, struct msdosfsmount *pmp) 1113120492Sfjoe{ 1114120492Sfjoe u_char *inp, *outp, inbuf[3], outbuf[3]; 1115120492Sfjoe size_t ilen, olen, len; 1116120492Sfjoe 1117120492Sfjoe if (wc == 0) 1118120492Sfjoe return (0); 1119120492Sfjoe 1120120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 1121120492Sfjoe inbuf[0] = (u_char)(wc>>8); 1122120492Sfjoe inbuf[1] = (u_char)wc; 1123120492Sfjoe inbuf[2] = '\0'; 1124120492Sfjoe 1125120492Sfjoe ilen = olen = len = 2; 1126120492Sfjoe inp = inbuf; 1127120492Sfjoe outp = outbuf; 1128120492Sfjoe msdosfs_iconv->convchr(pmp->pm_w2u, (const char **)&inp, &ilen, 1129120492Sfjoe (char **)&outp, &olen); 1130120492Sfjoe len -= olen; 1131120492Sfjoe 1132120492Sfjoe /* 1133120492Sfjoe * return '?' if failed to convert 1134120492Sfjoe */ 1135120492Sfjoe if (len == 0) { 1136120492Sfjoe wc = '?'; 1137120492Sfjoe return (wc); 1138120492Sfjoe } 1139120492Sfjoe 1140120492Sfjoe wc = 0; 1141120492Sfjoe while(len--) 1142120492Sfjoe wc |= (*(outp - len - 1) & 0xff) << (len << 3); 1143120492Sfjoe return (wc); 1144120492Sfjoe } 1145120492Sfjoe 1146120492Sfjoe if (wc & 0xff00) 1147120492Sfjoe wc = '?'; 1148120492Sfjoe 1149120492Sfjoe return (wc); 1150120492Sfjoe} 1151120492Sfjoe 1152120492Sfjoe/* 1153120492Sfjoe * Convert Local char to Windows char 1154120492Sfjoe */ 1155120492Sfjoestatic u_int16_t 1156120492Sfjoeunix2winchr(const u_char **instr, size_t *ilen, int lower, struct msdosfsmount *pmp) 1157120492Sfjoe{ 1158120492Sfjoe u_char *outp, outbuf[3]; 1159120492Sfjoe u_int16_t wc; 1160120492Sfjoe size_t olen; 1161120492Sfjoe 1162120492Sfjoe if (*ilen == 0) 1163120492Sfjoe return (0); 1164120492Sfjoe 1165120492Sfjoe if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { 1166120492Sfjoe outp = outbuf; 1167120492Sfjoe olen = 2; 1168120492Sfjoe if (lower & (LCASE_BASE | LCASE_EXT)) 1169120492Sfjoe msdosfs_iconv->convchr_case(pmp->pm_u2w, (const char **)instr, 1170120492Sfjoe ilen, (char **)&outp, &olen, 1171120492Sfjoe KICONV_FROM_LOWER); 1172120492Sfjoe else 1173120492Sfjoe msdosfs_iconv->convchr(pmp->pm_u2w, (const char **)instr, 1174120492Sfjoe ilen, (char **)&outp, &olen); 1175120492Sfjoe 1176120492Sfjoe /* 1177120492Sfjoe * return '0' if end of filename 1178120492Sfjoe */ 1179120492Sfjoe if (olen == 2) 1180120492Sfjoe return (0); 1181120492Sfjoe 1182120492Sfjoe wc = (outbuf[0]<<8) | outbuf[1]; 1183120492Sfjoe 1184120492Sfjoe return (wc); 1185120492Sfjoe } 1186120492Sfjoe 1187120492Sfjoe (*ilen)--; 1188120492Sfjoe wc = (*instr)[0]; 1189120492Sfjoe if (lower & (LCASE_BASE | LCASE_EXT)) 1190120492Sfjoe wc = u2l[wc]; 1191120492Sfjoe (*instr)++; 1192120492Sfjoe return (wc); 1193120492Sfjoe} 1194120492Sfjoe 1195120492Sfjoe/* 1196143435Snjl * Initialize the temporary concatenation buffer (once) and mark it as 1197143435Snjl * empty on subsequent calls. 1198120492Sfjoe */ 1199120492Sfjoevoid 1200120492Sfjoembnambuf_init(void) 1201120492Sfjoe{ 1202120492Sfjoe 1203143435Snjl if (nambuf_ptr == NULL) { 1204143435Snjl nambuf_ptr = malloc(MAXNAMLEN + 1, M_MSDOSFSMNT, M_WAITOK); 1205143435Snjl nambuf_ptr[MAXNAMLEN] = '\0'; 1206120492Sfjoe } 1207143435Snjl nambuf_len = 0; 1208143435Snjl nambuf_max_id = -1; 1209120492Sfjoe} 1210120492Sfjoe 1211120492Sfjoe/* 1212143435Snjl * Fill out our concatenation buffer with the given substring, at the 1213143435Snjl * offset specified by its id. For the last substring (i.e., the one with 1214143435Snjl * the maximum id), use it to calculate the length of the entire string. 1215143435Snjl * Since this function is usually called with ids in descending order, this 1216143435Snjl * reduces the number of times we need to call strlen() since we know that 1217143435Snjl * all previous substrings are exactly WIN_CHARS in length. 1218120492Sfjoe */ 1219120492Sfjoevoid 1220120492Sfjoembnambuf_write(char *name, int id) 1221120492Sfjoe{ 1222143435Snjl size_t count; 1223143435Snjl 1224143435Snjl count = WIN_CHARS; 1225143435Snjl if (id > nambuf_max_id) { 1226143435Snjl count = strlen(name); 1227143435Snjl nambuf_len = id * WIN_CHARS + count; 1228143435Snjl if (nambuf_len > MAXNAMLEN) { 1229143435Snjl printf("msdosfs: file name %d too long\n", nambuf_len); 1230143435Snjl return; 1231143435Snjl } 1232143435Snjl nambuf_max_id = id; 1233120492Sfjoe } 1234143435Snjl memcpy(nambuf_ptr + (id * WIN_CHARS), name, count); 1235120492Sfjoe} 1236120492Sfjoe 1237120492Sfjoe/* 1238143435Snjl * Take the completed string and use it to setup the struct dirent. 1239143435Snjl * Be sure to always nul-terminate the d_name and then copy the string 1240143435Snjl * from our buffer. Note that this function assumes the full string has 1241143435Snjl * been reassembled in the buffer. If it's called before all substrings 1242143435Snjl * have been written via mbnambuf_write(), the result will be incorrect. 1243120492Sfjoe */ 1244120492Sfjoechar * 1245120492Sfjoembnambuf_flush(struct dirent *dp) 1246120492Sfjoe{ 1247120492Sfjoe 1248143435Snjl if (nambuf_len > sizeof(dp->d_name) - 1) { 1249143435Snjl mbnambuf_init(); 1250143435Snjl return (NULL); 1251120492Sfjoe } 1252143435Snjl nambuf_ptr[nambuf_len] = '\0'; 1253143435Snjl memcpy(dp->d_name, nambuf_ptr, nambuf_len); 1254143435Snjl dp->d_namlen = nambuf_len; 1255143435Snjl 1256120492Sfjoe mbnambuf_init(); 1257120492Sfjoe return (dp->d_name); 1258120492Sfjoe} 1259