linux/fs/hfsplus/attributes.c
<<
>>
Prefs
   1/*
   2 * linux/fs/hfsplus/attributes.c
   3 *
   4 * Vyacheslav Dubeyko <slava@dubeyko.com>
   5 *
   6 * Handling of records in attributes tree
   7 */
   8
   9#include "hfsplus_fs.h"
  10#include "hfsplus_raw.h"
  11
  12static struct kmem_cache *hfsplus_attr_tree_cachep;
  13
  14int hfsplus_create_attr_tree_cache(void)
  15{
  16        if (hfsplus_attr_tree_cachep)
  17                return -EEXIST;
  18
  19        hfsplus_attr_tree_cachep =
  20                kmem_cache_create("hfsplus_attr_cache",
  21                        sizeof(hfsplus_attr_entry), 0,
  22                        SLAB_HWCACHE_ALIGN, NULL);
  23        if (!hfsplus_attr_tree_cachep)
  24                return -ENOMEM;
  25
  26        return 0;
  27}
  28
  29void hfsplus_destroy_attr_tree_cache(void)
  30{
  31        kmem_cache_destroy(hfsplus_attr_tree_cachep);
  32}
  33
  34int hfsplus_attr_bin_cmp_key(const hfsplus_btree_key *k1,
  35                                const hfsplus_btree_key *k2)
  36{
  37        __be32 k1_cnid, k2_cnid;
  38
  39        k1_cnid = k1->attr.cnid;
  40        k2_cnid = k2->attr.cnid;
  41        if (k1_cnid != k2_cnid)
  42                return be32_to_cpu(k1_cnid) < be32_to_cpu(k2_cnid) ? -1 : 1;
  43
  44        return hfsplus_strcmp(
  45                        (const struct hfsplus_unistr *)&k1->attr.key_name,
  46                        (const struct hfsplus_unistr *)&k2->attr.key_name);
  47}
  48
  49int hfsplus_attr_build_key(struct super_block *sb, hfsplus_btree_key *key,
  50                        u32 cnid, const char *name)
  51{
  52        int len;
  53
  54        memset(key, 0, sizeof(struct hfsplus_attr_key));
  55        key->attr.cnid = cpu_to_be32(cnid);
  56        if (name) {
  57                len = strlen(name);
  58                if (len > HFSPLUS_ATTR_MAX_STRLEN) {
  59                        pr_err("invalid xattr name's length\n");
  60                        return -EINVAL;
  61                }
  62                hfsplus_asc2uni(sb,
  63                                (struct hfsplus_unistr *)&key->attr.key_name,
  64                                HFSPLUS_ATTR_MAX_STRLEN, name, len);
  65                len = be16_to_cpu(key->attr.key_name.length);
  66        } else {
  67                key->attr.key_name.length = 0;
  68                len = 0;
  69        }
  70
  71        /* The length of the key, as stored in key_len field, does not include
  72         * the size of the key_len field itself.
  73         * So, offsetof(hfsplus_attr_key, key_name) is a trick because
  74         * it takes into consideration key_len field (__be16) of
  75         * hfsplus_attr_key structure instead of length field (__be16) of
  76         * hfsplus_attr_unistr structure.
  77         */
  78        key->key_len =
  79                cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
  80                                2 * len);
  81
  82        return 0;
  83}
  84
  85void hfsplus_attr_build_key_uni(hfsplus_btree_key *key,
  86                                        u32 cnid,
  87                                        struct hfsplus_attr_unistr *name)
  88{
  89        int ustrlen;
  90
  91        memset(key, 0, sizeof(struct hfsplus_attr_key));
  92        ustrlen = be16_to_cpu(name->length);
  93        key->attr.cnid = cpu_to_be32(cnid);
  94        key->attr.key_name.length = cpu_to_be16(ustrlen);
  95        ustrlen *= 2;
  96        memcpy(key->attr.key_name.unicode, name->unicode, ustrlen);
  97
  98        /* The length of the key, as stored in key_len field, does not include
  99         * the size of the key_len field itself.
 100         * So, offsetof(hfsplus_attr_key, key_name) is a trick because
 101         * it takes into consideration key_len field (__be16) of
 102         * hfsplus_attr_key structure instead of length field (__be16) of
 103         * hfsplus_attr_unistr structure.
 104         */
 105        key->key_len =
 106                cpu_to_be16(offsetof(struct hfsplus_attr_key, key_name) +
 107                                ustrlen);
 108}
 109
 110hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)
 111{
 112        return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL);
 113}
 114
 115void hfsplus_destroy_attr_entry(hfsplus_attr_entry *entry)
 116{
 117        if (entry)
 118                kmem_cache_free(hfsplus_attr_tree_cachep, entry);
 119}
 120
 121#define HFSPLUS_INVALID_ATTR_RECORD -1
 122
 123static int hfsplus_attr_build_record(hfsplus_attr_entry *entry, int record_type,
 124                                u32 cnid, const void *value, size_t size)
 125{
 126        if (record_type == HFSPLUS_ATTR_FORK_DATA) {
 127                /*
 128                 * Mac OS X supports only inline data attributes.
 129                 * Do nothing
 130                 */
 131                memset(entry, 0, sizeof(*entry));
 132                return sizeof(struct hfsplus_attr_fork_data);
 133        } else if (record_type == HFSPLUS_ATTR_EXTENTS) {
 134                /*
 135                 * Mac OS X supports only inline data attributes.
 136                 * Do nothing.
 137                 */
 138                memset(entry, 0, sizeof(*entry));
 139                return sizeof(struct hfsplus_attr_extents);
 140        } else if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
 141                u16 len;
 142
 143                memset(entry, 0, sizeof(struct hfsplus_attr_inline_data));
 144                entry->inline_data.record_type = cpu_to_be32(record_type);
 145                if (size <= HFSPLUS_MAX_INLINE_DATA_SIZE)
 146                        len = size;
 147                else
 148                        return HFSPLUS_INVALID_ATTR_RECORD;
 149                entry->inline_data.length = cpu_to_be16(len);
 150                memcpy(entry->inline_data.raw_bytes, value, len);
 151                /*
 152                 * Align len on two-byte boundary.
 153                 * It needs to add pad byte if we have odd len.
 154                 */
 155                len = round_up(len, 2);
 156                return offsetof(struct hfsplus_attr_inline_data, raw_bytes) +
 157                                        len;
 158        } else /* invalid input */
 159                memset(entry, 0, sizeof(*entry));
 160
 161        return HFSPLUS_INVALID_ATTR_RECORD;
 162}
 163
 164int hfsplus_find_attr(struct super_block *sb, u32 cnid,
 165                        const char *name, struct hfs_find_data *fd)
 166{
 167        int err = 0;
 168
 169        hfs_dbg(ATTR_MOD, "find_attr: %s,%d\n", name ? name : NULL, cnid);
 170
 171        if (!HFSPLUS_SB(sb)->attr_tree) {
 172                pr_err("attributes file doesn't exist\n");
 173                return -EINVAL;
 174        }
 175
 176        if (name) {
 177                err = hfsplus_attr_build_key(sb, fd->search_key, cnid, name);
 178                if (err)
 179                        goto failed_find_attr;
 180                err = hfs_brec_find(fd, hfs_find_rec_by_key);
 181                if (err)
 182                        goto failed_find_attr;
 183        } else {
 184                err = hfsplus_attr_build_key(sb, fd->search_key, cnid, NULL);
 185                if (err)
 186                        goto failed_find_attr;
 187                err = hfs_brec_find(fd, hfs_find_1st_rec_by_cnid);
 188                if (err)
 189                        goto failed_find_attr;
 190        }
 191
 192failed_find_attr:
 193        return err;
 194}
 195
 196int hfsplus_attr_exists(struct inode *inode, const char *name)
 197{
 198        int err = 0;
 199        struct super_block *sb = inode->i_sb;
 200        struct hfs_find_data fd;
 201
 202        if (!HFSPLUS_SB(sb)->attr_tree)
 203                return 0;
 204
 205        err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
 206        if (err)
 207                return 0;
 208
 209        err = hfsplus_find_attr(sb, inode->i_ino, name, &fd);
 210        if (err)
 211                goto attr_not_found;
 212
 213        hfs_find_exit(&fd);
 214        return 1;
 215
 216attr_not_found:
 217        hfs_find_exit(&fd);
 218        return 0;
 219}
 220
 221int hfsplus_create_attr(struct inode *inode,
 222                                const char *name,
 223                                const void *value, size_t size)
 224{
 225        struct super_block *sb = inode->i_sb;
 226        struct hfs_find_data fd;
 227        hfsplus_attr_entry *entry_ptr;
 228        int entry_size;
 229        int err;
 230
 231        hfs_dbg(ATTR_MOD, "create_attr: %s,%ld\n",
 232                name ? name : NULL, inode->i_ino);
 233
 234        if (!HFSPLUS_SB(sb)->attr_tree) {
 235                pr_err("attributes file doesn't exist\n");
 236                return -EINVAL;
 237        }
 238
 239        entry_ptr = hfsplus_alloc_attr_entry();
 240        if (!entry_ptr)
 241                return -ENOMEM;
 242
 243        err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
 244        if (err)
 245                goto failed_init_create_attr;
 246
 247        if (name) {
 248                err = hfsplus_attr_build_key(sb, fd.search_key,
 249                                                inode->i_ino, name);
 250                if (err)
 251                        goto failed_create_attr;
 252        } else {
 253                err = -EINVAL;
 254                goto failed_create_attr;
 255        }
 256
 257        /* Mac OS X supports only inline data attributes. */
 258        entry_size = hfsplus_attr_build_record(entry_ptr,
 259                                        HFSPLUS_ATTR_INLINE_DATA,
 260                                        inode->i_ino,
 261                                        value, size);
 262        if (entry_size == HFSPLUS_INVALID_ATTR_RECORD) {
 263                err = -EINVAL;
 264                goto failed_create_attr;
 265        }
 266
 267        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 268        if (err != -ENOENT) {
 269                if (!err)
 270                        err = -EEXIST;
 271                goto failed_create_attr;
 272        }
 273
 274        err = hfs_brec_insert(&fd, entry_ptr, entry_size);
 275        if (err)
 276                goto failed_create_attr;
 277
 278        hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
 279
 280failed_create_attr:
 281        hfs_find_exit(&fd);
 282
 283failed_init_create_attr:
 284        hfsplus_destroy_attr_entry(entry_ptr);
 285        return err;
 286}
 287
 288static int __hfsplus_delete_attr(struct inode *inode, u32 cnid,
 289                                        struct hfs_find_data *fd)
 290{
 291        int err = 0;
 292        __be32 found_cnid, record_type;
 293
 294        hfs_bnode_read(fd->bnode, &found_cnid,
 295                        fd->keyoffset +
 296                        offsetof(struct hfsplus_attr_key, cnid),
 297                        sizeof(__be32));
 298        if (cnid != be32_to_cpu(found_cnid))
 299                return -ENOENT;
 300
 301        hfs_bnode_read(fd->bnode, &record_type,
 302                        fd->entryoffset, sizeof(record_type));
 303
 304        switch (be32_to_cpu(record_type)) {
 305        case HFSPLUS_ATTR_INLINE_DATA:
 306                /* All is OK. Do nothing. */
 307                break;
 308        case HFSPLUS_ATTR_FORK_DATA:
 309        case HFSPLUS_ATTR_EXTENTS:
 310                pr_err("only inline data xattr are supported\n");
 311                return -EOPNOTSUPP;
 312        default:
 313                pr_err("invalid extended attribute record\n");
 314                return -ENOENT;
 315        }
 316
 317        err = hfs_brec_remove(fd);
 318        if (err)
 319                return err;
 320
 321        hfsplus_mark_inode_dirty(inode, HFSPLUS_I_ATTR_DIRTY);
 322        return err;
 323}
 324
 325int hfsplus_delete_attr(struct inode *inode, const char *name)
 326{
 327        int err = 0;
 328        struct super_block *sb = inode->i_sb;
 329        struct hfs_find_data fd;
 330
 331        hfs_dbg(ATTR_MOD, "delete_attr: %s,%ld\n",
 332                name ? name : NULL, inode->i_ino);
 333
 334        if (!HFSPLUS_SB(sb)->attr_tree) {
 335                pr_err("attributes file doesn't exist\n");
 336                return -EINVAL;
 337        }
 338
 339        err = hfs_find_init(HFSPLUS_SB(sb)->attr_tree, &fd);
 340        if (err)
 341                return err;
 342
 343        if (name) {
 344                err = hfsplus_attr_build_key(sb, fd.search_key,
 345                                                inode->i_ino, name);
 346                if (err)
 347                        goto out;
 348        } else {
 349                pr_err("invalid extended attribute name\n");
 350                err = -EINVAL;
 351                goto out;
 352        }
 353
 354        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 355        if (err)
 356                goto out;
 357
 358        err = __hfsplus_delete_attr(inode, inode->i_ino, &fd);
 359        if (err)
 360                goto out;
 361
 362out:
 363        hfs_find_exit(&fd);
 364        return err;
 365}
 366
 367int hfsplus_delete_all_attrs(struct inode *dir, u32 cnid)
 368{
 369        int err = 0;
 370        struct hfs_find_data fd;
 371
 372        hfs_dbg(ATTR_MOD, "delete_all_attrs: %d\n", cnid);
 373
 374        if (!HFSPLUS_SB(dir->i_sb)->attr_tree) {
 375                pr_err("attributes file doesn't exist\n");
 376                return -EINVAL;
 377        }
 378
 379        err = hfs_find_init(HFSPLUS_SB(dir->i_sb)->attr_tree, &fd);
 380        if (err)
 381                return err;
 382
 383        for (;;) {
 384                err = hfsplus_find_attr(dir->i_sb, cnid, NULL, &fd);
 385                if (err) {
 386                        if (err != -ENOENT)
 387                                pr_err("xattr search failed\n");
 388                        goto end_delete_all;
 389                }
 390
 391                err = __hfsplus_delete_attr(dir, cnid, &fd);
 392                if (err)
 393                        goto end_delete_all;
 394        }
 395
 396end_delete_all:
 397        hfs_find_exit(&fd);
 398        return err;
 399}
 400