Merge branch 'kbuild' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / hfsplus / xattr.c
1 /*
2  * linux/fs/hfsplus/xattr.c
3  *
4  * Vyacheslav Dubeyko <slava@dubeyko.com>
5  *
6  * Logic of processing extended attributes
7  */
8
9 #include "hfsplus_fs.h"
10 #include <linux/posix_acl_xattr.h>
11 #include "xattr.h"
12 #include "acl.h"
13
14 const struct xattr_handler *hfsplus_xattr_handlers[] = {
15         &hfsplus_xattr_osx_handler,
16         &hfsplus_xattr_user_handler,
17         &hfsplus_xattr_trusted_handler,
18 #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
19         &posix_acl_access_xattr_handler,
20         &posix_acl_default_xattr_handler,
21 #endif
22         &hfsplus_xattr_security_handler,
23         NULL
24 };
25
26 static int strcmp_xattr_finder_info(const char *name)
27 {
28         if (name) {
29                 return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
30                                 sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
31         }
32         return -1;
33 }
34
35 static int strcmp_xattr_acl(const char *name)
36 {
37         if (name) {
38                 return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
39                                 sizeof(HFSPLUS_XATTR_ACL_NAME));
40         }
41         return -1;
42 }
43
44 static inline int is_known_namespace(const char *name)
45 {
46         if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
47             strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
48             strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
49             strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
50                 return false;
51
52         return true;
53 }
54
55 static void hfsplus_init_header_node(struct inode *attr_file,
56                                         u32 clump_size,
57                                         char *buf, u16 node_size)
58 {
59         struct hfs_bnode_desc *desc;
60         struct hfs_btree_header_rec *head;
61         u16 offset;
62         __be16 *rec_offsets;
63         u32 hdr_node_map_rec_bits;
64         char *bmp;
65         u32 used_nodes;
66         u32 used_bmp_bytes;
67         loff_t tmp;
68
69         hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
70                                 clump_size, node_size);
71
72         /* The end of the node contains list of record offsets */
73         rec_offsets = (__be16 *)(buf + node_size);
74
75         desc = (struct hfs_bnode_desc *)buf;
76         desc->type = HFS_NODE_HEADER;
77         desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
78         offset = sizeof(struct hfs_bnode_desc);
79         *--rec_offsets = cpu_to_be16(offset);
80
81         head = (struct hfs_btree_header_rec *)(buf + offset);
82         head->node_size = cpu_to_be16(node_size);
83         tmp = i_size_read(attr_file);
84         do_div(tmp, node_size);
85         head->node_count = cpu_to_be32(tmp);
86         head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
87         head->clump_size = cpu_to_be32(clump_size);
88         head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
89         head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
90         offset += sizeof(struct hfs_btree_header_rec);
91         *--rec_offsets = cpu_to_be16(offset);
92         offset += HFSPLUS_BTREE_HDR_USER_BYTES;
93         *--rec_offsets = cpu_to_be16(offset);
94
95         hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
96         if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
97                 u32 map_node_bits;
98                 u32 map_nodes;
99
100                 desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
101                 map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
102                                         (2 * sizeof(u16)) - 2);
103                 map_nodes = (be32_to_cpu(head->node_count) -
104                                 hdr_node_map_rec_bits +
105                                 (map_node_bits - 1)) / map_node_bits;
106                 be32_add_cpu(&head->free_nodes, 0 - map_nodes);
107         }
108
109         bmp = buf + offset;
110         used_nodes =
111                 be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
112         used_bmp_bytes = used_nodes / 8;
113         if (used_bmp_bytes) {
114                 memset(bmp, 0xFF, used_bmp_bytes);
115                 bmp += used_bmp_bytes;
116                 used_nodes %= 8;
117         }
118         *bmp = ~(0xFF >> used_nodes);
119         offset += hdr_node_map_rec_bits / 8;
120         *--rec_offsets = cpu_to_be16(offset);
121 }
122
123 static int hfsplus_create_attributes_file(struct super_block *sb)
124 {
125         int err = 0;
126         struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
127         struct inode *attr_file;
128         struct hfsplus_inode_info *hip;
129         u32 clump_size;
130         u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
131         char *buf;
132         int index, written;
133         struct address_space *mapping;
134         struct page *page;
135         int old_state = HFSPLUS_EMPTY_ATTR_TREE;
136
137         hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
138
139 check_attr_tree_state_again:
140         switch (atomic_read(&sbi->attr_tree_state)) {
141         case HFSPLUS_EMPTY_ATTR_TREE:
142                 if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
143                                                 old_state,
144                                                 HFSPLUS_CREATING_ATTR_TREE))
145                         goto check_attr_tree_state_again;
146                 break;
147         case HFSPLUS_CREATING_ATTR_TREE:
148                 /*
149                  * This state means that another thread is in process
150                  * of AttributesFile creation. Theoretically, it is
151                  * possible to be here. But really __setxattr() method
152                  * first of all calls hfs_find_init() for lookup in
153                  * B-tree of CatalogFile. This method locks mutex of
154                  * CatalogFile's B-tree. As a result, if some thread
155                  * is inside AttributedFile creation operation then
156                  * another threads will be waiting unlocking of
157                  * CatalogFile's B-tree's mutex. However, if code will
158                  * change then we will return error code (-EAGAIN) from
159                  * here. Really, it means that first try to set of xattr
160                  * fails with error but second attempt will have success.
161                  */
162                 return -EAGAIN;
163         case HFSPLUS_VALID_ATTR_TREE:
164                 return 0;
165         case HFSPLUS_FAILED_ATTR_TREE:
166                 return -EOPNOTSUPP;
167         default:
168                 BUG();
169         }
170
171         attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
172         if (IS_ERR(attr_file)) {
173                 pr_err("failed to load attributes file\n");
174                 return PTR_ERR(attr_file);
175         }
176
177         BUG_ON(i_size_read(attr_file) != 0);
178
179         hip = HFSPLUS_I(attr_file);
180
181         clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
182                                                     node_size,
183                                                     sbi->sect_count,
184                                                     HFSPLUS_ATTR_CNID);
185
186         mutex_lock(&hip->extents_lock);
187         hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
188         mutex_unlock(&hip->extents_lock);
189
190         if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
191                 err = -ENOSPC;
192                 goto end_attr_file_creation;
193         }
194
195         while (hip->alloc_blocks < hip->clump_blocks) {
196                 err = hfsplus_file_extend(attr_file);
197                 if (unlikely(err)) {
198                         pr_err("failed to extend attributes file\n");
199                         goto end_attr_file_creation;
200                 }
201                 hip->phys_size = attr_file->i_size =
202                         (loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
203                 hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
204                 inode_set_bytes(attr_file, attr_file->i_size);
205         }
206
207         buf = kzalloc(node_size, GFP_NOFS);
208         if (!buf) {
209                 pr_err("failed to allocate memory for header node\n");
210                 err = -ENOMEM;
211                 goto end_attr_file_creation;
212         }
213
214         hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
215
216         mapping = attr_file->i_mapping;
217
218         index = 0;
219         written = 0;
220         for (; written < node_size; index++, written += PAGE_CACHE_SIZE) {
221                 void *kaddr;
222
223                 page = read_mapping_page(mapping, index, NULL);
224                 if (IS_ERR(page)) {
225                         err = PTR_ERR(page);
226                         goto failed_header_node_init;
227                 }
228
229                 kaddr = kmap_atomic(page);
230                 memcpy(kaddr, buf + written,
231                         min_t(size_t, PAGE_CACHE_SIZE, node_size - written));
232                 kunmap_atomic(kaddr);
233
234                 set_page_dirty(page);
235                 page_cache_release(page);
236         }
237
238         hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
239
240         sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
241         if (!sbi->attr_tree)
242                 pr_err("failed to load attributes file\n");
243
244 failed_header_node_init:
245         kfree(buf);
246
247 end_attr_file_creation:
248         iput(attr_file);
249
250         if (!err)
251                 atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
252         else if (err == -ENOSPC)
253                 atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
254         else
255                 atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
256
257         return err;
258 }
259
260 int __hfsplus_setxattr(struct inode *inode, const char *name,
261                         const void *value, size_t size, int flags)
262 {
263         int err = 0;
264         struct hfs_find_data cat_fd;
265         hfsplus_cat_entry entry;
266         u16 cat_entry_flags, cat_entry_type;
267         u16 folder_finderinfo_len = sizeof(struct DInfo) +
268                                         sizeof(struct DXInfo);
269         u16 file_finderinfo_len = sizeof(struct FInfo) +
270                                         sizeof(struct FXInfo);
271
272         if ((!S_ISREG(inode->i_mode) &&
273                         !S_ISDIR(inode->i_mode)) ||
274                                 HFSPLUS_IS_RSRC(inode))
275                 return -EOPNOTSUPP;
276
277         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
278                                 XATTR_MAC_OSX_PREFIX_LEN) == 0)
279                 name += XATTR_MAC_OSX_PREFIX_LEN;
280
281         if (value == NULL) {
282                 value = "";
283                 size = 0;
284         }
285
286         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
287         if (err) {
288                 pr_err("can't init xattr find struct\n");
289                 return err;
290         }
291
292         err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
293         if (err) {
294                 pr_err("catalog searching failed\n");
295                 goto end_setxattr;
296         }
297
298         if (!strcmp_xattr_finder_info(name)) {
299                 if (flags & XATTR_CREATE) {
300                         pr_err("xattr exists yet\n");
301                         err = -EOPNOTSUPP;
302                         goto end_setxattr;
303                 }
304                 hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
305                                         sizeof(hfsplus_cat_entry));
306                 if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
307                         if (size == folder_finderinfo_len) {
308                                 memcpy(&entry.folder.user_info, value,
309                                                 folder_finderinfo_len);
310                                 hfs_bnode_write(cat_fd.bnode, &entry,
311                                         cat_fd.entryoffset,
312                                         sizeof(struct hfsplus_cat_folder));
313                                 hfsplus_mark_inode_dirty(inode,
314                                                 HFSPLUS_I_CAT_DIRTY);
315                         } else {
316                                 err = -ERANGE;
317                                 goto end_setxattr;
318                         }
319                 } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
320                         if (size == file_finderinfo_len) {
321                                 memcpy(&entry.file.user_info, value,
322                                                 file_finderinfo_len);
323                                 hfs_bnode_write(cat_fd.bnode, &entry,
324                                         cat_fd.entryoffset,
325                                         sizeof(struct hfsplus_cat_file));
326                                 hfsplus_mark_inode_dirty(inode,
327                                                 HFSPLUS_I_CAT_DIRTY);
328                         } else {
329                                 err = -ERANGE;
330                                 goto end_setxattr;
331                         }
332                 } else {
333                         err = -EOPNOTSUPP;
334                         goto end_setxattr;
335                 }
336                 goto end_setxattr;
337         }
338
339         if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
340                 err = hfsplus_create_attributes_file(inode->i_sb);
341                 if (unlikely(err))
342                         goto end_setxattr;
343         }
344
345         if (hfsplus_attr_exists(inode, name)) {
346                 if (flags & XATTR_CREATE) {
347                         pr_err("xattr exists yet\n");
348                         err = -EOPNOTSUPP;
349                         goto end_setxattr;
350                 }
351                 err = hfsplus_delete_attr(inode, name);
352                 if (err)
353                         goto end_setxattr;
354                 err = hfsplus_create_attr(inode, name, value, size);
355                 if (err)
356                         goto end_setxattr;
357         } else {
358                 if (flags & XATTR_REPLACE) {
359                         pr_err("cannot replace xattr\n");
360                         err = -EOPNOTSUPP;
361                         goto end_setxattr;
362                 }
363                 err = hfsplus_create_attr(inode, name, value, size);
364                 if (err)
365                         goto end_setxattr;
366         }
367
368         cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
369         if (cat_entry_type == HFSPLUS_FOLDER) {
370                 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
371                                     cat_fd.entryoffset +
372                                     offsetof(struct hfsplus_cat_folder, flags));
373                 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
374                 if (!strcmp_xattr_acl(name))
375                         cat_entry_flags |= HFSPLUS_ACL_EXISTS;
376                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
377                                 offsetof(struct hfsplus_cat_folder, flags),
378                                 cat_entry_flags);
379                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
380         } else if (cat_entry_type == HFSPLUS_FILE) {
381                 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
382                                     cat_fd.entryoffset +
383                                     offsetof(struct hfsplus_cat_file, flags));
384                 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
385                 if (!strcmp_xattr_acl(name))
386                         cat_entry_flags |= HFSPLUS_ACL_EXISTS;
387                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
388                                     offsetof(struct hfsplus_cat_file, flags),
389                                     cat_entry_flags);
390                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
391         } else {
392                 pr_err("invalid catalog entry type\n");
393                 err = -EIO;
394                 goto end_setxattr;
395         }
396
397 end_setxattr:
398         hfs_find_exit(&cat_fd);
399         return err;
400 }
401
402 static inline int is_osx_xattr(const char *xattr_name)
403 {
404         return !is_known_namespace(xattr_name);
405 }
406
407 static int name_len(const char *xattr_name, int xattr_name_len)
408 {
409         int len = xattr_name_len + 1;
410
411         if (is_osx_xattr(xattr_name))
412                 len += XATTR_MAC_OSX_PREFIX_LEN;
413
414         return len;
415 }
416
417 static int copy_name(char *buffer, const char *xattr_name, int name_len)
418 {
419         int len = name_len;
420         int offset = 0;
421
422         if (is_osx_xattr(xattr_name)) {
423                 strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
424                 offset += XATTR_MAC_OSX_PREFIX_LEN;
425                 len += XATTR_MAC_OSX_PREFIX_LEN;
426         }
427
428         strncpy(buffer + offset, xattr_name, name_len);
429         memset(buffer + offset + name_len, 0, 1);
430         len += 1;
431
432         return len;
433 }
434
435 static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
436                                                 void *value, size_t size)
437 {
438         ssize_t res = 0;
439         struct hfs_find_data fd;
440         u16 entry_type;
441         u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
442         u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
443         u16 record_len = max(folder_rec_len, file_rec_len);
444         u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
445         u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
446
447         if (size >= record_len) {
448                 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
449                 if (res) {
450                         pr_err("can't init xattr find struct\n");
451                         return res;
452                 }
453                 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
454                 if (res)
455                         goto end_getxattr_finder_info;
456                 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
457
458                 if (entry_type == HFSPLUS_FOLDER) {
459                         hfs_bnode_read(fd.bnode, folder_finder_info,
460                                 fd.entryoffset +
461                                 offsetof(struct hfsplus_cat_folder, user_info),
462                                 folder_rec_len);
463                         memcpy(value, folder_finder_info, folder_rec_len);
464                         res = folder_rec_len;
465                 } else if (entry_type == HFSPLUS_FILE) {
466                         hfs_bnode_read(fd.bnode, file_finder_info,
467                                 fd.entryoffset +
468                                 offsetof(struct hfsplus_cat_file, user_info),
469                                 file_rec_len);
470                         memcpy(value, file_finder_info, file_rec_len);
471                         res = file_rec_len;
472                 } else {
473                         res = -EOPNOTSUPP;
474                         goto end_getxattr_finder_info;
475                 }
476         } else
477                 res = size ? -ERANGE : record_len;
478
479 end_getxattr_finder_info:
480         if (size >= record_len)
481                 hfs_find_exit(&fd);
482         return res;
483 }
484
485 ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
486                          void *value, size_t size)
487 {
488         struct hfs_find_data fd;
489         hfsplus_attr_entry *entry;
490         __be32 xattr_record_type;
491         u32 record_type;
492         u16 record_length = 0;
493         ssize_t res = 0;
494
495         if ((!S_ISREG(inode->i_mode) &&
496                         !S_ISDIR(inode->i_mode)) ||
497                                 HFSPLUS_IS_RSRC(inode))
498                 return -EOPNOTSUPP;
499
500         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
501                                 XATTR_MAC_OSX_PREFIX_LEN) == 0) {
502                 /* skip "osx." prefix */
503                 name += XATTR_MAC_OSX_PREFIX_LEN;
504                 /*
505                  * Don't allow retrieving properly prefixed attributes
506                  * by prepending them with "osx."
507                  */
508                 if (is_known_namespace(name))
509                         return -EOPNOTSUPP;
510         }
511
512         if (!strcmp_xattr_finder_info(name))
513                 return hfsplus_getxattr_finder_info(inode, value, size);
514
515         if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
516                 return -EOPNOTSUPP;
517
518         entry = hfsplus_alloc_attr_entry();
519         if (!entry) {
520                 pr_err("can't allocate xattr entry\n");
521                 return -ENOMEM;
522         }
523
524         res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
525         if (res) {
526                 pr_err("can't init xattr find struct\n");
527                 goto failed_getxattr_init;
528         }
529
530         res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
531         if (res) {
532                 if (res == -ENOENT)
533                         res = -ENODATA;
534                 else
535                         pr_err("xattr searching failed\n");
536                 goto out;
537         }
538
539         hfs_bnode_read(fd.bnode, &xattr_record_type,
540                         fd.entryoffset, sizeof(xattr_record_type));
541         record_type = be32_to_cpu(xattr_record_type);
542         if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
543                 record_length = hfs_bnode_read_u16(fd.bnode,
544                                 fd.entryoffset +
545                                 offsetof(struct hfsplus_attr_inline_data,
546                                 length));
547                 if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
548                         pr_err("invalid xattr record size\n");
549                         res = -EIO;
550                         goto out;
551                 }
552         } else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
553                         record_type == HFSPLUS_ATTR_EXTENTS) {
554                 pr_err("only inline data xattr are supported\n");
555                 res = -EOPNOTSUPP;
556                 goto out;
557         } else {
558                 pr_err("invalid xattr record\n");
559                 res = -EIO;
560                 goto out;
561         }
562
563         if (size) {
564                 hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
565                                 offsetof(struct hfsplus_attr_inline_data,
566                                         raw_bytes) + record_length);
567         }
568
569         if (size >= record_length) {
570                 memcpy(value, entry->inline_data.raw_bytes, record_length);
571                 res = record_length;
572         } else
573                 res = size ? -ERANGE : record_length;
574
575 out:
576         hfs_find_exit(&fd);
577
578 failed_getxattr_init:
579         hfsplus_destroy_attr_entry(entry);
580         return res;
581 }
582
583 static inline int can_list(const char *xattr_name)
584 {
585         if (!xattr_name)
586                 return 0;
587
588         return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
589                         XATTR_TRUSTED_PREFIX_LEN) ||
590                                 capable(CAP_SYS_ADMIN);
591 }
592
593 static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
594                                                 char *buffer, size_t size)
595 {
596         ssize_t res = 0;
597         struct inode *inode = dentry->d_inode;
598         struct hfs_find_data fd;
599         u16 entry_type;
600         u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
601         u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
602         unsigned long len, found_bit;
603         int xattr_name_len, symbols_count;
604
605         res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
606         if (res) {
607                 pr_err("can't init xattr find struct\n");
608                 return res;
609         }
610
611         res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
612         if (res)
613                 goto end_listxattr_finder_info;
614
615         entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
616         if (entry_type == HFSPLUS_FOLDER) {
617                 len = sizeof(struct DInfo) + sizeof(struct DXInfo);
618                 hfs_bnode_read(fd.bnode, folder_finder_info,
619                                 fd.entryoffset +
620                                 offsetof(struct hfsplus_cat_folder, user_info),
621                                 len);
622                 found_bit = find_first_bit((void *)folder_finder_info, len*8);
623         } else if (entry_type == HFSPLUS_FILE) {
624                 len = sizeof(struct FInfo) + sizeof(struct FXInfo);
625                 hfs_bnode_read(fd.bnode, file_finder_info,
626                                 fd.entryoffset +
627                                 offsetof(struct hfsplus_cat_file, user_info),
628                                 len);
629                 found_bit = find_first_bit((void *)file_finder_info, len*8);
630         } else {
631                 res = -EOPNOTSUPP;
632                 goto end_listxattr_finder_info;
633         }
634
635         if (found_bit >= (len*8))
636                 res = 0;
637         else {
638                 symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
639                 xattr_name_len =
640                         name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
641                 if (!buffer || !size) {
642                         if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
643                                 res = xattr_name_len;
644                 } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
645                         if (size < xattr_name_len)
646                                 res = -ERANGE;
647                         else {
648                                 res = copy_name(buffer,
649                                                 HFSPLUS_XATTR_FINDER_INFO_NAME,
650                                                 symbols_count);
651                         }
652                 }
653         }
654
655 end_listxattr_finder_info:
656         hfs_find_exit(&fd);
657
658         return res;
659 }
660
661 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
662 {
663         ssize_t err;
664         ssize_t res = 0;
665         struct inode *inode = dentry->d_inode;
666         struct hfs_find_data fd;
667         u16 key_len = 0;
668         struct hfsplus_attr_key attr_key;
669         char strbuf[HFSPLUS_ATTR_MAX_STRLEN +
670                         XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
671         int xattr_name_len;
672
673         if ((!S_ISREG(inode->i_mode) &&
674                         !S_ISDIR(inode->i_mode)) ||
675                                 HFSPLUS_IS_RSRC(inode))
676                 return -EOPNOTSUPP;
677
678         res = hfsplus_listxattr_finder_info(dentry, buffer, size);
679         if (res < 0)
680                 return res;
681         else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
682                 return (res == 0) ? -EOPNOTSUPP : res;
683
684         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
685         if (err) {
686                 pr_err("can't init xattr find struct\n");
687                 return err;
688         }
689
690         err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
691         if (err) {
692                 if (err == -ENOENT) {
693                         if (res == 0)
694                                 res = -ENODATA;
695                         goto end_listxattr;
696                 } else {
697                         res = err;
698                         goto end_listxattr;
699                 }
700         }
701
702         for (;;) {
703                 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
704                 if (key_len == 0 || key_len > fd.tree->max_key_len) {
705                         pr_err("invalid xattr key length: %d\n", key_len);
706                         res = -EIO;
707                         goto end_listxattr;
708                 }
709
710                 hfs_bnode_read(fd.bnode, &attr_key,
711                                 fd.keyoffset, key_len + sizeof(key_len));
712
713                 if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
714                         goto end_listxattr;
715
716                 xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN;
717                 if (hfsplus_uni2asc(inode->i_sb,
718                         (const struct hfsplus_unistr *)&fd.key->attr.key_name,
719                                         strbuf, &xattr_name_len)) {
720                         pr_err("unicode conversion failed\n");
721                         res = -EIO;
722                         goto end_listxattr;
723                 }
724
725                 if (!buffer || !size) {
726                         if (can_list(strbuf))
727                                 res += name_len(strbuf, xattr_name_len);
728                 } else if (can_list(strbuf)) {
729                         if (size < (res + name_len(strbuf, xattr_name_len))) {
730                                 res = -ERANGE;
731                                 goto end_listxattr;
732                         } else
733                                 res += copy_name(buffer + res,
734                                                 strbuf, xattr_name_len);
735                 }
736
737                 if (hfs_brec_goto(&fd, 1))
738                         goto end_listxattr;
739         }
740
741 end_listxattr:
742         hfs_find_exit(&fd);
743         return res;
744 }
745
746 int hfsplus_removexattr(struct dentry *dentry, const char *name)
747 {
748         int err = 0;
749         struct inode *inode = dentry->d_inode;
750         struct hfs_find_data cat_fd;
751         u16 flags;
752         u16 cat_entry_type;
753         int is_xattr_acl_deleted = 0;
754         int is_all_xattrs_deleted = 0;
755
756         if ((!S_ISREG(inode->i_mode) &&
757                         !S_ISDIR(inode->i_mode)) ||
758                                 HFSPLUS_IS_RSRC(inode))
759                 return -EOPNOTSUPP;
760
761         if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
762                 return -EOPNOTSUPP;
763
764         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
765                                 XATTR_MAC_OSX_PREFIX_LEN) == 0)
766                 name += XATTR_MAC_OSX_PREFIX_LEN;
767
768         if (!strcmp_xattr_finder_info(name))
769                 return -EOPNOTSUPP;
770
771         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
772         if (err) {
773                 pr_err("can't init xattr find struct\n");
774                 return err;
775         }
776
777         err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
778         if (err) {
779                 pr_err("catalog searching failed\n");
780                 goto end_removexattr;
781         }
782
783         err = hfsplus_delete_attr(inode, name);
784         if (err)
785                 goto end_removexattr;
786
787         is_xattr_acl_deleted = !strcmp_xattr_acl(name);
788         is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
789
790         if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
791                 goto end_removexattr;
792
793         cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
794
795         if (cat_entry_type == HFSPLUS_FOLDER) {
796                 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
797                                 offsetof(struct hfsplus_cat_folder, flags));
798                 if (is_xattr_acl_deleted)
799                         flags &= ~HFSPLUS_ACL_EXISTS;
800                 if (is_all_xattrs_deleted)
801                         flags &= ~HFSPLUS_XATTR_EXISTS;
802                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
803                                 offsetof(struct hfsplus_cat_folder, flags),
804                                 flags);
805                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
806         } else if (cat_entry_type == HFSPLUS_FILE) {
807                 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
808                                 offsetof(struct hfsplus_cat_file, flags));
809                 if (is_xattr_acl_deleted)
810                         flags &= ~HFSPLUS_ACL_EXISTS;
811                 if (is_all_xattrs_deleted)
812                         flags &= ~HFSPLUS_XATTR_EXISTS;
813                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
814                                 offsetof(struct hfsplus_cat_file, flags),
815                                 flags);
816                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
817         } else {
818                 pr_err("invalid catalog entry type\n");
819                 err = -EIO;
820                 goto end_removexattr;
821         }
822
823 end_removexattr:
824         hfs_find_exit(&cat_fd);
825         return err;
826 }
827
828 static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,
829                                         void *buffer, size_t size, int type)
830 {
831         char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
832                                 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
833         size_t len = strlen(name);
834
835         if (!strcmp(name, ""))
836                 return -EINVAL;
837
838         if (len > HFSPLUS_ATTR_MAX_STRLEN)
839                 return -EOPNOTSUPP;
840
841         strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
842         strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
843
844         return hfsplus_getxattr(dentry, xattr_name, buffer, size);
845 }
846
847 static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,
848                 const void *buffer, size_t size, int flags, int type)
849 {
850         char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
851                                 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
852         size_t len = strlen(name);
853
854         if (!strcmp(name, ""))
855                 return -EINVAL;
856
857         if (len > HFSPLUS_ATTR_MAX_STRLEN)
858                 return -EOPNOTSUPP;
859
860         if (is_known_namespace(name))
861                 return -EOPNOTSUPP;
862
863         strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
864         strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
865
866         return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags);
867 }
868
869 static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list,
870                 size_t list_size, const char *name, size_t name_len, int type)
871 {
872         /*
873          * This method is not used.
874          * It is used hfsplus_listxattr() instead of generic_listxattr().
875          */
876         return -EOPNOTSUPP;
877 }
878
879 const struct xattr_handler hfsplus_xattr_osx_handler = {
880         .prefix = XATTR_MAC_OSX_PREFIX,
881         .list   = hfsplus_osx_listxattr,
882         .get    = hfsplus_osx_getxattr,
883         .set    = hfsplus_osx_setxattr,
884 };