diff options
author | fjoe <fjoe@FreeBSD.org> | 2003-09-26 20:26:25 +0000 |
---|---|---|
committer | fjoe <fjoe@FreeBSD.org> | 2003-09-26 20:26:25 +0000 |
commit | 571ef024e3f3a472116a55a8489d77eb4f5f933e (patch) | |
tree | 5e4dbdee80eebe5477ad9c5637bb6b0ee47993d5 /sys/fs | |
parent | 0c8bfb6d004a87cd501c13516a69b3ef59ed6c7c (diff) | |
download | FreeBSD-src-571ef024e3f3a472116a55a8489d77eb4f5f933e.zip FreeBSD-src-571ef024e3f3a472116a55a8489d77eb4f5f933e.tar.gz |
- Support for multibyte charsets in LIBICONV.
- CD9660_ICONV, NTFS_ICONV and MSDOSFS_ICONV kernel options
(with corresponding modules).
- kiconv(3) for loadable charset conversion tables support.
Submitted by: Ryuichiro Imura <imura@ryu16.org>
Diffstat (limited to 'sys/fs')
-rw-r--r-- | sys/fs/cd9660/cd9660_iconv.c | 36 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_lookup.c | 7 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_mount.h | 3 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_rrip.c | 12 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_util.c | 155 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_vfsops.c | 20 | ||||
-rw-r--r-- | sys/fs/cd9660/cd9660_vnops.c | 4 | ||||
-rw-r--r-- | sys/fs/cd9660/iso.h | 10 | ||||
-rw-r--r-- | sys/fs/msdosfs/direntry.h | 20 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_conv.c | 771 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_iconv.c | 36 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_lookup.c | 43 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_vfsops.c | 34 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfs_vnops.c | 21 | ||||
-rw-r--r-- | sys/fs/msdosfs/msdosfsmount.h | 22 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs.h | 2 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs_iconv.c | 36 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs_subr.c | 166 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs_subr.h | 9 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs_vfsops.c | 13 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfs_vnops.c | 14 | ||||
-rw-r--r-- | sys/fs/ntfs/ntfsmount.h | 5 | ||||
-rw-r--r-- | sys/fs/smbfs/smbfs_vfsops.c | 2 |
23 files changed, 981 insertions, 460 deletions
diff --git a/sys/fs/cd9660/cd9660_iconv.c b/sys/fs/cd9660/cd9660_iconv.c new file mode 100644 index 0000000..43a5a0d --- /dev/null +++ b/sys/fs/cd9660/cd9660_iconv.c @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003 Ryuichiro Imura + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/iconv.h> + +VFS_DECLARE_ICONV(cd9660); diff --git a/sys/fs/cd9660/cd9660_lookup.c b/sys/fs/cd9660/cd9660_lookup.c index 056e893..49308ea 100644 --- a/sys/fs/cd9660/cd9660_lookup.c +++ b/sys/fs/cd9660/cd9660_lookup.c @@ -238,7 +238,12 @@ searchloop: if (namelen != 1 || ep->name[0] != 0) goto notfound; - } else if (!(res = isofncmp(name, len, ep->name, namelen, imp->joliet_level))) { + } else if (!(res = isofncmp(name, len, + ep->name, namelen, + imp->joliet_level, + imp->im_flags, + imp->im_d2l, + imp->im_l2d))) { if (isoflags & 2) ino = isodirino(ep, imp); else diff --git a/sys/fs/cd9660/cd9660_mount.h b/sys/fs/cd9660/cd9660_mount.h index 8cc6a86..0397d06 100644 --- a/sys/fs/cd9660/cd9660_mount.h +++ b/sys/fs/cd9660/cd9660_mount.h @@ -47,9 +47,12 @@ struct iso_args { struct export_args export; /* network export info */ int flags; /* mounting flags, see below */ int ssector; /* starting sector, 0 for 1st session */ + char *cs_disk; /* disk charset for Joliet cs conversion */ + char *cs_local; /* local charset for Joliet cs conversion */ }; #define ISOFSMNT_NORRIP 0x00000001 /* disable Rock Ridge Ext.*/ #define ISOFSMNT_GENS 0x00000002 /* enable generation numbers */ #define ISOFSMNT_EXTATT 0x00000004 /* enable extended attributes */ #define ISOFSMNT_NOJOLIET 0x00000008 /* disable Joliet Ext.*/ #define ISOFSMNT_BROKENJOLIET 0x00000010/* allow broken Joliet disks */ +#define ISOFSMNT_KICONV 0x00000020 /* Use libiconv to convert chars */ diff --git a/sys/fs/cd9660/cd9660_rrip.c b/sys/fs/cd9660/cd9660_rrip.c index 7f7ea15..1546e8d 100644 --- a/sys/fs/cd9660/cd9660_rrip.c +++ b/sys/fs/cd9660/cd9660_rrip.c @@ -293,7 +293,8 @@ cd9660_rrip_defname(isodir,ana) { isofntrans(isodir->name,isonum_711(isodir->name_len), ana->outbuf,ana->outlen, - 1,isonum_711(isodir->flags)&4, ana->imp->joliet_level); + 1,isonum_711(isodir->flags)&4, ana->imp->joliet_level, + ana->imp->im_flags, ana->imp->im_d2l); switch (*ana->outbuf) { default: break; @@ -491,7 +492,7 @@ cd9660_rrip_loop(isodir,ana,table) register ISO_SUSP_HEADER *pend; struct buf *bp = NULL; char *pwhead; - u_char c; + u_short c; int result; /* @@ -501,7 +502,8 @@ cd9660_rrip_loop(isodir,ana,table) pwhead = isodir->name + isonum_711(isodir->name_len); if (!(isonum_711(isodir->name_len)&1)) pwhead++; - isochar(isodir->name, pwhead, ana->imp->joliet_level, &c); + isochar(isodir->name, pwhead, ana->imp->joliet_level, &c, NULL, + ana->imp->im_flags, ana->imp->im_d2l); /* If it's not the '.' entry of the root dir obey SP field */ if (c != 0 || isonum_733(isodir->extent) != ana->imp->root_extent) @@ -627,7 +629,7 @@ cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp) { ISO_RRIP_ANALYZE analyze; RRIP_TABLE *tab; - u_char c; + u_short c; analyze.outbuf = outbuf; analyze.outlen = outlen; @@ -638,7 +640,7 @@ cd9660_rrip_getname(isodir,outbuf,outlen,inump,imp) *outlen = 0; isochar(isodir->name, isodir->name + isonum_711(isodir->name_len), - imp->joliet_level, &c); + imp->joliet_level, &c, NULL, imp->im_flags, imp->im_d2l); tab = rrip_table_getname; if (c == 0 || c == 1) { cd9660_rrip_defname(isodir,&analyze); diff --git a/sys/fs/cd9660/cd9660_util.c b/sys/fs/cd9660/cd9660_util.c index 8a132c0..dccb14c 100644 --- a/sys/fs/cd9660/cd9660_util.c +++ b/sys/fs/cd9660/cd9660_util.c @@ -46,16 +46,12 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/mount.h> #include <sys/vnode.h> +#include <sys/iconv.h> #include <isofs/cd9660/iso.h> +#include <isofs/cd9660/cd9660_mount.h> -/* - * XXX: limited support for loading of Unicode - * conversion routine as a kld at a run-time. - * Should be removed when native Unicode kernel - * interfaces have been introduced. - */ -u_char (*cd9660_wchar2char)(u_int32_t wchar) = NULL; +extern struct iconv_functions *cd9660_iconv; /* * Get one character out of an iso filename @@ -63,29 +59,47 @@ u_char (*cd9660_wchar2char)(u_int32_t wchar) = NULL; * Return number of bytes consumed */ int -isochar(isofn, isoend, joliet_level, c) +isochar(isofn, isoend, joliet_level, c, clen, flags, handle) u_char *isofn; u_char *isoend; int joliet_level; - u_char *c; + u_short *c; + int *clen; + int flags; + void *handle; { + size_t i, j, len; + char inbuf[3], outbuf[3], *inp, *outp; + *c = *isofn++; + if (clen) *clen = 1; if (joliet_level == 0 || isofn == isoend) /* (00) and (01) are one byte in Joliet, too */ return 1; - /* No Unicode support yet :-( */ - switch (*c) { - default: - *c = '?'; - break; - case '\0': - *c = *isofn; - break; + if (flags & ISOFSMNT_KICONV && cd9660_iconv) { + i = j = len = 2; + inbuf[0]=(char)*(isofn - 1); + inbuf[1]=(char)*isofn; + inbuf[2]='\0'; + inp = inbuf; + outp = outbuf; + cd9660_iconv->convchr(handle, (const char **)&inp, &i, &outp, &j); + len -= j; + if (clen) *clen = len; + *c = '\0'; + while(len--) + *c |= (*(outp - len - 1) & 0xff) << (len << 3); + } else { + switch (*c) { + default: + *c = '?'; + break; + case '\0': + *c = *isofn; + break; + } } - /* XXX: if Unicode conversion routine is loaded then use it */ - if (cd9660_wchar2char != NULL) - *c = cd9660_wchar2char((*(isofn - 1) << 8) | *isofn); return 2; } @@ -96,53 +110,60 @@ isochar(isofn, isoend, joliet_level, c) * Note: Version number plus ';' may be omitted. */ int -isofncmp(fn, fnlen, isofn, isolen, joliet_level) +isofncmp(fn, fnlen, isofn, isolen, joliet_level, flags, handle, lhandle) u_char *fn; int fnlen; u_char *isofn; int isolen; int joliet_level; + int flags; + void *handle; + void *lhandle; { int i, j; - u_char c, *fnend = fn + fnlen, *isoend = isofn + isolen; + u_short c, d; + u_char *fnend = fn + fnlen, *isoend = isofn + isolen; - for (; fn != fnend; fn++) { + for (; fn < fnend; ) { + d = sgetrune(fn, fnend - fn, (char const **)&fn, flags, lhandle); if (isofn == isoend) - return *fn; - isofn += isochar(isofn, isoend, joliet_level, &c); + return d; + isofn += isochar(isofn, isoend, joliet_level, &c, NULL, flags, handle); if (c == ';') { - if (*fn++ != ';') - return fn[-1]; - for (i = 0; fn != fnend; i = i * 10 + *fn++ - '0') { + if (d != ';') + return d; + for (i = 0; fn < fnend; i = i * 10 + *fn++ - '0') { if (*fn < '0' || *fn > '9') { return -1; } } for (j = 0; isofn != isoend; j = j * 10 + c - '0') isofn += isochar(isofn, isoend, - joliet_level, &c); + joliet_level, &c, + NULL, flags, handle); return i - j; } - if (c != *fn) { + if (c != d) { if (c >= 'A' && c <= 'Z') { - if (c + ('a' - 'A') != *fn) { - if (*fn >= 'a' && *fn <= 'z') - return *fn - ('a' - 'A') - c; + if (c + ('a' - 'A') != d) { + if (d >= 'a' && d <= 'z') + return d - ('a' - 'A') - c; else - return *fn - c; + return d - c; } } else - return *fn - c; + return d - c; } } if (isofn != isoend) { - isofn += isochar(isofn, isoend, joliet_level, &c); + isofn += isochar(isofn, isoend, joliet_level, &c, NULL, flags, handle); switch (c) { default: return -c; case '.': if (isofn != isoend) { - isochar(isofn, isoend, joliet_level, &c); + isochar(isofn, isoend, joliet_level, &c, + NULL, flags, handle); if (c == ';') return 0; } @@ -158,7 +179,7 @@ isofncmp(fn, fnlen, isofn, isolen, joliet_level) * translate a filename of length > 0 */ void -isofntrans(infn, infnlen, outfn, outfnlen, original, assoc, joliet_level) +isofntrans(infn, infnlen, outfn, outfnlen, original, assoc, joliet_level, flags, handle) u_char *infn; int infnlen; u_char *outfn; @@ -166,25 +187,61 @@ isofntrans(infn, infnlen, outfn, outfnlen, original, assoc, joliet_level) int original; int assoc; int joliet_level; + int flags; + void *handle; { - int fnidx = 0; - u_char c, d = '\0', *infnend = infn + infnlen; + u_short c, d = '\0'; + u_char *outp = outfn, *infnend = infn + infnlen; + int clen; if (assoc) { - *outfn++ = ASSOCCHAR; - fnidx++; + *outp++ = ASSOCCHAR; } - for (; infn != infnend; fnidx++) { - infn += isochar(infn, infnend, joliet_level, &c); + for (; infn != infnend; ) { + infn += isochar(infn, infnend, joliet_level, &c, &clen, flags, handle); if (!original && !joliet_level && c >= 'A' && c <= 'Z') - *outfn++ = c + ('a' - 'A'); + c += ('a' - 'A'); else if (!original && c == ';') { - fnidx -= (d == '.'); + outp -= (d == '.'); break; - } else - *outfn++ = c; + } d = c; + while(clen--) + *outp++ = c >> (clen << 3); } - *outfnlen = fnidx; + *outfnlen = outp - outfn; +} + +/* + * same as sgetrune(3) + */ +u_short +sgetrune(string, n, result, flags, handle) + const char *string; + size_t n; + char const **result; + int flags; + void *handle; +{ + size_t i, j, len; + char outbuf[3], *outp; + u_short c = '\0'; + + len = i = (n < 2) ? n : 2; + j = 2; + outp = outbuf; + + if (flags & ISOFSMNT_KICONV && cd9660_iconv) { + cd9660_iconv->convchr(handle, (const char **)&string, + &i, &outp, &j); + len -= i; + } else { + len = 1; + string++; + } + + if (result) *result = string; + while(len--) c |= (*(string - len - 1) & 0xff) << (len << 3); + return (c); } diff --git a/sys/fs/cd9660/cd9660_vfsops.c b/sys/fs/cd9660/cd9660_vfsops.c index 8df32e0..0f8f3c9 100644 --- a/sys/fs/cd9660/cd9660_vfsops.c +++ b/sys/fs/cd9660/cd9660_vfsops.c @@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/stat.h> #include <sys/syslog.h> +#include <sys/iconv.h> #include <isofs/cd9660/iso.h> @@ -66,6 +67,8 @@ __FBSDID("$FreeBSD$"); MALLOC_DEFINE(M_ISOFSMNT, "ISOFS mount", "ISOFS mount structure"); MALLOC_DEFINE(M_ISOFSNODE, "ISOFS node", "ISOFS vnode private part"); +struct iconv_functions *cd9660_iconv = NULL; + static vfs_mount_t cd9660_mount; static vfs_unmount_t cd9660_unmount; static vfs_root_t cd9660_root; @@ -471,7 +474,16 @@ iso_mountfs(devvp, mp, td, argp) bp = NULL; } isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS | - ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET); + ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET | + ISOFSMNT_KICONV); + + if (isomp->im_flags & ISOFSMNT_KICONV && cd9660_iconv) { + cd9660_iconv->open(argp->cs_local, argp->cs_disk, &isomp->im_d2l); + cd9660_iconv->open(argp->cs_disk, argp->cs_local, &isomp->im_l2d); + } else { + isomp->im_d2l = NULL; + isomp->im_l2d = NULL; + } if (high_sierra) { /* this effectively ignores all the mount flags */ @@ -551,6 +563,12 @@ cd9660_unmount(mp, mntflags, td) isomp = VFSTOISOFS(mp); + if (isomp->im_flags & ISOFSMNT_KICONV && cd9660_iconv) { + if (isomp->im_d2l) + cd9660_iconv->close(isomp->im_d2l); + if (isomp->im_l2d) + cd9660_iconv->close(isomp->im_l2d); + } isomp->im_devvp->v_rdev->si_mountpoint = NULL; error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, td); vrele(isomp->im_devvp); diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c index bf95bf7..c18b664 100644 --- a/sys/fs/cd9660/cd9660_vnops.c +++ b/sys/fs/cd9660/cd9660_vnops.c @@ -562,7 +562,9 @@ cd9660_readdir(ap) idp->current.d_name, &namelen, imp->iso_ftype == ISO_FTYPE_9660, isonum_711(ep->flags)&4, - imp->joliet_level); + imp->joliet_level, + imp->im_flags, + imp->im_d2l); idp->current.d_namlen = (u_char)namelen; if (imp->iso_ftype == ISO_FTYPE_DEFAULT) error = iso_shipdir(idp); diff --git a/sys/fs/cd9660/iso.h b/sys/fs/cd9660/iso.h index b41cd0c..15766ef 100644 --- a/sys/fs/cd9660/iso.h +++ b/sys/fs/cd9660/iso.h @@ -245,6 +245,9 @@ struct iso_mnt { int rr_skip0; int joliet_level; + + void *im_d2l; + void *im_l2d; }; #define VFSTOISOFS(mp) ((struct iso_mnt *)((mp)->mnt_data)) @@ -265,10 +268,11 @@ extern vop_t **cd9660_vnodeop_p; extern vop_t **cd9660_specop_p; extern vop_t **cd9660_fifoop_p; -int isochar(u_char *, u_char *, int, u_char *); -int isofncmp(u_char *, int, u_char *, int, int); -void isofntrans(u_char *, int, u_char *, u_short *, int, int, int); +int isochar(u_char *, u_char *, int, u_short *, int *, int, void *); +int isofncmp(u_char *, int, u_char *, int, int, int, void *, void *); +void isofntrans(u_char *, int, u_char *, u_short *, int, int, int, int, void *); ino_t isodirino(struct iso_directory_record *, struct iso_mnt *); +u_short sgetrune(const char *, size_t, char const **, int, void *); #endif /* _KERNEL */ diff --git a/sys/fs/msdosfs/direntry.h b/sys/fs/msdosfs/direntry.h index 8ed5335..c54cbe3 100644 --- a/sys/fs/msdosfs/direntry.h +++ b/sys/fs/msdosfs/direntry.h @@ -98,6 +98,11 @@ struct winentry { #define WIN_CHARS 13 /* Number of chars per winentry */ /* + * Maximum number of winentries for a filename + */ +#define WIN_MAXSUBENTRIES 20 + +/* * Maximum filename length in Win95 * Note: Must be < sizeof(dirent.d_name) */ @@ -132,12 +137,15 @@ struct dirent; 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, struct dirent *dp, 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(const u_char *un, int unlen, int chksum, struct msdosfsmount *pmp); +int win2unixfn(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 *); int winLenFixup(const u_char *un, int unlen); +void mbnambuf_init(void); +void mbnambuf_write(char *name, int id); +char * mbnambuf_flush(struct dirent *dp); #endif /* _KERNEL */ diff --git a/sys/fs/msdosfs/msdosfs_conv.c b/sys/fs/msdosfs/msdosfs_conv.c index d135bfa..5c663c8 100644 --- a/sys/fs/msdosfs/msdosfs_conv.c +++ b/sys/fs/msdosfs/msdosfs_conv.c @@ -57,10 +57,17 @@ #include <sys/systm.h> #include <machine/clock.h> #include <sys/dirent.h> +#include <sys/iconv.h> +#include <sys/mount.h> +#include <sys/malloc.h> + +extern struct iconv_functions *msdosfs_iconv; /* * MSDOSFS include files. */ +#include <fs/msdosfs/bpb.h> +#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/direntry.h> /* @@ -88,7 +95,17 @@ static u_long lastday; static u_short lastddate; static u_short lastdtime; -static __inline u_int8_t find_lcode(u_int16_t code, u_int16_t *u2w); +static int mbsadjpos(const char **, int, int, int, int, void *handle); +static u_int16_t dos2unixchr(const u_char **, size_t *, int, struct msdosfsmount *); +static u_int16_t unix2doschr(const u_char **, size_t *, struct msdosfsmount *); +static u_int16_t win2unixchr(u_int16_t, struct msdosfsmount *); +static u_int16_t unix2winchr(const u_char **, size_t *, int, struct msdosfsmount *); + +struct mbnambuf { + char * p; + size_t n; +}; +static struct mbnambuf subent[WIN_MAXSUBENTRIES]; /* * Convert the unix version of time to dos's idea of time to be used in @@ -239,6 +256,7 @@ dos2unixtime(dd, dt, dh, tsp) */ static u_char unix2dos[256] = { +/* iso8859-1 -> cp850 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 00-07 */ 0, 0, 0, 0, 0, 0, 0, 0, /* 08-0f */ 0, 0, 0, 0, 0, 0, 0, 0, /* 10-17 */ @@ -275,6 +293,7 @@ unix2dos[256] = { static u_char dos2unix[256] = { +/* cp850 -> iso8859-1 */ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 00-07 */ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 08-0f */ 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, /* 10-17 */ @@ -311,6 +330,7 @@ dos2unix[256] = { static u_char u2l[256] = { +/* tolower */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ @@ -347,6 +367,7 @@ u2l[256] = { static u_char l2u[256] = { +/* toupper */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 00-07 */ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 08-0f */ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 10-17 */ @@ -394,18 +415,15 @@ l2u[256] = { * null. */ int -dos2unixfn(dn, un, lower, d2u_loaded, d2u, ul_loaded, ul) +dos2unixfn(dn, un, lower, pmp) u_char dn[11]; u_char *un; int lower; - int d2u_loaded; - u_int8_t *d2u; - int ul_loaded; - u_int8_t *ul; + struct msdosfsmount *pmp; { int i; - int thislong = 1; - u_char c; + int thislong = 0; + u_int16_t c; /* * If first char of the filename is SLOT_E5 (0x05), then the real @@ -414,26 +432,21 @@ dos2unixfn(dn, un, lower, d2u_loaded, d2u, ul_loaded, ul) * 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; + for (i = 8; i > 0 && *dn != ' ';) { + c = dos2unixchr((const u_char **)&dn, (size_t *)&i, lower, pmp); + if (c & 0xff00) { + *un++ = c >> 8; + thislong++; + } + *un++ = c; thislong++; } - dn += 8 - i; + dn += i; /* * Now, if there is an extension then put in a period and copy in @@ -442,12 +455,13 @@ dos2unixfn(dn, un, lower, d2u_loaded, d2u, ul_loaded, ul) 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; + for (i = 3; i > 0 && *dn != ' ';) { + c = dos2unixchr((const u_char **)&dn, (size_t *)&i, lower, pmp); + if (c & 0xff00) { + *un++ = c >> 8; + thislong++; + } + *un++ = c; thislong++; } } @@ -468,22 +482,18 @@ dos2unixfn(dn, un, lower, d2u_loaded, d2u, ul_loaded, ul) * 3 if conversion was successful and generation number was inserted */ int -unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) +unix2dosfn(un, dn, unlen, gen, pmp) 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; + 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]) + u_int16_t c; /* * Fill the dos filename string with blanks. These are DOS's pad @@ -520,14 +530,18 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) /* * Filenames with some characters are not allowed! */ - for (cp = un, i = unlen; --i >= 0; cp++) - if (U2D(*cp) == 0) + 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;) { @@ -547,20 +561,29 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) } /* - * Now convert it + * Now convert it (this part is for extension) */ 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) + 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; + continue; + } else { + conv = 3; + dn[j-1] = ' '; + break; + } + } else { + dn[j] = c; + } + if (*(cp - 1) != dn[j] && conv != 3) conv = 2; if (dn[j] == 1) { conv = 3; @@ -571,7 +594,7 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) dn[j--] = ' '; } } - if (i < l) + if (i > 0) conv = 3; dp--; } else { @@ -582,12 +605,22 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) /* * 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) + for (i = dp - un, j = 0; un < dp && j < 8; j++) { + c = unix2doschr(&un, (size_t *)&i, pmp); + if (c & 0xff00) { + dn[j] = c >> 8; + if (++j < 8) { + dn[j] = c; + continue; + } else { + conv = 3; + dn[j-1] = ' '; + break; + } + } else { + dn[j] = c; + } + if (*(un - 1) != dn[j] && conv != 3) conv = 2; if (dn[j] == 1) { conv = 3; @@ -608,40 +641,56 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) 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; + conv = 0; + goto done; } /* * Now insert the generation number into the filename part */ if (gen == 0) - return conv; + goto done; for (wcp = gentext + sizeof(gentext); wcp > gentext && gen; gen /= 10) *--wcp = gen % 10 + '0'; - if (gen) - return 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++; - return 3; -#undef U2D + + /* + * 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; } /* @@ -650,27 +699,28 @@ unix2dosfn(un, dn, unlen, gen, u2d_loaded, u2d, lu_loaded, lu) * i.e. doesn't consist solely of blanks and dots */ int -unix2winfn(un, unlen, wep, cnt, chksum, table_loaded, u2w) +unix2winfn(un, unlen, wep, cnt, chksum, pmp) const u_char *un; int unlen; struct winentry *wep; int cnt; int chksum; - int table_loaded; - u_int16_t *u2w; + struct msdosfsmount *pmp; { - const u_int8_t *cp; u_int8_t *wcp; - int i; + int i, end; u_int16_t code; /* * Drop trailing blanks and dots */ - for (cp = un + unlen; *--cp == ' ' || *cp == '.'; unlen--); + 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 @@ -685,64 +735,32 @@ unix2winfn(un, unlen, wep, cnt, chksum, table_loaded, u2w) /* * 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; - } + end = 0; + for (wcp = wep->wePart1, i = sizeof(wep->wePart1)/2; --i >= 0 && !end;) { + code = unix2winchr(&un, (size_t *)&unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; } - 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->wePart2, i = sizeof(wep->wePart2)/2; --i >= 0 && !end;) { + code = unix2winchr(&un, (size_t *)&unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; } - 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; - } + for (wcp = wep->wePart3, i = sizeof(wep->wePart3)/2; --i >= 0 && !end;) { + code = unix2winchr(&un, (size_t *)&unlen, 0, pmp); + *wcp++ = code; + *wcp++ = code >> 8; + if (!code) + end = WIN_LAST; } - if (!unlen) - wep->weCnt |= WIN_LAST; - return unlen; - -done: - *wcp++ = 0; - *wcp++ = 0; - wep->weCnt |= WIN_LAST; - return 0; -} - -static __inline u_int8_t -find_lcode(code, u2w) - u_int16_t code; - u_int16_t *u2w; -{ - int i; - - for (i = 0; i < 128; i++) - if (u2w[i] == code) - return (i | 0x80); - return '?'; + if (*un == '\0') + end = WIN_LAST; + wep->weCnt |= end; + return !end; } /* @@ -750,116 +768,44 @@ find_lcode(code, u2w) * Returns the checksum or -1 if no match */ int -winChkName(un, unlen, wep, chksum, u2w_loaded, u2w, ul_loaded, ul) +winChkName(un, unlen, chksum, pmp) 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; + struct msdosfsmount *pmp; { - u_int8_t *cp; - int i; - u_int16_t code; - u_int8_t c1, c2; + int len; + u_int16_t c1, c2; + u_char *np; + struct dirent dirbuf; /* - * First compare checksums + * We alread have winentry in mbnambuf */ - if (wep->weCnt&WIN_LAST) - chksum = wep->weChksum; - else if (chksum != wep->weChksum) - chksum = -1; - if (chksum == -1) + if (!mbnambuf_flush(&dirbuf) || !dirbuf.d_namlen) return -1; - /* - * Offset of this entry - */ - i = ((wep->weCnt&WIN_CNT) - 1) * WIN_CHARS; - un += i; - unlen -= i; - - /* - * 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; +#ifdef MSDOSFS_DEBUG + printf("winChkName(): un=%s:%d,d_name=%s:%d\n", un, unlen, + dirbuf.d_name, + dirbuf.d_namlen); +#endif /* * 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]; + len = dirbuf.d_namlen; + if (unlen != len) + return -2; + + for (np = dirbuf.d_name; unlen > 0 && len > 0;) { + /* + * Should comparison be case insensitive? + */ + c1 = unix2winchr((const u_char **)&np, (size_t *)&len, 0, pmp); + c2 = unix2winchr(&un, (size_t *)&unlen, 0, pmp); if (c1 != c2) - return -1; - cp += 2; - un++; + return -2; } return chksum; } @@ -869,15 +815,13 @@ winChkName(un, unlen, wep, chksum, u2w_loaded, u2w, ul_loaded, ul) * Returns the checksum or -1 if impossible */ int -win2unixfn(wep, dp, chksum, table_loaded, u2w) +win2unixfn(wep, chksum, pmp) struct winentry *wep; - struct dirent *dp; int chksum; - int table_loaded; - u_int16_t *u2w; + struct msdosfsmount *pmp; { u_int8_t *cp; - u_int8_t *np, *ep = dp->d_name + WIN_MAXLEN; + u_int8_t *np, name[WIN_CHARS * 2 + 1]; u_int16_t code; int i; @@ -890,54 +834,32 @@ win2unixfn(wep, dp, chksum, table_loaded, u2w) */ if (wep->weCnt&WIN_LAST) { chksum = wep->weChksum; - /* - * This works even though d_namlen is one byte! - */ - dp->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 *)dp->d_name + i; - - /* * 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'; - dp->d_namlen -= sizeof(wep->wePart2)/2 - + sizeof(wep->wePart3)/2 + i + 1; + mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 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 = '?'; - } + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; *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;) { @@ -945,30 +867,18 @@ win2unixfn(wep, dp, chksum, table_loaded, u2w) switch (code) { case 0: *np = '\0'; - dp->d_namlen -= sizeof(wep->wePart3)/2 + i + 1; + mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 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 = '?'; - } + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; *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;) { @@ -976,31 +886,22 @@ win2unixfn(wep, dp, chksum, table_loaded, u2w) switch (code) { case 0: *np = '\0'; - dp->d_namlen -= i + 1; + mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 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 = '?'; - } + code = win2unixchr(code, pmp); + if (code & 0xff00) + *np++ = code >> 8; *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; } + *np = '\0'; + mbnambuf_write(name, (wep->weCnt & WIN_CNT) - 1); return chksum; } @@ -1023,11 +924,25 @@ winChksum(name) * Determine the number of slots necessary for Win95 names */ int -winSlotCnt(un, unlen) +winSlotCnt(un, unlen, pmp) const u_char *un; int unlen; + struct msdosfsmount *pmp; { + size_t wlen; + char wn[WIN_MAXLEN * 2 + 1], *wnp; + unlen = winLenFixup(un, unlen); + + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { + wlen = WIN_MAXLEN * 2; + wnp = wn; + msdosfs_iconv->conv(pmp->pm_u2w, (const char **)&un, (size_t *)&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); @@ -1046,3 +961,281 @@ winLenFixup(un, unlen) break; return unlen; } + +/* + * 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, int inlen, int outlen, int weight, int flag, void *handle) +{ + char *outp, outstr[outlen * weight + 1]; + + if (flag & MSDOSFSMNT_KICONV && msdosfs_iconv) { + outp = outstr; + outlen *= weight; + msdosfs_iconv->conv(handle, instr, (size_t *)&inlen, &outp, (size_t *)&outlen); + return (inlen); + } + + (*instr) += min(inlen, outlen); + return (inlen - min(inlen, outlen)); +} + +/* + * 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 && msdosfs_iconv) { + olen = len = 2; + outp = outbuf; + + if (lower & (LCASE_BASE | LCASE_EXT)) + msdosfs_iconv->convchr_case(pmp->pm_d2u, (const char **)instr, + ilen, &outp, &olen, KICONV_LOWER); + else + msdosfs_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); +} + +/* + * 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, ulen, olen; + + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { + /* + * to hide an invisible character, using a unicode filter + */ + ulen = 2; + len = *ilen; + up = unicode; + msdosfs_iconv->convchr(pmp->pm_u2w, (const char **)instr, + ilen, &up, &ulen); + + /* + * cannot be converted + */ + if (ulen == 2) { + (*ilen)--; + (*instr)++; + return (0); + } + + /* + * return magic number for ascii char + */ + if ((len - *ilen) == 1) { + c = *(*instr -1); + if (! (c & 0x80)) { + c = unix2dos[c]; + if (c <= 2) + return (c); + } + } + + /* + * now convert using libiconv + */ + *instr -= len - *ilen; + *ilen = (int)len; + + olen = len = 2; + outp = outbuf; + msdosfs_iconv->convchr_case(pmp->pm_u2d, (const char **)instr, + ilen, &outp, &olen, KICONV_FROM_UPPER); + len -= olen; + 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 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 && msdosfs_iconv) { + inbuf[0] = (u_char)(wc>>8); + inbuf[1] = (u_char)wc; + inbuf[2] = '\0'; + + ilen = olen = len = 2; + inp = inbuf; + outp = outbuf; + msdosfs_iconv->convchr(pmp->pm_w2u, (const char **)&inp, &ilen, + (char **)&outp, &olen); + len -= olen; + + /* + * 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); +} + +/* + * 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 && msdosfs_iconv) { + outp = outbuf; + olen = 2; + if (lower & (LCASE_BASE | LCASE_EXT)) + msdosfs_iconv->convchr_case(pmp->pm_u2w, (const char **)instr, + ilen, (char **)&outp, &olen, + KICONV_FROM_LOWER); + else + msdosfs_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); +} + +/* + * Make subent empty + */ +void +mbnambuf_init(void) +{ + int i; + + for (i = 0; i < WIN_MAXSUBENTRIES; i++) { + if (subent[i].p) { + free(subent[i].p, M_MSDOSFSMNT); + subent[i].p = NULL; + subent[i].n = 0; + } + } +} + +/* + * Write a subent entry from a slot + */ +void +mbnambuf_write(char *name, int id) +{ + if (subent[id].p) { + printf("mbnambuf_write(): %s -> %s\n", subent[id].p, name); + free(subent[id].p, M_MSDOSFSMNT); + } + subent[id].n = strlen(name); + subent[id].p = malloc(subent[id].n + 1, M_MSDOSFSMNT, M_WAITOK); + strcpy(subent[id].p, name); +} + +/* + * Combine each subents to the *dp and initialize subent + */ +char * +mbnambuf_flush(struct dirent *dp) +{ + int i; + char *name = dp->d_name; + + *name = '\0'; + dp->d_namlen = 0; + for (i = 0; i < WIN_MAXSUBENTRIES; i++) { + if (subent[i].p) { + if (dp->d_namlen + subent[i].n > sizeof(dp->d_name) - 1) { + mbnambuf_init(); + return (NULL); + } + strcpy(name, subent[i].p); + dp->d_namlen += subent[i].n; + name += subent[i].n; + } + } + mbnambuf_init(); + return (dp->d_name); +} diff --git a/sys/fs/msdosfs/msdosfs_iconv.c b/sys/fs/msdosfs/msdosfs_iconv.c new file mode 100644 index 0000000..26ef1a4 --- /dev/null +++ b/sys/fs/msdosfs/msdosfs_iconv.c @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003 Ryuichiro Imura + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/iconv.h> + +VFS_DECLARE_ICONV(msdosfs); diff --git a/sys/fs/msdosfs/msdosfs_lookup.c b/sys/fs/msdosfs/msdosfs_lookup.c index 00598c8..1492098 100644 --- a/sys/fs/msdosfs/msdosfs_lookup.c +++ b/sys/fs/msdosfs/msdosfs_lookup.c @@ -57,9 +57,9 @@ #include <sys/mount.h> #include <fs/msdosfs/bpb.h> +#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/direntry.h> #include <fs/msdosfs/denode.h> -#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/fat.h> /* @@ -150,21 +150,19 @@ msdosfs_lookup(ap) } 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) { @@ -193,6 +191,7 @@ msdosfs_lookup(ap) * by cnp->cn_nameptr. */ tdp = NULL; + mbnambuf_init(); /* * The outer loop ranges over the clusters that make up the * directory. Note that the root directory is different from all @@ -232,6 +231,7 @@ msdosfs_lookup(ap) * Drop memory of previous long matches */ chksum = -1; + mbnambuf_init(); if (slotcount < wincnt) { slotcount++; @@ -256,14 +256,18 @@ msdosfs_lookup(ap) if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; - chksum = winChkName((const u_char *)cnp->cn_nameptr, - unlen, - (struct winentry *)dep, + chksum = win2unixfn((struct winentry *)dep, chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w, - pmp->pm_flags & MSDOSFSMNT_ULTABLE, - pmp->pm_ul); + pmp); + continue; + } + + chksum = winChkName((const u_char *)cnp->cn_nameptr, + unlen, + chksum, + pmp); + if (chksum == -2) { + chksum = -1; continue; } @@ -659,9 +663,7 @@ createde(dep, ddep, depp, cnp) ddep->de_fndoffset -= sizeof(struct direntry); } if (!unix2winfn(un, unlen, (struct winentry *)ndep, - cnt++, chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w)) + cnt++, chksum, pmp)) break; } } @@ -992,19 +994,14 @@ uniqdosname(dep, cnp, 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) ? - 0 : EINVAL); + cnp->cn_namelen, 0, pmp) ? 0 : EINVAL); for (gen = 1;; gen++) { /* * 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/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c index 11a3d6d..875d580 100644 --- a/sys/fs/msdosfs/msdosfs_vfsops.c +++ b/sys/fs/msdosfs/msdosfs_vfsops.c @@ -61,13 +61,14 @@ #include <sys/fcntl.h> #include <sys/malloc.h> #include <sys/stat.h> /* defines ALLPERMS */ +#include <sys/iconv.h> #include <sys/mutex.h> #include <fs/msdosfs/bpb.h> #include <fs/msdosfs/bootsect.h> +#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/direntry.h> #include <fs/msdosfs/denode.h> -#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/fat.h> #define MSDOSFS_DFLTBSIZE 4096 @@ -86,6 +87,8 @@ MALLOC_DEFINE(M_MSDOSFSMNT, "MSDOSFS mount", "MSDOSFS mount structure"); static MALLOC_DEFINE(M_MSDOSFSFAT, "MSDOSFS FAT", "MSDOSFS file allocation table"); +struct iconv_functions *msdosfs_iconv = NULL; + static int update_mp(struct mount *mp, struct msdosfs_args *argp); static int mountmsdosfs(struct vnode *devvp, struct mount *mp, struct thread *td, struct msdosfs_args *argp); @@ -110,14 +113,16 @@ update_mp(mp, argp) pmp->pm_mask = argp->mask & ALLPERMS; pmp->pm_dirmask = argp->dirmask & 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 && msdosfs_iconv) { + msdosfs_iconv->open(argp->cs_win, argp->cs_local , &pmp->pm_u2w); + msdosfs_iconv->open(argp->cs_local, argp->cs_win , &pmp->pm_w2u); + msdosfs_iconv->open(argp->cs_dos, argp->cs_local , &pmp->pm_u2d); + msdosfs_iconv->open(argp->cs_local, argp->cs_dos , &pmp->pm_d2u); + } else { + pmp->pm_w2u = NULL; + pmp->pm_u2w = NULL; + pmp->pm_d2u = NULL; + pmp->pm_u2d = NULL; } if (pmp->pm_flags & MSDOSFSMNT_NOWIN95) @@ -651,6 +656,16 @@ msdosfs_unmount(mp, mntflags, td) if (error) return error; pmp = VFSTOMSDOSFS(mp); + if (pmp->pm_flags & MSDOSFSMNT_KICONV && msdosfs_iconv) { + if (pmp->pm_w2u) + msdosfs_iconv->close(pmp->pm_w2u); + if (pmp->pm_u2w) + msdosfs_iconv->close(pmp->pm_u2w); + if (pmp->pm_d2u) + msdosfs_iconv->close(pmp->pm_d2u); + if (pmp->pm_u2d) + msdosfs_iconv->close(pmp->pm_u2d); + } pmp->pm_devvp->v_rdev->si_mountpoint = NULL; #ifdef MSDOSFS_DEBUG { @@ -864,3 +879,4 @@ static struct vfsops msdosfs_vfsops = { }; VFS_SET(msdosfs_vfsops, msdosfs, 0); +MODULE_VERSION(msdosfs, 1); diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c index ba36c5b..a4c60cc 100644 --- a/sys/fs/msdosfs/msdosfs_vnops.c +++ b/sys/fs/msdosfs/msdosfs_vnops.c @@ -71,9 +71,9 @@ #include <machine/mutex.h> #include <fs/msdosfs/bpb.h> +#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/direntry.h> #include <fs/msdosfs/denode.h> -#include <fs/msdosfs/msdosfsmount.h> #include <fs/msdosfs/fat.h> #define DOS_FILESIZE_MAX 0xffffffff @@ -1554,6 +1554,7 @@ msdosfs_readdir(ap) } } + mbnambuf_init(); off = offset; while (uio->uio_resid > 0) { lbn = de_cluster(pmp, offset - bias); @@ -1600,6 +1601,7 @@ msdosfs_readdir(ap) */ if (dentp->deName[0] == SLOT_DELETED) { chksum = -1; + mbnambuf_init(); continue; } @@ -1610,9 +1612,7 @@ msdosfs_readdir(ap) if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; chksum = win2unixfn((struct winentry *)dentp, - &dirbuf, chksum, - pmp->pm_flags & MSDOSFSMNT_U2WTABLE, - pmp->pm_u2w); + chksum, pmp); continue; } @@ -1621,6 +1621,7 @@ msdosfs_readdir(ap) */ if (dentp->deAttributes & ATTR_VOLUME) { chksum = -1; + mbnambuf_init(); continue; } /* @@ -1648,18 +1649,16 @@ msdosfs_readdir(ap) dirbuf.d_fileno = offset / sizeof(struct direntry); dirbuf.d_type = DT_REG; } - if (chksum != winChksum(dentp->deName)) + if (chksum != winChksum(dentp->deName)) { dirbuf.d_namlen = dos2unixfn(dentp->deName, (u_char *)dirbuf.d_name, 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); - else - dirbuf.d_name[dirbuf.d_namlen] = 0; + pmp); + mbnambuf_init(); + } else + mbnambuf_flush(&dirbuf); chksum = -1; dirbuf.d_reclen = GENERIC_DIRSIZ(&dirbuf); if (uio->uio_resid < dirbuf.d_reclen) { diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h index d525771..468677a 100644 --- a/sys/fs/msdosfs/msdosfsmount.h +++ b/sys/fs/msdosfs/msdosfsmount.h @@ -95,11 +95,10 @@ struct msdosfsmount { u_int pm_curfat; /* current fat for FAT32 (0 otherwise) */ u_int *pm_inusemap; /* ptr to bitmap of in-use clusters */ u_int pm_flags; /* see below */ - 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 iconv handle */ + void *pm_w2u; /* Unicode->Local iconv handle */ + void *pm_u2d; /* Unicode->DOS iconv handle */ + void *pm_d2u; /* DOS->Local iconv handle */ }; /* Byte offset in FAT on filesystem pmp, cluster cn */ #define FATOFS(pmp, cn) ((cn) * (pmp)->pm_fatmult / (pmp)->pm_fatdiv) @@ -218,10 +217,9 @@ struct msdosfs_args { 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_win; /* Windows(Unicode) Charset */ + char *cs_dos; /* DOS Charset */ + char *cs_local; /* Local Charset */ }; /* @@ -230,13 +228,11 @@ 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 */ - /* tables loaded */ -#define MSDOSFSMNT_ULTABLE 0x20 /* Local upper<->lower table loaded */ +#define MSDOSFSMNT_KICONV 0x10 /* Use libiconv to convert chars */ /* All flags above: */ #define MSDOSFSMNT_MNTOPT \ (MSDOSFSMNT_SHORTNAME|MSDOSFSMNT_LONGNAME|MSDOSFSMNT_NOWIN95 \ - |MSDOSFSMNT_U2WTABLE|MSDOSFSMNT_ULTABLE) + |MSDOSFSMNT_KICONV) #define MSDOSFSMNT_RONLY 0x80000000 /* mounted read-only */ #define MSDOSFSMNT_WAITONFAT 0x40000000 /* mounted synchronous */ #define MSDOSFS_FATMIRROR 0x20000000 /* FAT is mirrored */ diff --git a/sys/fs/ntfs/ntfs.h b/sys/fs/ntfs/ntfs.h index fba06c1..6e19029 100644 --- a/sys/fs/ntfs/ntfs.h +++ b/sys/fs/ntfs/ntfs.h @@ -254,6 +254,8 @@ struct ntfsmount { int ntm_adnum; wchar * ntm_82u; /* 8bit to Unicode */ char ** ntm_u28; /* Unicode to 8 bit */ + void * ntm_ic_l2u; /* Local to Unicode (iconv) */ + void * ntm_ic_u2l; /* Unicode to Local (iconv) */ }; #define ntm_mftcn ntm_bootfile.bf_mftcn diff --git a/sys/fs/ntfs/ntfs_iconv.c b/sys/fs/ntfs/ntfs_iconv.c new file mode 100644 index 0000000..347306e --- /dev/null +++ b/sys/fs/ntfs/ntfs_iconv.c @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 2003 Ryuichiro Imura + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/mount.h> +#include <sys/iconv.h> + +VFS_DECLARE_ICONV(ntfs); diff --git a/sys/fs/ntfs/ntfs_subr.c b/sys/fs/ntfs/ntfs_subr.c index cf2879c..aff97a3 100644 --- a/sys/fs/ntfs/ntfs_subr.c +++ b/sys/fs/ntfs/ntfs_subr.c @@ -40,6 +40,7 @@ #include <sys/file.h> #include <sys/malloc.h> #include <sys/lock.h> +#include <sys/iconv.h> /* #define NTFS_DEBUG 1 */ #include <fs/ntfs/ntfs.h> @@ -67,6 +68,8 @@ static wchar *ntfs_toupper_tab; static struct lock ntfs_toupper_lock; static signed int ntfs_toupper_usecount; +struct iconv_functions *ntfs_iconv = NULL; + /* support macro for ntfs_ntvattrget() */ #define NTFS_AALPCMP(aalp,type,name,namelen) ( \ (aalp->al_type == type) && (aalp->al_namelen == namelen) && \ @@ -665,20 +668,41 @@ ntfs_uastricmp(ntmp, ustr, ustrlen, astr, astrlen) const char *astr; size_t astrlen; { - size_t i; - int res; + int len; + size_t i, j, mbstrlen = astrlen; + int res; + wchar wc; + + if (ntmp->ntm_ic_l2u) { + for (i = 0, j = 0; i < ustrlen && j < astrlen; i++, j++) { + if (j < astrlen -1) { + wc = (wchar)astr[j]<<8 | (astr[j+1]&0xFF); + len = 2; + } else { + wc = (wchar)astr[j]<<8 & 0xFF00; + len = 1; + } + res = ((int) NTFS_TOUPPER(ustr[i])) - + ((int)NTFS_TOUPPER(NTFS_82U(wc, &len))); + j += len - 1; + mbstrlen -= len - 1; - /* - * XXX We use NTFS_82U(NTFS_U28(c)) to get rid of unicode - * symbols not covered by translation table - */ - for (i = 0; i < ustrlen && i < astrlen; i++) { - res = ((int) NTFS_TOUPPER(NTFS_82U(NTFS_U28(ustr[i])))) - - ((int)NTFS_TOUPPER(NTFS_82U(astr[i]))); - if (res) - return res; + if (res) + return res; + } + } else { + /* + * We use NTFS_82U(NTFS_U28(c)) to get rid of unicode + * symbols not covered by translation table + */ + for (i = 0; i < ustrlen && i < astrlen; i++) { + res = ((int) NTFS_TOUPPER(NTFS_82U(NTFS_U28(ustr[i]), &len))) - + ((int)NTFS_TOUPPER(NTFS_82U((wchar)astr[i], &len))); + if (res) + return res; + } } - return (ustrlen - astrlen); + return (ustrlen - mbstrlen); } /* @@ -692,15 +716,25 @@ ntfs_uastrcmp(ntmp, ustr, ustrlen, astr, astrlen) const char *astr; size_t astrlen; { - size_t i; - int res; - - for (i = 0; (i < ustrlen) && (i < astrlen); i++) { - res = (int) (((char)NTFS_U28(ustr[i])) - astr[i]); + char u, l; + size_t i, j, mbstrlen = astrlen; + int res; + wchar wc; + + for (i = 0, j = 0; (i < ustrlen) && (j < astrlen); i++, j++) { + res = 0; + wc = NTFS_U28(ustr[i]); + u = (char)(wc>>8); + l = (char)wc; + if (u != '\0' && j < astrlen -1) { + res = (int) (u - astr[j++]); + mbstrlen--; + } + res = (res<<8) + (int) (l - astr[j]); if (res) return res; } - return (ustrlen - astrlen); + return (ustrlen - mbstrlen); } /* @@ -2003,11 +2037,18 @@ ntfs_toupper_unuse() int ntfs_u28_init( struct ntfsmount *ntmp, - wchar *u2w) + wchar *u2w, + char *cs_local, + char *cs_ntfs) { char ** u28; int i, j, h, l; + if (ntfs_iconv && cs_local) { + ntfs_iconv->open(cs_local, cs_ntfs, &ntmp->ntm_ic_u2l); + return (0); + } + MALLOC(u28, char **, 256 * sizeof(char*), M_TEMP, M_WAITOK | M_ZERO); for (i=0; i<256; i++) { @@ -2034,8 +2075,12 @@ ntfs_u28_uninit(struct ntfsmount *ntmp) char ** u28; int i; - if (ntmp->ntm_u28 == NULL) + if (ntmp->ntm_u28 == NULL) { + if (ntfs_iconv && ntmp->ntm_ic_u2l) { + ntfs_iconv->close(ntmp->ntm_ic_u2l); + } return (0); + } u28 = ntmp->ntm_u28; @@ -2051,22 +2096,21 @@ ntfs_u28_uninit(struct ntfsmount *ntmp) int ntfs_82u_init( struct ntfsmount *ntmp, - u_int16_t *u2w) + char *cs_local, + char *cs_ntfs) { wchar * _82u; int i; + if (ntfs_iconv && cs_local) { + ntfs_iconv->open(cs_ntfs, cs_local, &ntmp->ntm_ic_l2u); + return (0); + } + MALLOC(_82u, wchar *, 256 * sizeof(wchar), M_TEMP, M_WAITOK); - if (u2w == NULL) { - for (i=0; i<256; i++) - _82u[i] = i; - } else { - for (i=0; i<128; i++) + for (i=0; i<256; i++) _82u[i] = i; - for (i=0; i<128; i++) - _82u[i+128] = u2w[i]; - } ntmp->ntm_82u = _82u; @@ -2076,6 +2120,14 @@ ntfs_82u_init( int ntfs_82u_uninit(struct ntfsmount *ntmp) { + + if (ntmp->ntm_82u == NULL) { + if (ntfs_iconv && ntmp->ntm_ic_l2u) { + ntfs_iconv->close(ntmp->ntm_ic_l2u); + } + return (0); + } + FREE(ntmp->ntm_82u, M_TEMP); return (0); } @@ -2086,12 +2138,31 @@ ntfs_82u_uninit(struct ntfsmount *ntmp) * and substitutes a '_' for it if the result would be '\0'; * something better has to be definitely though out */ -char +wchar ntfs_u28( struct ntfsmount *ntmp, wchar wc) { - char * p; + char *p, *outp, inbuf[3], outbuf[3]; + size_t ilen, olen; + + if (ntfs_iconv && ntmp->ntm_ic_u2l) { + ilen = olen = 2; + + inbuf[0] = (char)(wc>>8); + inbuf[1] = (char)wc; + inbuf[2] = '\0'; + p = inbuf; + outp = outbuf; + ntfs_iconv->convchr(ntmp->ntm_ic_u2l, (const char **)&p, &ilen, + &outp, &olen); + if (olen == 1) { + return ((wchar)(outbuf[0])); + } else if (olen == 0) { + return ((wchar)((outbuf[0]<<8) | (outbuf[1]&0xFF))); + } + return ('?'); + } p = ntmp->ntm_u28[(wc>>8)&0xFF]; if (p == NULL) @@ -2099,3 +2170,36 @@ ntfs_u28( return (p[wc&0xFF]); } +wchar +ntfs_82u( + struct ntfsmount *ntmp, + wchar wc, + int *len) +{ + char *p, *outp, inbuf[3], outbuf[3]; + wchar uc; + size_t ilen, olen; + + if (ntfs_iconv && ntmp->ntm_ic_l2u) { + ilen = (size_t)*len; + olen = 2; + + inbuf[0] = (char)(wc>>8); + inbuf[1] = (char)wc; + inbuf[2] = '\0'; + p = inbuf; + outp = outbuf; + ntfs_iconv->convchr(ntmp->ntm_ic_l2u, (const char **)&p, &ilen, + &outp, &olen); + *len -= (int)ilen; + uc = (wchar)((outbuf[0]<<8) | (outbuf[1]&0xFF)); + + return (uc); + } + + if (ntmp->ntm_82u != NULL) + return (ntmp->ntm_82u[wc&0xFF]); + + return ('?'); +} + diff --git a/sys/fs/ntfs/ntfs_subr.h b/sys/fs/ntfs/ntfs_subr.h index 1d676ab..c0ecc75 100644 --- a/sys/fs/ntfs/ntfs_subr.h +++ b/sys/fs/ntfs/ntfs_subr.h @@ -108,13 +108,14 @@ void ntfs_toupper_unuse(void); int ntfs_fget(struct ntfsmount *, struct ntnode *, int, char *, struct fnode **); void ntfs_frele(struct fnode *); -int ntfs_u28_init(struct ntfsmount *ntmp, wchar *u2w); +int ntfs_u28_init(struct ntfsmount *ntmp, wchar *u2w, char *cs_local, char *cs_ntfs); int ntfs_u28_uninit(struct ntfsmount *ntmp); -int ntfs_82u_init(struct ntfsmount *ntmp, u_int16_t *u2w); +int ntfs_82u_init(struct ntfsmount *ntmp, char *cs_local, char *cs_ntfs); int ntfs_82u_uninit(struct ntfsmount *ntmp); -char ntfs_u28(struct ntfsmount *ntmp, wchar wc); +wchar ntfs_u28(struct ntfsmount *ntmp, wchar wc); +wchar ntfs_82u(struct ntfsmount *ntmp, wchar wc, int *len); #define NTFS_U28(ch) ntfs_u28(ntmp, (ch)) -#define NTFS_82U(ch) (ntmp->ntm_82u[(ch)&0xFF]) +#define NTFS_82U(ch, len) ntfs_82u(ntmp, (ch), len) #define NTFS_UASTRCMP(ustr, ustrlen, astr, astrlen) \ ntfs_uastrcmp(ntmp, (ustr), (ustrlen), (astr), (astrlen)) #define NTFS_UASTRICMP(ustr, ustrlen, astr, astrlen) \ diff --git a/sys/fs/ntfs/ntfs_vfsops.c b/sys/fs/ntfs/ntfs_vfsops.c index cd5e062..a959e8c 100644 --- a/sys/fs/ntfs/ntfs_vfsops.c +++ b/sys/fs/ntfs/ntfs_vfsops.c @@ -350,15 +350,15 @@ ntfs_mountfs(devvp, mp, argsp, td) ntmp->ntm_flag = argsp->flag; /* Copy in the 8-bit to Unicode conversion table */ - if (argsp->flag & NTFSMNT_U2WTABLE) { - ntfs_82u_init(ntmp, argsp->u2w); + /* Initialize Unicode to 8-bit table from 8toU table */ + if (argsp->flag & NTFS_MFLAG_KICONV) { + ntfs_82u_init(ntmp, argsp->cs_local, argsp->cs_ntfs); + ntfs_u28_init(ntmp, NULL, argsp->cs_local, argsp->cs_ntfs); } else { - ntfs_82u_init(ntmp, NULL); + ntfs_82u_init(ntmp, NULL, NULL); + ntfs_u28_init(ntmp, ntmp->ntm_82u, NULL, NULL); } - /* Initialize Unicode to 8-bit table from 8toU table */ - ntfs_u28_init(ntmp, ntmp->ntm_82u); - mp->mnt_data = (qaddr_t)ntmp; dprintf(("ntfs_mountfs(): case-%s,%s uid: %d, gid: %d, mode: %o\n", @@ -794,3 +794,4 @@ static struct vfsops ntfs_vfsops = { .vfs_vptofh = ntfs_vptofh, }; VFS_SET(ntfs_vfsops, ntfs, 0); +MODULE_VERSION(ntfs, 1); diff --git a/sys/fs/ntfs/ntfs_vnops.c b/sys/fs/ntfs/ntfs_vnops.c index c24c9bf..4333c36 100644 --- a/sys/fs/ntfs/ntfs_vnops.c +++ b/sys/fs/ntfs/ntfs_vnops.c @@ -495,7 +495,8 @@ ntfs_readdir(ap) register struct ntnode *ip = FTONT(fp); struct uio *uio = ap->a_uio; struct ntfsmount *ntmp = ip->i_mp; - int i, error = 0; + int i, j, error = 0; + wchar c; u_int32_t faked = 0, num; int ncookies = 0; struct dirent cde; @@ -552,14 +553,17 @@ ntfs_readdir(ap) if(!ntfs_isnamepermitted(ntmp,iep)) continue; - for(i=0; i<iep->ie_fnamelen; i++) { - cde.d_name[i] = NTFS_U28(iep->ie_fname[i]); + for(i=0, j=0; i<iep->ie_fnamelen; i++, j++) { + c = NTFS_U28(iep->ie_fname[i]); + if (c&0xFF00) + cde.d_name[j++] = (char)(c>>8); + cde.d_name[j] = (char)c&0xFF; } - cde.d_name[i] = '\0'; + cde.d_name[j] = '\0'; dprintf(("ntfs_readdir: elem: %d, fname:[%s] type: %d, flag: %d, ", num, cde.d_name, iep->ie_fnametype, iep->ie_flag)); - cde.d_namlen = iep->ie_fnamelen; + cde.d_namlen = j; cde.d_fileno = iep->ie_number; cde.d_type = (iep->ie_fflag & NTFS_FFLAG_DIR) ? DT_DIR : DT_REG; cde.d_reclen = sizeof(struct dirent); diff --git a/sys/fs/ntfs/ntfsmount.h b/sys/fs/ntfs/ntfsmount.h index 0c143ed..5497ad9 100644 --- a/sys/fs/ntfs/ntfsmount.h +++ b/sys/fs/ntfs/ntfsmount.h @@ -30,7 +30,7 @@ #define NTFS_MFLAG_CASEINS 0x00000001 #define NTFS_MFLAG_ALLNAMES 0x00000002 -#define NTFSMNT_U2WTABLE 0x00000004 +#define NTFS_MFLAG_KICONV 0x00000004 struct ntfs_args { char *fspec; /* block special device to mount */ @@ -39,5 +39,6 @@ struct ntfs_args { gid_t gid; /* gid that owns ntfs files */ mode_t mode; /* mask to be applied for ntfs perms */ u_long flag; /* additional flags */ - u_int16_t u2w[256]; /* Unix to Wchar */ + char *cs_ntfs; /* NTFS Charset */ + char *cs_local; /* Local Charset */ }; diff --git a/sys/fs/smbfs/smbfs_vfsops.c b/sys/fs/smbfs/smbfs_vfsops.c index 5bc646a..1a279f1 100644 --- a/sys/fs/smbfs/smbfs_vfsops.c +++ b/sys/fs/smbfs/smbfs_vfsops.c @@ -101,7 +101,7 @@ static struct vfsops smbfs_vfsops = { VFS_SET(smbfs_vfsops, smbfs, VFCF_NETWORK); MODULE_DEPEND(smbfs, netsmb, NSMB_VERSION, NSMB_VERSION, NSMB_VERSION); -MODULE_DEPEND(smbfs, libiconv, 1, 1, 1); +MODULE_DEPEND(smbfs, libiconv, 1, 1, 2); MODULE_DEPEND(smbfs, libmchain, 1, 1, 1); int smbfs_pbuf_freecnt = -1; /* start out unlimited */ |