root/fs/hfsplus/xattr.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. strcmp_xattr_finder_info
  2. strcmp_xattr_acl
  3. is_known_namespace
  4. hfsplus_init_header_node
  5. hfsplus_create_attributes_file
  6. __hfsplus_setxattr
  7. name_len
  8. copy_name
  9. hfsplus_setxattr
  10. hfsplus_getxattr_finder_info
  11. __hfsplus_getxattr
  12. hfsplus_getxattr
  13. can_list
  14. hfsplus_listxattr_finder_info
  15. hfsplus_listxattr
  16. hfsplus_removexattr
  17. hfsplus_osx_getxattr
  18. hfsplus_osx_setxattr

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

/* [<][>][^][v][top][bottom][index][help] */