diff options
author | Vyacheslav Dubeyko <slava@dubeyko.com> | 2013-02-27 20:03:04 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 22:10:10 -0500 |
commit | 324ef39a8a4f693035d63527f16100ed27310ecc (patch) | |
tree | 1814515cf6139fe1b4ebae8d6641c9ec6293d396 /fs/hfsplus | |
parent | 127e5f5ae51eff44c9bff673f24e8587caa4e29f (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/Makefile | 4 | ||||
-rw-r--r-- | fs/hfsplus/bfind.c | 93 | ||||
-rw-r--r-- | fs/hfsplus/bnode.c | 6 | ||||
-rw-r--r-- | fs/hfsplus/brec.c | 23 | ||||
-rw-r--r-- | fs/hfsplus/btree.c | 8 | ||||
-rw-r--r-- | fs/hfsplus/catalog.c | 36 | ||||
-rw-r--r-- | fs/hfsplus/dir.c | 55 | ||||
-rw-r--r-- | fs/hfsplus/extents.c | 4 | ||||
-rw-r--r-- | fs/hfsplus/hfsplus_fs.h | 52 | ||||
-rw-r--r-- | fs/hfsplus/inode.c | 18 | ||||
-rw-r--r-- | fs/hfsplus/ioctl.c | 108 | ||||
-rw-r--r-- | fs/hfsplus/super.c | 56 | ||||
-rw-r--r-- | fs/hfsplus/unicode.c | 7 |
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 @@ | |||
5 | obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o | 5 | obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o |
6 | 6 | ||
7 | hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \ | 7 | hfsplus-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...)*/ | 53 | int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode, |
42 | int __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 | |||
87 | int 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...)*/ | ||
109 | int __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 | |||
82 | done: | 148 | done: |
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 | |||
88 | fail: | 155 | fail: |
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 */ |
94 | int hfs_brec_find(struct hfs_find_data *fd) | 161 | int 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 | ||
204 | int hfsplus_create_cat(u32 cnid, struct inode *dir, | 206 | int 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 | ||
254 | err1: | 256 | err1: |
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); |
258 | err2: | 260 | err2: |
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 | |||
340 | out: | 348 | out: |
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 | ||
19 | static inline void hfsplus_instantiate(struct dentry *dentry, | 20 | static 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 | |||
479 | failed_mknod: | ||
480 | clear_nlink(inode); | ||
481 | hfsplus_delete_inode(inode); | ||
482 | iput(inode); | ||
462 | out: | 483 | out: |
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 | ||
501 | const struct inode_operations hfsplus_dir_inode_operations = { | 522 | const 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 | ||
513 | const struct file_operations hfsplus_dir_operations = { | 538 | const 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 */ | ||
51 | enum 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 */ |
50 | struct hfs_btree { | 58 | struct 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 | ||
336 | typedef 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 */ | ||
345 | int hfsplus_create_attr_tree_cache(void); | ||
346 | void hfsplus_destroy_attr_tree_cache(void); | ||
347 | hfsplus_attr_entry *hfsplus_alloc_attr_entry(void); | ||
348 | void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry_p); | ||
349 | int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *, | ||
350 | const hfsplus_btree_key *); | ||
351 | int hfsplus_attr_build_key(struct super_block *, hfsplus_btree_key *, | ||
352 | u32, const char *); | ||
353 | void hfsplus_attr_build_key_uni(hfsplus_btree_key *key, | ||
354 | u32 cnid, | ||
355 | struct hfsplus_attr_unistr *name); | ||
356 | int hfsplus_find_attr(struct super_block *, u32, | ||
357 | const char *, struct hfs_find_data *); | ||
358 | int hfsplus_attr_exists(struct inode *inode, const char *name); | ||
359 | int hfsplus_create_attr(struct inode *, const char *, const void *, size_t); | ||
360 | int hfsplus_delete_attr(struct inode *, const char *); | ||
361 | int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid); | ||
362 | |||
331 | /* bitmap.c */ | 363 | /* bitmap.c */ |
332 | int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *); | 364 | int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *); |
333 | int hfsplus_block_free(struct super_block *, u32, u32); | 365 | int 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 */ |
370 | int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); | 402 | int hfs_find_init(struct hfs_btree *, struct hfs_find_data *); |
371 | void hfs_find_exit(struct hfs_find_data *); | 403 | void hfs_find_exit(struct hfs_find_data *); |
372 | int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *); | 404 | int hfs_find_1st_rec_by_cnid(struct hfs_bnode *, |
373 | int hfs_brec_find(struct hfs_find_data *); | 405 | struct hfs_find_data *, |
406 | int *, int *, int *); | ||
407 | int hfs_find_rec_by_key(struct hfs_bnode *, | ||
408 | struct hfs_find_data *, | ||
409 | int *, int *, int *); | ||
410 | int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *, | ||
411 | search_strategy_t); | ||
412 | int hfs_brec_find(struct hfs_find_data *, search_strategy_t); | ||
374 | int hfs_brec_read(struct hfs_find_data *, void *, int); | 413 | int hfs_brec_read(struct hfs_find_data *, void *, int); |
375 | int hfs_brec_goto(struct hfs_find_data *, int); | 414 | int 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 */ |
419 | long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); | 458 | long hfsplus_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); |
420 | int hfsplus_setxattr(struct dentry *dentry, const char *name, | ||
421 | const void *value, size_t size, int flags); | ||
422 | ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, | ||
423 | void *value, size_t size); | ||
424 | ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size); | ||
425 | 459 | ||
426 | /* options.c */ | 460 | /* options.c */ |
427 | int hfsplus_parse_options(char *, struct hfsplus_sb_info *); | 461 | int hfsplus_parse_options(char *, struct hfsplus_sb_info *); |
@@ -446,7 +480,7 @@ int hfsplus_strcmp(const struct hfsplus_unistr *, | |||
446 | int hfsplus_uni2asc(struct super_block *, | 480 | int hfsplus_uni2asc(struct super_block *, |
447 | const struct hfsplus_unistr *, char *, int *); | 481 | const struct hfsplus_unistr *, char *, int *); |
448 | int hfsplus_asc2uni(struct super_block *, | 482 | int hfsplus_asc2uni(struct super_block *, |
449 | struct hfsplus_unistr *, const char *, int); | 483 | struct hfsplus_unistr *, int, const char *, int); |
450 | int hfsplus_hash_dentry(const struct dentry *dentry, | 484 | int hfsplus_hash_dentry(const struct dentry *dentry, |
451 | const struct inode *inode, struct qstr *str); | 485 | const struct inode *inode, struct qstr *str); |
452 | int hfsplus_compare_dentry(const struct dentry *parent, | 486 | int 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 | ||
21 | static int hfsplus_readpage(struct file *file, struct page *page) | 22 | static 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, | |||
365 | static const struct inode_operations hfsplus_file_inode_operations = { | 378 | static 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 | ||
373 | static const struct file_operations hfsplus_file_operations = { | 387 | static 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 | |||
155 | int 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 | } | ||
194 | out: | ||
195 | hfs_find_exit(&fd); | ||
196 | return res; | ||
197 | } | ||
198 | |||
199 | ssize_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; | ||
237 | out: | ||
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 | |||
245 | ssize_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); | |||
20 | static void hfsplus_destroy_inode(struct inode *inode); | 20 | static 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 | ||
24 | static int hfsplus_system_read_inode(struct inode *inode) | 25 | static 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; |
563 | out_put_alloc_file: | 597 | out_put_alloc_file: |
564 | iput(sbi->alloc_file); | 598 | iput(sbi->alloc_file); |
599 | out_close_attr_tree: | ||
600 | hfs_btree_close(sbi->attr_tree); | ||
565 | out_close_cat_tree: | 601 | out_close_cat_tree: |
566 | hfs_btree_close(sbi->cat_tree); | 602 | hfs_btree_close(sbi->cat_tree); |
567 | out_close_ext_tree: | 603 | out_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 | |||
682 | destroy_attr_tree_cache: | ||
683 | hfsplus_destroy_attr_tree_cache(); | ||
684 | |||
685 | destroy_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 | ||
298 | int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, | 298 | int 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++); |