diff options
author | Roman Zippel <zippel@linux-m68k.org> | 2005-03-28 04:09:45 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-03-28 04:09:45 -0800 |
commit | 61e76bc2799e5836f81019224757ac15a8455d0d (patch) | |
tree | cb5f456eb81720ca04ae759ee3e825fc3d762b37 | |
parent | fad7d09c3c3ea606b992e17d97bf7f8bdd985fda (diff) | |
download | history-61e76bc2799e5836f81019224757ac15a8455d0d.tar.gz |
[PATCH] hfs: add nls support
Add full nls support for HFS+. The default is still utf8, but that can be
changed now via a mount option.
Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/Kconfig | 1 | ||||
-rw-r--r-- | fs/hfsplus/catalog.c | 39 | ||||
-rw-r--r-- | fs/hfsplus/dir.c | 8 | ||||
-rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 7 | ||||
-rw-r--r-- | fs/hfsplus/options.c | 30 | ||||
-rw-r--r-- | fs/hfsplus/super.c | 31 | ||||
-rw-r--r-- | fs/hfsplus/unicode.c | 55 |
7 files changed, 102 insertions, 69 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index 4bb214ea133a6..6a4ad4bb7a54e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -936,6 +936,7 @@ config HFS_FS config HFSPLUS_FS tristate "Apple Extended HFS file system support" select NLS + select NLS_UTF8 help If you say Y here, you will be able to mount extended format Macintosh-formatted hard drive partitions with full read-write access. diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 8f194a492a27c..94712790c8b3f 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -25,14 +25,14 @@ int hfsplus_cat_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2) return hfsplus_unistrcmp(&k1->cat.name, &k2->cat.name); } -void hfsplus_cat_build_key(hfsplus_btree_key *key, u32 parent, - struct qstr *str) +void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key, + u32 parent, struct qstr *str) { int len; key->cat.parent = cpu_to_be32(parent); if (str) { - hfsplus_asc2uni(&key->cat.name, str->name, str->len); + hfsplus_asc2uni(sb, &key->cat.name, str->name, str->len); len = be16_to_cpu(key->cat.name.length); } else { key->cat.name.length = 0; @@ -113,13 +113,14 @@ static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct i } } -static int hfsplus_fill_cat_thread(hfsplus_cat_entry *entry, int type, +static int hfsplus_fill_cat_thread(struct super_block *sb, + hfsplus_cat_entry *entry, int type, u32 parentid, struct qstr *str) { entry->type = cpu_to_be16(type); entry->thread.reserved = 0; entry->thread.parentID = cpu_to_be32(parentid); - hfsplus_asc2uni(&entry->thread.nodeName, str->name, str->len); + hfsplus_asc2uni(sb, &entry->thread.nodeName, str->name, str->len); return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2; } @@ -131,7 +132,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid, int err; u16 type; - hfsplus_cat_build_key(fd->search_key, cnid, NULL); + hfsplus_cat_build_key(sb, fd->search_key, cnid, NULL); err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry)); if (err) return err; @@ -159,8 +160,8 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct ino sb = dir->i_sb; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); - hfsplus_cat_build_key(fd.search_key, cnid, NULL); - entry_size = hfsplus_fill_cat_thread(&entry, S_ISDIR(inode->i_mode) ? + hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); + entry_size = hfsplus_fill_cat_thread(sb, &entry, S_ISDIR(inode->i_mode) ? HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD, dir->i_ino, str); err = hfs_brec_find(&fd); @@ -173,7 +174,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct ino if (err) goto err2; - hfsplus_cat_build_key(fd.search_key, dir->i_ino, str); + hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); entry_size = hfsplus_cat_build_record(&entry, cnid, inode); err = hfs_brec_find(&fd); if (err != -ENOENT) { @@ -193,7 +194,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct ino return 0; err1: - hfsplus_cat_build_key(fd.search_key, cnid, NULL); + hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); if (!hfs_brec_find(&fd)) hfs_brec_remove(&fd); err2: @@ -217,7 +218,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) if (!str) { int len; - hfsplus_cat_build_key(fd.search_key, cnid, NULL); + hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); err = hfs_brec_find(&fd); if (err) goto out; @@ -229,7 +230,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.unicode, off + 2, len); fd.search_key->key_len = cpu_to_be16(6 + len); } else - hfsplus_cat_build_key(fd.search_key, dir->i_ino, str); + hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); err = hfs_brec_find(&fd); if (err) @@ -259,7 +260,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str) if (err) goto out; - hfsplus_cat_build_key(fd.search_key, cnid, NULL); + hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); err = hfs_brec_find(&fd); if (err) goto out; @@ -294,7 +295,7 @@ int hfsplus_rename_cat(u32 cnid, dst_fd = src_fd; /* find the old dir entry and read the data */ - hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -303,7 +304,7 @@ int hfsplus_rename_cat(u32 cnid, src_fd.entrylength); /* create new dir entry with the data from the old entry */ - hfsplus_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name); + hfsplus_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) @@ -319,7 +320,7 @@ int hfsplus_rename_cat(u32 cnid, mark_inode_dirty(dst_dir); /* finally remove the old entry */ - hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name); + hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -331,7 +332,7 @@ int hfsplus_rename_cat(u32 cnid, mark_inode_dirty(src_dir); /* remove old thread entry */ - hfsplus_cat_build_key(src_fd.search_key, cnid, NULL); + hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL); err = hfs_brec_find(&src_fd); if (err) goto out; @@ -341,8 +342,8 @@ int hfsplus_rename_cat(u32 cnid, goto out; /* create new thread entry */ - hfsplus_cat_build_key(dst_fd.search_key, cnid, NULL); - entry_size = hfsplus_fill_cat_thread(&entry, type, dst_dir->i_ino, dst_name); + hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL); + entry_size = hfsplus_fill_cat_thread(sb, &entry, type, dst_dir->i_ino, dst_name); err = hfs_brec_find(&dst_fd); if (err != -ENOENT) { if (!err) diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 2793e3489b1e9..7bda76667a4af 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -40,7 +40,7 @@ static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; dentry->d_fsdata = NULL; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); - hfsplus_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name); + hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); again: err = hfs_brec_read(&fd, &entry, sizeof(entry)); if (err) { @@ -80,7 +80,7 @@ again: linkid = be32_to_cpu(entry.file.permissions.dev); str.len = sprintf(name, "iNode%d", linkid); str.name = name; - hfsplus_cat_build_key(fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str); + hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str); goto again; } else if (!dentry->d_fsdata) dentry->d_fsdata = (void *)(unsigned long)cnid; @@ -118,7 +118,7 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) return 0; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); - hfsplus_cat_build_key(fd.search_key, inode->i_ino, NULL); + hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); err = hfs_brec_find(&fd); if (err) goto out; @@ -164,7 +164,7 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir) hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength); type = be16_to_cpu(entry.type); len = HFSPLUS_MAX_STRLEN; - err = hfsplus_uni2asc(&fd.key->cat.name, strbuf, &len); + err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); if (err) goto out; if (type == HFSPLUS_FOLDER) { diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 5078b43db9fd3..92c76648006ac 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -114,6 +114,7 @@ struct hfsplus_sb_info { struct hfs_btree *attr_tree; struct inode *alloc_file; struct inode *hidden_dir; + struct nls_table *nls; /* Runtime variables */ u32 blockoffset; @@ -305,7 +306,7 @@ int hfs_brec_goto(struct hfs_find_data *, int); /* catalog.c */ int hfsplus_cat_cmp_key(hfsplus_btree_key *, hfsplus_btree_key *); -void hfsplus_cat_build_key(hfsplus_btree_key *, u32, struct qstr *); +void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *, u32, struct qstr *); int hfsplus_find_cat(struct super_block *, u32, struct hfs_find_data *); int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *); int hfsplus_delete_cat(u32, struct inode *, struct qstr *); @@ -349,8 +350,8 @@ extern u16 case_fold_table[]; /* unicode.c */ int hfsplus_unistrcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *); -int hfsplus_uni2asc(const struct hfsplus_unistr *, char *, int *); -int hfsplus_asc2uni(struct hfsplus_unistr *, const char *, int); +int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *); +int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int); /* wrapper.c */ int hfsplus_read_wrapper(struct super_block *); diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index 4c088f5dd1c36..93d40031e662b 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -12,12 +12,13 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/parser.h> +#include <linux/nls.h> #include "hfsplus_fs.h" enum { opt_creator, opt_type, opt_umask, opt_uid, opt_gid, - opt_part, opt_session, + opt_part, opt_session, opt_nls, opt_err }; @@ -29,6 +30,7 @@ static match_table_t tokens = { { opt_gid, "gid=%u" }, { opt_part, "part=%u" }, { opt_session, "session=%u" }, + { opt_nls, "nls=%s" }, { opt_err, NULL } }; @@ -65,7 +67,7 @@ int parse_options(char *input, struct hfsplus_sb_info *sbi) int tmp, token; if (!input) - return 1; + goto done; while ((p = strsep(&input, ",")) != NULL) { if (!*p) @@ -118,10 +120,34 @@ int parse_options(char *input, struct hfsplus_sb_info *sbi) return 0; } break; + case opt_nls: + if (sbi->nls) { + printk("HFS+-fs: unable to change nls mapping\n"); + return 0; + } + p = match_strdup(&args[0]); + sbi->nls = load_nls(p); + if (!sbi->nls) { + printk("HFS+-fs: unable to load nls mapping \"%s\"\n", p); + kfree(p); + return 0; + } + kfree(p); + break; default: return 0; } } +done: + if (!sbi->nls) { + /* try utf8 first, as this is the old default behaviour */ + sbi->nls = load_nls("utf8"); + if (!sbi->nls) + sbi->nls = load_nls_default(); + if (!sbi->nls) + return 0; + } + return 1; } diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index d16322f930c29..5f8044664a3c4 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -16,6 +16,7 @@ #include <linux/slab.h> #include <linux/version.h> #include <linux/vfs.h> +#include <linux/nls.h> static struct inode *hfsplus_alloc_inode(struct super_block *sb); static void hfsplus_destroy_inode(struct inode *inode); @@ -223,6 +224,8 @@ static void hfsplus_put_super(struct super_block *sb) iput(HFSPLUS_SB(sb).alloc_file); iput(HFSPLUS_SB(sb).hidden_dir); brelse(HFSPLUS_SB(sb).s_vhbh); + if (HFSPLUS_SB(sb).nls) + unload_nls(HFSPLUS_SB(sb).nls); } static int hfsplus_statfs(struct super_block *sb, struct kstatfs *buf) @@ -280,13 +283,13 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) struct hfs_find_data fd; struct inode *root; struct qstr str; + struct nls_table *nls = NULL; int err = -EINVAL; sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL); - if (!sbi) { - err = -ENOMEM; - goto out2; - } + if (!sbi) + return -ENOMEM; + memset(sbi, 0, sizeof(HFSPLUS_SB(sb))); sb->s_fs_info = sbi; INIT_HLIST_HEAD(&sbi->rsrc_inodes); @@ -295,7 +298,16 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (!silent) printk("HFS+-fs: unable to parse mount options\n"); err = -EINVAL; - goto out2; + goto cleanup; + } + + /* temporarily use utf8 to correctly find the hidden dir below */ + nls = sbi->nls; + sbi->nls = load_nls("utf8"); + if (!nls) { + printk("HFS+: unable to load nls for utf8\n"); + err = -EINVAL; + goto cleanup; } /* Grab the volume header */ @@ -303,7 +315,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) if (!silent) printk("HFS+-fs: unable to find HFS+ superblock\n"); err = -EINVAL; - goto out2; + goto cleanup; } vhdr = HFSPLUS_SB(sb).s_vhdr; @@ -376,7 +388,7 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1; str.name = HFSP_HIDDENDIR_NAME; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); - hfsplus_cat_build_key(fd.search_key, HFSPLUS_ROOT_CNID, &str); + hfsplus_cat_build_key(sb, fd.search_key, HFSPLUS_ROOT_CNID, &str); if (!hfs_brec_read(&fd, &entry, sizeof(entry))) { hfs_find_exit(&fd); if (entry.type != cpu_to_be16(HFSPLUS_FOLDER)) @@ -410,11 +422,14 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent) mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir); } out: + unload_nls(sbi->nls); + sbi->nls = nls; return 0; cleanup: hfsplus_put_super(sb); -out2: + if (nls) + unload_nls(nls); return err; } diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 4821cd239d595..59b4649a55163 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -59,19 +59,20 @@ int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unis } } -int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len) +int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, char *astr, int *len_p) { const hfsplus_unichr *ip; + struct nls_table *nls = HFSPLUS_SB(sb).nls; u8 *op; u16 ustrlen, cc; - int size, tmp; + int size, len; op = astr; ip = ustr->unicode; ustrlen = be16_to_cpu(ustr->length); - tmp = *len; - while (ustrlen > 0 && tmp > 0) { - cc = be16_to_cpu(*ip); + len = *len_p; + while (ustrlen > 0 && len > 0) { + cc = be16_to_cpu(*ip++); switch (cc) { case 0: cc = 0x2400; @@ -80,48 +81,36 @@ int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len) cc = ':'; break; } - if (cc > 0x7f) { - size = utf8_wctomb(op, cc, tmp); - if (size == -1) { - /* ignore */ - } else { - op += size; - tmp -= size; - } - } else { - *op++ = (u8) cc; - tmp--; + size = nls->uni2char(cc, op, len); + if (size <= 0) { + *op = '?'; + size = 1; } - ip++; + op += size; + len -= size; ustrlen--; } - *len = (char *)op - astr; + *len_p = (char *)op - astr; if (ustrlen) return -ENAMETOOLONG; return 0; } -int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len) +int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) { - int tmp; + struct nls_table *nls = HFSPLUS_SB(sb).nls; + int size; wchar_t c; u16 outlen = 0; while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) { - if (*astr & 0x80) { - tmp = utf8_mbtowc(&c, astr, len); - if (tmp < 0) { - astr++; - len--; - continue; - } else { - astr += tmp; - len -= tmp; - } - } else { - c = *astr++; - len--; + size = nls->char2uni(astr, len, &c); + if (size <= 0) { + c = '?'; + size = 1; } + astr += size; + len -= size; switch (c) { case 0x2400: c = 0; |