Stage 2/5: Change msdosfs to use libiconv in case.
authorAlexander Polakov <polachok@gmail.com>
Fri, 5 Jun 2009 08:10:38 +0000 (12:10 +0400)
committerSimon Schubert <corecode@dragonflybsd.org>
Thu, 23 Jul 2009 10:08:32 +0000 (12:08 +0200)
Code mostly from FreeBSD, ported by me. Still works with old
userspace (if mounted without conversion).

sys/vfs/msdosfs/Makefile
sys/vfs/msdosfs/direntry.h
sys/vfs/msdosfs/msdosfs_conv.c
sys/vfs/msdosfs/msdosfs_lookup.c
sys/vfs/msdosfs/msdosfs_vfsops.c
sys/vfs/msdosfs/msdosfs_vnops.c
sys/vfs/msdosfs/msdosfsmount.h

index cb20645..7a244e0 100644 (file)
@@ -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 <bsd.kmod.mk>
index c26b88b..c57f127 100644 (file)
@@ -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 */
index 72c9915..f66efce 100644 (file)
 #include <sys/time.h>
 #include <sys/kernel.h>                /* defines tz */
 #include <sys/systm.h>
+#include <sys/iconv.h>
 #include <machine/clock.h>
 #include <sys/dirent.h>
-
+#include <sys/mount.h>
 /*
  * 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.
  */
@@ -168,6 +173,74 @@ 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
  * not be too efficient.
@@ -375,6 +448,48 @@ l2u[256] = {
 };
 
 /*
+ * 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
  * characters long.  They may contain trailing blanks if the name or
@@ -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,49 +515,141 @@ 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
  * filename as a generation number.
@@ -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);
 }
 
 /*
index 729dcdd..27ab76b 100644 (file)
@@ -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;
 
                /*
index 58e21e8..b87d6f7 100644 (file)
@@ -56,6 +56,7 @@
 #include <sys/nlookup.h>
 #include <sys/kernel.h>
 #include <sys/vnode.h>
+#include <sys/iconv.h>
 #include <sys/mount.h>
 #include <sys/buf.h>
 #include <sys/fcntl.h>
 #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;
index 131b2b2..4df2dfc 100644 (file)
@@ -1545,6 +1545,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;
@@ -1567,7 +1568,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);
@@ -1641,7 +1642,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 = "..";
                                }
@@ -1662,6 +1663,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) {
@@ -1695,6 +1697,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.
                         */
@@ -1707,19 +1710,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;
                        }
 
@@ -1728,6 +1731,7 @@ msdosfs_readdir(struct vop_readdir_args *ap)
                         */
                        if (dentp->deAttributes & ATTR_VOLUME) {
                                chksum = -1;
+                               mbnambuf_init(&nb);
                                continue;
                        }
                        /*
@@ -1758,11 +1762,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)) {
index a751ca6..d67fd29 100644 (file)
@@ -58,9 +58,8 @@
 MALLOC_DECLARE(M_MSDOSFSMNT);
 #endif
 #endif
-
+#include <sys/iconv.h>
 #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 */