root/fs/hfsplus/attributes.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hfsplus_create_attr_tree_cache
  2. hfsplus_destroy_attr_tree_cache
  3. hfsplus_attr_bin_cmp_key
  4. hfsplus_attr_build_key
  5. hfsplus_alloc_attr_entry
  6. hfsplus_destroy_attr_entry
  7. hfsplus_attr_build_record
  8. hfsplus_find_attr
  9. hfsplus_attr_exists
  10. hfsplus_create_attr
  11. __hfsplus_delete_attr
  12. hfsplus_delete_attr
  13. hfsplus_delete_all_attrs

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

/* [<][>][^][v][top][bottom][index][help] */