From: BingJing Chang <bingjingc@xxxxxxxxxxxx> Some OSX extended attributes couldn't be displayed in ubuntu: $ mount -t hfsplus /dev/sdb3 /mnt $ cd /mnt $ getfattr -d -m ".*" picture.png Here, /dev/sdb is a usb stick from OSX computer. Output: osx.com.apple.FinderInfo=0sAAAAAAAAAAAEHAAAAAAAAAAAAABgf5OhAAAAAAAAAAg= osx.com.apple.lastuseddate#PS=0soCWGYAAAAAATBhINAAAAAA== osx.com.apple.macl=0sAgCH2+S5OA9OG5WnnVb5d67AAAAAAAAAAAAAAAAAAAAAAAAAAA... Voronoi.png: osx.com.apple.metadata:_kMDItemUserTags: No such attribute Voronoi.png: osx.com.apple.metadata:kMDItemIsScreenCapture: No suchattr... Voronoi.png: osx.com.apple.metadata:kMDItemScreenCaptureGlobalRect: No ... Voronoi.png: osx.com.apple.metadata:kMDItemScreenCaptureType: No such a... osx.com.apple.quarantine="0082;608625a1;Preview;" Here are 8 extended attributes in this file. However, some contents of them could not be retrieved. The problem is caused by the name-mangling scheme of ascii-unicode conversions in fs/hfsplus/unicode.c. The character '/' is illegal in Linux filenames but valid in OSX filenames. In contrast, the character ':' is opposite. So a simple name-mangling scheme can be applied to hfsplus filenames by replacing unicode '/' by ascii ':' and ascii ':' by unicode '/'. However, there're no such constraints in attribute names. Forcely converting ':' to '/' will cause the xattr lookup failures. To fix this, we introduce a new parameter name_mangling in hfsplus_uni2asc() and hfsplus_asc2uni() to indicate whether to perform such name-mangling scheme or not. And we give the hints not to perform the scheme for attribute keys. Reviewed-by: Chung-Chiang Cheng <cccheng@xxxxxxxxxxxx> Signed-off-by: BingJing Chang <bingjingc@xxxxxxxxxxxx> --- fs/hfsplus/attributes.c | 7 ++++--- fs/hfsplus/catalog.c | 4 ++-- fs/hfsplus/dir.c | 3 ++- fs/hfsplus/hfsplus_fs.h | 5 +++-- fs/hfsplus/unicode.c | 22 ++++++++++++---------- fs/hfsplus/xattr.c | 5 +++-- 6 files changed, 26 insertions(+), 20 deletions(-) diff --git a/fs/hfsplus/attributes.c b/fs/hfsplus/attributes.c index eeebe80..234badf 100644 --- a/fs/hfsplus/attributes.c +++ b/fs/hfsplus/attributes.c @@ -55,9 +55,10 @@ int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key, memset(key, 0, sizeof(struct hfsplus_attr_key)); key->attr.cnid = cpu_to_be32(cnid); if (name) { - int res = hfsplus_asc2uni(sb, - (struct hfsplus_unistr *)&key->attr.key_name, - HFSPLUS_ATTR_MAX_STRLEN, name, strlen(name)); + int res = hfsplus_asc2uni(sb, (struct hfsplus_unistr *) + &key->attr.key_name, + HFSPLUS_ATTR_MAX_STRLEN, + name, strlen(name), false); if (res) return res; len = be16_to_cpu(key->attr.key_name.length); diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c index 35472cb..f40fc9e 100644 --- a/fs/hfsplus/catalog.c +++ b/fs/hfsplus/catalog.c @@ -47,7 +47,7 @@ int hfsplus_cat_build_key(struct super_block *sb, key->cat.parent = cpu_to_be32(parent); err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN, - str->name, str->len); + str->name, str->len, true); if (unlikely(err < 0)) return err; @@ -183,7 +183,7 @@ static int hfsplus_fill_cat_thread(struct super_block *sb, entry->thread.reserved = 0; entry->thread.parentID = cpu_to_be32(parentid); err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN, - str->name, str->len); + str->name, str->len, true); if (unlikely(err < 0)) return err; diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c index 03e6c04..505b4e6 100644 --- a/fs/hfsplus/dir.c +++ b/fs/hfsplus/dir.c @@ -204,7 +204,8 @@ static int hfsplus_readdir(struct file *file, struct dir_context *ctx) fd.entrylength); type = be16_to_cpu(entry.type); len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN; - err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len); + err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len, + true); if (err) goto out; if (type == HFSPLUS_FOLDER) { diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 12b2047..1481d0a 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -522,9 +522,10 @@ int hfsplus_strcasecmp(const struct hfsplus_unistr *s1, int hfsplus_strcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2); int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, - char *astr, int *len_p); + char *astr, int *len_p, bool name_mangling); int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, - int max_unistr_len, const char *astr, int len); + int max_unistr_len, const char *astr, int len, + bool name_mangling); int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str); int hfsplus_compare_dentry(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name); diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c index 73342c9..52d9186 100644 --- a/fs/hfsplus/unicode.c +++ b/fs/hfsplus/unicode.c @@ -121,7 +121,7 @@ static u16 *hfsplus_compose_lookup(u16 *p, u16 cc) int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, - char *astr, int *len_p) + char *astr, int *len_p, bool name_mangling) { const hfsplus_unichr *ip; struct nls_table *nls = HFSPLUS_SB(sb)->nls; @@ -187,7 +187,8 @@ int hfsplus_uni2asc(struct super_block *sb, c0 = 0x2400; break; case '/': - c0 = ':'; + if (name_mangling) + c0 = ':'; break; } res = nls->uni2char(c0, op, len); @@ -253,8 +254,8 @@ int hfsplus_uni2asc(struct super_block *sb, * Convert one or more ASCII characters into a single unicode character. * Returns the number of ASCII characters corresponding to the unicode char. */ -static inline int asc2unichar(struct super_block *sb, const char *astr, int len, - wchar_t *uc) +static inline int asc2unichar(struct super_block *sb, const char *astr, + int len, wchar_t *uc, bool name_mangling) { int size = HFSPLUS_SB(sb)->nls->char2uni(astr, len, uc); if (size <= 0) { @@ -266,7 +267,8 @@ static inline int asc2unichar(struct super_block *sb, const char *astr, int len, *uc = 0; break; case ':': - *uc = '/'; + if (name_mangling) + *uc = '/'; break; } return size; @@ -343,7 +345,7 @@ static u16 *decompose_unichar(wchar_t uc, int *size, u16 *hangul_buffer) int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, int max_unistr_len, - const char *astr, int len) + const char *astr, int len, bool name_mangling) { int size, dsize, decompose; u16 *dstr, outlen = 0; @@ -352,7 +354,7 @@ int hfsplus_asc2uni(struct super_block *sb, decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); while (outlen < max_unistr_len && len > 0) { - size = asc2unichar(sb, astr, len, &c); + size = asc2unichar(sb, astr, len, &c, name_mangling); if (decompose) dstr = decompose_unichar(c, &dsize, dhangul); @@ -399,7 +401,7 @@ int hfsplus_hash_dentry(const struct dentry *dentry, struct qstr *str) len = str->len; while (len > 0) { int dsize; - size = asc2unichar(sb, astr, len, &c); + size = asc2unichar(sb, astr, len, &c, true); astr += size; len -= size; @@ -456,7 +458,7 @@ int hfsplus_compare_dentry(const struct dentry *dentry, while (len1 > 0 && len2 > 0) { if (!dsize1) { - size = asc2unichar(sb, astr1, len1, &c); + size = asc2unichar(sb, astr1, len1, &c, true); astr1 += size; len1 -= size; @@ -471,7 +473,7 @@ int hfsplus_compare_dentry(const struct dentry *dentry, } if (!dsize2) { - size = asc2unichar(sb, astr2, len2, &c); + size = asc2unichar(sb, astr2, len2, &c, true); astr2 += size; len2 -= size; diff --git a/fs/hfsplus/xattr.c b/fs/hfsplus/xattr.c index 4d169c5..de6a1c9 100644 --- a/fs/hfsplus/xattr.c +++ b/fs/hfsplus/xattr.c @@ -735,8 +735,9 @@ ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN; if (hfsplus_uni2asc(inode->i_sb, - (const struct hfsplus_unistr *)&fd.key->attr.key_name, - strbuf, &xattr_name_len)) { + (const struct hfsplus_unistr *) + &fd.key->attr.key_name, strbuf, + &xattr_name_len, false)) { pr_err("unicode conversion failed\n"); res = -EIO; goto end_listxattr; -- 2.7.4