blob: 568a45cb114588cd498b841692678553e960e08c [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
Vyacheslav Dubeyko099e9242013-11-12 15:11:08 -0800130static void hfsplus_init_header_node(struct inode *attr_file,
131 u32 clump_size,
132 char *buf, size_t node_size)
133{
134 struct hfs_bnode_desc *desc;
135 struct hfs_btree_header_rec *head;
136 u16 offset;
137 __be16 *rec_offsets;
138 u32 hdr_node_map_rec_bits;
139 char *bmp;
140 u32 used_nodes;
141 u32 used_bmp_bytes;
142
143 hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %zu\n",
144 clump_size, node_size);
145
146 /* The end of the node contains list of record offsets */
147 rec_offsets = (__be16 *)(buf + node_size);
148
149 desc = (struct hfs_bnode_desc *)buf;
150 desc->type = HFS_NODE_HEADER;
151 desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
152 offset = sizeof(struct hfs_bnode_desc);
153 *--rec_offsets = cpu_to_be16(offset);
154
155 head = (struct hfs_btree_header_rec *)(buf + offset);
156 head->node_size = cpu_to_be16(node_size);
157 head->node_count = cpu_to_be32(i_size_read(attr_file) / node_size);
158 head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
159 head->clump_size = cpu_to_be32(clump_size);
160 head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
161 head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
162 offset += sizeof(struct hfs_btree_header_rec);
163 *--rec_offsets = cpu_to_be16(offset);
164 offset += HFSPLUS_BTREE_HDR_USER_BYTES;
165 *--rec_offsets = cpu_to_be16(offset);
166
167 hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
168 if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
169 u32 map_node_bits;
170 u32 map_nodes;
171
172 desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
173 map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
174 (2 * sizeof(u16)) - 2);
175 map_nodes = (be32_to_cpu(head->node_count) -
176 hdr_node_map_rec_bits +
177 (map_node_bits - 1)) / map_node_bits;
178 be32_add_cpu(&head->free_nodes, 0 - map_nodes);
179 }
180
181 bmp = buf + offset;
182 used_nodes =
183 be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
184 used_bmp_bytes = used_nodes / 8;
185 if (used_bmp_bytes) {
186 memset(bmp, 0xFF, used_bmp_bytes);
187 bmp += used_bmp_bytes;
188 used_nodes %= 8;
189 }
190 *bmp = ~(0xFF >> used_nodes);
191 offset += hdr_node_map_rec_bits / 8;
192 *--rec_offsets = cpu_to_be16(offset);
193}
194
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800195int __hfsplus_setxattr(struct inode *inode, const char *name,
196 const void *value, size_t size, int flags)
197{
198 int err = 0;
199 struct hfs_find_data cat_fd;
200 hfsplus_cat_entry entry;
201 u16 cat_entry_flags, cat_entry_type;
202 u16 folder_finderinfo_len = sizeof(struct DInfo) +
203 sizeof(struct DXInfo);
204 u16 file_finderinfo_len = sizeof(struct FInfo) +
205 sizeof(struct FXInfo);
206
207 if ((!S_ISREG(inode->i_mode) &&
208 !S_ISDIR(inode->i_mode)) ||
209 HFSPLUS_IS_RSRC(inode))
210 return -EOPNOTSUPP;
211
212 err = can_set_xattr(inode, name, value, size);
213 if (err)
214 return err;
215
216 if (strncmp(name, XATTR_MAC_OSX_PREFIX,
217 XATTR_MAC_OSX_PREFIX_LEN) == 0)
218 name += XATTR_MAC_OSX_PREFIX_LEN;
219
220 if (value == NULL) {
221 value = "";
222 size = 0;
223 }
224
225 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
226 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700227 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800228 return err;
229 }
230
231 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
232 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700233 pr_err("catalog searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800234 goto end_setxattr;
235 }
236
237 if (!strcmp_xattr_finder_info(name)) {
238 if (flags & XATTR_CREATE) {
Joe Perchesd6142672013-04-30 15:27:55 -0700239 pr_err("xattr exists yet\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800240 err = -EOPNOTSUPP;
241 goto end_setxattr;
242 }
243 hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
244 sizeof(hfsplus_cat_entry));
245 if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
246 if (size == folder_finderinfo_len) {
247 memcpy(&entry.folder.user_info, value,
248 folder_finderinfo_len);
249 hfs_bnode_write(cat_fd.bnode, &entry,
250 cat_fd.entryoffset,
251 sizeof(struct hfsplus_cat_folder));
252 hfsplus_mark_inode_dirty(inode,
253 HFSPLUS_I_CAT_DIRTY);
254 } else {
255 err = -ERANGE;
256 goto end_setxattr;
257 }
258 } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
259 if (size == file_finderinfo_len) {
260 memcpy(&entry.file.user_info, value,
261 file_finderinfo_len);
262 hfs_bnode_write(cat_fd.bnode, &entry,
263 cat_fd.entryoffset,
264 sizeof(struct hfsplus_cat_file));
265 hfsplus_mark_inode_dirty(inode,
266 HFSPLUS_I_CAT_DIRTY);
267 } else {
268 err = -ERANGE;
269 goto end_setxattr;
270 }
271 } else {
272 err = -EOPNOTSUPP;
273 goto end_setxattr;
274 }
275 goto end_setxattr;
276 }
277
278 if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
279 err = -EOPNOTSUPP;
280 goto end_setxattr;
281 }
282
283 if (hfsplus_attr_exists(inode, name)) {
284 if (flags & XATTR_CREATE) {
Joe Perchesd6142672013-04-30 15:27:55 -0700285 pr_err("xattr exists yet\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800286 err = -EOPNOTSUPP;
287 goto end_setxattr;
288 }
289 err = hfsplus_delete_attr(inode, name);
290 if (err)
291 goto end_setxattr;
292 err = hfsplus_create_attr(inode, name, value, size);
293 if (err)
294 goto end_setxattr;
295 } else {
296 if (flags & XATTR_REPLACE) {
Joe Perchesd6142672013-04-30 15:27:55 -0700297 pr_err("cannot replace xattr\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800298 err = -EOPNOTSUPP;
299 goto end_setxattr;
300 }
301 err = hfsplus_create_attr(inode, name, value, size);
302 if (err)
303 goto end_setxattr;
304 }
305
306 cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
307 if (cat_entry_type == HFSPLUS_FOLDER) {
308 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
309 cat_fd.entryoffset +
310 offsetof(struct hfsplus_cat_folder, flags));
311 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
312 if (!strcmp_xattr_acl(name))
313 cat_entry_flags |= HFSPLUS_ACL_EXISTS;
314 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
315 offsetof(struct hfsplus_cat_folder, flags),
316 cat_entry_flags);
317 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
318 } else if (cat_entry_type == HFSPLUS_FILE) {
319 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
320 cat_fd.entryoffset +
321 offsetof(struct hfsplus_cat_file, flags));
322 cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
323 if (!strcmp_xattr_acl(name))
324 cat_entry_flags |= HFSPLUS_ACL_EXISTS;
325 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
326 offsetof(struct hfsplus_cat_file, flags),
327 cat_entry_flags);
328 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
329 } else {
Joe Perchesd6142672013-04-30 15:27:55 -0700330 pr_err("invalid catalog entry type\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800331 err = -EIO;
332 goto end_setxattr;
333 }
334
335end_setxattr:
336 hfs_find_exit(&cat_fd);
337 return err;
338}
339
340static inline int is_osx_xattr(const char *xattr_name)
341{
342 return !is_known_namespace(xattr_name);
343}
344
345static int name_len(const char *xattr_name, int xattr_name_len)
346{
347 int len = xattr_name_len + 1;
348
349 if (is_osx_xattr(xattr_name))
350 len += XATTR_MAC_OSX_PREFIX_LEN;
351
352 return len;
353}
354
355static int copy_name(char *buffer, const char *xattr_name, int name_len)
356{
357 int len = name_len;
358 int offset = 0;
359
360 if (is_osx_xattr(xattr_name)) {
361 strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
362 offset += XATTR_MAC_OSX_PREFIX_LEN;
363 len += XATTR_MAC_OSX_PREFIX_LEN;
364 }
365
366 strncpy(buffer + offset, xattr_name, name_len);
367 memset(buffer + offset + name_len, 0, 1);
368 len += 1;
369
370 return len;
371}
372
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700373static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800374 void *value, size_t size)
375{
376 ssize_t res = 0;
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800377 struct hfs_find_data fd;
378 u16 entry_type;
379 u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
380 u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
381 u16 record_len = max(folder_rec_len, file_rec_len);
382 u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
383 u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
384
385 if (size >= record_len) {
386 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
387 if (res) {
Joe Perchesd6142672013-04-30 15:27:55 -0700388 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800389 return res;
390 }
391 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
392 if (res)
393 goto end_getxattr_finder_info;
394 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
395
396 if (entry_type == HFSPLUS_FOLDER) {
397 hfs_bnode_read(fd.bnode, folder_finder_info,
398 fd.entryoffset +
399 offsetof(struct hfsplus_cat_folder, user_info),
400 folder_rec_len);
401 memcpy(value, folder_finder_info, folder_rec_len);
402 res = folder_rec_len;
403 } else if (entry_type == HFSPLUS_FILE) {
404 hfs_bnode_read(fd.bnode, file_finder_info,
405 fd.entryoffset +
406 offsetof(struct hfsplus_cat_file, user_info),
407 file_rec_len);
408 memcpy(value, file_finder_info, file_rec_len);
409 res = file_rec_len;
410 } else {
411 res = -EOPNOTSUPP;
412 goto end_getxattr_finder_info;
413 }
414 } else
415 res = size ? -ERANGE : record_len;
416
417end_getxattr_finder_info:
418 if (size >= record_len)
419 hfs_find_exit(&fd);
420 return res;
421}
422
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700423ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800424 void *value, size_t size)
425{
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800426 struct hfs_find_data fd;
427 hfsplus_attr_entry *entry;
428 __be32 xattr_record_type;
429 u32 record_type;
430 u16 record_length = 0;
431 ssize_t res = 0;
432
433 if ((!S_ISREG(inode->i_mode) &&
434 !S_ISDIR(inode->i_mode)) ||
435 HFSPLUS_IS_RSRC(inode))
436 return -EOPNOTSUPP;
437
438 if (strncmp(name, XATTR_MAC_OSX_PREFIX,
439 XATTR_MAC_OSX_PREFIX_LEN) == 0) {
440 /* skip "osx." prefix */
441 name += XATTR_MAC_OSX_PREFIX_LEN;
442 /*
443 * Don't allow retrieving properly prefixed attributes
444 * by prepending them with "osx."
445 */
446 if (is_known_namespace(name))
447 return -EOPNOTSUPP;
448 }
449
450 if (!strcmp_xattr_finder_info(name))
Vyacheslav Dubeykob4c11072013-09-11 14:24:30 -0700451 return hfsplus_getxattr_finder_info(inode, value, size);
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800452
453 if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
454 return -EOPNOTSUPP;
455
456 entry = hfsplus_alloc_attr_entry();
457 if (!entry) {
Joe Perchesd6142672013-04-30 15:27:55 -0700458 pr_err("can't allocate xattr entry\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800459 return -ENOMEM;
460 }
461
462 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
463 if (res) {
Joe Perchesd6142672013-04-30 15:27:55 -0700464 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800465 goto failed_getxattr_init;
466 }
467
468 res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
469 if (res) {
470 if (res == -ENOENT)
471 res = -ENODATA;
472 else
Joe Perchesd6142672013-04-30 15:27:55 -0700473 pr_err("xattr searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800474 goto out;
475 }
476
477 hfs_bnode_read(fd.bnode, &xattr_record_type,
478 fd.entryoffset, sizeof(xattr_record_type));
479 record_type = be32_to_cpu(xattr_record_type);
480 if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
481 record_length = hfs_bnode_read_u16(fd.bnode,
482 fd.entryoffset +
483 offsetof(struct hfsplus_attr_inline_data,
484 length));
485 if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
Joe Perchesd6142672013-04-30 15:27:55 -0700486 pr_err("invalid xattr record size\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800487 res = -EIO;
488 goto out;
489 }
490 } else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
491 record_type == HFSPLUS_ATTR_EXTENTS) {
Joe Perchesd6142672013-04-30 15:27:55 -0700492 pr_err("only inline data xattr are supported\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800493 res = -EOPNOTSUPP;
494 goto out;
495 } else {
Joe Perchesd6142672013-04-30 15:27:55 -0700496 pr_err("invalid xattr record\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800497 res = -EIO;
498 goto out;
499 }
500
501 if (size) {
502 hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
503 offsetof(struct hfsplus_attr_inline_data,
504 raw_bytes) + record_length);
505 }
506
507 if (size >= record_length) {
508 memcpy(value, entry->inline_data.raw_bytes, record_length);
509 res = record_length;
510 } else
511 res = size ? -ERANGE : record_length;
512
513out:
514 hfs_find_exit(&fd);
515
516failed_getxattr_init:
517 hfsplus_destroy_attr_entry(entry);
518 return res;
519}
520
521static inline int can_list(const char *xattr_name)
522{
523 if (!xattr_name)
524 return 0;
525
526 return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
527 XATTR_TRUSTED_PREFIX_LEN) ||
528 capable(CAP_SYS_ADMIN);
529}
530
531static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
532 char *buffer, size_t size)
533{
534 ssize_t res = 0;
535 struct inode *inode = dentry->d_inode;
536 struct hfs_find_data fd;
537 u16 entry_type;
538 u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
539 u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
540 unsigned long len, found_bit;
541 int xattr_name_len, symbols_count;
542
543 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
544 if (res) {
Joe Perchesd6142672013-04-30 15:27:55 -0700545 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800546 return res;
547 }
548
549 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
550 if (res)
551 goto end_listxattr_finder_info;
552
553 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
554 if (entry_type == HFSPLUS_FOLDER) {
555 len = sizeof(struct DInfo) + sizeof(struct DXInfo);
556 hfs_bnode_read(fd.bnode, folder_finder_info,
557 fd.entryoffset +
558 offsetof(struct hfsplus_cat_folder, user_info),
559 len);
560 found_bit = find_first_bit((void *)folder_finder_info, len*8);
561 } else if (entry_type == HFSPLUS_FILE) {
562 len = sizeof(struct FInfo) + sizeof(struct FXInfo);
563 hfs_bnode_read(fd.bnode, file_finder_info,
564 fd.entryoffset +
565 offsetof(struct hfsplus_cat_file, user_info),
566 len);
567 found_bit = find_first_bit((void *)file_finder_info, len*8);
568 } else {
569 res = -EOPNOTSUPP;
570 goto end_listxattr_finder_info;
571 }
572
573 if (found_bit >= (len*8))
574 res = 0;
575 else {
576 symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
577 xattr_name_len =
578 name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
579 if (!buffer || !size) {
580 if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
581 res = xattr_name_len;
582 } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
583 if (size < xattr_name_len)
584 res = -ERANGE;
585 else {
586 res = copy_name(buffer,
587 HFSPLUS_XATTR_FINDER_INFO_NAME,
588 symbols_count);
589 }
590 }
591 }
592
593end_listxattr_finder_info:
594 hfs_find_exit(&fd);
595
596 return res;
597}
598
599ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
600{
601 ssize_t err;
602 ssize_t res = 0;
603 struct inode *inode = dentry->d_inode;
604 struct hfs_find_data fd;
605 u16 key_len = 0;
606 struct hfsplus_attr_key attr_key;
607 char strbuf[HFSPLUS_ATTR_MAX_STRLEN +
608 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
609 int xattr_name_len;
610
611 if ((!S_ISREG(inode->i_mode) &&
612 !S_ISDIR(inode->i_mode)) ||
613 HFSPLUS_IS_RSRC(inode))
614 return -EOPNOTSUPP;
615
616 res = hfsplus_listxattr_finder_info(dentry, buffer, size);
617 if (res < 0)
618 return res;
619 else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
620 return (res == 0) ? -EOPNOTSUPP : res;
621
622 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
623 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700624 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800625 return err;
626 }
627
628 err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
629 if (err) {
630 if (err == -ENOENT) {
631 if (res == 0)
632 res = -ENODATA;
633 goto end_listxattr;
634 } else {
635 res = err;
636 goto end_listxattr;
637 }
638 }
639
640 for (;;) {
641 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
642 if (key_len == 0 || key_len > fd.tree->max_key_len) {
Joe Perchesd6142672013-04-30 15:27:55 -0700643 pr_err("invalid xattr key length: %d\n", key_len);
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800644 res = -EIO;
645 goto end_listxattr;
646 }
647
648 hfs_bnode_read(fd.bnode, &attr_key,
649 fd.keyoffset, key_len + sizeof(key_len));
650
651 if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
652 goto end_listxattr;
653
654 xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN;
655 if (hfsplus_uni2asc(inode->i_sb,
656 (const struct hfsplus_unistr *)&fd.key->attr.key_name,
657 strbuf, &xattr_name_len)) {
Joe Perchesd6142672013-04-30 15:27:55 -0700658 pr_err("unicode conversion failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800659 res = -EIO;
660 goto end_listxattr;
661 }
662
663 if (!buffer || !size) {
664 if (can_list(strbuf))
665 res += name_len(strbuf, xattr_name_len);
666 } else if (can_list(strbuf)) {
667 if (size < (res + name_len(strbuf, xattr_name_len))) {
668 res = -ERANGE;
669 goto end_listxattr;
670 } else
671 res += copy_name(buffer + res,
672 strbuf, xattr_name_len);
673 }
674
675 if (hfs_brec_goto(&fd, 1))
676 goto end_listxattr;
677 }
678
679end_listxattr:
680 hfs_find_exit(&fd);
681 return res;
682}
683
684int hfsplus_removexattr(struct dentry *dentry, const char *name)
685{
686 int err = 0;
687 struct inode *inode = dentry->d_inode;
688 struct hfs_find_data cat_fd;
689 u16 flags;
690 u16 cat_entry_type;
691 int is_xattr_acl_deleted = 0;
692 int is_all_xattrs_deleted = 0;
693
694 if ((!S_ISREG(inode->i_mode) &&
695 !S_ISDIR(inode->i_mode)) ||
696 HFSPLUS_IS_RSRC(inode))
697 return -EOPNOTSUPP;
698
699 if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
700 return -EOPNOTSUPP;
701
702 err = can_set_xattr(inode, name, NULL, 0);
703 if (err)
704 return err;
705
706 if (strncmp(name, XATTR_MAC_OSX_PREFIX,
707 XATTR_MAC_OSX_PREFIX_LEN) == 0)
708 name += XATTR_MAC_OSX_PREFIX_LEN;
709
710 if (!strcmp_xattr_finder_info(name))
711 return -EOPNOTSUPP;
712
713 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
714 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700715 pr_err("can't init xattr find struct\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800716 return err;
717 }
718
719 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
720 if (err) {
Joe Perchesd6142672013-04-30 15:27:55 -0700721 pr_err("catalog searching failed\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800722 goto end_removexattr;
723 }
724
725 err = hfsplus_delete_attr(inode, name);
726 if (err)
727 goto end_removexattr;
728
729 is_xattr_acl_deleted = !strcmp_xattr_acl(name);
730 is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
731
732 if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
733 goto end_removexattr;
734
735 cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
736
737 if (cat_entry_type == HFSPLUS_FOLDER) {
738 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
739 offsetof(struct hfsplus_cat_folder, flags));
740 if (is_xattr_acl_deleted)
741 flags &= ~HFSPLUS_ACL_EXISTS;
742 if (is_all_xattrs_deleted)
743 flags &= ~HFSPLUS_XATTR_EXISTS;
744 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
745 offsetof(struct hfsplus_cat_folder, flags),
746 flags);
747 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
748 } else if (cat_entry_type == HFSPLUS_FILE) {
749 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
750 offsetof(struct hfsplus_cat_file, flags));
751 if (is_xattr_acl_deleted)
752 flags &= ~HFSPLUS_ACL_EXISTS;
753 if (is_all_xattrs_deleted)
754 flags &= ~HFSPLUS_XATTR_EXISTS;
755 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
756 offsetof(struct hfsplus_cat_file, flags),
757 flags);
758 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
759 } else {
Joe Perchesd6142672013-04-30 15:27:55 -0700760 pr_err("invalid catalog entry type\n");
Vyacheslav Dubeyko127e5f52013-02-27 17:03:03 -0800761 err = -EIO;
762 goto end_removexattr;
763 }
764
765end_removexattr:
766 hfs_find_exit(&cat_fd);
767 return err;
768}
769
770static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,
771 void *buffer, size_t size, int type)
772{
773 char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
774 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
775 size_t len = strlen(name);
776
777 if (!strcmp(name, ""))
778 return -EINVAL;
779
780 if (len > HFSPLUS_ATTR_MAX_STRLEN)
781 return -EOPNOTSUPP;
782
783 strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
784 strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
785
786 return hfsplus_getxattr(dentry, xattr_name, buffer, size);
787}
788
789static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,
790 const void *buffer, size_t size, int flags, int type)
791{
792 char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
793 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
794 size_t len = strlen(name);
795
796 if (!strcmp(name, ""))
797 return -EINVAL;
798
799 if (len > HFSPLUS_ATTR_MAX_STRLEN)
800 return -EOPNOTSUPP;
801
802 strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
803 strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
804
805 return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags);
806}
807
808static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list,
809 size_t list_size, const char *name, size_t name_len, int type)
810{
811 /*
812 * This method is not used.
813 * It is used hfsplus_listxattr() instead of generic_listxattr().
814 */
815 return -EOPNOTSUPP;
816}
817
818const struct xattr_handler hfsplus_xattr_osx_handler = {
819 .prefix = XATTR_MAC_OSX_PREFIX,
820 .list = hfsplus_osx_listxattr,
821 .get = hfsplus_osx_getxattr,
822 .set = hfsplus_osx_setxattr,
823};