aboutsummaryrefslogtreecommitdiffstats
path: root/fs/hfsplus
diff options
context:
space:
mode:
authorVyacheslav Dubeyko <slava@dubeyko.com>2013-02-27 20:03:04 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-27 22:10:10 -0500
commit324ef39a8a4f693035d63527f16100ed27310ecc (patch)
tree1814515cf6139fe1b4ebae8d6641c9ec6293d396 /fs/hfsplus
parent127e5f5ae51eff44c9bff673f24e8587caa4e29f (diff)
hfsplus: add support of manipulation by attributes file
Add support of manipulation by attributes file. Signed-off-by: Vyacheslav Dubeyko <slava@dubeyko.com> Reported-by: Hin-Tak Leung <htl10@users.sourceforge.net> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Christoph Hellwig <hch@lst.de> Cc: Jan Kara <jack@suse.cz> 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/Makefile4
-rw-r--r--fs/hfsplus/bfind.c93
-rw-r--r--fs/hfsplus/bnode.c6
-rw-r--r--fs/hfsplus/brec.c23
-rw-r--r--fs/hfsplus/btree.c8
-rw-r--r--fs/hfsplus/catalog.c36
-rw-r--r--fs/hfsplus/dir.c55
-rw-r--r--fs/hfsplus/extents.c4
-rw-r--r--fs/hfsplus/hfsplus_fs.h52
-rw-r--r--fs/hfsplus/inode.c18
-rw-r--r--fs/hfsplus/ioctl.c108
-rw-r--r--fs/hfsplus/super.c56
-rw-r--r--fs/hfsplus/unicode.c7
13 files changed, 287 insertions, 183 deletions
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
index 3cc0df730156..09d278bb7b91 100644
--- a/fs/hfsplus/Makefile
+++ b/fs/hfsplus/Makefile
@@ -5,5 +5,5 @@
5obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o 5obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
6 6
7hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \ 7hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
8 bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o 8 bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o \
9 9 attributes.o xattr.o xattr_user.o xattr_security.o xattr_trusted.o
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
index 5d799c13205f..d73c98d1ee99 100644
--- a/fs/hfsplus/bfind.c
+++ b/fs/hfsplus/bfind.c
@@ -24,7 +24,19 @@ int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
24 fd->key = ptr + tree->max_key_len + 2; 24 fd->key = ptr + tree->max_key_len + 2;
25 dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", 25 dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n",
26 tree->cnid, __builtin_return_address(0)); 26 tree->cnid, __builtin_return_address(0));
27 mutex_lock(&tree->tree_lock); 27 switch (tree->cnid) {
28 case HFSPLUS_CAT_CNID:
29 mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX);
30 break;
31 case HFSPLUS_EXT_CNID:
32 mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX);
33 break;
34 case HFSPLUS_ATTR_CNID:
35 mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX);
36 break;
37 default:
38 BUG();
39 }
28 return 0; 40 return 0;
29} 41}
30 42
@@ -38,15 +50,73 @@ void hfs_find_exit(struct hfs_find_data *fd)
38 fd->tree = NULL; 50 fd->tree = NULL;
39} 51}
40 52
41/* Find the record in bnode that best matches key (not greater than...)*/ 53int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode,
42int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) 54 struct hfs_find_data *fd,
55 int *begin,
56 int *end,
57 int *cur_rec)
58{
59 __be32 cur_cnid, search_cnid;
60
61 if (bnode->tree->cnid == HFSPLUS_EXT_CNID) {
62 cur_cnid = fd->key->ext.cnid;
63 search_cnid = fd->search_key->ext.cnid;
64 } else if (bnode->tree->cnid == HFSPLUS_CAT_CNID) {
65 cur_cnid = fd->key->cat.parent;
66 search_cnid = fd->search_key->cat.parent;
67 } else if (bnode->tree->cnid == HFSPLUS_ATTR_CNID) {
68 cur_cnid = fd->key->attr.cnid;
69 search_cnid = fd->search_key->attr.cnid;
70 } else
71 BUG();
72
73 if (cur_cnid == search_cnid) {
74 (*end) = (*cur_rec);
75 if ((*begin) == (*end))
76 return 1;
77 } else {
78 if (be32_to_cpu(cur_cnid) < be32_to_cpu(search_cnid))
79 (*begin) = (*cur_rec) + 1;
80 else
81 (*end) = (*cur_rec) - 1;
82 }
83
84 return 0;
85}
86
87int hfs_find_rec_by_key(struct hfs_bnode *bnode,
88 struct hfs_find_data *fd,
89 int *begin,
90 int *end,
91 int *cur_rec)
43{ 92{
44 int cmpval; 93 int cmpval;
94
95 cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
96 if (!cmpval) {
97 (*end) = (*cur_rec);
98 return 1;
99 }
100 if (cmpval < 0)
101 (*begin) = (*cur_rec) + 1;
102 else
103 *(end) = (*cur_rec) - 1;
104
105 return 0;
106}
107
108/* Find the record in bnode that best matches key (not greater than...)*/
109int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
110 search_strategy_t rec_found)
111{
45 u16 off, len, keylen; 112 u16 off, len, keylen;
46 int rec; 113 int rec;
47 int b, e; 114 int b, e;
48 int res; 115 int res;
49 116
117 if (!rec_found)
118 BUG();
119
50 b = 0; 120 b = 0;
51 e = bnode->num_recs - 1; 121 e = bnode->num_recs - 1;
52 res = -ENOENT; 122 res = -ENOENT;
@@ -59,17 +129,12 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
59 goto fail; 129 goto fail;
60 } 130 }
61 hfs_bnode_read(bnode, fd->key, off, keylen); 131 hfs_bnode_read(bnode, fd->key, off, keylen);
62 cmpval = bnode->tree->keycmp(fd->key, fd->search_key); 132 if (rec_found(bnode, fd, &b, &e, &rec)) {
63 if (!cmpval) {
64 e = rec;
65 res = 0; 133 res = 0;
66 goto done; 134 goto done;
67 } 135 }
68 if (cmpval < 0)
69 b = rec + 1;
70 else
71 e = rec - 1;
72 } while (b <= e); 136 } while (b <= e);
137
73 if (rec != e && e >= 0) { 138 if (rec != e && e >= 0) {
74 len = hfs_brec_lenoff(bnode, e, &off); 139 len = hfs_brec_lenoff(bnode, e, &off);
75 keylen = hfs_brec_keylen(bnode, e); 140 keylen = hfs_brec_keylen(bnode, e);
@@ -79,19 +144,21 @@ int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
79 } 144 }
80 hfs_bnode_read(bnode, fd->key, off, keylen); 145 hfs_bnode_read(bnode, fd->key, off, keylen);
81 } 146 }
147
82done: 148done:
83 fd->record = e; 149 fd->record = e;
84 fd->keyoffset = off; 150 fd->keyoffset = off;
85 fd->keylength = keylen; 151 fd->keylength = keylen;
86 fd->entryoffset = off + keylen; 152 fd->entryoffset = off + keylen;
87 fd->entrylength = len - keylen; 153 fd->entrylength = len - keylen;
154
88fail: 155fail:
89 return res; 156 return res;
90} 157}
91 158
92/* Traverse a B*Tree from the root to a leaf finding best fit to key */ 159/* Traverse a B*Tree from the root to a leaf finding best fit to key */
93/* Return allocated copy of node found, set recnum to best record */ 160/* Return allocated copy of node found, set recnum to best record */
94int hfs_brec_find(struct hfs_find_data *fd) 161int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare)
95{ 162{
96 struct hfs_btree *tree; 163 struct hfs_btree *tree;
97 struct hfs_bnode *bnode; 164 struct hfs_bnode *bnode;
@@ -122,7 +189,7 @@ int hfs_brec_find(struct hfs_find_data *fd)
122 goto invalid; 189 goto invalid;
123 bnode->parent = parent; 190 bnode->parent = parent;
124 191
125 res = __hfs_brec_find(bnode, fd); 192 res = __hfs_brec_find(bnode, fd, do_key_compare);
126 if (!height) 193 if (!height)
127 break; 194 break;
128 if (fd->record < 0) 195 if (fd->record < 0)
@@ -149,7 +216,7 @@ int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
149{ 216{
150 int res; 217 int res;
151 218
152 res = hfs_brec_find(fd); 219 res = hfs_brec_find(fd, hfs_find_rec_by_key);
153 if (res) 220 if (res)
154 return res; 221 return res;
155 if (fd->entrylength > rec_len) 222 if (fd->entrylength > rec_len)
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
index 1c42cc5b899f..5c125ce6bd72 100644
--- a/fs/hfsplus/bnode.c
+++ b/fs/hfsplus/bnode.c
@@ -62,7 +62,8 @@ void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
62 62
63 tree = node->tree; 63 tree = node->tree;
64 if (node->type == HFS_NODE_LEAF || 64 if (node->type == HFS_NODE_LEAF ||
65 tree->attributes & HFS_TREE_VARIDXKEYS) 65 tree->attributes & HFS_TREE_VARIDXKEYS ||
66 node->tree->cnid == HFSPLUS_ATTR_CNID)
66 key_len = hfs_bnode_read_u16(node, off) + 2; 67 key_len = hfs_bnode_read_u16(node, off) + 2;
67 else 68 else
68 key_len = tree->max_key_len + 2; 69 key_len = tree->max_key_len + 2;
@@ -314,7 +315,8 @@ void hfs_bnode_dump(struct hfs_bnode *node)
314 if (i && node->type == HFS_NODE_INDEX) { 315 if (i && node->type == HFS_NODE_INDEX) {
315 int tmp; 316 int tmp;
316 317
317 if (node->tree->attributes & HFS_TREE_VARIDXKEYS) 318 if (node->tree->attributes & HFS_TREE_VARIDXKEYS ||
319 node->tree->cnid == HFSPLUS_ATTR_CNID)
318 tmp = hfs_bnode_read_u16(node, key_off) + 2; 320 tmp = hfs_bnode_read_u16(node, key_off) + 2;
319 else 321 else
320 tmp = node->tree->max_key_len + 2; 322 tmp = node->tree->max_key_len + 2;
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
index 2a734cfccc92..298d4e45604b 100644
--- a/fs/hfsplus/brec.c
+++ b/fs/hfsplus/brec.c
@@ -36,7 +36,8 @@ u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
36 return 0; 36 return 0;
37 37
38 if ((node->type == HFS_NODE_INDEX) && 38 if ((node->type == HFS_NODE_INDEX) &&
39 !(node->tree->attributes & HFS_TREE_VARIDXKEYS)) { 39 !(node->tree->attributes & HFS_TREE_VARIDXKEYS) &&
40 (node->tree->cnid != HFSPLUS_ATTR_CNID)) {
40 retval = node->tree->max_key_len + 2; 41 retval = node->tree->max_key_len + 2;
41 } else { 42 } else {
42 recoff = hfs_bnode_read_u16(node, 43 recoff = hfs_bnode_read_u16(node,
@@ -151,12 +152,13 @@ skip:
151 152
152 /* get index key */ 153 /* get index key */
153 hfs_bnode_read_key(new_node, fd->search_key, 14); 154 hfs_bnode_read_key(new_node, fd->search_key, 14);
154 __hfs_brec_find(fd->bnode, fd); 155 __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key);
155 156
156 hfs_bnode_put(new_node); 157 hfs_bnode_put(new_node);
157 new_node = NULL; 158 new_node = NULL;
158 159
159 if (tree->attributes & HFS_TREE_VARIDXKEYS) 160 if ((tree->attributes & HFS_TREE_VARIDXKEYS) ||
161 (tree->cnid == HFSPLUS_ATTR_CNID))
160 key_len = be16_to_cpu(fd->search_key->key_len) + 2; 162 key_len = be16_to_cpu(fd->search_key->key_len) + 2;
161 else { 163 else {
162 fd->search_key->key_len = 164 fd->search_key->key_len =
@@ -201,7 +203,7 @@ again:
201 hfs_bnode_put(node); 203 hfs_bnode_put(node);
202 node = fd->bnode = parent; 204 node = fd->bnode = parent;
203 205
204 __hfs_brec_find(node, fd); 206 __hfs_brec_find(node, fd, hfs_find_rec_by_key);
205 goto again; 207 goto again;
206 } 208 }
207 hfs_bnode_write_u16(node, 209 hfs_bnode_write_u16(node,
@@ -367,12 +369,13 @@ again:
367 parent = hfs_bnode_find(tree, node->parent); 369 parent = hfs_bnode_find(tree, node->parent);
368 if (IS_ERR(parent)) 370 if (IS_ERR(parent))
369 return PTR_ERR(parent); 371 return PTR_ERR(parent);
370 __hfs_brec_find(parent, fd); 372 __hfs_brec_find(parent, fd, hfs_find_rec_by_key);
371 hfs_bnode_dump(parent); 373 hfs_bnode_dump(parent);
372 rec = fd->record; 374 rec = fd->record;
373 375
374 /* size difference between old and new key */ 376 /* size difference between old and new key */
375 if (tree->attributes & HFS_TREE_VARIDXKEYS) 377 if ((tree->attributes & HFS_TREE_VARIDXKEYS) ||
378 (tree->cnid == HFSPLUS_ATTR_CNID))
376 newkeylen = hfs_bnode_read_u16(node, 14) + 2; 379 newkeylen = hfs_bnode_read_u16(node, 14) + 2;
377 else 380 else
378 fd->keylength = newkeylen = tree->max_key_len + 2; 381 fd->keylength = newkeylen = tree->max_key_len + 2;
@@ -427,7 +430,7 @@ skip:
427 hfs_bnode_read_key(new_node, fd->search_key, 14); 430 hfs_bnode_read_key(new_node, fd->search_key, 14);
428 cnid = cpu_to_be32(new_node->this); 431 cnid = cpu_to_be32(new_node->this);
429 432
430 __hfs_brec_find(fd->bnode, fd); 433 __hfs_brec_find(fd->bnode, fd, hfs_find_rec_by_key);
431 hfs_brec_insert(fd, &cnid, sizeof(cnid)); 434 hfs_brec_insert(fd, &cnid, sizeof(cnid));
432 hfs_bnode_put(fd->bnode); 435 hfs_bnode_put(fd->bnode);
433 hfs_bnode_put(new_node); 436 hfs_bnode_put(new_node);
@@ -495,13 +498,15 @@ static int hfs_btree_inc_height(struct hfs_btree *tree)
495 /* insert old root idx into new root */ 498 /* insert old root idx into new root */
496 node->parent = tree->root; 499 node->parent = tree->root;
497 if (node->type == HFS_NODE_LEAF || 500 if (node->type == HFS_NODE_LEAF ||
498 tree->attributes & HFS_TREE_VARIDXKEYS) 501 tree->attributes & HFS_TREE_VARIDXKEYS ||
502 tree->cnid == HFSPLUS_ATTR_CNID)
499 key_size = hfs_bnode_read_u16(node, 14) + 2; 503 key_size = hfs_bnode_read_u16(node, 14) + 2;
500 else 504 else
501 key_size = tree->max_key_len + 2; 505 key_size = tree->max_key_len + 2;
502 hfs_bnode_copy(new_node, 14, node, 14, key_size); 506 hfs_bnode_copy(new_node, 14, node, 14, key_size);
503 507
504 if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) { 508 if (!(tree->attributes & HFS_TREE_VARIDXKEYS) &&
509 (tree->cnid != HFSPLUS_ATTR_CNID)) {
505 key_size = tree->max_key_len + 2; 510 key_size = tree->max_key_len + 2;
506 hfs_bnode_write_u16(new_node, 14, tree->max_key_len); 511 hfs_bnode_write_u16(new_node, 14, tree->max_key_len);
507 } 512 }
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
index 685d07d0ed18..efb689c21a95 100644
--- a/fs/hfsplus/btree.c
+++ b/fs/hfsplus/btree.c
@@ -98,6 +98,14 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
98 set_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags); 98 set_bit(HFSPLUS_SB_CASEFOLD, &HFSPLUS_SB(sb)->flags);
99 } 99 }
100 break; 100 break;
101 case HFSPLUS_ATTR_CNID:
102 if (tree->max_key_len != HFSPLUS_ATTR_KEYLEN - sizeof(u16)) {
103 printk(KERN_ERR "hfs: invalid attributes max_key_len %d\n",
104 tree->max_key_len);
105 goto fail_page;
106 }
107 tree->keycmp = hfsplus_attr_bin_cmp_key;
108 break;
101 default: 109 default:
102 printk(KERN_ERR "hfs: unknown B*Tree requested\n"); 110 printk(KERN_ERR "hfs: unknown B*Tree requested\n");
103 goto fail_page; 111 goto fail_page;
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
index 798d9c4c5e71..840d71edd193 100644
--- a/fs/hfsplus/catalog.c
+++ b/fs/hfsplus/catalog.c
@@ -45,7 +45,8 @@ void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
45 45
46 key->cat.parent = cpu_to_be32(parent); 46 key->cat.parent = cpu_to_be32(parent);
47 if (str) { 47 if (str) {
48 hfsplus_asc2uni(sb, &key->cat.name, str->name, str->len); 48 hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
49 str->name, str->len);
49 len = be16_to_cpu(key->cat.name.length); 50 len = be16_to_cpu(key->cat.name.length);
50 } else { 51 } else {
51 key->cat.name.length = 0; 52 key->cat.name.length = 0;
@@ -167,7 +168,8 @@ static int hfsplus_fill_cat_thread(struct super_block *sb,
167 entry->type = cpu_to_be16(type); 168 entry->type = cpu_to_be16(type);
168 entry->thread.reserved = 0; 169 entry->thread.reserved = 0;
169 entry->thread.parentID = cpu_to_be32(parentid); 170 entry->thread.parentID = cpu_to_be32(parentid);
170 hfsplus_asc2uni(sb, &entry->thread.nodeName, str->name, str->len); 171 hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
172 str->name, str->len);
171 return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2; 173 return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
172} 174}
173 175
@@ -198,7 +200,7 @@ int hfsplus_find_cat(struct super_block *sb, u32 cnid,
198 hfsplus_cat_build_key_uni(fd->search_key, 200 hfsplus_cat_build_key_uni(fd->search_key,
199 be32_to_cpu(tmp.thread.parentID), 201 be32_to_cpu(tmp.thread.parentID),
200 &tmp.thread.nodeName); 202 &tmp.thread.nodeName);
201 return hfs_brec_find(fd); 203 return hfs_brec_find(fd, hfs_find_rec_by_key);
202} 204}
203 205
204int hfsplus_create_cat(u32 cnid, struct inode *dir, 206int hfsplus_create_cat(u32 cnid, struct inode *dir,
@@ -221,7 +223,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
221 S_ISDIR(inode->i_mode) ? 223 S_ISDIR(inode->i_mode) ?
222 HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD, 224 HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
223 dir->i_ino, str); 225 dir->i_ino, str);
224 err = hfs_brec_find(&fd); 226 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
225 if (err != -ENOENT) { 227 if (err != -ENOENT) {
226 if (!err) 228 if (!err)
227 err = -EEXIST; 229 err = -EEXIST;
@@ -233,7 +235,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
233 235
234 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); 236 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
235 entry_size = hfsplus_cat_build_record(&entry, cnid, inode); 237 entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
236 err = hfs_brec_find(&fd); 238 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
237 if (err != -ENOENT) { 239 if (err != -ENOENT) {
238 /* panic? */ 240 /* panic? */
239 if (!err) 241 if (!err)
@@ -253,7 +255,7 @@ int hfsplus_create_cat(u32 cnid, struct inode *dir,
253 255
254err1: 256err1:
255 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 257 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
256 if (!hfs_brec_find(&fd)) 258 if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
257 hfs_brec_remove(&fd); 259 hfs_brec_remove(&fd);
258err2: 260err2:
259 hfs_find_exit(&fd); 261 hfs_find_exit(&fd);
@@ -279,7 +281,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
279 int len; 281 int len;
280 282
281 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 283 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
282 err = hfs_brec_find(&fd); 284 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
283 if (err) 285 if (err)
284 goto out; 286 goto out;
285 287
@@ -296,7 +298,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
296 } else 298 } else
297 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); 299 hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
298 300
299 err = hfs_brec_find(&fd); 301 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
300 if (err) 302 if (err)
301 goto out; 303 goto out;
302 304
@@ -326,7 +328,7 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
326 goto out; 328 goto out;
327 329
328 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL); 330 hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
329 err = hfs_brec_find(&fd); 331 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
330 if (err) 332 if (err)
331 goto out; 333 goto out;
332 334
@@ -337,6 +339,12 @@ int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
337 dir->i_size--; 339 dir->i_size--;
338 dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; 340 dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
339 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); 341 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
342
343 if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
344 if (HFSPLUS_SB(sb)->attr_tree)
345 hfsplus_delete_all_attrs(dir, cnid);
346 }
347
340out: 348out:
341 hfs_find_exit(&fd); 349 hfs_find_exit(&fd);
342 350
@@ -363,7 +371,7 @@ int hfsplus_rename_cat(u32 cnid,
363 371
364 /* find the old dir entry and read the data */ 372 /* find the old dir entry and read the data */
365 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); 373 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
366 err = hfs_brec_find(&src_fd); 374 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
367 if (err) 375 if (err)
368 goto out; 376 goto out;
369 if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) { 377 if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
@@ -376,7 +384,7 @@ int hfsplus_rename_cat(u32 cnid,
376 384
377 /* create new dir entry with the data from the old entry */ 385 /* create new dir entry with the data from the old entry */
378 hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name); 386 hfsplus_cat_build_key(sb, dst_fd.search_key, dst_dir->i_ino, dst_name);
379 err = hfs_brec_find(&dst_fd); 387 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
380 if (err != -ENOENT) { 388 if (err != -ENOENT) {
381 if (!err) 389 if (!err)
382 err = -EEXIST; 390 err = -EEXIST;
@@ -391,7 +399,7 @@ int hfsplus_rename_cat(u32 cnid,
391 399
392 /* finally remove the old entry */ 400 /* finally remove the old entry */
393 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name); 401 hfsplus_cat_build_key(sb, src_fd.search_key, src_dir->i_ino, src_name);
394 err = hfs_brec_find(&src_fd); 402 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
395 if (err) 403 if (err)
396 goto out; 404 goto out;
397 err = hfs_brec_remove(&src_fd); 405 err = hfs_brec_remove(&src_fd);
@@ -402,7 +410,7 @@ int hfsplus_rename_cat(u32 cnid,
402 410
403 /* remove old thread entry */ 411 /* remove old thread entry */
404 hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL); 412 hfsplus_cat_build_key(sb, src_fd.search_key, cnid, NULL);
405 err = hfs_brec_find(&src_fd); 413 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
406 if (err) 414 if (err)
407 goto out; 415 goto out;
408 type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset); 416 type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
@@ -414,7 +422,7 @@ int hfsplus_rename_cat(u32 cnid,
414 hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL); 422 hfsplus_cat_build_key(sb, dst_fd.search_key, cnid, NULL);
415 entry_size = hfsplus_fill_cat_thread(sb, &entry, type, 423 entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
416 dst_dir->i_ino, dst_name); 424 dst_dir->i_ino, dst_name);
417 err = hfs_brec_find(&dst_fd); 425 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
418 if (err != -ENOENT) { 426 if (err != -ENOENT) {
419 if (!err) 427 if (!err)
420 err = -EEXIST; 428 err = -EEXIST;
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
index 074e04589248..031c24e50521 100644
--- a/fs/hfsplus/dir.c
+++ b/fs/hfsplus/dir.c
@@ -15,6 +15,7 @@
15 15
16#include "hfsplus_fs.h" 16#include "hfsplus_fs.h"
17#include "hfsplus_raw.h" 17#include "hfsplus_raw.h"
18#include "xattr.h"
18 19
19static inline void hfsplus_instantiate(struct dentry *dentry, 20static inline void hfsplus_instantiate(struct dentry *dentry,
20 struct inode *inode, u32 cnid) 21 struct inode *inode, u32 cnid)
@@ -138,7 +139,7 @@ static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
138 if (err) 139 if (err)
139 return err; 140 return err;
140 hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL); 141 hfsplus_cat_build_key(sb, fd.search_key, inode->i_ino, NULL);
141 err = hfs_brec_find(&fd); 142 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
142 if (err) 143 if (err)
143 goto out; 144 goto out;
144 145
@@ -421,6 +422,15 @@ static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
421 if (res) 422 if (res)
422 goto out_err; 423 goto out_err;
423 424
425 res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
426 if (res == -EOPNOTSUPP)
427 res = 0; /* Operation is not supported. */
428 else if (res) {
429 /* Try to delete anyway without error analysis. */
430 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
431 goto out_err;
432 }
433
424 hfsplus_instantiate(dentry, inode, inode->i_ino); 434 hfsplus_instantiate(dentry, inode, inode->i_ino);
425 mark_inode_dirty(inode); 435 mark_inode_dirty(inode);
426 goto out; 436 goto out;
@@ -450,15 +460,26 @@ static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
450 init_special_inode(inode, mode, rdev); 460 init_special_inode(inode, mode, rdev);
451 461
452 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode); 462 res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
453 if (res) { 463 if (res)
454 clear_nlink(inode); 464 goto failed_mknod;
455 hfsplus_delete_inode(inode); 465
456 iput(inode); 466 res = hfsplus_init_inode_security(inode, dir, &dentry->d_name);
457 goto out; 467 if (res == -EOPNOTSUPP)
468 res = 0; /* Operation is not supported. */
469 else if (res) {
470 /* Try to delete anyway without error analysis. */
471 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
472 goto failed_mknod;
458 } 473 }
459 474
460 hfsplus_instantiate(dentry, inode, inode->i_ino); 475 hfsplus_instantiate(dentry, inode, inode->i_ino);
461 mark_inode_dirty(inode); 476 mark_inode_dirty(inode);
477 goto out;
478
479failed_mknod:
480 clear_nlink(inode);
481 hfsplus_delete_inode(inode);
482 iput(inode);
462out: 483out:
463 mutex_unlock(&sbi->vh_mutex); 484 mutex_unlock(&sbi->vh_mutex);
464 return res; 485 return res;
@@ -499,15 +520,19 @@ static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
499} 520}
500 521
501const struct inode_operations hfsplus_dir_inode_operations = { 522const struct inode_operations hfsplus_dir_inode_operations = {
502 .lookup = hfsplus_lookup, 523 .lookup = hfsplus_lookup,
503 .create = hfsplus_create, 524 .create = hfsplus_create,
504 .link = hfsplus_link, 525 .link = hfsplus_link,
505 .unlink = hfsplus_unlink, 526 .unlink = hfsplus_unlink,
506 .mkdir = hfsplus_mkdir, 527 .mkdir = hfsplus_mkdir,
507 .rmdir = hfsplus_rmdir, 528 .rmdir = hfsplus_rmdir,
508 .symlink = hfsplus_symlink, 529 .symlink = hfsplus_symlink,
509 .mknod = hfsplus_mknod, 530 .mknod = hfsplus_mknod,
510 .rename = hfsplus_rename, 531 .rename = hfsplus_rename,
532 .setxattr = generic_setxattr,
533 .getxattr = generic_getxattr,
534 .listxattr = hfsplus_listxattr,
535 .removexattr = hfsplus_removexattr,
511}; 536};
512 537
513const struct file_operations hfsplus_dir_operations = { 538const struct file_operations hfsplus_dir_operations = {
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
index eba76eab6d62..a94f0f779d5e 100644
--- a/fs/hfsplus/extents.c
+++ b/fs/hfsplus/extents.c
@@ -95,7 +95,7 @@ static void __hfsplus_ext_write_extent(struct inode *inode,
95 HFSPLUS_IS_RSRC(inode) ? 95 HFSPLUS_IS_RSRC(inode) ?
96 HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA); 96 HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
97 97
98 res = hfs_brec_find(fd); 98 res = hfs_brec_find(fd, hfs_find_rec_by_key);
99 if (hip->extent_state & HFSPLUS_EXT_NEW) { 99 if (hip->extent_state & HFSPLUS_EXT_NEW) {
100 if (res != -ENOENT) 100 if (res != -ENOENT)
101 return; 101 return;
@@ -154,7 +154,7 @@ static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
154 154
155 hfsplus_ext_build_key(fd->search_key, cnid, block, type); 155 hfsplus_ext_build_key(fd->search_key, cnid, block, type);
156 fd->key->ext.cnid = 0; 156 fd->key->ext.cnid = 0;
157 res = hfs_brec_find(fd); 157 res = hfs_brec_find(fd, hfs_find_rec_by_key);
158 if (res && res != -ENOENT) 158 if (res && res != -ENOENT)
159 return res; 159 return res;
160 if (fd->key->ext.cnid != fd->search_key->ext.cnid || 160 if (fd->key->ext.cnid != fd->search_key->ext.cnid ||
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index a6da86b1b4c1..05b11f36024c 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -23,6 +23,7 @@
23#define DBG_SUPER 0x00000010 23#define DBG_SUPER 0x00000010
24#define DBG_EXTENT 0x00000020 24#define DBG_EXTENT 0x00000020
25#define DBG_BITMAP 0x00000040 25#define DBG_BITMAP 0x00000040
26#define DBG_ATTR_MOD 0x00000080
26 27
27#if 0 28#if 0
28#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD) 29#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
@@ -46,6 +47,13 @@ typedef int (*btree_keycmp)(const hfsplus_btree_key *,
46 47
47#define NODE_HASH_SIZE 256 48#define NODE_HASH_SIZE 256
48 49
50/* B-tree mutex nested subclasses */
51enum hfsplus_btree_mutex_classes {
52 CATALOG_BTREE_MUTEX,
53 EXTENTS_BTREE_MUTEX,
54 ATTR_BTREE_MUTEX,
55};
56
49/* An HFS+ BTree held in memory */ 57/* An HFS+ BTree held in memory */
50struct hfs_btree { 58struct hfs_btree {
51 struct super_block *sb; 59 struct super_block *sb;
@@ -223,6 +231,7 @@ struct hfsplus_inode_info {
223#define HFSPLUS_I_CAT_DIRTY 1 /* has changes in the catalog tree */ 231#define HFSPLUS_I_CAT_DIRTY 1 /* has changes in the catalog tree */
224#define HFSPLUS_I_EXT_DIRTY 2 /* has changes in the extent tree */ 232#define HFSPLUS_I_EXT_DIRTY 2 /* has changes in the extent tree */
225#define HFSPLUS_I_ALLOC_DIRTY 3 /* has changes in the allocation file */ 233#define HFSPLUS_I_ALLOC_DIRTY 3 /* has changes in the allocation file */
234#define HFSPLUS_I_ATTR_DIRTY 4 /* has changes in the attributes tree */
226 235
227#define HFSPLUS_IS_RSRC(inode) \ 236#define HFSPLUS_IS_RSRC(inode) \
228 test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags) 237 test_bit(HFSPLUS_I_RSRC, &HFSPLUS_I(inode)->flags)
@@ -302,7 +311,7 @@ static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
302#define hfs_brec_remove hfsplus_brec_remove 311#define hfs_brec_remove hfsplus_brec_remove
303#define hfs_find_init hfsplus_find_init 312#define hfs_find_init hfsplus_find_init
304#define hfs_find_exit hfsplus_find_exit 313#define hfs_find_exit hfsplus_find_exit
305#define __hfs_brec_find __hplusfs_brec_find 314#define __hfs_brec_find __hfsplus_brec_find
306#define hfs_brec_find hfsplus_brec_find 315#define hfs_brec_find hfsplus_brec_find
307#define hfs_brec_read hfsplus_brec_read 316#define hfs_brec_read hfsplus_brec_read
308#define hfs_brec_goto hfsplus_brec_goto 317#define hfs_brec_goto hfsplus_brec_goto
@@ -324,10 +333,33 @@ static inline unsigned short hfsplus_min_io_size(struct super_block *sb)
324 */ 333 */
325#define HFSPLUS_IOC_BLESS _IO('h', 0x80) 334#define HFSPLUS_IOC_BLESS _IO('h', 0x80)
326 335
336typedef int (*search_strategy_t)(struct hfs_bnode *,
337 struct hfs_find_data *,
338 int *, int *, int *);
339
327/* 340/*
328 * Functions in any *.c used in other files 341 * Functions in any *.c used in other files
329 */ 342 */
330 343
344/* attributes.c */
345int hfsplus_create_attr_tree_cache(void);
346void hfsplus_destroy_attr_tree_cache(void);
347hfsplus_attr_entry *hfsplus_alloc_attr_entry(void);
348void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry_p);
349int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *,
350 const hfsplus_btree_key *);
351int hfsplus_attr_build_key(struct super_block *, hfsplus_btree_key *,
352 u32, const char *);
353void hfsplus_attr_build_key_uni(hfsplus_btree_key *key,
354 u32 cnid,
355 struct hfsplus_attr_unistr *name);
356int hfsplus_find_attr(struct super_block *, u32,
357 const char *, struct hfs_find_data *);
358int hfsplus_attr_exists(struct inode *inode, const char *name);
359int hfsplus_create_attr(struct inode *, const char *, const void *, size_t);
360int hfsplus_delete_attr(struct inode *, const char *);
361int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid);
362
331/* bitmap.c */ 363/* bitmap.c */
332int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *); 364int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *);
333int hfsplus_block_free(struct super_block *, u32, u32); 365int hfsplus_block_free(struct super_block *, u32, u32);
@@ -369,8 +401,15 @@ int hfs_brec_remove(struct hfs_find_data *);
369/* bfind.c */ 401/* bfind.c */
370int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); 402int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
371void hfs_find_exit(struct hfs_find_data *); 403void hfs_find_exit(struct hfs_find_data *);
372int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); 404int hfs_find_1st_rec_by_cnid(struct hfs_bnode *,
373int hfs_brec_find(struct hfs_find_data *); 405 struct hfs_find_data *,
406 int *, int *, int *);
407int hfs_find_rec_by_key(struct hfs_bnode *,
408 struct hfs_find_data *,
409 int *, int *, int *);
410int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *,
411 search_strategy_t);
412int hfs_brec_find(struct hfs_find_data *, search_strategy_t);
374int hfs_brec_read(struct hfs_find_data *, void *, int); 413int hfs_brec_read(struct hfs_find_data *, void *, int);
375int hfs_brec_goto(struct hfs_find_data *, int); 414int hfs_brec_goto(struct hfs_find_data *, int);
376 415
@@ -417,11 +456,6 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
417 456
418/* ioctl.c */ 457/* ioctl.c */
419long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); 458long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
420int hfsplus_setxattr(struct dentry *dentry, const char *name,
421 const void *value, size_t size, int flags);
422ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
423 void *value, size_t size);
424ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size);
425 459
426/* options.c */ 460/* options.c */
427int hfsplus_parse_options(char *, struct hfsplus_sb_info *); 461int hfsplus_parse_options(char *, struct hfsplus_sb_info *);
@@ -446,7 +480,7 @@ int hfsplus_strcmp(const struct hfsplus_unistr *,
446int hfsplus_uni2asc(struct super_block *, 480int hfsplus_uni2asc(struct super_block *,
447 const struct hfsplus_unistr *, char *, int *); 481 const struct hfsplus_unistr *, char *, int *);
448int hfsplus_asc2uni(struct super_block *, 482int hfsplus_asc2uni(struct super_block *,
449 struct hfsplus_unistr *, const char *, int); 483 struct hfsplus_unistr *, int, const char *, int);
450int hfsplus_hash_dentry(const struct dentry *dentry, 484int hfsplus_hash_dentry(const struct dentry *dentry,
451 const struct inode *inode, struct qstr *str); 485 const struct inode *inode, struct qstr *str);
452int hfsplus_compare_dentry(const struct dentry *parent, 486int hfsplus_compare_dentry(const struct dentry *parent,
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index dcd05be5344b..160ccc9cdb4b 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -17,6 +17,7 @@
17 17
18#include "hfsplus_fs.h" 18#include "hfsplus_fs.h"
19#include "hfsplus_raw.h" 19#include "hfsplus_raw.h"
20#include "xattr.h"
20 21
21static int hfsplus_readpage(struct file *file, struct page *page) 22static int hfsplus_readpage(struct file *file, struct page *page)
22{ 23{
@@ -348,6 +349,18 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
348 error = error2; 349 error = error2;
349 } 350 }
350 351
352 if (test_and_clear_bit(HFSPLUS_I_ATTR_DIRTY, &hip->flags)) {
353 if (sbi->attr_tree) {
354 error2 =
355 filemap_write_and_wait(
356 sbi->attr_tree->inode->i_mapping);
357 if (!error)
358 error = error2;
359 } else {
360 printk(KERN_ERR "hfs: sync non-existent attributes tree\n");
361 }
362 }
363
351 if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) { 364 if (test_and_clear_bit(HFSPLUS_I_ALLOC_DIRTY, &hip->flags)) {
352 error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 365 error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
353 if (!error) 366 if (!error)
@@ -365,9 +378,10 @@ int hfsplus_file_fsync(struct file *file, loff_t start, loff_t end,
365static const struct inode_operations hfsplus_file_inode_operations = { 378static const struct inode_operations hfsplus_file_inode_operations = {
366 .lookup = hfsplus_file_lookup, 379 .lookup = hfsplus_file_lookup,
367 .setattr = hfsplus_setattr, 380 .setattr = hfsplus_setattr,
368 .setxattr = hfsplus_setxattr, 381 .setxattr = generic_setxattr,
369 .getxattr = hfsplus_getxattr, 382 .getxattr = generic_getxattr,
370 .listxattr = hfsplus_listxattr, 383 .listxattr = hfsplus_listxattr,
384 .removexattr = hfsplus_removexattr,
371}; 385};
372 386
373static const struct file_operations hfsplus_file_operations = { 387static const struct file_operations hfsplus_file_operations = {
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index e3c4c4209428..d3ff5cc317d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -16,7 +16,6 @@
16#include <linux/fs.h> 16#include <linux/fs.h>
17#include <linux/mount.h> 17#include <linux/mount.h>
18#include <linux/sched.h> 18#include <linux/sched.h>
19#include <linux/xattr.h>
20#include <asm/uaccess.h> 19#include <asm/uaccess.h>
21#include "hfsplus_fs.h" 20#include "hfsplus_fs.h"
22 21
@@ -151,110 +150,3 @@ long hfsplus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
151 return -ENOTTY; 150 return -ENOTTY;
152 } 151 }
153} 152}
154
155int hfsplus_setxattr(struct dentry *dentry, const char *name,
156 const void *value, size_t size, int flags)
157{
158 struct inode *inode = dentry->d_inode;
159 struct hfs_find_data fd;
160 hfsplus_cat_entry entry;
161 struct hfsplus_cat_file *file;
162 int res;
163
164 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
165 return -EOPNOTSUPP;
166
167 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
168 if (res)
169 return res;
170 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
171 if (res)
172 goto out;
173 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
174 sizeof(struct hfsplus_cat_file));
175 file = &entry.file;
176
177 if (!strcmp(name, "hfs.type")) {
178 if (size == 4)
179 memcpy(&file->user_info.fdType, value, 4);
180 else
181 res = -ERANGE;
182 } else if (!strcmp(name, "hfs.creator")) {
183 if (size == 4)
184 memcpy(&file->user_info.fdCreator, value, 4);
185 else
186 res = -ERANGE;
187 } else
188 res = -EOPNOTSUPP;
189 if (!res) {
190 hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
191 sizeof(struct hfsplus_cat_file));
192 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
193 }
194out:
195 hfs_find_exit(&fd);
196 return res;
197}
198
199ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
200 void *value, size_t size)
201{
202 struct inode *inode = dentry->d_inode;
203 struct hfs_find_data fd;
204 hfsplus_cat_entry entry;
205 struct hfsplus_cat_file *file;
206 ssize_t res = 0;
207
208 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
209 return -EOPNOTSUPP;
210
211 if (size) {
212 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
213 if (res)
214 return res;
215 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
216 if (res)
217 goto out;
218 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
219 sizeof(struct hfsplus_cat_file));
220 }
221 file = &entry.file;
222
223 if (!strcmp(name, "hfs.type")) {
224 if (size >= 4) {
225 memcpy(value, &file->user_info.fdType, 4);
226 res = 4;
227 } else
228 res = size ? -ERANGE : 4;
229 } else if (!strcmp(name, "hfs.creator")) {
230 if (size >= 4) {
231 memcpy(value, &file->user_info.fdCreator, 4);
232 res = 4;
233 } else
234 res = size ? -ERANGE : 4;
235 } else
236 res = -EOPNOTSUPP;
237out:
238 if (size)
239 hfs_find_exit(&fd);
240 return res;
241}
242
243#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
244
245ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
246{
247 struct inode *inode = dentry->d_inode;
248
249 if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
250 return -EOPNOTSUPP;
251
252 if (!buffer || !size)
253 return HFSPLUS_ATTRLIST_SIZE;
254 if (size < HFSPLUS_ATTRLIST_SIZE)
255 return -ERANGE;
256 strcpy(buffer, "hfs.type");
257 strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
258
259 return HFSPLUS_ATTRLIST_SIZE;
260}
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
index 796198d26553..974c26f96fae 100644
--- a/fs/hfsplus/super.c
+++ b/fs/hfsplus/super.c
@@ -20,6 +20,7 @@ static struct inode *hfsplus_alloc_inode(struct super_block *sb);
20static void hfsplus_destroy_inode(struct inode *inode); 20static void hfsplus_destroy_inode(struct inode *inode);
21 21
22#include "hfsplus_fs.h" 22#include "hfsplus_fs.h"
23#include "xattr.h"
23 24
24static int hfsplus_system_read_inode(struct inode *inode) 25static int hfsplus_system_read_inode(struct inode *inode)
25{ 26{
@@ -118,6 +119,7 @@ static int hfsplus_system_write_inode(struct inode *inode)
118 case HFSPLUS_ATTR_CNID: 119 case HFSPLUS_ATTR_CNID:
119 fork = &vhdr->attr_file; 120 fork = &vhdr->attr_file;
120 tree = sbi->attr_tree; 121 tree = sbi->attr_tree;
122 break;
121 default: 123 default:
122 return -EIO; 124 return -EIO;
123 } 125 }
@@ -191,6 +193,12 @@ static int hfsplus_sync_fs(struct super_block *sb, int wait)
191 error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping); 193 error2 = filemap_write_and_wait(sbi->ext_tree->inode->i_mapping);
192 if (!error) 194 if (!error)
193 error = error2; 195 error = error2;
196 if (sbi->attr_tree) {
197 error2 =
198 filemap_write_and_wait(sbi->attr_tree->inode->i_mapping);
199 if (!error)
200 error = error2;
201 }
194 error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping); 202 error2 = filemap_write_and_wait(sbi->alloc_file->i_mapping);
195 if (!error) 203 if (!error)
196 error = error2; 204 error = error2;
@@ -281,6 +289,7 @@ static void hfsplus_put_super(struct super_block *sb)
281 hfsplus_sync_fs(sb, 1); 289 hfsplus_sync_fs(sb, 1);
282 } 290 }
283 291
292 hfs_btree_close(sbi->attr_tree);
284 hfs_btree_close(sbi->cat_tree); 293 hfs_btree_close(sbi->cat_tree);
285 hfs_btree_close(sbi->ext_tree); 294 hfs_btree_close(sbi->ext_tree);
286 iput(sbi->alloc_file); 295 iput(sbi->alloc_file);
@@ -477,12 +486,20 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
477 printk(KERN_ERR "hfs: failed to load catalog file\n"); 486 printk(KERN_ERR "hfs: failed to load catalog file\n");
478 goto out_close_ext_tree; 487 goto out_close_ext_tree;
479 } 488 }
489 if (vhdr->attr_file.total_blocks != 0) {
490 sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
491 if (!sbi->attr_tree) {
492 printk(KERN_ERR "hfs: failed to load attributes file\n");
493 goto out_close_cat_tree;
494 }
495 }
496 sb->s_xattr = hfsplus_xattr_handlers;
480 497
481 inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID); 498 inode = hfsplus_iget(sb, HFSPLUS_ALLOC_CNID);
482 if (IS_ERR(inode)) { 499 if (IS_ERR(inode)) {
483 printk(KERN_ERR "hfs: failed to load allocation file\n"); 500 printk(KERN_ERR "hfs: failed to load allocation file\n");
484 err = PTR_ERR(inode); 501 err = PTR_ERR(inode);
485 goto out_close_cat_tree; 502 goto out_close_attr_tree;
486 } 503 }
487 sbi->alloc_file = inode; 504 sbi->alloc_file = inode;
488 505
@@ -542,10 +559,27 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
542 } 559 }
543 err = hfsplus_create_cat(sbi->hidden_dir->i_ino, root, 560 err = hfsplus_create_cat(sbi->hidden_dir->i_ino, root,
544 &str, sbi->hidden_dir); 561 &str, sbi->hidden_dir);
545 mutex_unlock(&sbi->vh_mutex); 562 if (err) {
546 if (err) 563 mutex_unlock(&sbi->vh_mutex);
564 goto out_put_hidden_dir;
565 }
566
567 err = hfsplus_init_inode_security(sbi->hidden_dir,
568 root, &str);
569 if (err == -EOPNOTSUPP)
570 err = 0; /* Operation is not supported. */
571 else if (err) {
572 /*
573 * Try to delete anyway without
574 * error analysis.
575 */
576 hfsplus_delete_cat(sbi->hidden_dir->i_ino,
577 root, &str);
578 mutex_unlock(&sbi->vh_mutex);
547 goto out_put_hidden_dir; 579 goto out_put_hidden_dir;
580 }
548 581
582 mutex_unlock(&sbi->vh_mutex);
549 hfsplus_mark_inode_dirty(sbi->hidden_dir, 583 hfsplus_mark_inode_dirty(sbi->hidden_dir,
550 HFSPLUS_I_CAT_DIRTY); 584 HFSPLUS_I_CAT_DIRTY);
551 } 585 }
@@ -562,6 +596,8 @@ out_put_root:
562 sb->s_root = NULL; 596 sb->s_root = NULL;
563out_put_alloc_file: 597out_put_alloc_file:
564 iput(sbi->alloc_file); 598 iput(sbi->alloc_file);
599out_close_attr_tree:
600 hfs_btree_close(sbi->attr_tree);
565out_close_cat_tree: 601out_close_cat_tree:
566 hfs_btree_close(sbi->cat_tree); 602 hfs_btree_close(sbi->cat_tree);
567out_close_ext_tree: 603out_close_ext_tree:
@@ -635,9 +671,20 @@ static int __init init_hfsplus_fs(void)
635 hfsplus_init_once); 671 hfsplus_init_once);
636 if (!hfsplus_inode_cachep) 672 if (!hfsplus_inode_cachep)
637 return -ENOMEM; 673 return -ENOMEM;
674 err = hfsplus_create_attr_tree_cache();
675 if (err)
676 goto destroy_inode_cache;
638 err = register_filesystem(&hfsplus_fs_type); 677 err = register_filesystem(&hfsplus_fs_type);
639 if (err) 678 if (err)
640 kmem_cache_destroy(hfsplus_inode_cachep); 679 goto destroy_attr_tree_cache;
680 return 0;
681
682destroy_attr_tree_cache:
683 hfsplus_destroy_attr_tree_cache();
684
685destroy_inode_cache:
686 kmem_cache_destroy(hfsplus_inode_cachep);
687
641 return err; 688 return err;
642} 689}
643 690
@@ -650,6 +697,7 @@ static void __exit exit_hfsplus_fs(void)
650 * destroy cache. 697 * destroy cache.
651 */ 698 */
652 rcu_barrier(); 699 rcu_barrier();
700 hfsplus_destroy_attr_tree_cache();
653 kmem_cache_destroy(hfsplus_inode_cachep); 701 kmem_cache_destroy(hfsplus_inode_cachep);
654} 702}
655 703
diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
index a32998f29f0b..2c2e47dcfdd8 100644
--- a/fs/hfsplus/unicode.c
+++ b/fs/hfsplus/unicode.c
@@ -295,7 +295,8 @@ static inline u16 *decompose_unichar(wchar_t uc, int *size)
295 return hfsplus_decompose_table + (off / 4); 295 return hfsplus_decompose_table + (off / 4);
296} 296}
297 297
298int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, 298int hfsplus_asc2uni(struct super_block *sb,
299 struct hfsplus_unistr *ustr, int max_unistr_len,
299 const char *astr, int len) 300 const char *astr, int len)
300{ 301{
301 int size, dsize, decompose; 302 int size, dsize, decompose;
@@ -303,7 +304,7 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
303 wchar_t c; 304 wchar_t c;
304 305
305 decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags); 306 decompose = !test_bit(HFSPLUS_SB_NODECOMPOSE, &HFSPLUS_SB(sb)->flags);
306 while (outlen < HFSPLUS_MAX_STRLEN && len > 0) { 307 while (outlen < max_unistr_len && len > 0) {
307 size = asc2unichar(sb, astr, len, &c); 308 size = asc2unichar(sb, astr, len, &c);
308 309
309 if (decompose) 310 if (decompose)
@@ -311,7 +312,7 @@ int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr,
311 else 312 else
312 dstr = NULL; 313 dstr = NULL;
313 if (dstr) { 314 if (dstr) {
314 if (outlen + dsize > HFSPLUS_MAX_STRLEN) 315 if (outlen + dsize > max_unistr_len)
315 break; 316 break;
316 do { 317 do {
317 ustr->unicode[outlen++] = cpu_to_be16(*dstr++); 318 ustr->unicode[outlen++] = cpu_to_be16(*dstr++);