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