From: Alexander Polakov Date: Fri, 5 Jun 2009 08:10:38 +0000 (+0400) Subject: Stage 2/5: Change msdosfs to use libiconv in case. X-Git-Url: https://gitweb.dragonflybsd.org/~polachok/dragonfly.git/commitdiff_plain/3435d5778808adf9c89a4f9f0e34010b4dc8888f Stage 2/5: Change msdosfs to use libiconv in case. Code mostly from FreeBSD, ported by me. Still works with old userspace (if mounted without conversion). --- diff --git a/sys/vfs/msdosfs/Makefile b/sys/vfs/msdosfs/Makefile index cb20645bef..7a244e0f58 100644 --- a/sys/vfs/msdosfs/Makefile +++ b/sys/vfs/msdosfs/Makefile @@ -4,5 +4,6 @@ KMOD= msdos SRCS= msdosfs_conv.c msdosfs_denode.c msdosfs_fat.c msdosfs_lookup.c \ msdosfs_vfsops.c msdosfs_vnops.c opt_msdosfs.h +EXPORT_SYMS= msdos_iconv .include diff --git a/sys/vfs/msdosfs/direntry.h b/sys/vfs/msdosfs/direntry.h index c26b88bddf..c57f127b53 100644 --- a/sys/vfs/msdosfs/direntry.h +++ b/sys/vfs/msdosfs/direntry.h @@ -129,16 +129,28 @@ struct winentry { #define DD_YEAR_SHIFT 9 #ifdef _KERNEL +struct mbnambuf { + size_t nb_len; + int nb_last_id; + char nb_buf[WIN_MAXLEN + 1]; +}; struct dirent; +struct msdosfsmount; + +char *mbnambuf_flush(struct mbnambuf *nbp, char *d_name, u_int16_t *d_namlen); +void mbnambuf_init(struct mbnambuf *nbp); +void mbnambuf_write(struct mbnambuf *nbp, char *name, int id); void unix2dostime (struct timespec *tsp, u_int16_t *ddp, u_int16_t *dtp, u_int8_t *dhp); void dos2unixtime (u_int dd, u_int dt, u_int dh, struct timespec *tsp); -int dos2unixfn (u_char dn[11], u_char *un, int lower, int d2u_loaded, u_int8_t *d2u, int ul_loaded, u_int8_t *ul); -int unix2dosfn (const u_char *un, u_char dn[12], int unlen, u_int gen, int u2d_loaded, u_int8_t *u2d, int lu_loaded, u_int8_t *lu); -int unix2winfn (const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum, int table_loaded, u_int16_t *u2w); -int winChkName (const u_char *un, int unlen, struct winentry *wep, int chksum, int u2w_loaded, u_int16_t *u2w, int ul_loaded, u_int8_t *ul); -int win2unixfn (struct winentry *wep, char *d_name, uint16_t *d_namlen, int chksum, int table_loaded, u_int16_t *u2w); +int dos2unixfn (u_char dn[11], u_char *un, int lower, struct msdosfsmount *pmp); +int unix2dosfn (const u_char *un, u_char dn[12], int unlen, u_int gen, struct msdosfsmount *pmp); +int unix2winfn (const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum, struct msdosfsmount *pmp); +int winChkName(struct mbnambuf *nbp, const u_char *un, size_t unlen, + int chksum, struct msdosfsmount *pmp); +int win2unixfn(struct mbnambuf *nbp, struct winentry *wep, int chksum, + struct msdosfsmount *pmp); u_int8_t winChksum (u_int8_t *name); -int winSlotCnt (const u_char *un, int unlen); +int winSlotCnt (const u_char *un, int unlen, struct msdosfsmount *pmp); int winLenFixup (const u_char *un, int unlen); #endif /* _KERNEL */ diff --git a/sys/vfs/msdosfs/msdosfs_conv.c b/sys/vfs/msdosfs/msdosfs_conv.c index 72c9915d2b..f66efceb48 100644 --- a/sys/vfs/msdosfs/msdosfs_conv.c +++ b/sys/vfs/msdosfs/msdosfs_conv.c @@ -56,14 +56,19 @@ #include #include /* defines tz */ #include +#include #include #include - +#include /* * MSDOSFS include files. */ +#include "bpb.h" +#include "msdosfsmount.h" #include "direntry.h" +extern struct iconv_functions *msdos_iconv; + /* * Total number of days that have passed for each month in a regular year. */ @@ -167,6 +172,74 @@ unix2dostime(struct timespec *tsp, u_int16_t *ddp, u_int16_t *dtp, u_int8_t *dhp static u_short lastdosdate; static u_long lastseconds; +/* + * Initialize the temporary concatenation buffer. + */ +void +mbnambuf_init(struct mbnambuf *nbp) +{ + nbp->nb_len = 0; + nbp->nb_last_id = -1; + nbp->nb_buf[sizeof(nbp->nb_buf) - 1] = '\0'; +} +/* + * Fill out our concatenation buffer with the given substring, at the offset + * specified by its id. Since this function must be called with ids in + * descending order, we take advantage of the fact that ASCII substrings are + * exactly WIN_CHARS in length. For non-ASCII substrings, we shift all + * previous (i.e. higher id) substrings upwards to make room for this one. + * This only penalizes portions of substrings that contain more than + * WIN_CHARS bytes when they are first encountered. + */ +void +mbnambuf_write(struct mbnambuf *nbp, char *name, int id) +{ + char *slot; + size_t count, newlen; + if (nbp->nb_len != 0 && id != nbp->nb_last_id - 1) { + kprintf("msdosfs: non-decreasing id: id %d, last id %d\n", + id, nbp->nb_last_id); + return; + } + /* Will store this substring in a WIN_CHARS-aligned slot. */ + slot = &nbp->nb_buf[id * WIN_CHARS]; + count = strlen(name); + newlen = nbp->nb_len + count; + if (newlen > WIN_MAXLEN || newlen > 127) { + kprintf("msdosfs: file name length %zu too large\n", newlen); + return; + } + /* Shift suffix upwards by the amount length exceeds WIN_CHARS. */ + if (count > WIN_CHARS && nbp->nb_len != 0) + bcopy(slot + WIN_CHARS, slot + count, nbp->nb_len); + /* Copy in the substring to its slot and update length so far. */ + bcopy(name, slot, count); + nbp->nb_len = newlen; + nbp->nb_last_id = id; +} +/* + * Take the completed string and use it to setup the struct dirent. + * Be sure to always nul-terminate the d_name and then copy the string + * from our buffer. Note that this function assumes the full string has + * been reassembled in the buffer. If it's called before all substrings + * have been written via mbnambuf_write(), the result will be incorrect. + */ +char * +mbnambuf_flush(struct mbnambuf *nbp, char *d_name, u_int16_t *d_namlen) +{ +#if 0 + if (nbp->nb_len > 127) { + mbnambuf_init(nbp); + return (NULL); + } +#endif + bcopy(&nbp->nb_buf[0], d_name, nbp->nb_len); + d_name[nbp->nb_len] = '\0'; + *d_namlen = nbp->nb_len; + mbnambuf_init(nbp); + return (d_name); +} + /* * Convert from dos' idea of time to unix'. This will probably only be * called from the stat(), and fstat() system calls and so probably need @@ -374,6 +447,48 @@ l2u[256] = { 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* f8-ff */ }; +/* + * Convert DOS char to Local char + */ +static u_int16_t +dos2unixchr(const u_char **instr, size_t *ilen, int lower, struct msdosfsmount * +pmp) +{ + u_char c; + char *outp, outbuf[3]; + u_int16_t wc; + size_t len, olen; + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + olen = len = 2; + outp = outbuf; + if (lower & (LCASE_BASE | LCASE_EXT)) + msdos_iconv->convchr_case(pmp->pm_d2u, (const char **)instr, + ilen, &outp, &olen, KICONV_LOWER); + else + msdos_iconv->convchr(pmp->pm_d2u, (const char **)instr, + ilen, &outp, &olen); + len -= olen; + /* + * return '?' if failed to convert + */ + if (len == 0) { + (*ilen)--; + (*instr)++; + return ('?'); + } + wc = 0; + while(len--) + wc |= (*(outp - len - 1) & 0xff) << (len << 3); + return (wc); + } + (*ilen)--; + c = *(*instr)++; + c = dos2unix[c]; + if (lower & (LCASE_BASE | LCASE_EXT)) + c = u2l[c]; + return ((u_int16_t)c); +} + /* * DOS filenames are made of 2 parts, the name part and the extension part. * The name part is 8 characters long and the extension part is 3 @@ -387,8 +502,7 @@ l2u[256] = { * null. */ int -dos2unixfn(u_char dn[11], u_char *un, int lower, int d2u_loaded, u_int8_t *d2u, - int ul_loaded, u_int8_t *ul) +dos2unixfn(u_char dn[11], u_char *un, int lower, struct msdosfsmount *pmp) { int i; int thislong = 1; @@ -401,48 +515,140 @@ dos2unixfn(u_char dn[11], u_char *un, int lower, int d2u_loaded, u_int8_t *d2u, * directory slot. Another dos quirk. */ if (*dn == SLOT_E5) - c = d2u_loaded ? d2u[0xe5 & 0x7f] : dos2unix[0xe5]; - else - c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] : - dos2unix[*dn]; - *un++ = (lower & LCASE_BASE) ? (ul_loaded && (c & 0x80) ? - ul[c & 0x7f] : u2l[c]) : c; - dn++; - + *dn = 0xe5; /* * Copy the name portion into the unix filename string. - */ - for (i = 1; i < 8 && *dn != ' '; i++) { - c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] : - dos2unix[*dn]; - dn++; - *un++ = (lower & LCASE_BASE) ? (ul_loaded && (c & 0x80) ? - ul[c & 0x7f] : u2l[c]) : c; - thislong++; - } - dn += 8 - i; + */ + for(i = 8; i > 0 && *dn != ' ';) { + c = dos2unixchr((const u_char **)&dn, &i, lower & LCASE_BASE, + pmp); + if (c & 0xff00) { + *un++ = c >> 8; + thislong++; + } + *un++ = c; + thislong++; + } + dn += i; /* * Now, if there is an extension then put in a period and copy in * the extension. */ - if (*dn != ' ') { - *un++ = '.'; - thislong++; - for (i = 0; i < 3 && *dn != ' '; i++) { - c = d2u_loaded && (*dn & 0x80) ? d2u[*dn & 0x7f] : - dos2unix[*dn]; - dn++; - *un++ = (lower & LCASE_EXT) ? (ul_loaded && (c & 0x80) ? - ul[c & 0x7f] : u2l[c]) : c; - thislong++; - } - } + if (*dn != ' ') { + *un++ = '.'; + thislong++; + for (i = 3; i > 0 && *dn != ' ';) { + c = dos2unixchr((const u_char **)&dn, &i, + lower & LCASE_EXT, pmp); + if (c & 0xff00) { + *un++ = c >> 8; + thislong++; + } + *un++ = c; + thislong++; + } + } *un++ = 0; - +/* + for(i = 0; un[i] != 0; i++) + kprintf("0x%x", un[i]); + kprintf(" ->%s\n", dn); +*/ return (thislong); } +/* + * Store an area with multi byte string instr, and reterns left + * byte of instr and moves pointer forward. The area's size is + * inlen or outlen. + */ +static int +mbsadjpos(const char **instr, size_t inlen, size_t outlen, int weight, int flag, + void *handle) +{ + char *outp, outstr[outlen * weight + 1]; + bzero(outstr, outlen*weight+1); + if (flag & MSDOSFSMNT_KICONV && msdos_iconv) { + outp = outstr; + outlen *= weight; + msdos_iconv->conv(handle, instr, &inlen, &outp, &outlen); + return (inlen); + } + (*instr) += min(inlen, outlen); + return (inlen - min(inlen, outlen)); +} + +/* + * Convert Local char to DOS char + */ +static u_int16_t +unix2doschr(const u_char **instr, size_t *ilen, struct msdosfsmount *pmp) +{ + u_char c; + char *up, *outp, unicode[3], outbuf[3]; + u_int16_t wc; + size_t len, ucslen, unixlen, olen; + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + /* + * to hide an invisible character, using a unicode filter + */ + ucslen = 2; + len = *ilen; + up = unicode; + msdos_iconv->convchr(pmp->pm_u2w, (const char **)instr, + ilen, &up, &ucslen); + unixlen = len - *ilen; + + /* + * cannot be converted + */ + if (unixlen == 0) { + (*ilen)--; + (*instr)++; + return (0); + } + /* + * return magic number for ascii char + */ + if (unixlen == 1) { + c = *(*instr -1); + if (! (c & 0x80)) { + c = unix2dos[c]; + if (c <= 2) + return (c); + } + } + /* + * now convert using libiconv + */ + *instr -= unixlen; + *ilen = len; + olen = len = 2; + outp = outbuf; + msdos_iconv->convchr_case(pmp->pm_u2d, (const char **)instr, + ilen, &outp, &olen, KICONV_FROM_UPPER); + len -= olen; + /* + * cannot be converted, but has unicode char, should return magic number + */ + if (len == 0) { + (*ilen) -= unixlen; + (*instr) += unixlen; + return (1); + } + wc = 0; + while(len--) + wc |= (*(outp - len - 1) & 0xff) << (len << 3); + return (wc); + } + (*ilen)--; + c = *(*instr)++; + c = l2u[c]; + c = unix2dos[c]; + return ((u_int16_t)c); +} + /* * Convert a unix filename to a DOS filename according to Win95 rules. * If applicable and gen is not 0, it is inserted into the converted @@ -454,174 +660,244 @@ dos2unixfn(u_char dn[11], u_char *un, int lower, int d2u_loaded, u_int8_t *d2u, * 2 if conversion was successful * 3 if conversion was successful and generation number was inserted */ + int -unix2dosfn(const u_char *un, u_char dn[12], int unlen, u_int gen, int u2d_loaded, - u_int8_t *u2d, int lu_loaded, u_int8_t *lu) +unix2dosfn(const u_char *un, u_char dn[12], int unlen, u_int gen, struct msdosfsmount *pmp) { - int i, j, l; - int conv = 1; - const u_char *cp, *dp, *dp1; - u_char gentext[6], *wcp; - u_int8_t c; -#define U2D(c) (u2d_loaded && ((c) & 0x80) ? u2d[(c) & 0x7f] : unix2dos[c]) - - /* - * Fill the dos filename string with blanks. These are DOS's pad - * characters. - */ - for (i = 0; i < 11; i++) - dn[i] = ' '; - dn[11] = 0; - - /* - * The filenames "." and ".." are handled specially, since they - * don't follow dos filename rules. - */ - if (un[0] == '.' && unlen == 1) { - dn[0] = '.'; - return gen <= 1; - } - if (un[0] == '.' && un[1] == '.' && unlen == 2) { - dn[0] = '.'; - dn[1] = '.'; - return gen <= 1; - } - - /* - * Filenames with only blanks and dots are not allowed! - */ - for (cp = un, i = unlen; --i >= 0; cp++) - if (*cp != ' ' && *cp != '.') - break; - if (i < 0) - return 0; - - - /* - * Filenames with some characters are not allowed! - */ - for (cp = un, i = unlen; --i >= 0; cp++) - if (U2D(*cp) == 0) - return 0; - - /* - * Now find the extension - * Note: dot as first char doesn't start extension - * and trailing dots and blanks are ignored - */ - dp = dp1 = 0; - for (cp = un + 1, i = unlen - 1; --i >= 0;) { - switch (*cp++) { - case '.': - if (!dp1) - dp1 = cp; - break; - case ' ': - break; - default: - if (dp1) - dp = dp1; - dp1 = 0; - break; - } - } - - /* - * Now convert it - */ - if (dp) { - if (dp1) - l = dp1 - dp; - else - l = unlen - (dp - un); - for (i = 0, j = 8; i < l && j < 11; i++, j++) { - c = dp[i]; - c = lu_loaded && (c & 0x80) ? - lu[c & 0x7f] : l2u[c]; - c = U2D(c); - if (dp[i] != (dn[j] = c) - && conv != 3) - conv = 2; - if (dn[j] == 1) { - conv = 3; - dn[j] = '_'; - } - if (dn[j] == 2) { - conv = 3; - dn[j--] = ' '; - } - } - if (i < l) - conv = 3; - dp--; - } else { - for (dp = cp; *--dp == ' ' || *dp == '.';); - dp++; - } - - /* - * Now convert the rest of the name - */ - for (i = j = 0; un < dp && j < 8; i++, j++, un++) { - c = lu_loaded && (*un & 0x80) ? - lu[*un & 0x7f] : l2u[*un]; - c = U2D(c); - if (*un != (dn[j] = c) - && conv != 3) - conv = 2; - if (dn[j] == 1) { - conv = 3; - dn[j] = '_'; - } - if (dn[j] == 2) { - conv = 3; - dn[j--] = ' '; - } - } - if (un < dp) - conv = 3; - /* - * If we didn't have any chars in filename, - * generate a default - */ - if (!j) - dn[0] = '_'; - - /* - * The first character cannot be E5, - * because that means a deleted entry - */ - if (dn[0] == 0xe5) - dn[0] = SLOT_E5; - - /* - * If there wasn't any char dropped, - * there is no place for generation numbers - */ - if (conv != 3) { - if (gen > 1) - return 0; - return conv; - } + ssize_t i, j; + int l; + int conv = 1; + const u_char *cp, *dp, *dp1; + u_char gentext[6], *wcp; + u_int16_t c; + /* + * Fill the dos filename string with blanks. These are DOS's pad + * characters. + */ + for (i = 0; i < 11; i++) + dn[i] = ' '; + dn[11] = 0; + /* + * The filenames "." and ".." are handled specially, since they + * don't follow dos filename rules. + */ + if (un[0] == '.' && unlen == 1) { + dn[0] = '.'; + return gen <= 1; + } + if (un[0] == '.' && un[1] == '.' && unlen == 2) { + dn[0] = '.'; + dn[1] = '.'; + return gen <= 1; + } + /* + * Filenames with only blanks and dots are not allowed! + */ + for (cp = un, i = unlen; --i >= 0; cp++) + if (*cp != ' ' && *cp != '.') + break; + if (i < 0) + return 0; + /* + * Filenames with some characters are not allowed! + */ + for (cp = un, i = unlen; i > 0;) + if (unix2doschr(&cp, (size_t *)&i, pmp) == 0) + return 0; + /* + * Now find the extension + * Note: dot as first char doesn't start extension + * and trailing dots and blanks are ignored + * Note(2003/7): It seems recent Windows has + * defferent rule than this code, that Windows + * ignores all dots before extension, and use all + * chars as filename except for dots. + */ + dp = dp1 = 0; + for (cp = un + 1, i = unlen - 1; --i >= 0;) { + switch (*cp++) { + case '.': + if (!dp1) + dp1 = cp; + break; + case ' ': + break; + default: + if (dp1) + dp = dp1; + dp1 = 0; + break; + } + } + /* + * Now convert it (this part is for extension). + * As Windows XP do, if it's not ascii char, + * this function should return 2 or 3, so that checkng out Unicode name. + */ + if (dp) { + if (dp1) + l = dp1 - dp; + else + l = unlen - (dp - un); + for (cp = dp, i = l, j = 8; i > 0 && j < 11; j++) { + c = unix2doschr(&cp, (size_t *)&i, pmp); + if (c & 0xff00) { + dn[j] = c >> 8; + if (++j < 11) { + dn[j] = c; + if (conv != 3) + conv = 2; + continue; + } else { + conv = 3; + dn[j-1] = ' '; + break; + } + } else { + dn[j] = c; + } + if (((dn[j] & 0x80) || *(cp - 1) != dn[j]) && conv != 3) + conv = 2; + if (dn[j] == 1) { + conv = 3; + dn[j] = '_'; + } + if (dn[j] == 2) { + conv = 3; + dn[j--] = ' '; + } + } + if (i > 0) + conv = 3; + dp--; + } else { + for (dp = cp; *--dp == ' ' || *dp == '.';); + dp++; + } + /* + * Now convert the rest of the name + */ + for (i = dp - un, j = 0; un < dp && j < 8; j++) { + c = unix2doschr(&un, &i, pmp); + if (c & 0xff00) { + dn[j] = c >> 8; + if (++j < 8) { + dn[j] = c; + if (conv != 3) + conv = 2; + continue; + } else { + conv = 3; + dn[j-1] = ' '; + break; + } + } else { + dn[j] = c; + } + if (((dn[j] & 0x80) || *(un - 1) != dn[j]) && conv != 3) + conv = 2; + if (dn[j] == 1) { + conv = 3; + dn[j] = '_'; + } + if (dn[j] == 2) { + conv = 3; + dn[j--] = ' '; + } + } + if (un < dp) + conv = 3; + /* + * If we didn't have any chars in filename, + * generate a default + */ + if (!j) + dn[0] = '_'; + /* + * If there wasn't any char dropped, + * there is no place for generation numbers + */ + if (conv != 3) { + if (gen > 1) + conv = 0; + goto done; + } + /* + * Now insert the generation number into the filename part + */ + if (gen == 0) + goto done; + for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) + *--wcp = gen % 10 + '0'; + if (gen) { + conv = 0; + goto done; + } + for (i = 8; dn[--i] == ' ';); + i++; + if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) + i = 8 - (gentext + sizeof(gentext) - wcp + 1); + /* + * Correct posision to where insert the generation number + */ + cp = dn; + i -= mbsadjpos((const char**)&cp, i, unlen, 1, pmp->pm_flags, pmp->pm_d2u); + dn[i++] = '~'; + while (wcp < gentext + sizeof(gentext)) + dn[i++] = *wcp++; + /* + * Tail of the filename should be space + */ + while (i < 8) + dn[i++] = ' '; + conv = 3; +done: + /* + * The first character cannot be E5, + * because that means a deleted entry + */ + if (dn[0] == 0xe5) + dn[0] = SLOT_E5; + return conv; +} - /* - * Now insert the generation number into the filename part - */ - if (gen == 0) - return conv; - for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) - *--wcp = gen % 10 + '0'; - if (gen) - return 0; - for (i = 8; dn[--i] == ' ';); - i++; - if (gentext + sizeof(gentext) - wcp + 1 > 8 - i) - i = 8 - (gentext + sizeof(gentext) - wcp + 1); - dn[i++] = '~'; - while (wcp < gentext + sizeof(gentext)) - dn[i++] = *wcp++; - return 3; -#undef U2D +/* + * Convert Local char to Windows char + */ +static u_int16_t +unix2winchr(const u_char **instr, size_t *ilen, int lower, struct msdosfsmount * +pmp) +{ + u_char *outp, outbuf[3]; + u_int16_t wc; + size_t olen; + if (*ilen == 0) + return (0); + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + outp = outbuf; + olen = 2; + if (lower & (LCASE_BASE | LCASE_EXT)) + msdos_iconv->convchr_case(pmp->pm_u2w, (const char **) +instr, + ilen, (char **)&outp, &olen, + KICONV_FROM_LOWER); + else + msdos_iconv->convchr(pmp->pm_u2w, (const char **)instr, + ilen, (char **)&outp, &olen); + /* + * return '0' if end of filename + */ + if (olen == 2) + return (0); + wc = (outbuf[0]<<8) | outbuf[1]; + return (wc); + } + (*ilen)--; + wc = (*instr)[0]; + if (lower & (LCASE_BASE | LCASE_EXT)) + wc = u2l[wc]; + (*instr)++; + return (wc); } /* @@ -630,80 +906,65 @@ unix2dosfn(const u_char *un, u_char dn[12], int unlen, u_int gen, int u2d_loaded * i.e. doesn't consist solely of blanks and dots */ int -unix2winfn(const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum, - int table_loaded, u_int16_t *u2w) +unix2winfn(const u_char *un, int unlen, struct winentry *wep, int cnt, int chksum, struct msdosfsmount *pmp) { - const u_int8_t *cp; - u_int8_t *wcp; - int i; - u_int16_t code; + u_int8_t *wcp; + int i, end; + u_int16_t code; - /* - * Drop trailing blanks and dots - */ - for (cp = un + unlen; *--cp == ' ' || *cp == '.'; unlen--); + /* + * Drop trailing blanks and dots + */ + unlen = winLenFixup(un, unlen); - un += (cnt - 1) * WIN_CHARS; - unlen -= (cnt - 1) * WIN_CHARS; + /* + * Cut *un for this slot + */ + unlen = mbsadjpos((const char **)&un, unlen, (cnt - 1) * WIN_CHARS, 2, + pmp->pm_flags, pmp->pm_u2w); - /* - * Initialize winentry to some useful default - */ - for (wcp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); - wep->weCnt = cnt; - wep->weAttributes = ATTR_WIN95; - wep->weReserved1 = 0; - wep->weChksum = chksum; + /* + * Initialize winentry to some useful default + */ + for (wcp = (u_int8_t *)wep, i = sizeof(*wep); --i >= 0; *wcp++ = 0xff); + wep->weCnt = cnt; + wep->weAttributes = ATTR_WIN95; + wep->weReserved1 = 0; + wep->weChksum = chksum; wep->weReserved2 = 0; - /* - * Now convert the filename parts - */ - for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { - if (--unlen < 0) - goto done; - if (table_loaded && (*un & 0x80)) { - code = u2w[*un++ & 0x7f]; - *wcp++ = code; - *wcp++ = code >> 8; - } else { - *wcp++ = *un++; - *wcp++ = 0; - } - } - for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { - if (--unlen < 0) - goto done; - if (table_loaded && (*un & 0x80)) { - code = u2w[*un++ & 0x7f]; - *wcp++ = code; - *wcp++ = code >> 8; - } else { - *wcp++ = *un++; - *wcp++ = 0; - } - } - for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { - if (--unlen < 0) - goto done; - if (table_loaded && (*un & 0x80)) { - code = u2w[*un++ & 0x7f]; - *wcp++ = code; - *wcp++ = code >> 8; - } else { - *wcp++ = *un++; - *wcp++ = 0; - } - } - if (!unlen) - wep->weCnt |= WIN_LAST; - return unlen; - -done: - *wcp++ = 0; - *wcp++ = 0; - wep->weCnt |= WIN_LAST; - return 0; + /* + * Now convert the filename parts + */ + end = 0; + for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0 && !end;) + { + code = unix2winchr(&un, &unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; + } + for (wcp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0 && !end;) + { + code = unix2winchr(&un, &unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; + } + for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0 && !end;) + { + code = unix2winchr(&un, &unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; + } + if (*un == '\0') + end = WIN_LAST; + wep->weCnt |= end; + return !end; } static __inline u_int8_t @@ -722,111 +983,85 @@ find_lcode(u_int16_t code, u_int16_t *u2w) * Returns the checksum or -1 if no match */ int -winChkName(const u_char *un, int unlen, struct winentry *wep, int chksum, - int u2w_loaded, u_int16_t *u2w, int ul_loaded, u_int8_t *ul) +winChkName(nbp, un, unlen, chksum, pmp) + struct mbnambuf *nbp; + const u_char *un; + size_t unlen; + int chksum; + struct msdosfsmount *pmp; { - u_int8_t *cp; - int i; - u_int16_t code; - u_int8_t c1, c2; - - /* - * First compare checksums - */ - if (wep->weCnt&WIN_LAST) - chksum = wep->weChksum; - else if (chksum != wep->weChksum) - chksum = -1; - if (chksum == -1) - return -1; - - /* - * Offset of this entry - */ - i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; - un += i; - unlen -= i; + size_t len; + u_int16_t c1, c2; + u_char *np; + u_int16_t d_namlen; + char d_name[127]; + bzero(d_name, 127); + /* + * We already have winentry in *nbp. + */ + if (!mbnambuf_flush(nbp, d_name, &d_namlen) || d_namlen == 0) + return -1; +#ifdef MSDOSFS_DEBUG + kprintf("winChkName(): un=%s:%d,d_name=%s:%d\n", un, unlen, + d_name, + d_namlen); +#endif + /* + * Compare the name parts + */ + len = d_namlen; + if (unlen != len) + return -2; + for (np = d_name; unlen > 0 && len > 0;) { + /* + * Comparison must be case insensitive, because FAT disallows + * to look up or create files in case sensitive even when + * it's a long file name. + */ + c1 = unix2winchr((const u_char **)&np, &len, LCASE_BASE, pmp); + c2 = unix2winchr(&un, &unlen, LCASE_BASE, pmp); + if (c1 != c2) + return -2; + } + return chksum; +} - /* - * unlen being zero must not be treated as length missmatch. It is - * possible if the entry is WIN_LAST and contains nothing but the - * terminating 0. - */ - if (unlen < 0) - return -1; - if ((wep->weCnt&WIN_LAST) && unlen > WIN_CHARS) - return -1; +/* + * Convert Windows char to Local char + */ +static u_int16_t +win2unixchr(u_int16_t wc, struct msdosfsmount *pmp) +{ + u_char *inp, *outp, inbuf[3], outbuf[3]; + size_t ilen, olen, len; + if (wc == 0) + return (0); + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + inbuf[0] = (u_char)(wc>>8); + inbuf[1] = (u_char)wc; + inbuf[2] = '\0'; + ilen = olen = len = 2; + inp = inbuf; + outp = outbuf; + msdos_iconv->convchr(pmp->pm_w2u, (const char **)&inp, &ilen, + (char **)&outp, &olen); + len -= olen; - /* - * Compare the name parts - */ - for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { - if (--unlen < 0) { - if (!*cp++ && !*cp) - return chksum; - return -1; - } - code = (cp[1] << 8) | cp[0]; - if (code & 0xff80) { - if (u2w_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - c1 = ul_loaded && (code & 0x80) ? - ul[code & 0x7f] : u2l[code]; - c2 = ul_loaded && (*un & 0x80) ? - ul[*un & 0x7f] : u2l[*un]; - if (c1 != c2) - return -1; - cp += 2; - un++; - } - for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { - if (--unlen < 0) { - if (!*cp++ && !*cp) - return chksum; - return -1; - } - code = (cp[1] << 8) | cp[0]; - if (code & 0xff80) { - if (u2w_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - c1 = ul_loaded && (code & 0x80) ? - ul[code & 0x7f] : u2l[code]; - c2 = ul_loaded && (*un & 0x80) ? - ul[*un & 0x7f] : u2l[*un]; - if (c1 != c2) - return -1; - cp += 2; - un++; - } - for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { - if (--unlen < 0) { - if (!*cp++ && !*cp) - return chksum; - return -1; - } - code = (cp[1] << 8) | cp[0]; - if (code & 0xff80) { - if (u2w_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - c1 = ul_loaded && (code & 0x80) ? - ul[code & 0x7f] : u2l[code]; - c2 = ul_loaded && (*un & 0x80) ? - ul[*un & 0x7f] : u2l[*un]; - if (c1 != c2) - return -1; - cp += 2; - un++; - } - return chksum; + /* + * return '?' if failed to convert + */ + if (len == 0) { + wc = '?'; + return (wc); + } + wc = 0; + while(len--) + wc |= (*(outp - len - 1) & 0xff) << (len << 3); + return (wc); + } + if (wc & 0xff00) + wc = '?'; + return (wc); } /* @@ -834,137 +1069,93 @@ winChkName(const u_char *un, int unlen, struct winentry *wep, int chksum, * Returns the checksum or -1 if impossible */ int -win2unixfn(struct winentry *wep, char *d_name, uint16_t *d_namlen, int chksum, int table_loaded, - u_int16_t *u2w) +win2unixfn(nbp, wep, chksum, pmp) + struct mbnambuf *nbp; + struct winentry *wep; + int chksum; + struct msdosfsmount *pmp; { - u_int8_t *cp; - u_int8_t *np, *ep = d_name + WIN_MAXLEN; - u_int16_t code; - int i; - - if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) - || !(wep->weCnt&WIN_CNT)) - return -1; - - /* - * First compare checksums - */ - if (wep->weCnt&WIN_LAST) { - chksum = wep->weChksum; - /* - * This works even though d_namlen is one byte! - */ - *d_namlen = (wep->weCnt&WIN_CNT) * WIN_CHARS; - } else if (chksum != wep->weChksum) - chksum = -1; - if (chksum == -1) - return -1; - - /* - * Offset of this entry - */ - i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; - np = (u_int8_t *)d_name + i; - - /* - * Convert the name parts - */ - for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { - code = (cp[1] << 8) | cp[0]; - switch (code) { - case 0: - *np = '\0'; - *d_namlen -= sizeof(wep->wePart2)/2 - + sizeof(wep->wePart3)/2 + i + 1; - return chksum; - case '/': - *np = '\0'; - return -1; - default: - if (code & 0xff80) { - if (table_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - *np++ = code; - break; - } - /* - * The size comparison should result in the compiler - * optimizing the whole if away - */ - if (WIN_MAXLEN % WIN_CHARS < sizeof(wep->wePart1) / 2 - && np > ep) { - np[-1] = 0; - return -1; - } - cp += 2; - } - for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { - code = (cp[1] << 8) | cp[0]; - switch (code) { - case 0: - *np = '\0'; - *d_namlen -= sizeof(wep->wePart3)/2 + i + 1; - return chksum; - case '/': - *np = '\0'; - return -1; - default: - if (code & 0xff80) { - if (table_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - *np++ = code; - break; - } - /* - * The size comparisons should be optimized away - */ - if (WIN_MAXLEN % WIN_CHARS >= sizeof(wep->wePart1) / 2 - && WIN_MAXLEN % WIN_CHARS < (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 - && np > ep) { - np[-1] = 0; - return -1; - } - cp += 2; - } - for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { - code = (cp[1] << 8) | cp[0]; - switch (code) { - case 0: - *np = '\0'; - *d_namlen -= i + 1; - return chksum; - case '/': - *np = '\0'; - return -1; - default: - if (code & 0xff80) { - if (table_loaded) - code = find_lcode(code, u2w); - else if (code & 0xff00) - code = '?'; - } - *np++ = code; - break; - } - /* - * See above - */ - if (WIN_MAXLEN % WIN_CHARS >= (sizeof(wep->wePart1) + sizeof(wep->wePart2)) / 2 - && np > ep) { - np[-1] = 0; - return -1; - } - cp += 2; - } - return chksum; + u_int8_t *cp; + u_int8_t *np, name[WIN_CHARS * 2 + 1]; + u_int16_t code; + int i; + if ((wep->weCnt&WIN_CNT) > howmany(WIN_MAXLEN, WIN_CHARS) + || !(wep->weCnt&WIN_CNT)) + return -1; + /* + * First compare checksums + */ + if (wep->weCnt&WIN_LAST) { + chksum = wep->weChksum; + } else if (chksum != wep->weChksum) + chksum = -1; + if (chksum == -1) + return -1; + /* + * Convert the name parts + */ + np = name; + for (cp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0;) { + code = (cp[1] << 8) | cp[0]; + switch (code) { + case 0: + *np = '\0'; + mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); + return chksum; + case '/': + *np = '\0'; + return -1; + default: + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; + *np++ = code; + break; + } + cp += 2; + } + for (cp = wep->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0;) { + code = (cp[1] << 8) | cp[0]; + switch (code) { + case 0: + *np = '\0'; + mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); + return chksum; + case '/': + *np = '\0'; + return -1; + default: + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; + *np++ = code; + break; + } + cp += 2; + } + for (cp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0;) { + code = (cp[1] << 8) | cp[0]; + switch (code) { + case 0: + *np = '\0'; + mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); + return chksum; + case '/': + *np = '\0'; + return -1; + default: + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; + *np++ = code; + break; + } + cp += 2; + } + *np = '\0'; + mbnambuf_write(nbp, name, (wep->weCnt & WIN_CNT) - 1); + return chksum; } - /* * Compute the checksum of a DOS filename for Win95 use */ @@ -983,12 +1174,25 @@ winChksum(u_int8_t *name) * Determine the number of slots necessary for Win95 names */ int -winSlotCnt(const u_char *un, int unlen) +winSlotCnt(un, unlen, pmp) + const u_char *un; + int unlen; + struct msdosfsmount *pmp; { - unlen = winLenFixup(un, unlen); - if (unlen > WIN_MAXLEN) - return 0; - return howmany(unlen, WIN_CHARS); + size_t wlen; + char wn[WIN_MAXLEN * 2 + 1], *wnp; + unlen = winLenFixup(un, unlen); + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + wlen = WIN_MAXLEN * 2; + wnp = wn; + msdos_iconv->conv(pmp->pm_u2w, (const char **)&un, &unlen, &wnp, &wlen); + if (unlen > 0) + return 0; + return howmany(WIN_MAXLEN - wlen/2, WIN_CHARS); + } + if (unlen > WIN_MAXLEN) + return 0; + return howmany(unlen, WIN_CHARS); } /* diff --git a/sys/vfs/msdosfs/msdosfs_lookup.c b/sys/vfs/msdosfs/msdosfs_lookup.c index 01cb43d7e3..3659369e51 100644 --- a/sys/vfs/msdosfs/msdosfs_lookup.c +++ b/sys/vfs/msdosfs/msdosfs_lookup.c @@ -84,6 +84,7 @@ int msdosfs_lookup(struct vop_old_lookup_args *ap) { + struct mbnambuf nb; struct vnode *vdp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; struct componentname *cnp = ap->a_cnp; @@ -146,23 +147,20 @@ msdosfs_lookup(struct vop_old_lookup_args *ap) blkoff = MSDOSFSROOT_OFS; goto foundroot; } - switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, - cnp->cn_namelen, 0, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu)) { + cnp->cn_namelen, 0, pmp)) { case 0: return (EINVAL); case 1: break; case 2: wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, - cnp->cn_namelen) + 1; + cnp->cn_namelen, pmp) + 1; break; case 3: olddos = 0; wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, - cnp->cn_namelen) + 1; + cnp->cn_namelen, pmp) + 1; break; } if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) { @@ -190,6 +188,7 @@ msdosfs_lookup(struct vop_old_lookup_args *ap) * by cnp->cn_nameptr. */ tdp = NULL; + mbnambuf_init(&nb); /* * The outer loop ranges over the clusters that make up the * directory. Note that the root directory is different from all @@ -229,6 +228,7 @@ msdosfs_lookup(struct vop_old_lookup_args *ap) * Drop memory of previous long matches */ chksum = -1; + mbnambuf_init(&nb); if (slotcount < wincnt) { slotcount++; @@ -250,20 +250,22 @@ msdosfs_lookup(struct vop_old_lookup_args *ap) * Check for Win95 long filename entry */ if (dep->deAttributes == ATTR_WIN95) { - if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) + if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; - - chksum = winChkName((const u_char *)cnp->cn_nameptr, - unlen, - (struct winentry *)dep, - chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, - pmp->pm_ul); + chksum = win2unixfn(&nb, + (struct winentry *)dep, chksum, + pmp); continue; } + chksum = winChkName(&nb, + (const u_char *)cnp->cn_nameptr, unlen, + chksum, pmp); + if (chksum == -2) { + chksum = -1; + continue; + } + /* * Ignore volume labels (anywhere, not just * the root directory). @@ -639,8 +641,7 @@ createde(struct denode *dep, struct denode *ddep, struct denode **depp, } if (!unix2winfn(un, unlen, (struct winentry *)ndep, cnt++, chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w)) + pmp)) break; } } @@ -959,9 +960,7 @@ uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp) if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) return (unix2dosfn((const u_char *)cnp->cn_nameptr, cp, - cnp->cn_namelen, 0, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu) ? + cnp->cn_namelen, 0, pmp) ? 0 : EINVAL); for (gen = 1;; gen++) { @@ -969,9 +968,7 @@ uniqdosname(struct denode *dep, struct componentname *cnp, u_char *cp) * Generate DOS name with generation number */ if (!unix2dosfn((const u_char *)cnp->cn_nameptr, cp, - cnp->cn_namelen, gen, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, pmp->pm_u2d, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, pmp->pm_lu)) + cnp->cn_namelen, gen, pmp)) return gen == 1 ? EINVAL : EEXIST; /* diff --git a/sys/vfs/msdosfs/msdosfs_vfsops.c b/sys/vfs/msdosfs/msdosfs_vfsops.c index 7cdc8cec12..35232bdee0 100644 --- a/sys/vfs/msdosfs/msdosfs_vfsops.c +++ b/sys/vfs/msdosfs/msdosfs_vfsops.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -71,9 +72,10 @@ #include "fat.h" extern struct vop_ops msdosfs_vnode_vops; +struct iconv_functions *msdos_iconv; #define MSDOSFS_DFLTBSIZE 4096 - +#define ENCODING_UNICODE "UTF-16BE" #if 1 /*def PC98*/ /* * XXX - The boot signature formatted by NEC PC-98 DOS looks like a @@ -109,19 +111,29 @@ update_mp(struct mount *mp, struct msdosfs_args *argp) { struct msdosfsmount *pmp = VFSTOMSDOSFS(mp); int error; + char cs_local[ICONV_CSNMAXLEN]; + char cs_dos[ICONV_CSNMAXLEN]; pmp->pm_gid = argp->gid; pmp->pm_uid = argp->uid; pmp->pm_mask = argp->mask & ALLPERMS; pmp->pm_flags |= argp->flags & MSDOSFSMNT_MNTOPT; - if (pmp->pm_flags & MSDOSFSMNT_U2WTABLE) { - bcopy(argp->u2w, pmp->pm_u2w, sizeof(pmp->pm_u2w)); - bcopy(argp->d2u, pmp->pm_d2u, sizeof(pmp->pm_d2u)); - bcopy(argp->u2d, pmp->pm_u2d, sizeof(pmp->pm_u2d)); - } - if (pmp->pm_flags & MSDOSFSMNT_ULTABLE) { - bcopy(argp->ul, pmp->pm_ul, sizeof(pmp->pm_ul)); - bcopy(argp->lu, pmp->pm_lu, sizeof(pmp->pm_lu)); + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + bcopy(argp->cs_local, cs_local, sizeof(cs_local)); + bcopy(argp->cs_dos, cs_dos, sizeof(cs_dos)); + kprintf("local: %s dos: %s\n",argp->cs_local, argp->cs_dos); + error = msdos_iconv->open(cs_local, ENCODING_UNICODE, &pmp->pm_w2u); + if(error) + return error; + error = msdos_iconv->open(ENCODING_UNICODE, cs_local, &pmp->pm_u2w); + if(error) + return error; + error = msdos_iconv->open(cs_dos, cs_local, &pmp->pm_u2d); + if(error) + return error; + error = msdos_iconv->open(cs_local, cs_dos, &pmp->pm_d2u); + if(error) + return error; } if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) @@ -622,6 +634,16 @@ msdosfs_unmount(struct mount *mp, int mntflags) return error; pmp = VFSTOMSDOSFS(mp); pmp->pm_devvp->v_rdev->si_mountpoint = NULL; + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdos_iconv) { + if(pmp->pm_w2u) + msdos_iconv->close(pmp->pm_w2u); + if(pmp->pm_u2w) + msdos_iconv->close(pmp->pm_u2w); + if(pmp->pm_d2u) + msdos_iconv->close(pmp->pm_d2u); + if(pmp->pm_u2d) + msdos_iconv->close(pmp->pm_u2d); + } #ifdef MSDOSFS_DEBUG { struct vnode *vp = pmp->pm_devvp; diff --git a/sys/vfs/msdosfs/msdosfs_vnops.c b/sys/vfs/msdosfs/msdosfs_vnops.c index fdb549c69a..f0c6aff468 100644 --- a/sys/vfs/msdosfs/msdosfs_vnops.c +++ b/sys/vfs/msdosfs/msdosfs_vnops.c @@ -1490,6 +1490,7 @@ msdosfs_symlink(struct vop_old_symlink_args *ap) static int msdosfs_readdir(struct vop_readdir_args *ap) { + struct mbnambuf nb; int error = 0; int diff; long n; @@ -1512,7 +1513,7 @@ msdosfs_readdir(struct vop_readdir_args *ap) uint16_t d_namlen; uint8_t d_type; char *d_name_storage = NULL; - char *d_name; + char *d_name = NULL; if ((error = vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY)) != 0) return (error); @@ -1586,7 +1587,7 @@ msdosfs_readdir(struct vop_readdir_args *ap) if (n == 0) { d_namlen = 1; d_name = "."; - } else /* if (n == 1) */{ + } else if (n == 1) { d_namlen = 2; d_name = ".."; } @@ -1607,6 +1608,7 @@ msdosfs_readdir(struct vop_readdir_args *ap) } d_name_storage = kmalloc(WIN_MAXLEN, M_TEMP, M_WAITOK); + mbnambuf_init(&nb); off = offset; while (uio->uio_resid > 0) { @@ -1640,6 +1642,7 @@ msdosfs_readdir(struct vop_readdir_args *ap) dentp, prev, crnt, dentp->deName[0], dentp->deAttributes); #endif d_name = d_name_storage; + d_namlen = 0; /* * If this is an unused entry, we can stop. */ @@ -1652,19 +1655,19 @@ msdosfs_readdir(struct vop_readdir_args *ap) */ if (dentp->deName[0] == SLOT_DELETED) { chksum = -1; + mbnambuf_init(&nb); continue; } - /* * Handle Win95 long directory entries */ if (dentp->deAttributes == ATTR_WIN95) { if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; - chksum = win2unixfn((struct winentry *)dentp, - d_name, &d_namlen, chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w); + chksum = win2unixfn(&nb, + (struct winentry *)dentp, + chksum, + pmp); continue; } @@ -1673,6 +1676,7 @@ msdosfs_readdir(struct vop_readdir_args *ap) */ if (dentp->deAttributes & ATTR_VOLUME) { chksum = -1; + mbnambuf_init(&nb); continue; } /* @@ -1703,11 +1707,11 @@ msdosfs_readdir(struct vop_readdir_args *ap) dentp->deLowerCase | ((pmp->pm_flags & MSDOSFSMNT_SHORTNAME) ? (LCASE_BASE | LCASE_EXT) : 0), - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_d2u, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, - pmp->pm_ul); - } + pmp); + mbnambuf_init(&nb); + } else { + mbnambuf_flush(&nb, d_name, &d_namlen); +} chksum = -1; if (vop_write_dirent(&error, uio, d_ino, d_type, d_namlen, d_name)) { diff --git a/sys/vfs/msdosfs/msdosfsmount.h b/sys/vfs/msdosfs/msdosfsmount.h index a751ca644e..d67fd295fd 100644 --- a/sys/vfs/msdosfs/msdosfsmount.h +++ b/sys/vfs/msdosfs/msdosfsmount.h @@ -58,9 +58,8 @@ MALLOC_DECLARE(M_MSDOSFSMNT); #endif #endif - +#include #if defined(_KERNEL) || defined(_KERNEL_STRUCTURES) - /* * Layout of the mount control block for a msdos file system. */ @@ -97,11 +96,10 @@ struct msdosfsmount { u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */ u_int pm_flags; /* see below */ struct netexport pm_export; /* export information */ - u_int16_t pm_u2w[128]; /* Local->Unicode table */ - u_int8_t pm_ul[128]; /* Local upper->lower table */ - u_int8_t pm_lu[128]; /* Local lower->upper table */ - u_int8_t pm_d2u[128]; /* DOS->local table */ - u_int8_t pm_u2d[128]; /* Local->DOS table */ + void *pm_u2w; /* Local->Unicode handle */ + void *pm_w2u; /* Unicode->Local handle */ + void *pm_d2u; /* DOS->local handle */ + void *pm_u2d; /* Local->DOS handle */ }; /* Byte offset in FAT on filesystem pmp, cluster cn */ #define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv) @@ -248,11 +246,8 @@ struct msdosfs_args { mode_t mask; /* mask to be applied for msdosfs perms */ int flags; /* see below */ int magic; /* version number */ - u_int16_t u2w[128]; /* Local->Unicode table */ - u_int8_t ul[128]; /* Local upper->lower table */ - u_int8_t lu[128]; /* Local lower->upper table */ - u_int8_t d2u[128]; /* DOS->local table */ - u_int8_t u2d[128]; /* Local->DOS table */ + char cs_local[ICONV_CSNMAXLEN]; + char cs_dos[ICONV_CSNMAXLEN]; }; /* @@ -261,13 +256,12 @@ struct msdosfs_args { #define MSDOSFSMNT_SHORTNAME 1 /* Force old DOS short names only */ #define MSDOSFSMNT_LONGNAME 2 /* Force Win'95 long names */ #define MSDOSFSMNT_NOWIN95 4 /* Completely ignore Win95 entries */ -#define MSDOSFSMNT_U2WTABLE 0x10 /* Local->Unicode and local<->DOS */ +#define MSDOSFSMNT_KICONV 0x10 /* Local->Unicode and local<->DOS */ /* tables loaded */ -#define MSDOSFSMNT_ULTABLE 0x20 /* Local upper<->lower table loaded */ /* All flags above: */ #define MSDOSFSMNT_MNTOPT \ (MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \ - /*|MSDOSFSMNT_GEMDOSFS*/|MSDOSFSMNT_U2WTABLE|MSDOSFSMNT_ULTABLE) + /*|MSDOSFSMNT_GEMDOSFS*/|MSDOSFSMNT_KICONV) #define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */ #define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */ #define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */