From: Roman Zippel This adds NLS support to HFS. Using the kernel options iocharset and codepage it's possible to map the disk encoding to a local mapping. If these options are not used, it falls back to the old direct mapping. Signed-off-by: Roman Zippel Signed-off-by: Andrew Morton --- fs/hfs/catalog.c | 35 ++++++++-------- fs/hfs/dir.c | 11 ++--- fs/hfs/hfs.h | 1 fs/hfs/hfs_fs.h | 8 ++- fs/hfs/inode.c | 2 fs/hfs/mdb.c | 6 ++ fs/hfs/super.c | 43 ++++++++++++++++++++ fs/hfs/trans.c | 118 +++++++++++++++++++++++++++++++++++++++++++++---------- 8 files changed, 177 insertions(+), 47 deletions(-) diff -puN fs/hfs/catalog.c~hfs-nls-support fs/hfs/catalog.c --- devel/fs/hfs/catalog.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/catalog.c 2005-08-01 16:46:56.000000000 -0700 @@ -20,12 +20,12 @@ * * Given the ID of the parent and the name build a search key. */ -void hfs_cat_build_key(btree_key *key, u32 parent, struct qstr *name) +void hfs_cat_build_key(struct super_block *sb, btree_key *key, u32 parent, struct qstr *name) { key->cat.reserved = 0; key->cat.ParID = cpu_to_be32(parent); if (name) { - hfs_triv2mac(&key->cat.CName, name); + hfs_asc2mac(sb, &key->cat.CName, name); key->key_len = 6 + key->cat.CName.len; } else { memset(&key->cat.CName, 0, sizeof(struct hfs_name)); @@ -62,13 +62,14 @@ static int hfs_cat_build_record(hfs_cat_ } } -static int hfs_cat_build_thread(hfs_cat_rec *rec, int type, +static int hfs_cat_build_thread(struct super_block *sb, + hfs_cat_rec *rec, int type, u32 parentid, struct qstr *name) { rec->type = type; memset(rec->thread.reserved, 0, sizeof(rec->thread.reserved)); rec->thread.ParID = cpu_to_be32(parentid); - hfs_triv2mac(&rec->thread.CName, name); + hfs_asc2mac(sb, &rec->thread.CName, name); return sizeof(struct hfs_cat_thread); } @@ -93,8 +94,8 @@ int hfs_cat_create(u32 cnid, struct inod sb = dir->i_sb; hfs_find_init(HFS_SB(sb)->cat_tree, &fd); - hfs_cat_build_key(fd.search_key, cnid, NULL); - entry_size = hfs_cat_build_thread(&entry, S_ISDIR(inode->i_mode) ? + hfs_cat_build_key(sb, fd.search_key, cnid, NULL); + entry_size = hfs_cat_build_thread(sb, &entry, S_ISDIR(inode->i_mode) ? HFS_CDR_THD : HFS_CDR_FTH, dir->i_ino, str); err = hfs_brec_find(&fd); @@ -107,7 +108,7 @@ int hfs_cat_create(u32 cnid, struct inod if (err) goto err2; - hfs_cat_build_key(fd.search_key, dir->i_ino, str); + hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str); entry_size = hfs_cat_build_record(&entry, cnid, inode); err = hfs_brec_find(&fd); if (err != -ENOENT) { @@ -127,7 +128,7 @@ int hfs_cat_create(u32 cnid, struct inod return 0; err1: - hfs_cat_build_key(fd.search_key, cnid, NULL); + hfs_cat_build_key(sb, fd.search_key, cnid, NULL); if (!hfs_brec_find(&fd)) hfs_brec_remove(&fd); err2: @@ -176,7 +177,7 @@ int hfs_cat_find_brec(struct super_block hfs_cat_rec rec; int res, len, type; - hfs_cat_build_key(fd->search_key, cnid, NULL); + hfs_cat_build_key(sb, fd->search_key, cnid, NULL); res = hfs_brec_read(fd, &rec, sizeof(rec)); if (res) return res; @@ -211,7 +212,7 @@ int hfs_cat_delete(u32 cnid, struct inod sb = dir->i_sb; hfs_find_init(HFS_SB(sb)->cat_tree, &fd); - hfs_cat_build_key(fd.search_key, dir->i_ino, str); + hfs_cat_build_key(sb, fd.search_key, dir->i_ino, str); res = hfs_brec_find(&fd); if (res) goto out; @@ -239,7 +240,7 @@ int hfs_cat_delete(u32 cnid, struct inod if (res) goto out; - hfs_cat_build_key(fd.search_key, cnid, NULL); + hfs_cat_build_key(sb, fd.search_key, cnid, NULL); res = hfs_brec_find(&fd); if (!res) { res = hfs_brec_remove(&fd); @@ -280,7 +281,7 @@ int hfs_cat_move(u32 cnid, struct inode dst_fd = src_fd; /* find the old dir entry and read the data */ - hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -289,7 +290,7 @@ int hfs_cat_move(u32 cnid, struct inode src_fd.entrylength); /* create new dir entry with the data from the old entry */ - hfs_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name); + hfs_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); err = hfs_brec_find(&dst_fd); if (err != -ENOENT) { if (!err) @@ -305,7 +306,7 @@ int hfs_cat_move(u32 cnid, struct inode mark_inode_dirty(dst_dir); /* finally remove the old entry */ - hfs_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + hfs_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -321,7 +322,7 @@ int hfs_cat_move(u32 cnid, struct inode goto out; /* remove old thread entry */ - hfs_cat_build_key(src_fd.search_key, cnid, NULL); + hfs_cat_build_key(sb, src_fd.search_key, cnid, NULL); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -330,8 +331,8 @@ int hfs_cat_move(u32 cnid, struct inode goto out; /* create new thread entry */ - hfs_cat_build_key(dst_fd.search_key, cnid, NULL); - entry_size = hfs_cat_build_thread(&entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD, + hfs_cat_build_key(sb, dst_fd.search_key, cnid, NULL); + entry_size = hfs_cat_build_thread(sb, &entry, type == HFS_CDR_FIL ? HFS_CDR_FTH : HFS_CDR_THD, dst_dir->i_ino, dst_name); err = hfs_brec_find(&dst_fd); if (err != -ENOENT) { diff -puN fs/hfs/dir.c~hfs-nls-support fs/hfs/dir.c --- devel/fs/hfs/dir.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/dir.c 2005-08-01 16:46:56.000000000 -0700 @@ -28,7 +28,7 @@ static struct dentry *hfs_lookup(struct dentry->d_op = &hfs_dentry_operations; hfs_find_init(HFS_SB(dir->i_sb)->cat_tree, &fd); - hfs_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name); + hfs_cat_build_key(dir->i_sb, fd.search_key, dir->i_ino, &dentry->d_name); res = hfs_brec_read(&fd, &rec, sizeof(rec)); if (res) { hfs_find_exit(&fd); @@ -56,7 +56,7 @@ static int hfs_readdir(struct file *filp struct inode *inode = filp->f_dentry->d_inode; struct super_block *sb = inode->i_sb; int len, err; - char strbuf[HFS_NAMELEN + 1]; + char strbuf[HFS_MAX_NAMELEN]; union hfs_cat_rec entry; struct hfs_find_data fd; struct hfs_readdir_data *rd; @@ -66,7 +66,7 @@ static int hfs_readdir(struct file *filp return 0; hfs_find_init(HFS_SB(sb)->cat_tree, &fd); - hfs_cat_build_key(fd.search_key, inode->i_ino, NULL); + hfs_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); err = hfs_brec_find(&fd); if (err) goto out; @@ -111,7 +111,7 @@ static int hfs_readdir(struct file *filp } hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); type = entry.type; - len = hfs_mac2triv(strbuf, &fd.key->cat.CName); + len = hfs_mac2asc(sb, strbuf, &fd.key->cat.CName); if (type == HFS_CDR_DIR) { if (fd.entrylength < sizeof(struct hfs_cat_dir)) { printk("HFS: small dir entry\n"); @@ -307,7 +307,8 @@ static int hfs_rename(struct inode *old_ old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) - hfs_cat_build_key((btree_key *)&HFS_I(old_dentry->d_inode)->cat_key, + hfs_cat_build_key(old_dir->i_sb, + (btree_key *)&HFS_I(old_dentry->d_inode)->cat_key, new_dir->i_ino, &new_dentry->d_name); return res; } diff -puN fs/hfs/hfs_fs.h~hfs-nls-support fs/hfs/hfs_fs.h --- devel/fs/hfs/hfs_fs.h~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/hfs_fs.h 2005-08-01 16:46:56.000000000 -0700 @@ -141,6 +141,8 @@ struct hfs_sb_info { int session, part; + struct nls_table *nls_io, *nls_disk; + struct semaphore bitmap_lock; unsigned long flags; @@ -168,7 +170,7 @@ extern int hfs_cat_create(u32, struct in extern int hfs_cat_delete(u32, struct inode *, struct qstr *); extern int hfs_cat_move(u32, struct inode *, struct qstr *, struct inode *, struct qstr *); -extern void hfs_cat_build_key(btree_key *, u32, struct qstr *); +extern void hfs_cat_build_key(struct super_block *, btree_key *, u32, struct qstr *); /* dir.c */ extern struct file_operations hfs_dir_operations; @@ -222,8 +224,8 @@ extern int hfs_strcmp(const unsigned cha extern int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *); /* trans.c */ -extern void hfs_triv2mac(struct hfs_name *, struct qstr *); -extern int hfs_mac2triv(char *, const struct hfs_name *); +extern void hfs_asc2mac(struct super_block *, struct hfs_name *, struct qstr *); +extern int hfs_mac2asc(struct super_block *, char *, const struct hfs_name *); extern struct timezone sys_tz; diff -puN fs/hfs/hfs.h~hfs-nls-support fs/hfs/hfs.h --- devel/fs/hfs/hfs.h~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/hfs.h 2005-08-01 16:46:56.000000000 -0700 @@ -25,6 +25,7 @@ #define HFS_SECTOR_SIZE 512 /* size of an HFS sector */ #define HFS_SECTOR_SIZE_BITS 9 /* log_2(HFS_SECTOR_SIZE) */ #define HFS_NAMELEN 31 /* maximum length of an HFS filename */ +#define HFS_MAX_NAMELEN 128 #define HFS_MAX_VALENCE 32767U /* Meanings of the drAtrb field of the MDB, diff -puN fs/hfs/inode.c~hfs-nls-support fs/hfs/inode.c --- devel/fs/hfs/inode.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/inode.c 2005-08-01 16:46:56.000000000 -0700 @@ -160,7 +160,7 @@ struct inode *hfs_new_inode(struct inode init_MUTEX(&HFS_I(inode)->extents_lock); INIT_LIST_HEAD(&HFS_I(inode)->open_dir_list); - hfs_cat_build_key((btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name); + hfs_cat_build_key(sb, (btree_key *)&HFS_I(inode)->cat_key, dir->i_ino, name); inode->i_ino = HFS_SB(sb)->next_id++; inode->i_mode = mode; inode->i_uid = current->fsuid; diff -puN fs/hfs/mdb.c~hfs-nls-support fs/hfs/mdb.c --- devel/fs/hfs/mdb.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/mdb.c 2005-08-01 16:46:56.000000000 -0700 @@ -10,6 +10,7 @@ #include #include +#include #include "hfs_fs.h" #include "btree.h" @@ -343,6 +344,11 @@ void hfs_mdb_put(struct super_block *sb) brelse(HFS_SB(sb)->mdb_bh); brelse(HFS_SB(sb)->alt_mdb_bh); + if (HFS_SB(sb)->nls_io) + unload_nls(HFS_SB(sb)->nls_io); + if (HFS_SB(sb)->nls_disk) + unload_nls(HFS_SB(sb)->nls_disk); + kfree(HFS_SB(sb)); sb->s_fs_info = NULL; } diff -puN fs/hfs/super.c~hfs-nls-support fs/hfs/super.c --- devel/fs/hfs/super.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/super.c 2005-08-01 16:46:56.000000000 -0700 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -130,6 +131,10 @@ static int hfs_show_options(struct seq_f seq_printf(seq, ",part=%u", sbi->part); if (sbi->session >= 0) seq_printf(seq, ",session=%u", sbi->session); + if (sbi->nls_disk) + seq_printf(seq, ",codepage=%s", sbi->nls_disk->charset); + if (sbi->nls_io) + seq_printf(seq, ",iocharset=%s", sbi->nls_io->charset); if (sbi->s_quiet) seq_printf(seq, ",quiet"); return 0; @@ -163,6 +168,7 @@ static struct super_operations hfs_super enum { opt_uid, opt_gid, opt_umask, opt_file_umask, opt_dir_umask, opt_part, opt_session, opt_type, opt_creator, opt_quiet, + opt_codepage, opt_iocharset, opt_err }; @@ -177,6 +183,8 @@ static match_table_t tokens = { { opt_type, "type=%s" }, { opt_creator, "creator=%s" }, { opt_quiet, "quiet" }, + { opt_codepage, "codepage=%s" }, + { opt_iocharset, "iocharset=%s" }, { opt_err, NULL } }; @@ -282,11 +290,46 @@ static int parse_options(char *options, case opt_quiet: hsb->s_quiet = 1; break; + case opt_codepage: + if (hsb->nls_disk) { + printk("HFS+-fs: unable to change codepage\n"); + return 0; + } + p = match_strdup(&args[0]); + hsb->nls_disk = load_nls(p); + if (!hsb->nls_disk) { + printk("HFS+-fs: unable to load codepage \"%s\"\n", p); + kfree(p); + return 0; + } + kfree(p); + break; + case opt_iocharset: + if (hsb->nls_io) { + printk("HFS: unable to change iocharset\n"); + return 0; + } + p = match_strdup(&args[0]); + hsb->nls_io = load_nls(p); + if (!hsb->nls_io) { + printk("HFS: unable to load iocharset \"%s\"\n", p); + kfree(p); + return 0; + } + kfree(p); + break; default: return 0; } } + if (hsb->nls_disk && !hsb->nls_io) { + hsb->nls_io = load_nls_default(); + if (!hsb->nls_io) { + printk("HFS: unable to load default iocharset\n"); + return 0; + } + } hsb->s_dir_umask &= 0777; hsb->s_file_umask &= 0577; diff -puN fs/hfs/trans.c~hfs-nls-support fs/hfs/trans.c --- devel/fs/hfs/trans.c~hfs-nls-support 2005-08-01 16:46:56.000000000 -0700 +++ devel-akpm/fs/hfs/trans.c 2005-08-01 16:46:56.000000000 -0700 @@ -9,12 +9,15 @@ * with ':' vs. '/' as the path-element separator. */ +#include +#include + #include "hfs_fs.h" /*================ Global functions ================*/ /* - * hfs_mac2triv() + * hfs_mac2asc() * * Given a 'Pascal String' (a string preceded by a length byte) in * the Macintosh character set produce the corresponding filename using @@ -27,23 +30,58 @@ * by ':' which never appears in HFS filenames. All other characters * are passed unchanged from input to output. */ -int hfs_mac2triv(char *out, const struct hfs_name *in) +int hfs_mac2asc(struct super_block *sb, char *out, const struct hfs_name *in) { - const char *p; - char c; - int i, len; - - len = in->len; - p = in->name; - for (i = 0; i < len; i++) { - c = *p++; - *out++ = c == '/' ? ':' : c; + struct nls_table *nls_disk = HFS_SB(sb)->nls_disk; + struct nls_table *nls_io = HFS_SB(sb)->nls_io; + const char *src; + char *dst; + int srclen, dstlen, size; + + src = in->name; + srclen = in->len; + dst = out; + dstlen = HFS_MAX_NAMELEN; + if (nls_io) { + wchar_t ch; + + while (srclen > 0) { + if (nls_disk) { + size = nls_disk->char2uni(src, srclen, &ch); + if (size <= 0) { + ch = '?'; + size = 1; + } + src += size; + srclen -= size; + } else { + ch = *src++; + srclen--; + } + if (ch == '/') + ch = ':'; + size = nls_io->uni2char(ch, dst, dstlen); + if (size < 0) { + if (size == -ENAMETOOLONG) + goto out; + *dst = '?'; + size = 1; + } + dst += size; + dstlen -= size; + } + } else { + char ch; + + while (--srclen >= 0) + *dst++ = (ch = *src++) == '/' ? ':' : ch; } - return i; +out: + return dst - out; } /* - * hfs_triv2mac() + * hfs_asc2mac() * * Given an ASCII string (not null-terminated) and its length, * generate the corresponding filename in the Macintosh character set @@ -54,19 +92,57 @@ int hfs_mac2triv(char *out, const struct * This routine is a inverse to hfs_mac2triv(). * A ':' is replaced by a '/'. */ -void hfs_triv2mac(struct hfs_name *out, struct qstr *in) +void hfs_asc2mac(struct super_block *sb, struct hfs_name *out, struct qstr *in) { + struct nls_table *nls_disk = HFS_SB(sb)->nls_disk; + struct nls_table *nls_io = HFS_SB(sb)->nls_io; const char *src; - char *dst, c; - int i, len; + char *dst; + int srclen, dstlen, size; - out->len = len = min((unsigned int)HFS_NAMELEN, in->len); src = in->name; + srclen = in->len; dst = out->name; - for (i = 0; i < len; i++) { - c = *src++; - *dst++ = c == ':' ? '/' : c; + dstlen = HFS_NAMELEN; + if (nls_io) { + wchar_t ch; + + while (srclen > 0) { + size = nls_io->char2uni(src, srclen, &ch); + if (size < 0) { + ch = '?'; + size = 1; + } + src += size; + srclen -= size; + if (ch == ':') + ch = '/'; + if (nls_disk) { + size = nls_disk->uni2char(ch, dst, dstlen); + if (size < 0) { + if (size == -ENAMETOOLONG) + goto out; + *dst = '?'; + size = 1; + } + dst += size; + dstlen -= size; + } else { + *dst++ = ch > 0xff ? '?' : ch; + dstlen--; + } + } + } else { + char ch; + + if (dstlen > srclen) + dstlen = srclen; + while (--dstlen >= 0) + *dst++ = (ch = *src++) == ':' ? '/' : ch; } - for (; i < HFS_NAMELEN; i++) +out: + out->len = dst - (char *)out->name; + dstlen = HFS_NAMELEN - out->len; + while (--dstlen >= 0) *dst++ = 0; } _