]> git.karo-electronics.de Git - karo-tx-linux.git/blob - fs/hfsplus/xattr.c
arm: imx6: defconfig: update tx6 defconfigs
[karo-tx-linux.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 "xattr.h"
11 #include "acl.h"
12
13 const struct xattr_handler *hfsplus_xattr_handlers[] = {
14         &hfsplus_xattr_osx_handler,
15         &hfsplus_xattr_user_handler,
16         &hfsplus_xattr_trusted_handler,
17 #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
18         &hfsplus_xattr_acl_access_handler,
19         &hfsplus_xattr_acl_default_handler,
20 #endif
21         &hfsplus_xattr_security_handler,
22         NULL
23 };
24
25 static int strcmp_xattr_finder_info(const char *name)
26 {
27         if (name) {
28                 return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
29                                 sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
30         }
31         return -1;
32 }
33
34 static int strcmp_xattr_acl(const char *name)
35 {
36         if (name) {
37                 return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
38                                 sizeof(HFSPLUS_XATTR_ACL_NAME));
39         }
40         return -1;
41 }
42
43 static inline int is_known_namespace(const char *name)
44 {
45         if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
46             strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
47             strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
48             strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
49                 return false;
50
51         return true;
52 }
53
54 static int can_set_system_xattr(struct inode *inode, const char *name,
55                                 const void *value, size_t size)
56 {
57 #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
58         struct posix_acl *acl;
59         int err;
60
61         if (!inode_owner_or_capable(inode))
62                 return -EPERM;
63
64         /*
65          * POSIX_ACL_XATTR_ACCESS is tied to i_mode
66          */
67         if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) {
68                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
69                 if (IS_ERR(acl))
70                         return PTR_ERR(acl);
71                 if (acl) {
72                         err = posix_acl_equiv_mode(acl, &inode->i_mode);
73                         posix_acl_release(acl);
74                         if (err < 0)
75                                 return err;
76                         mark_inode_dirty(inode);
77                 }
78                 /*
79                  * We're changing the ACL.  Get rid of the cached one
80                  */
81                 forget_cached_acl(inode, ACL_TYPE_ACCESS);
82
83                 return 0;
84         } else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) {
85                 acl = posix_acl_from_xattr(&init_user_ns, value, size);
86                 if (IS_ERR(acl))
87                         return PTR_ERR(acl);
88                 posix_acl_release(acl);
89
90                 /*
91                  * We're changing the default ACL.  Get rid of the cached one
92                  */
93                 forget_cached_acl(inode, ACL_TYPE_DEFAULT);
94
95                 return 0;
96         }
97 #endif /* CONFIG_HFSPLUS_FS_POSIX_ACL */
98         return -EOPNOTSUPP;
99 }
100
101 static int can_set_xattr(struct inode *inode, const char *name,
102                                 const void *value, size_t value_len)
103 {
104         if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
105                 return can_set_system_xattr(inode, name, value, value_len);
106
107         if (!strncmp(name, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN)) {
108                 /*
109                  * This makes sure that we aren't trying to set an
110                  * attribute in a different namespace by prefixing it
111                  * with "osx."
112                  */
113                 if (is_known_namespace(name + XATTR_MAC_OSX_PREFIX_LEN))
114                         return -EOPNOTSUPP;
115
116                 return 0;
117         }
118
119         /*
120          * Don't allow setting an attribute in an unknown namespace.
121          */
122         if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) &&
123             strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
124             strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
125                 return -EOPNOTSUPP;
126
127         return 0;
128 }
129
130 int __hfsplus_setxattr(struct inode *inode, const char *name,
131                         const void *value, size_t size, int flags)
132 {
133         int err = 0;
134         struct hfs_find_data cat_fd;
135         hfsplus_cat_entry entry;
136         u16 cat_entry_flags, cat_entry_type;
137         u16 folder_finderinfo_len = sizeof(struct DInfo) +
138                                         sizeof(struct DXInfo);
139         u16 file_finderinfo_len = sizeof(struct FInfo) +
140                                         sizeof(struct FXInfo);
141
142         if ((!S_ISREG(inode->i_mode) &&
143                         !S_ISDIR(inode->i_mode)) ||
144                                 HFSPLUS_IS_RSRC(inode))
145                 return -EOPNOTSUPP;
146
147         err = can_set_xattr(inode, name, value, size);
148         if (err)
149                 return err;
150
151         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
152                                 XATTR_MAC_OSX_PREFIX_LEN) == 0)
153                 name += XATTR_MAC_OSX_PREFIX_LEN;
154
155         if (value == NULL) {
156                 value = "";
157                 size = 0;
158         }
159
160         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
161         if (err) {
162                 pr_err("can't init xattr find struct\n");
163                 return err;
164         }
165
166         err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
167         if (err) {
168                 pr_err("catalog searching failed\n");
169                 goto end_setxattr;
170         }
171
172         if (!strcmp_xattr_finder_info(name)) {
173                 if (flags & XATTR_CREATE) {
174                         pr_err("xattr exists yet\n");
175                         err = -EOPNOTSUPP;
176                         goto end_setxattr;
177                 }
178                 hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
179                                         sizeof(hfsplus_cat_entry));
180                 if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
181                         if (size == folder_finderinfo_len) {
182                                 memcpy(&entry.folder.user_info, value,
183                                                 folder_finderinfo_len);
184                                 hfs_bnode_write(cat_fd.bnode, &entry,
185                                         cat_fd.entryoffset,
186                                         sizeof(struct hfsplus_cat_folder));
187                                 hfsplus_mark_inode_dirty(inode,
188                                                 HFSPLUS_I_CAT_DIRTY);
189                         } else {
190                                 err = -ERANGE;
191                                 goto end_setxattr;
192                         }
193                 } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
194                         if (size == file_finderinfo_len) {
195                                 memcpy(&entry.file.user_info, value,
196                                                 file_finderinfo_len);
197                                 hfs_bnode_write(cat_fd.bnode, &entry,
198                                         cat_fd.entryoffset,
199                                         sizeof(struct hfsplus_cat_file));
200                                 hfsplus_mark_inode_dirty(inode,
201                                                 HFSPLUS_I_CAT_DIRTY);
202                         } else {
203                                 err = -ERANGE;
204                                 goto end_setxattr;
205                         }
206                 } else {
207                         err = -EOPNOTSUPP;
208                         goto end_setxattr;
209                 }
210                 goto end_setxattr;
211         }
212
213         if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
214                 err = -EOPNOTSUPP;
215                 goto end_setxattr;
216         }
217
218         if (hfsplus_attr_exists(inode, name)) {
219                 if (flags & XATTR_CREATE) {
220                         pr_err("xattr exists yet\n");
221                         err = -EOPNOTSUPP;
222                         goto end_setxattr;
223                 }
224                 err = hfsplus_delete_attr(inode, name);
225                 if (err)
226                         goto end_setxattr;
227                 err = hfsplus_create_attr(inode, name, value, size);
228                 if (err)
229                         goto end_setxattr;
230         } else {
231                 if (flags & XATTR_REPLACE) {
232                         pr_err("cannot replace xattr\n");
233                         err = -EOPNOTSUPP;
234                         goto end_setxattr;
235                 }
236                 err = hfsplus_create_attr(inode, name, value, size);
237                 if (err)
238                         goto end_setxattr;
239         }
240
241         cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
242         if (cat_entry_type == HFSPLUS_FOLDER) {
243                 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
244                                     cat_fd.entryoffset +
245                                     offsetof(struct hfsplus_cat_folder, flags));
246                 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
247                 if (!strcmp_xattr_acl(name))
248                         cat_entry_flags |= HFSPLUS_ACL_EXISTS;
249                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
250                                 offsetof(struct hfsplus_cat_folder, flags),
251                                 cat_entry_flags);
252                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
253         } else if (cat_entry_type == HFSPLUS_FILE) {
254                 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
255                                     cat_fd.entryoffset +
256                                     offsetof(struct hfsplus_cat_file, flags));
257                 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
258                 if (!strcmp_xattr_acl(name))
259                         cat_entry_flags |= HFSPLUS_ACL_EXISTS;
260                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
261                                     offsetof(struct hfsplus_cat_file, flags),
262                                     cat_entry_flags);
263                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
264         } else {
265                 pr_err("invalid catalog entry type\n");
266                 err = -EIO;
267                 goto end_setxattr;
268         }
269
270 end_setxattr:
271         hfs_find_exit(&cat_fd);
272         return err;
273 }
274
275 static inline int is_osx_xattr(const char *xattr_name)
276 {
277         return !is_known_namespace(xattr_name);
278 }
279
280 static int name_len(const char *xattr_name, int xattr_name_len)
281 {
282         int len = xattr_name_len + 1;
283
284         if (is_osx_xattr(xattr_name))
285                 len += XATTR_MAC_OSX_PREFIX_LEN;
286
287         return len;
288 }
289
290 static int copy_name(char *buffer, const char *xattr_name, int name_len)
291 {
292         int len = name_len;
293         int offset = 0;
294
295         if (is_osx_xattr(xattr_name)) {
296                 strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
297                 offset += XATTR_MAC_OSX_PREFIX_LEN;
298                 len += XATTR_MAC_OSX_PREFIX_LEN;
299         }
300
301         strncpy(buffer + offset, xattr_name, name_len);
302         memset(buffer + offset + name_len, 0, 1);
303         len += 1;
304
305         return len;
306 }
307
308 static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
309                                                 void *value, size_t size)
310 {
311         ssize_t res = 0;
312         struct hfs_find_data fd;
313         u16 entry_type;
314         u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
315         u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
316         u16 record_len = max(folder_rec_len, file_rec_len);
317         u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
318         u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
319
320         if (size >= record_len) {
321                 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
322                 if (res) {
323                         pr_err("can't init xattr find struct\n");
324                         return res;
325                 }
326                 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
327                 if (res)
328                         goto end_getxattr_finder_info;
329                 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
330
331                 if (entry_type == HFSPLUS_FOLDER) {
332                         hfs_bnode_read(fd.bnode, folder_finder_info,
333                                 fd.entryoffset +
334                                 offsetof(struct hfsplus_cat_folder, user_info),
335                                 folder_rec_len);
336                         memcpy(value, folder_finder_info, folder_rec_len);
337                         res = folder_rec_len;
338                 } else if (entry_type == HFSPLUS_FILE) {
339                         hfs_bnode_read(fd.bnode, file_finder_info,
340                                 fd.entryoffset +
341                                 offsetof(struct hfsplus_cat_file, user_info),
342                                 file_rec_len);
343                         memcpy(value, file_finder_info, file_rec_len);
344                         res = file_rec_len;
345                 } else {
346                         res = -EOPNOTSUPP;
347                         goto end_getxattr_finder_info;
348                 }
349         } else
350                 res = size ? -ERANGE : record_len;
351
352 end_getxattr_finder_info:
353         if (size >= record_len)
354                 hfs_find_exit(&fd);
355         return res;
356 }
357
358 ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
359                          void *value, size_t size)
360 {
361         struct hfs_find_data fd;
362         hfsplus_attr_entry *entry;
363         __be32 xattr_record_type;
364         u32 record_type;
365         u16 record_length = 0;
366         ssize_t res = 0;
367
368         if ((!S_ISREG(inode->i_mode) &&
369                         !S_ISDIR(inode->i_mode)) ||
370                                 HFSPLUS_IS_RSRC(inode))
371                 return -EOPNOTSUPP;
372
373         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
374                                 XATTR_MAC_OSX_PREFIX_LEN) == 0) {
375                 /* skip "osx." prefix */
376                 name += XATTR_MAC_OSX_PREFIX_LEN;
377                 /*
378                  * Don't allow retrieving properly prefixed attributes
379                  * by prepending them with "osx."
380                  */
381                 if (is_known_namespace(name))
382                         return -EOPNOTSUPP;
383         }
384
385         if (!strcmp_xattr_finder_info(name))
386                 return hfsplus_getxattr_finder_info(inode, value, size);
387
388         if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
389                 return -EOPNOTSUPP;
390
391         entry = hfsplus_alloc_attr_entry();
392         if (!entry) {
393                 pr_err("can't allocate xattr entry\n");
394                 return -ENOMEM;
395         }
396
397         res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
398         if (res) {
399                 pr_err("can't init xattr find struct\n");
400                 goto failed_getxattr_init;
401         }
402
403         res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
404         if (res) {
405                 if (res == -ENOENT)
406                         res = -ENODATA;
407                 else
408                         pr_err("xattr searching failed\n");
409                 goto out;
410         }
411
412         hfs_bnode_read(fd.bnode, &xattr_record_type,
413                         fd.entryoffset, sizeof(xattr_record_type));
414         record_type = be32_to_cpu(xattr_record_type);
415         if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
416                 record_length = hfs_bnode_read_u16(fd.bnode,
417                                 fd.entryoffset +
418                                 offsetof(struct hfsplus_attr_inline_data,
419                                 length));
420                 if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
421                         pr_err("invalid xattr record size\n");
422                         res = -EIO;
423                         goto out;
424                 }
425         } else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
426                         record_type == HFSPLUS_ATTR_EXTENTS) {
427                 pr_err("only inline data xattr are supported\n");
428                 res = -EOPNOTSUPP;
429                 goto out;
430         } else {
431                 pr_err("invalid xattr record\n");
432                 res = -EIO;
433                 goto out;
434         }
435
436         if (size) {
437                 hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
438                                 offsetof(struct hfsplus_attr_inline_data,
439                                         raw_bytes) + record_length);
440         }
441
442         if (size >= record_length) {
443                 memcpy(value, entry->inline_data.raw_bytes, record_length);
444                 res = record_length;
445         } else
446                 res = size ? -ERANGE : record_length;
447
448 out:
449         hfs_find_exit(&fd);
450
451 failed_getxattr_init:
452         hfsplus_destroy_attr_entry(entry);
453         return res;
454 }
455
456 static inline int can_list(const char *xattr_name)
457 {
458         if (!xattr_name)
459                 return 0;
460
461         return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
462                         XATTR_TRUSTED_PREFIX_LEN) ||
463                                 capable(CAP_SYS_ADMIN);
464 }
465
466 static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
467                                                 char *buffer, size_t size)
468 {
469         ssize_t res = 0;
470         struct inode *inode = dentry->d_inode;
471         struct hfs_find_data fd;
472         u16 entry_type;
473         u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
474         u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
475         unsigned long len, found_bit;
476         int xattr_name_len, symbols_count;
477
478         res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
479         if (res) {
480                 pr_err("can't init xattr find struct\n");
481                 return res;
482         }
483
484         res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
485         if (res)
486                 goto end_listxattr_finder_info;
487
488         entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
489         if (entry_type == HFSPLUS_FOLDER) {
490                 len = sizeof(struct DInfo) + sizeof(struct DXInfo);
491                 hfs_bnode_read(fd.bnode, folder_finder_info,
492                                 fd.entryoffset +
493                                 offsetof(struct hfsplus_cat_folder, user_info),
494                                 len);
495                 found_bit = find_first_bit((void *)folder_finder_info, len*8);
496         } else if (entry_type == HFSPLUS_FILE) {
497                 len = sizeof(struct FInfo) + sizeof(struct FXInfo);
498                 hfs_bnode_read(fd.bnode, file_finder_info,
499                                 fd.entryoffset +
500                                 offsetof(struct hfsplus_cat_file, user_info),
501                                 len);
502                 found_bit = find_first_bit((void *)file_finder_info, len*8);
503         } else {
504                 res = -EOPNOTSUPP;
505                 goto end_listxattr_finder_info;
506         }
507
508         if (found_bit >= (len*8))
509                 res = 0;
510         else {
511                 symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
512                 xattr_name_len =
513                         name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
514                 if (!buffer || !size) {
515                         if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
516                                 res = xattr_name_len;
517                 } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
518                         if (size < xattr_name_len)
519                                 res = -ERANGE;
520                         else {
521                                 res = copy_name(buffer,
522                                                 HFSPLUS_XATTR_FINDER_INFO_NAME,
523                                                 symbols_count);
524                         }
525                 }
526         }
527
528 end_listxattr_finder_info:
529         hfs_find_exit(&fd);
530
531         return res;
532 }
533
534 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
535 {
536         ssize_t err;
537         ssize_t res = 0;
538         struct inode *inode = dentry->d_inode;
539         struct hfs_find_data fd;
540         u16 key_len = 0;
541         struct hfsplus_attr_key attr_key;
542         char strbuf[HFSPLUS_ATTR_MAX_STRLEN +
543                         XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
544         int xattr_name_len;
545
546         if ((!S_ISREG(inode->i_mode) &&
547                         !S_ISDIR(inode->i_mode)) ||
548                                 HFSPLUS_IS_RSRC(inode))
549                 return -EOPNOTSUPP;
550
551         res = hfsplus_listxattr_finder_info(dentry, buffer, size);
552         if (res < 0)
553                 return res;
554         else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
555                 return (res == 0) ? -EOPNOTSUPP : res;
556
557         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
558         if (err) {
559                 pr_err("can't init xattr find struct\n");
560                 return err;
561         }
562
563         err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
564         if (err) {
565                 if (err == -ENOENT) {
566                         if (res == 0)
567                                 res = -ENODATA;
568                         goto end_listxattr;
569                 } else {
570                         res = err;
571                         goto end_listxattr;
572                 }
573         }
574
575         for (;;) {
576                 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
577                 if (key_len == 0 || key_len > fd.tree->max_key_len) {
578                         pr_err("invalid xattr key length: %d\n", key_len);
579                         res = -EIO;
580                         goto end_listxattr;
581                 }
582
583                 hfs_bnode_read(fd.bnode, &attr_key,
584                                 fd.keyoffset, key_len + sizeof(key_len));
585
586                 if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
587                         goto end_listxattr;
588
589                 xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN;
590                 if (hfsplus_uni2asc(inode->i_sb,
591                         (const struct hfsplus_unistr *)&fd.key->attr.key_name,
592                                         strbuf, &xattr_name_len)) {
593                         pr_err("unicode conversion failed\n");
594                         res = -EIO;
595                         goto end_listxattr;
596                 }
597
598                 if (!buffer || !size) {
599                         if (can_list(strbuf))
600                                 res += name_len(strbuf, xattr_name_len);
601                 } else if (can_list(strbuf)) {
602                         if (size < (res + name_len(strbuf, xattr_name_len))) {
603                                 res = -ERANGE;
604                                 goto end_listxattr;
605                         } else
606                                 res += copy_name(buffer + res,
607                                                 strbuf, xattr_name_len);
608                 }
609
610                 if (hfs_brec_goto(&fd, 1))
611                         goto end_listxattr;
612         }
613
614 end_listxattr:
615         hfs_find_exit(&fd);
616         return res;
617 }
618
619 int hfsplus_removexattr(struct dentry *dentry, const char *name)
620 {
621         int err = 0;
622         struct inode *inode = dentry->d_inode;
623         struct hfs_find_data cat_fd;
624         u16 flags;
625         u16 cat_entry_type;
626         int is_xattr_acl_deleted = 0;
627         int is_all_xattrs_deleted = 0;
628
629         if ((!S_ISREG(inode->i_mode) &&
630                         !S_ISDIR(inode->i_mode)) ||
631                                 HFSPLUS_IS_RSRC(inode))
632                 return -EOPNOTSUPP;
633
634         if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
635                 return -EOPNOTSUPP;
636
637         err = can_set_xattr(inode, name, NULL, 0);
638         if (err)
639                 return err;
640
641         if (strncmp(name, XATTR_MAC_OSX_PREFIX,
642                                 XATTR_MAC_OSX_PREFIX_LEN) == 0)
643                 name += XATTR_MAC_OSX_PREFIX_LEN;
644
645         if (!strcmp_xattr_finder_info(name))
646                 return -EOPNOTSUPP;
647
648         err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
649         if (err) {
650                 pr_err("can't init xattr find struct\n");
651                 return err;
652         }
653
654         err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
655         if (err) {
656                 pr_err("catalog searching failed\n");
657                 goto end_removexattr;
658         }
659
660         err = hfsplus_delete_attr(inode, name);
661         if (err)
662                 goto end_removexattr;
663
664         is_xattr_acl_deleted = !strcmp_xattr_acl(name);
665         is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
666
667         if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
668                 goto end_removexattr;
669
670         cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
671
672         if (cat_entry_type == HFSPLUS_FOLDER) {
673                 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
674                                 offsetof(struct hfsplus_cat_folder, flags));
675                 if (is_xattr_acl_deleted)
676                         flags &= ~HFSPLUS_ACL_EXISTS;
677                 if (is_all_xattrs_deleted)
678                         flags &= ~HFSPLUS_XATTR_EXISTS;
679                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
680                                 offsetof(struct hfsplus_cat_folder, flags),
681                                 flags);
682                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
683         } else if (cat_entry_type == HFSPLUS_FILE) {
684                 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
685                                 offsetof(struct hfsplus_cat_file, flags));
686                 if (is_xattr_acl_deleted)
687                         flags &= ~HFSPLUS_ACL_EXISTS;
688                 if (is_all_xattrs_deleted)
689                         flags &= ~HFSPLUS_XATTR_EXISTS;
690                 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
691                                 offsetof(struct hfsplus_cat_file, flags),
692                                 flags);
693                 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
694         } else {
695                 pr_err("invalid catalog entry type\n");
696                 err = -EIO;
697                 goto end_removexattr;
698         }
699
700 end_removexattr:
701         hfs_find_exit(&cat_fd);
702         return err;
703 }
704
705 static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,
706                                         void *buffer, size_t size, int type)
707 {
708         char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
709                                 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
710         size_t len = strlen(name);
711
712         if (!strcmp(name, ""))
713                 return -EINVAL;
714
715         if (len > HFSPLUS_ATTR_MAX_STRLEN)
716                 return -EOPNOTSUPP;
717
718         strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
719         strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
720
721         return hfsplus_getxattr(dentry, xattr_name, buffer, size);
722 }
723
724 static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,
725                 const void *buffer, size_t size, int flags, int type)
726 {
727         char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
728                                 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
729         size_t len = strlen(name);
730
731         if (!strcmp(name, ""))
732                 return -EINVAL;
733
734         if (len > HFSPLUS_ATTR_MAX_STRLEN)
735                 return -EOPNOTSUPP;
736
737         strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
738         strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
739
740         return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags);
741 }
742
743 static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list,
744                 size_t list_size, const char *name, size_t name_len, int type)
745 {
746         /*
747          * This method is not used.
748          * It is used hfsplus_listxattr() instead of generic_listxattr().
749          */
750         return -EOPNOTSUPP;
751 }
752
753 const struct xattr_handler hfsplus_xattr_osx_handler = {
754         .prefix = XATTR_MAC_OSX_PREFIX,
755         .list   = hfsplus_osx_listxattr,
756         .get    = hfsplus_osx_getxattr,
757         .set    = hfsplus_osx_setxattr,
758 };