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