diff --git a/sys/fs/udf/udf.h b/sys/fs/udf/udf.h index f214c14e04b2..540a3a678057 100644 --- a/sys/fs/udf/udf.h +++ b/sys/fs/udf/udf.h @@ -58,6 +58,10 @@ struct udf_mnt { int p_sectors; int s_table_entries; struct udf_sparing_table *s_table; + void *im_d2l; /* disk->local iconv handle */ +#if 0 + void *im_l2d; /* local->disk iconv handle */ +#endif }; struct udf_dirstream { diff --git a/sys/fs/udf/udf_iconv.c b/sys/fs/udf/udf_iconv.c new file mode 100644 index 000000000000..934516986c85 --- /dev/null +++ b/sys/fs/udf/udf_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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +VFS_DECLARE_ICONV(udf); diff --git a/sys/fs/udf/udf_mount.h b/sys/fs/udf/udf_mount.h new file mode 100644 index 000000000000..1c34694c3d4c --- /dev/null +++ b/sys/fs/udf/udf_mount.h @@ -0,0 +1,29 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#define UDFMNT_KICONV 0x00000001 diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c index c8f7e49c42d2..0c093d6f1d24 100644 --- a/sys/fs/udf/udf_vfsops.c +++ b/sys/fs/udf/udf_vfsops.c @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include @@ -90,12 +91,15 @@ #include #include -#include #include +#include +#include MALLOC_DEFINE(M_UDFMOUNT, "UDF mount", "UDF mount structure"); MALLOC_DEFINE(M_UDFFENTRY, "UDF fentry", "UDF file entry structure"); +struct iconv_functions *udf_iconv = NULL; + /* Zones */ uma_zone_t udf_zone_trans = NULL; uma_zone_t udf_zone_node = NULL; @@ -125,6 +129,8 @@ static struct vfsops udf_vfsops = { }; VFS_SET(udf_vfsops, udf, VFCF_READONLY); +MODULE_VERSION(udf, 1); + static int udf_mountfs(struct vnode *, struct mount *, struct thread *); static int @@ -183,9 +189,9 @@ udf_mount(struct mount *mp, struct nameidata *ndp, struct thread *td) struct udf_mnt *imp = 0; struct export_args *export; struct vfsoptlist *opts; - char *fspec; + char *fspec, *cs_disk, *cs_local; size_t size; - int error, len; + int error, len, *udf_flags; opts = mp->mnt_optnew; @@ -246,6 +252,28 @@ udf_mount(struct mount *mp, struct nameidata *ndp, struct thread *td) } imp = VFSTOUDFFS(mp); + + udf_flags = NULL; + error = vfs_getopt(opts, "flags", (void **)&udf_flags, &len); + if (error || len != sizeof(int)) + return (EINVAL); + imp->im_flags = *udf_flags; + + if (imp->im_flags & UDFMNT_KICONV && udf_iconv) { + cs_disk = NULL; + error = vfs_getopt(opts, "cs_disk", (void **)&cs_disk, &len); + if (!error && cs_disk[len - 1] != '\0') + return (EINVAL); + cs_local = NULL; + error = vfs_getopt(opts, "cs_local", (void **)&cs_local, &len); + if (!error && cs_local[len - 1] != '\0') + return (EINVAL); + udf_iconv->open(cs_local, cs_disk, &imp->im_d2l); +#if 0 + udf_iconv->open(cs_disk, cs_local, &imp->im_l2d); +#endif + } + copystr(fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size); udf_statfs(mp, &mp->mnt_stat, td); @@ -326,6 +354,10 @@ udf_mountfs(struct vnode *devvp, struct mount *mp, struct thread *td) { udfmp->im_mountp = mp; udfmp->im_dev = devvp->v_rdev; udfmp->im_devvp = devvp; + udfmp->im_d2l = NULL; +#if 0 + udfmp->im_l2d = NULL; +#endif bsize = 2048; /* XXX Should probe the media for it's size */ @@ -471,6 +503,15 @@ udf_unmount(struct mount *mp, int mntflags, struct thread *td) if ((error = vflush(mp, 0, flags))) return (error); + if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) { + if (udfmp->im_d2l) + udf_iconv->close(udfmp->im_d2l); +#if 0 + if (udfmp->im_l2d) + udf_iconv->close(udfmp->im_l2d); +#endif + } + udfmp->im_devvp->v_rdev->si_mountpoint = NULL; error = VOP_CLOSE(udfmp->im_devvp, FREAD, NOCRED, td); vrele(udfmp->im_devvp); diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c index 6c22663af806..c0a018e79004 100644 --- a/sys/fs/udf/udf_vnops.c +++ b/sys/fs/udf/udf_vnops.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,9 @@ #include #include #include +#include + +extern struct iconv_functions *udf_iconv; static int udf_access(struct vop_access_args *); static int udf_getattr(struct vop_getattr_args *); @@ -446,41 +450,65 @@ udf_dumpblock(void *data, int len) /* * Call the OSTA routines to translate the name from a CS0 dstring to a * 16-bit Unicode String. Hooks need to be placed in here to translate from - * Unicode to the encoding that the kernel/user expects. For now, compact - * the encoding to 8 bits if possible. Return the length of the translated - * string. - * XXX This horribly pessimizes the 8bit case + * Unicode to the encoding that the kernel/user expects. Return the length + * of the translated string. */ static int -udf_transname(char *cs0string, char *destname, int len) +udf_transname(char *cs0string, char *destname, int len, struct udf_mnt *udfmp) { unicode_t *transname; - int i, unilen = 0; + char *unibuf, *unip; + int i, unilen = 0, destlen; + size_t destleft = MAXNAMLEN; - /* allocate a buffer big enough to hold an 8->16 bit expansion */ - transname = uma_zalloc(udf_zone_trans, M_WAITOK); - - if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) { - printf("udf: Unicode translation failed\n"); - uma_zfree(udf_zone_trans, transname); - return 0; - } - - /* At this point, the name is in 16-bit Unicode. Compact it down - * to 8-bit - */ - for (i = 0; i < unilen ; i++) { - if (transname[i] & 0xff00) { - destname[i] = '.'; /* Fudge the 16bit chars */ - } else { - destname[i] = transname[i] & 0xff; + /* Convert 16-bit Unicode to destname */ + if (udfmp->im_flags & UDFMNT_KICONV && udf_iconv) { + /* allocate a buffer big enough to hold an 8->16 bit expansion */ + unibuf = uma_zalloc(udf_zone_trans, M_WAITOK); + unip = unibuf; + if ((unilen = udf_UncompressUnicodeByte(len, cs0string, unibuf)) == -1) { + printf("udf: Unicode translation failed\n"); + uma_zfree(udf_zone_trans, unibuf); + return 0; } + + while (unilen > 0 && destleft > 0) { + udf_iconv->conv(udfmp->im_d2l, (const char **)&unibuf, + (size_t *)&unilen, (char **)&destname, &destleft); + /* Unconverted character found */ + if (unilen > 0 && destleft > 0) { + *destname++ = '?'; + destleft--; + unibuf += 2; + unilen -= 2; + } + } + uma_zfree(udf_zone_trans, unip); + *destname = '\0'; + destlen = MAXNAMLEN - (int)destleft; + } else { + /* allocate a buffer big enough to hold an 8->16 bit expansion */ + transname = uma_zalloc(udf_zone_trans, M_WAITOK); + + if ((unilen = udf_UncompressUnicode(len, cs0string, transname)) == -1) { + printf("udf: Unicode translation failed\n"); + uma_zfree(udf_zone_trans, transname); + return 0; + } + + for (i = 0; i < unilen ; i++) { + if (transname[i] & 0xff00) { + destname[i] = '.'; /* Fudge the 16bit chars */ + } else { + destname[i] = transname[i] & 0xff; + } + } + uma_zfree(udf_zone_trans, transname); + destname[unilen] = 0; + destlen = unilen; } - destname[unilen] = 0; - uma_zfree(udf_zone_trans, transname); - - return unilen; + return (destlen); } /* @@ -489,7 +517,7 @@ udf_transname(char *cs0string, char *destname, int len) * here also. */ static int -udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen) +udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen, struct udf_mnt *udfmp) { char *transname; int error = 0; @@ -497,7 +525,7 @@ udf_cmpname(char *cs0string, char *cmpname, int cs0len, int cmplen) /* This is overkill, but not worth creating a new zone */ transname = uma_zalloc(udf_zone_trans, M_WAITOK); - cs0len = udf_transname(cs0string, transname, cs0len); + cs0len = udf_transname(cs0string, transname, cs0len, udfmp); /* Easy check. If they aren't the same length, they aren't equal */ if ((cs0len == 0) || (cs0len != cmplen)) @@ -686,6 +714,7 @@ udf_readdir(struct vop_readdir_args *a) struct uio *uio; struct dirent dir; struct udf_node *node; + struct udf_mnt *udfmp; struct fileid_desc *fid; struct udf_uiodir uiodir; struct udf_dirstream *ds; @@ -696,6 +725,7 @@ udf_readdir(struct vop_readdir_args *a) vp = a->a_vp; uio = a->a_uio; node = VTON(vp); + udfmp = node->udfmp; uiodir.eofflag = 1; if (a->a_ncookies != NULL) { @@ -762,7 +792,7 @@ udf_readdir(struct vop_readdir_args *a) error = udf_uiodir(&uiodir, dir.d_reclen, uio, 2); } else { dir.d_namlen = udf_transname(&fid->data[fid->l_iu], - &dir.d_name[0], fid->l_fi); + &dir.d_name[0], fid->l_fi, udfmp); dir.d_fileno = udf_getid(&fid->icb); dir.d_type = (fid->file_char & UDF_FILE_CHAR_DIR) ? DT_DIR : DT_UNKNOWN; @@ -946,7 +976,7 @@ udf_lookup(struct vop_cachedlookup_args *a) } } else { if (!(udf_cmpname(&fid->data[fid->l_iu], - nameptr, fid->l_fi, namelen))) { + nameptr, fid->l_fi, namelen, udfmp))) { id = udf_getid(&fid->icb); break; }