cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

attributes.c (8860B)


      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
     13static struct kmem_cache *hfsplus_attr_tree_cachep;
     14
     15int __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
     30void hfsplus_destroy_attr_tree_cache(void)
     31{
     32	kmem_cache_destroy(hfsplus_attr_tree_cachep);
     33}
     34
     35int 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
     50int 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
     83hfsplus_attr_entry *hfsplus_alloc_attr_entry(void)
     84{
     85	return kmem_cache_alloc(hfsplus_attr_tree_cachep, GFP_KERNEL);
     86}
     87
     88void 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
     96static 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
    137int 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
    165failed_find_attr:
    166	return err;
    167}
    168
    169int 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
    189attr_not_found:
    190	hfs_find_exit(&fd);
    191	return 0;
    192}
    193
    194int 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
    258failed_create_attr:
    259	hfs_find_exit(&fd);
    260
    261failed_init_create_attr:
    262	hfsplus_destroy_attr_entry(entry_ptr);
    263	return err;
    264}
    265
    266static 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
    307int 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
    349out:
    350	hfs_find_exit(&fd);
    351	return err;
    352}
    353
    354int 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
    383end_delete_all:
    384	hfs_find_exit(&fd);
    385	return err;
    386}