blob: bd8471fb9a6a80fdf74abdbd673714931c0b7867 [file] [log] [blame]
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -08001/*
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"
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -070011#include "acl.h"
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -080012
13const struct xattr_handler *hfsplus_xattr_handlers[] = {
14 &hfsplus_xattr_osx_handler,
15 &hfsplus_xattr_user_handler,
16 &hfsplus_xattr_trusted_handler,
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -070017#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
18 &hfsplus_xattr_acl_access_handler,
19 &hfsplus_xattr_acl_default_handler,
20#endif
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -080021 &hfsplus_xattr_security_handler,
22 NULL
23};
24
25static 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
34static 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
43static 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
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -070054static 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
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800101static 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))
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700105 return can_set_system_xattr(inode, name, value, value_len);
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800106
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
130int __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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700162 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800163 return err;
164 }
165
166 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
167 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700168 pr_err("catalog searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800169 goto end_setxattr;
170 }
171
172 if (!strcmp_xattr_finder_info(name)) {
173 if (flags & XATTR_CREATE) {
Joe Perchesd6142672013-04-30 15:27:55 -0700174 pr_err("xattr exists yet\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800175 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700220 pr_err("xattr exists yet\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800221 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700232 pr_err("cannot replace xattr\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800233 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 {
Joe Perchesd6142672013-04-30 15:27:55 -0700265 pr_err("invalid catalog entry type\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800266 err = -EIO;
267 goto end_setxattr;
268 }
269
270end_setxattr:
271 hfs_find_exit(&cat_fd);
272 return err;
273}
274
275static inline int is_osx_xattr(const char *xattr_name)
276{
277 return !is_known_namespace(xattr_name);
278}
279
280static 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
290static 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
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700308static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800309 void *value, size_t size)
310{
311 ssize_t res = 0;
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800312 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700323 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800324 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
352end_getxattr_finder_info:
353 if (size >= record_len)
354 hfs_find_exit(&fd);
355 return res;
356}
357
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700358ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800359 void *value, size_t size)
360{
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800361 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))
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700386 return hfsplus_getxattr_finder_info(inode, value, size);
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800387
388 if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
389 return -EOPNOTSUPP;
390
391 entry = hfsplus_alloc_attr_entry();
392 if (!entry) {
Joe Perchesd6142672013-04-30 15:27:55 -0700393 pr_err("can't allocate xattr entry\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800394 return -ENOMEM;
395 }
396
397 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
398 if (res) {
Joe Perchesd6142672013-04-30 15:27:55 -0700399 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800400 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
Joe Perchesd6142672013-04-30 15:27:55 -0700408 pr_err("xattr searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800409 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700421 pr_err("invalid xattr record size\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800422 res = -EIO;
423 goto out;
424 }
425 } else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
426 record_type == HFSPLUS_ATTR_EXTENTS) {
Joe Perchesd6142672013-04-30 15:27:55 -0700427 pr_err("only inline data xattr are supported\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800428 res = -EOPNOTSUPP;
429 goto out;
430 } else {
Joe Perchesd6142672013-04-30 15:27:55 -0700431 pr_err("invalid xattr record\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800432 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
448out:
449 hfs_find_exit(&fd);
450
451failed_getxattr_init:
452 hfsplus_destroy_attr_entry(entry);
453 return res;
454}
455
456static 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
466static 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700480 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800481 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
528end_listxattr_finder_info:
529 hfs_find_exit(&fd);
530
531 return res;
532}
533
534ssize_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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700559 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800560 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700578 pr_err("invalid xattr key length: %d\n", key_len);
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800579 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)) {
Joe Perchesd6142672013-04-30 15:27:55 -0700593 pr_err("unicode conversion failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800594 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
614end_listxattr:
615 hfs_find_exit(&fd);
616 return res;
617}
618
619int 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) {
Joe Perchesd6142672013-04-30 15:27:55 -0700650 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800651 return err;
652 }
653
654 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
655 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700656 pr_err("catalog searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800657 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 {
Joe Perchesd6142672013-04-30 15:27:55 -0700695 pr_err("invalid catalog entry type\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800696 err = -EIO;
697 goto end_removexattr;
698 }
699
700end_removexattr:
701 hfs_find_exit(&cat_fd);
702 return err;
703}
704
705static 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
724static 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
743static 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
753const 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};