aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoman Zippel <zippel@linux-m68k.org>2005-03-28 04:09:45 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-03-28 04:09:45 -0800
commit61e76bc2799e5836f81019224757ac15a8455d0d (patch)
treecb5f456eb81720ca04ae759ee3e825fc3d762b37
parentfad7d09c3c3ea606b992e17d97bf7f8bdd985fda (diff)
downloadhistory-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/Kconfig1
-rw-r--r--fs/hfsplus/catalog.c39
-rw-r--r--fs/hfsplus/dir.c8
-rw-r--r--fs/hfsplus/hfsplus_fs.h7
-rw-r--r--fs/hfsplus/options.c30
-rw-r--r--fs/hfsplus/super.c31
-rw-r--r--fs/hfsplus/unicode.c55
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;