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                err = -ENOMEM;
 208                goto end_attr_file_creation;
 209        }
 210
 211        hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
 212
 213        mapping = attr_file->i_mapping;
 214
 215        index = 0;
 216        written = 0;
 217        for (; written < node_size; index++, written += PAGE_SIZE) {
 218                void *kaddr;
 219
 220                page = read_mapping_page(mapping, index, NULL);
 221                if (IS_ERR(page)) {
 222                        err = PTR_ERR(page);
 223                        goto failed_header_node_init;
 224                }
 225
 226                kaddr = kmap_atomic(page);
 227                memcpy(kaddr, buf + written,
 228                        min_t(size_t, PAGE_SIZE, node_size - written));
 229                kunmap_atomic(kaddr);
 230
 231                set_page_dirty(page);
 232                put_page(page);
 233        }
 234
 235        hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
 236
 237        sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
 238        if (!sbi->attr_tree)
 239                pr_err("failed to load attributes file\n");
 240
 241failed_header_node_init:
 242        kfree(buf);
 243
 244end_attr_file_creation:
 245        iput(attr_file);
 246
 247        if (!err)
 248                atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
 249        else if (err == -ENOSPC)
 250                atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
 251        else
 252                atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
 253
 254        return err;
 255}
 256
 257int __hfsplus_setxattr(struct inode *inode, const char *name,
 258                        const void *value, size_t size, int flags)
 259{
 260        int err = 0;
 261        struct hfs_find_data cat_fd;
 262        hfsplus_cat_entry entry;
 263        u16 cat_entry_flags, cat_entry_type;
 264        u16 folder_finderinfo_len = sizeof(struct DInfo) +
 265                                        sizeof(struct DXInfo);
 266        u16 file_finderinfo_len = sizeof(struct FInfo) +
 267                                        sizeof(struct FXInfo);
 268
 269        if ((!S_ISREG(inode->i_mode) &&
 270                        !S_ISDIR(inode->i_mode)) ||
 271                                HFSPLUS_IS_RSRC(inode))
 272                return -EOPNOTSUPP;
 273
 274        if (value == NULL)
 275                return hfsplus_removexattr(inode, name);
 276
 277        err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
 278        if (err) {
 279                pr_err("can't init xattr find struct\n");
 280                return err;
 281        }
 282
 283        err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
 284        if (err) {
 285                pr_err("catalog searching failed\n");
 286                goto end_setxattr;
 287        }
 288
 289        if (!strcmp_xattr_finder_info(name)) {
 290                if (flags & XATTR_CREATE) {
 291                        pr_err("xattr exists yet\n");
 292                        err = -EOPNOTSUPP;
 293                        goto end_setxattr;
 294                }
 295                hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
 296                                        sizeof(hfsplus_cat_entry));
 297                if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
 298                        if (size == folder_finderinfo_len) {
 299                                memcpy(&entry.folder.user_info, value,
 300                                                folder_finderinfo_len);
 301                                hfs_bnode_write(cat_fd.bnode, &entry,
 302                                        cat_fd.entryoffset,
 303                                        sizeof(struct hfsplus_cat_folder));
 304                                hfsplus_mark_inode_dirty(inode,
 305                                                HFSPLUS_I_CAT_DIRTY);
 306                        } else {
 307                                err = -ERANGE;
 308                                goto end_setxattr;
 309                        }
 310                } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
 311                        if (size == file_finderinfo_len) {
 312                                memcpy(&entry.file.user_info, value,
 313                                                file_finderinfo_len);
 314                                hfs_bnode_write(cat_fd.bnode, &entry,
 315                                        cat_fd.entryoffset,
 316                                        sizeof(struct hfsplus_cat_file));
 317                                hfsplus_mark_inode_dirty(inode,
 318                                                HFSPLUS_I_CAT_DIRTY);
 319                        } else {
 320                                err = -ERANGE;
 321                                goto end_setxattr;
 322                        }
 323                } else {
 324                        err = -EOPNOTSUPP;
 325                        goto end_setxattr;
 326                }
 327                goto end_setxattr;
 328        }
 329
 330        if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
 331                err = hfsplus_create_attributes_file(inode->i_sb);
 332                if (unlikely(err))
 333                        goto end_setxattr;
 334        }
 335
 336        if (hfsplus_attr_exists(inode, name)) {
 337                if (flags & XATTR_CREATE) {
 338                        pr_err("xattr exists yet\n");
 339                        err = -EOPNOTSUPP;
 340                        goto end_setxattr;
 341                }
 342                err = hfsplus_delete_attr(inode, name);
 343                if (err)
 344                        goto end_setxattr;
 345                err = hfsplus_create_attr(inode, name, value, size);
 346                if (err)
 347                        goto end_setxattr;
 348        } else {
 349                if (flags & XATTR_REPLACE) {
 350                        pr_err("cannot replace xattr\n");
 351                        err = -EOPNOTSUPP;
 352                        goto end_setxattr;
 353                }
 354                err = hfsplus_create_attr(inode, name, value, size);
 355                if (err)
 356                        goto end_setxattr;
 357        }
 358
 359        cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
 360        if (cat_entry_type == HFSPLUS_FOLDER) {
 361                cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
 362                                    cat_fd.entryoffset +
 363                                    offsetof(struct hfsplus_cat_folder, flags));
 364                cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
 365                if (!strcmp_xattr_acl(name))
 366                        cat_entry_flags |= HFSPLUS_ACL_EXISTS;
 367                hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
 368                                offsetof(struct hfsplus_cat_folder, flags),
 369                                cat_entry_flags);
 370                hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
 371        } else if (cat_entry_type == HFSPLUS_FILE) {
 372                cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
 373                                    cat_fd.entryoffset +
 374                                    offsetof(struct hfsplus_cat_file, flags));
 375                cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
 376                if (!strcmp_xattr_acl(name))
 377                        cat_entry_flags |= HFSPLUS_ACL_EXISTS;
 378                hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
 379                                    offsetof(struct hfsplus_cat_file, flags),
 380                                    cat_entry_flags);
 381                hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
 382        } else {
 383                pr_err("invalid catalog entry type\n");
 384                err = -EIO;
 385                goto end_setxattr;
 386        }
 387
 388end_setxattr:
 389        hfs_find_exit(&cat_fd);
 390        return err;
 391}
 392
 393static int name_len(const char *xattr_name, int xattr_name_len)
 394{
 395        int len = xattr_name_len + 1;
 396
 397        if (!is_known_namespace(xattr_name))
 398                len += XATTR_MAC_OSX_PREFIX_LEN;
 399
 400        return len;
 401}
 402
 403static int copy_name(char *buffer, const char *xattr_name, int name_len)
 404{
 405        int len = name_len;
 406        int offset = 0;
 407
 408        if (!is_known_namespace(xattr_name)) {
 409                memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
 410                offset += XATTR_MAC_OSX_PREFIX_LEN;
 411                len += XATTR_MAC_OSX_PREFIX_LEN;
 412        }
 413
 414        strncpy(buffer + offset, xattr_name, name_len);
 415        memset(buffer + offset + name_len, 0, 1);
 416        len += 1;
 417
 418        return len;
 419}
 420
 421int hfsplus_setxattr(struct inode *inode, const char *name,
 422                     const void *value, size_t size, int flags,
 423                     const char *prefix, size_t prefixlen)
 424{
 425        char *xattr_name;
 426        int res;
 427
 428        xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
 429                GFP_KERNEL);
 430        if (!xattr_name)
 431                return -ENOMEM;
 432        strcpy(xattr_name, prefix);
 433        strcpy(xattr_name + prefixlen, name);
 434        res = __hfsplus_setxattr(inode, xattr_name, value, size, flags);
 435        kfree(xattr_name);
 436        return res;
 437}
 438
 439static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
 440                                                void *value, size_t size)
 441{
 442        ssize_t res = 0;
 443        struct hfs_find_data fd;
 444        u16 entry_type;
 445        u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
 446        u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
 447        u16 record_len = max(folder_rec_len, file_rec_len);
 448        u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
 449        u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
 450
 451        if (size >= record_len) {
 452                res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
 453                if (res) {
 454                        pr_err("can't init xattr find struct\n");
 455                        return res;
 456                }
 457                res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
 458                if (res)
 459                        goto end_getxattr_finder_info;
 460                entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
 461
 462                if (entry_type == HFSPLUS_FOLDER) {
 463                        hfs_bnode_read(fd.bnode, folder_finder_info,
 464                                fd.entryoffset +
 465                                offsetof(struct hfsplus_cat_folder, user_info),
 466                                folder_rec_len);
 467                        memcpy(value, folder_finder_info, folder_rec_len);
 468                        res = folder_rec_len;
 469                } else if (entry_type == HFSPLUS_FILE) {
 470                        hfs_bnode_read(fd.bnode, file_finder_info,
 471                                fd.entryoffset +
 472                                offsetof(struct hfsplus_cat_file, user_info),
 473                                file_rec_len);
 474                        memcpy(value, file_finder_info, file_rec_len);
 475                        res = file_rec_len;
 476                } else {
 477                        res = -EOPNOTSUPP;
 478                        goto end_getxattr_finder_info;
 479                }
 480        } else
 481                res = size ? -ERANGE : record_len;
 482
 483end_getxattr_finder_info:
 484        if (size >= record_len)
 485                hfs_find_exit(&fd);
 486        return res;
 487}
 488
 489ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
 490                         void *value, size_t size)
 491{
 492        struct hfs_find_data fd;
 493        hfsplus_attr_entry *entry;
 494        __be32 xattr_record_type;
 495        u32 record_type;
 496        u16 record_length = 0;
 497        ssize_t res = 0;
 498
 499        if ((!S_ISREG(inode->i_mode) &&
 500                        !S_ISDIR(inode->i_mode)) ||
 501                                HFSPLUS_IS_RSRC(inode))
 502                return -EOPNOTSUPP;
 503
 504        if (!strcmp_xattr_finder_info(name))
 505                return hfsplus_getxattr_finder_info(inode, value, size);
 506
 507        if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
 508                return -EOPNOTSUPP;
 509
 510        entry = hfsplus_alloc_attr_entry();
 511        if (!entry) {
 512                pr_err("can't allocate xattr entry\n");
 513                return -ENOMEM;
 514        }
 515
 516        res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
 517        if (res) {
 518                pr_err("can't init xattr find struct\n");
 519                goto failed_getxattr_init;
 520        }
 521
 522        res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
 523        if (res) {
 524                if (res == -ENOENT)
 525                        res = -ENODATA;
 526                else
 527                        pr_err("xattr searching failed\n");
 528                goto out;
 529        }
 530
 531        hfs_bnode_read(fd.bnode, &xattr_record_type,
 532                        fd.entryoffset, sizeof(xattr_record_type));
 533        record_type = be32_to_cpu(xattr_record_type);
 534        if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
 535                record_length = hfs_bnode_read_u16(fd.bnode,
 536                                fd.entryoffset +
 537                                offsetof(struct hfsplus_attr_inline_data,
 538                                length));
 539                if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
 540                        pr_err("invalid xattr record size\n");
 541                        res = -EIO;
 542                        goto out;
 543                }
 544        } else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
 545                        record_type == HFSPLUS_ATTR_EXTENTS) {
 546                pr_err("only inline data xattr are supported\n");
 547                res = -EOPNOTSUPP;
 548                goto out;
 549        } else {
 550                pr_err("invalid xattr record\n");
 551                res = -EIO;
 552                goto out;
 553        }
 554
 555        if (size) {
 556                hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
 557                                offsetof(struct hfsplus_attr_inline_data,
 558                                        raw_bytes) + record_length);
 559        }
 560
 561        if (size >= record_length) {
 562                memcpy(value, entry->inline_data.raw_bytes, record_length);
 563                res = record_length;
 564        } else
 565                res = size ? -ERANGE : record_length;
 566
 567out:
 568        hfs_find_exit(&fd);
 569
 570failed_getxattr_init:
 571        hfsplus_destroy_attr_entry(entry);
 572        return res;
 573}
 574
 575ssize_t hfsplus_getxattr(struct inode *inode, const char *name,
 576                         void *value, size_t size,
 577                         const char *prefix, size_t prefixlen)
 578{
 579        int res;
 580        char *xattr_name;
 581
 582        xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
 583                             GFP_KERNEL);
 584        if (!xattr_name)
 585                return -ENOMEM;
 586
 587        strcpy(xattr_name, prefix);
 588        strcpy(xattr_name + prefixlen, name);
 589
 590        res = __hfsplus_getxattr(inode, xattr_name, value, size);
 591        kfree(xattr_name);
 592        return res;
 593
 594}
 595
 596static inline int can_list(const char *xattr_name)
 597{
 598        if (!xattr_name)
 599                return 0;
 600
 601        return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
 602                        XATTR_TRUSTED_PREFIX_LEN) ||
 603                                capable(CAP_SYS_ADMIN);
 604}
 605
 606static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
 607                                                char *buffer, size_t size)
 608{
 609        ssize_t res = 0;
 610        struct inode *inode = d_inode(dentry);
 611        struct hfs_find_data fd;
 612        u16 entry_type;
 613        u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
 614        u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
 615        unsigned long len, found_bit;
 616        int xattr_name_len, symbols_count;
 617
 618        res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
 619        if (res) {
 620                pr_err("can't init xattr find struct\n");
 621                return res;
 622        }
 623
 624        res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
 625        if (res)
 626                goto end_listxattr_finder_info;
 627
 628        entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
 629        if (entry_type == HFSPLUS_FOLDER) {
 630                len = sizeof(struct DInfo) + sizeof(struct DXInfo);
 631                hfs_bnode_read(fd.bnode, folder_finder_info,
 632                                fd.entryoffset +
 633                                offsetof(struct hfsplus_cat_folder, user_info),
 634                                len);
 635                found_bit = find_first_bit((void *)folder_finder_info, len*8);
 636        } else if (entry_type == HFSPLUS_FILE) {
 637                len = sizeof(struct FInfo) + sizeof(struct FXInfo);
 638                hfs_bnode_read(fd.bnode, file_finder_info,
 639                                fd.entryoffset +
 640                                offsetof(struct hfsplus_cat_file, user_info),
 641                                len);
 642                found_bit = find_first_bit((void *)file_finder_info, len*8);
 643        } else {
 644                res = -EOPNOTSUPP;
 645                goto end_listxattr_finder_info;
 646        }
 647
 648        if (found_bit >= (len*8))
 649                res = 0;
 650        else {
 651                symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
 652                xattr_name_len =
 653                        name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
 654                if (!buffer || !size) {
 655                        if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
 656                                res = xattr_name_len;
 657                } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
 658                        if (size < xattr_name_len)
 659                                res = -ERANGE;
 660                        else {
 661                                res = copy_name(buffer,
 662                                                HFSPLUS_XATTR_FINDER_INFO_NAME,
 663                                                symbols_count);
 664                        }
 665                }
 666        }
 667
 668end_listxattr_finder_info:
 669        hfs_find_exit(&fd);
 670
 671        return res;
 672}
 673
 674ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
 675{
 676        ssize_t err;
 677        ssize_t res = 0;
 678        struct inode *inode = d_inode(dentry);
 679        struct hfs_find_data fd;
 680        u16 key_len = 0;
 681        struct hfsplus_attr_key attr_key;
 682        char *strbuf;
 683        int xattr_name_len;
 684
 685        if ((!S_ISREG(inode->i_mode) &&
 686                        !S_ISDIR(inode->i_mode)) ||
 687                                HFSPLUS_IS_RSRC(inode))
 688                return -EOPNOTSUPP;
 689
 690        res = hfsplus_listxattr_finder_info(dentry, buffer, size);
 691        if (res < 0)
 692                return res;
 693        else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
 694                return (res == 0) ? -EOPNOTSUPP : res;
 695
 696        err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
 697        if (err) {
 698                pr_err("can't init xattr find struct\n");
 699                return err;
 700        }
 701
 702        strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +
 703                        XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL);
 704        if (!strbuf) {
 705                res = -ENOMEM;
 706                goto out;
 707        }
 708
 709        err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
 710        if (err) {
 711                if (err == -ENOENT) {
 712                        if (res == 0)
 713                                res = -ENODATA;
 714                        goto end_listxattr;
 715                } else {
 716                        res = err;
 717                        goto end_listxattr;
 718                }
 719        }
 720
 721        for (;;) {
 722                key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
 723                if (key_len == 0 || key_len > fd.tree->max_key_len) {
 724                        pr_err("invalid xattr key length: %d\n", key_len);
 725                        res = -EIO;
 726                        goto end_listxattr;
 727                }
 728
 729                hfs_bnode_read(fd.bnode, &attr_key,
 730                                fd.keyoffset, key_len + sizeof(key_len));
 731
 732                if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
 733                        goto end_listxattr;
 734
 735                xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN;
 736                if (hfsplus_uni2asc(inode->i_sb,
 737                        (const struct hfsplus_unistr *)&fd.key->attr.key_name,
 738                                        strbuf, &xattr_name_len)) {
 739                        pr_err("unicode conversion failed\n");
 740                        res = -EIO;
 741                        goto end_listxattr;
 742                }
 743
 744                if (!buffer || !size) {
 745                        if (can_list(strbuf))
 746                                res += name_len(strbuf, xattr_name_len);
 747                } else if (can_list(strbuf)) {
 748                        if (size < (res + name_len(strbuf, xattr_name_len))) {
 749                                res = -ERANGE;
 750                                goto end_listxattr;
 751                        } else
 752                                res += copy_name(buffer + res,
 753                                                strbuf, xattr_name_len);
 754                }
 755
 756                if (hfs_brec_goto(&fd, 1))
 757                        goto end_listxattr;
 758        }
 759
 760end_listxattr:
 761        kfree(strbuf);
 762out:
 763        hfs_find_exit(&fd);
 764        return res;
 765}
 766
 767static int hfsplus_removexattr(struct inode *inode, const char *name)
 768{
 769        int err = 0;
 770        struct hfs_find_data cat_fd;
 771        u16 flags;
 772        u16 cat_entry_type;
 773        int is_xattr_acl_deleted = 0;
 774        int is_all_xattrs_deleted = 0;
 775
 776        if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
 777                return -EOPNOTSUPP;
 778
 779        if (!strcmp_xattr_finder_info(name))
 780                return -EOPNOTSUPP;
 781
 782        err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
 783        if (err) {
 784                pr_err("can't init xattr find struct\n");
 785                return err;
 786        }
 787
 788        err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
 789        if (err) {
 790                pr_err("catalog searching failed\n");
 791                goto end_removexattr;
 792        }
 793
 794        err = hfsplus_delete_attr(inode, name);
 795        if (err)
 796                goto end_removexattr;
 797
 798        is_xattr_acl_deleted = !strcmp_xattr_acl(name);
 799        is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
 800
 801        if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
 802                goto end_removexattr;
 803
 804        cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
 805
 806        if (cat_entry_type == HFSPLUS_FOLDER) {
 807                flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
 808                                offsetof(struct hfsplus_cat_folder, flags));
 809                if (is_xattr_acl_deleted)
 810                        flags &= ~HFSPLUS_ACL_EXISTS;
 811                if (is_all_xattrs_deleted)
 812                        flags &= ~HFSPLUS_XATTR_EXISTS;
 813                hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
 814                                offsetof(struct hfsplus_cat_folder, flags),
 815                                flags);
 816                hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
 817        } else if (cat_entry_type == HFSPLUS_FILE) {
 818                flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
 819                                offsetof(struct hfsplus_cat_file, flags));
 820                if (is_xattr_acl_deleted)
 821                        flags &= ~HFSPLUS_ACL_EXISTS;
 822                if (is_all_xattrs_deleted)
 823                        flags &= ~HFSPLUS_XATTR_EXISTS;
 824                hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
 825                                offsetof(struct hfsplus_cat_file, flags),
 826                                flags);
 827                hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
 828        } else {
 829                pr_err("invalid catalog entry type\n");
 830                err = -EIO;
 831                goto end_removexattr;
 832        }
 833
 834end_removexattr:
 835        hfs_find_exit(&cat_fd);
 836        return err;
 837}
 838
 839static int hfsplus_osx_getxattr(const struct xattr_handler *handler,
 840                                struct dentry *unused, struct inode *inode,
 841                                const char *name, void *buffer, size_t size)
 842{
 843        /*
 844         * Don't allow retrieving properly prefixed attributes
 845         * by prepending them with "osx."
 846         */
 847        if (is_known_namespace(name))
 848                return -EOPNOTSUPP;
 849
 850        /*
 851         * osx is the namespace we use to indicate an unprefixed
 852         * attribute on the filesystem (like the ones that OS X
 853         * creates), so we pass the name through unmodified (after
 854         * ensuring it doesn't conflict with another namespace).
 855         */
 856        return __hfsplus_getxattr(inode, name, buffer, size);
 857}
 858
 859static int hfsplus_osx_setxattr(const struct xattr_handler *handler,
 860                                struct user_namespace *mnt_userns,
 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