diff options
author | Hin-Tak Leung <htl10@users.sourceforge.net> | 2014-06-06 17:36:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-06-06 19:08:09 -0400 |
commit | 017f8da43e92ddd9989884720b694a512e09ccce (patch) | |
tree | 2edff7cb8d45e6e859b91d8a7382799459f818f0 /fs/hfsplus | |
parent | 6d6bd94f4d83d70cdff67d0bf2a64ef6878216e7 (diff) |
hfsplus: fix worst-case unicode to char conversion of file names and attributes
This is a series of 3 patches which corrects issues in HFS+ concerning
the use of non-english file names and attributes. Names and attributes
are stored internally as UTF-16 units up to a fixed maximum size, and
convert to and from user-representation by NLS. The code incorrectly
assume that NLS string lengths are equal to unicode lengths, which is
only true for English ascii usage.
This patch (of 3):
The HFS Plus Volume Format specification (TN1150) states that file names
are stored internally as a maximum of 255 unicode characters, as defined
by The Unicode Standard, Version 2.0 [Unicode, Inc. ISBN
0-201-48345-9]. File names are converted by the NLS system on Linux
before presented to the user.
255 CJK characters converts to UTF-8 with 1 unicode character to up to 3
bytes, and to GB18030 with 1 unicode character to up to 4 bytes. Thus,
trying in a UTF-8 locale to list files with names of more than 85 CJK
characters results in:
$ ls /mnt
ls: reading directory /mnt: File name too long
The receiving buffer to hfsplus_uni2asc() needs to be 255 x
NLS_MAX_CHARSET_SIZE bytes, not 255 bytes as the code has always been.
Similar consideration applies to attributes, which are stored internally
as a maximum of 127 UTF-16BE units. See XNU source for an up-to-date
reference on attributes.
Strictly speaking, the maximum value of NLS_MAX_CHARSET_SIZE = 6 is not
attainable in the case of conversion to UTF-8, as going beyond 3 bytes
requires the use of surrogate pairs, i.e. consuming two input units.
Thanks Anton Altaparmakov for reviewing an earlier version of this
change.
This patch fixes all callers of hfsplus_uni2asc(), and also enables the
use of long non-English file names in HFS+. The getting and setting,
and general usage of long non-English attributes requires further
forthcoming work, in the following patches of this series.
[akpm@linux-foundation.org: fix build]
Signed-off-by: Hin-Tak Leung <htl10@users.sourceforge.net>
Reviewed-by: Anton Altaparmakov <anton@tuxera.com>
Cc: Vyacheslav Dubeyko <slava@dubeyko.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Sougata Santra <sougata@tuxera.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/hfsplus')
-rw-r--r-- | fs/hfsplus/dir.c | 11 | ||||
-rw-r--r-- | fs/hfsplus/xattr.c | 15 |
2 files changed, 21 insertions, 5 deletions
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index bdec66522de3..fb07d260e692 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/fs.h> | 12 | #include <linux/fs.h> |
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/random.h> | 14 | #include <linux/random.h> |
15 | #include <linux/nls.h> | ||
15 | 16 | ||
16 | #include "hfsplus_fs.h" | 17 | #include "hfsplus_fs.h" |
17 | #include "hfsplus_raw.h" | 18 | #include "hfsplus_raw.h" |
@@ -127,7 +128,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) | |||
127 | struct inode *inode = file_inode(file); | 128 | struct inode *inode = file_inode(file); |
128 | struct super_block *sb = inode->i_sb; | 129 | struct super_block *sb = inode->i_sb; |
129 | int len, err; | 130 | int len, err; |
130 | char strbuf[HFSPLUS_MAX_STRLEN + 1]; | 131 | char *strbuf; |
131 | hfsplus_cat_entry entry; | 132 | hfsplus_cat_entry entry; |
132 | struct hfs_find_data fd; | 133 | struct hfs_find_data fd; |
133 | struct hfsplus_readdir_data *rd; | 134 | struct hfsplus_readdir_data *rd; |
@@ -139,6 +140,11 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) | |||
139 | err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); | 140 | err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd); |
140 | if (err) | 141 | if (err) |
141 | return err; | 142 | return err; |
143 | strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL); | ||
144 | if (!strbuf) { | ||
145 | err = -ENOMEM; | ||
146 | goto out; | ||
147 | } | ||
142 | hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); | 148 | hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); |
143 | err = hfs_brec_find(&fd, hfs_find_rec_by_key); | 149 | err = hfs_brec_find(&fd, hfs_find_rec_by_key); |
144 | if (err) | 150 | if (err) |
@@ -193,7 +199,7 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) | |||
193 | hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, | 199 | hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, |
194 | fd.entrylength); | 200 | fd.entrylength); |
195 | type = be16_to_cpu(entry.type); | 201 | type = be16_to_cpu(entry.type); |
196 | len = HFSPLUS_MAX_STRLEN; | 202 | len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN; |
197 | err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); | 203 | err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); |
198 | if (err) | 204 | if (err) |
199 | goto out; | 205 | goto out; |
@@ -246,6 +252,7 @@ next: | |||
246 | } | 252 | } |
247 | memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); | 253 | memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key)); |
248 | out: | 254 | out: |
255 | kfree(strbuf); | ||
249 | hfs_find_exit(&fd); | 256 | hfs_find_exit(&fd); |
250 | return err; | 257 | return err; |
251 | } | 258 | } |
diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index 4e27edc082a4..e2b3c9ea1c71 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include "hfsplus_fs.h" | 9 | #include "hfsplus_fs.h" |
10 | #include <linux/posix_acl_xattr.h> | 10 | #include <linux/posix_acl_xattr.h> |
11 | #include <linux/nls.h> | ||
11 | #include "xattr.h" | 12 | #include "xattr.h" |
12 | #include "acl.h" | 13 | #include "acl.h" |
13 | 14 | ||
@@ -645,8 +646,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
645 | struct hfs_find_data fd; | 646 | struct hfs_find_data fd; |
646 | u16 key_len = 0; | 647 | u16 key_len = 0; |
647 | struct hfsplus_attr_key attr_key; | 648 | struct hfsplus_attr_key attr_key; |
648 | char strbuf[HFSPLUS_ATTR_MAX_STRLEN + | 649 | char *strbuf; |
649 | XATTR_MAC_OSX_PREFIX_LEN + 1] = {0}; | ||
650 | int xattr_name_len; | 650 | int xattr_name_len; |
651 | 651 | ||
652 | if ((!S_ISREG(inode->i_mode) && | 652 | if ((!S_ISREG(inode->i_mode) && |
@@ -666,6 +666,13 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
666 | return err; | 666 | return err; |
667 | } | 667 | } |
668 | 668 | ||
669 | strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + | ||
670 | XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); | ||
671 | if (!strbuf) { | ||
672 | res = -ENOMEM; | ||
673 | goto out; | ||
674 | } | ||
675 | |||
669 | err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); | 676 | err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); |
670 | if (err) { | 677 | if (err) { |
671 | if (err == -ENOENT) { | 678 | if (err == -ENOENT) { |
@@ -692,7 +699,7 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
692 | if (be32_to_cpu(attr_key.cnid) != inode->i_ino) | 699 | if (be32_to_cpu(attr_key.cnid) != inode->i_ino) |
693 | goto end_listxattr; | 700 | goto end_listxattr; |
694 | 701 | ||
695 | xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN; | 702 | xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN; |
696 | if (hfsplus_uni2asc(inode->i_sb, | 703 | if (hfsplus_uni2asc(inode->i_sb, |
697 | (const struct hfsplus_unistr *)&fd.key->attr.key_name, | 704 | (const struct hfsplus_unistr *)&fd.key->attr.key_name, |
698 | strbuf, &xattr_name_len)) { | 705 | strbuf, &xattr_name_len)) { |
@@ -718,6 +725,8 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) | |||
718 | } | 725 | } |
719 | 726 | ||
720 | end_listxattr: | 727 | end_listxattr: |
728 | kfree(strbuf); | ||
729 | out: | ||
721 | hfs_find_exit(&fd); | 730 | hfs_find_exit(&fd); |
722 | return res; | 731 | return res; |
723 | } | 732 | } |