linux/fs/hfsplus/xattr.c
<<
>>
Prefs
   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
  14static int hfsplus_removexattr(struct inode *inode, const char *name);
  15
  16const 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
  24static 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
  33static 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
  42static 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
  53static 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
 121static 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
 137check_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
 242failed_header_node_init:
 243        kfree(buf);
 244
 245end_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
 258int __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
 389end_setxattr:
 390        hfs_find_exit(&cat_fd);
 391        return err;
 392}
 393
 394static 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
 404static 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
 422int 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
 440static 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
 484end_getxattr_finder_info:
 485        if (size >= record_len)
 486                hfs_find_exit(&fd);
 487        return res;
 488}
 489
 490ssize_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
 568out:
 569        hfs_find_exit(&fd);
 570
 571failed_getxattr_init:
 572        hfsplus_destroy_attr_entry(entry);
 573        return res;
 574}
 575
 576ssize_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
 597static 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
 607static 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
 669end_listxattr_finder_info:
 670        hfs_find_exit(&fd);
 671
 672        return res;
 673}
 674
 675ssize_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
 761end_listxattr:
 762        kfree(strbuf);
 763out:
 764        hfs_find_exit(&fd);
 765        return res;
 766}
 767
 768static 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
 835end_removexattr:
 836        hfs_find_exit(&cat_fd);
 837        return err;
 838}
 839
 840static 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
 860static 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
 881const struct xattr_handler hfsplus_xattr_osx_handler = {
 882        .prefix = XATTR_MAC_OSX_PREFIX,
 883        .get    = hfsplus_osx_getxattr,
 884        .set    = hfsplus_osx_setxattr,
 885};
 886