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