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

attrlist.c (9481B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *
      4 * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
      5 *
      6 */
      7
      8#include <linux/fs.h>
      9
     10#include "debug.h"
     11#include "ntfs.h"
     12#include "ntfs_fs.h"
     13
     14/*
     15 * al_is_valid_le
     16 *
     17 * Return: True if @le is valid.
     18 */
     19static inline bool al_is_valid_le(const struct ntfs_inode *ni,
     20				  struct ATTR_LIST_ENTRY *le)
     21{
     22	if (!le || !ni->attr_list.le || !ni->attr_list.size)
     23		return false;
     24
     25	return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <=
     26	       ni->attr_list.size;
     27}
     28
     29void al_destroy(struct ntfs_inode *ni)
     30{
     31	run_close(&ni->attr_list.run);
     32	kfree(ni->attr_list.le);
     33	ni->attr_list.le = NULL;
     34	ni->attr_list.size = 0;
     35	ni->attr_list.dirty = false;
     36}
     37
     38/*
     39 * ntfs_load_attr_list
     40 *
     41 * This method makes sure that the ATTRIB list, if present,
     42 * has been properly set up.
     43 */
     44int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
     45{
     46	int err;
     47	size_t lsize;
     48	void *le = NULL;
     49
     50	if (ni->attr_list.size)
     51		return 0;
     52
     53	if (!attr->non_res) {
     54		lsize = le32_to_cpu(attr->res.data_size);
     55		le = kmalloc(al_aligned(lsize), GFP_NOFS);
     56		if (!le) {
     57			err = -ENOMEM;
     58			goto out;
     59		}
     60		memcpy(le, resident_data(attr), lsize);
     61	} else if (attr->nres.svcn) {
     62		err = -EINVAL;
     63		goto out;
     64	} else {
     65		u16 run_off = le16_to_cpu(attr->nres.run_off);
     66
     67		lsize = le64_to_cpu(attr->nres.data_size);
     68
     69		run_init(&ni->attr_list.run);
     70
     71		err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno,
     72				    0, le64_to_cpu(attr->nres.evcn), 0,
     73				    Add2Ptr(attr, run_off),
     74				    le32_to_cpu(attr->size) - run_off);
     75		if (err < 0)
     76			goto out;
     77
     78		le = kmalloc(al_aligned(lsize), GFP_NOFS);
     79		if (!le) {
     80			err = -ENOMEM;
     81			goto out;
     82		}
     83
     84		err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le,
     85				       lsize, NULL);
     86		if (err)
     87			goto out;
     88	}
     89
     90	ni->attr_list.size = lsize;
     91	ni->attr_list.le = le;
     92
     93	return 0;
     94
     95out:
     96	ni->attr_list.le = le;
     97	al_destroy(ni);
     98
     99	return err;
    100}
    101
    102/*
    103 * al_enumerate
    104 *
    105 * Return:
    106 * * The next list le.
    107 * * If @le is NULL then return the first le.
    108 */
    109struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
    110				     struct ATTR_LIST_ENTRY *le)
    111{
    112	size_t off;
    113	u16 sz;
    114
    115	if (!le) {
    116		le = ni->attr_list.le;
    117	} else {
    118		sz = le16_to_cpu(le->size);
    119		if (sz < sizeof(struct ATTR_LIST_ENTRY)) {
    120			/* Impossible 'cause we should not return such le. */
    121			return NULL;
    122		}
    123		le = Add2Ptr(le, sz);
    124	}
    125
    126	/* Check boundary. */
    127	off = PtrOffset(ni->attr_list.le, le);
    128	if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
    129		/* The regular end of list. */
    130		return NULL;
    131	}
    132
    133	sz = le16_to_cpu(le->size);
    134
    135	/* Check le for errors. */
    136	if (sz < sizeof(struct ATTR_LIST_ENTRY) ||
    137	    off + sz > ni->attr_list.size ||
    138	    sz < le->name_off + le->name_len * sizeof(short)) {
    139		return NULL;
    140	}
    141
    142	return le;
    143}
    144
    145/*
    146 * al_find_le
    147 *
    148 * Find the first le in the list which matches type, name and VCN.
    149 *
    150 * Return: NULL if not found.
    151 */
    152struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni,
    153				   struct ATTR_LIST_ENTRY *le,
    154				   const struct ATTRIB *attr)
    155{
    156	CLST svcn = attr_svcn(attr);
    157
    158	return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len,
    159			  &svcn);
    160}
    161
    162/*
    163 * al_find_ex
    164 *
    165 * Find the first le in the list which matches type, name and VCN.
    166 *
    167 * Return: NULL if not found.
    168 */
    169struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni,
    170				   struct ATTR_LIST_ENTRY *le,
    171				   enum ATTR_TYPE type, const __le16 *name,
    172				   u8 name_len, const CLST *vcn)
    173{
    174	struct ATTR_LIST_ENTRY *ret = NULL;
    175	u32 type_in = le32_to_cpu(type);
    176
    177	while ((le = al_enumerate(ni, le))) {
    178		u64 le_vcn;
    179		int diff = le32_to_cpu(le->type) - type_in;
    180
    181		/* List entries are sorted by type, name and VCN. */
    182		if (diff < 0)
    183			continue;
    184
    185		if (diff > 0)
    186			return ret;
    187
    188		if (le->name_len != name_len)
    189			continue;
    190
    191		le_vcn = le64_to_cpu(le->vcn);
    192		if (!le_vcn) {
    193			/*
    194			 * Compare entry names only for entry with vcn == 0.
    195			 */
    196			diff = ntfs_cmp_names(le_name(le), name_len, name,
    197					      name_len, ni->mi.sbi->upcase,
    198					      true);
    199			if (diff < 0)
    200				continue;
    201
    202			if (diff > 0)
    203				return ret;
    204		}
    205
    206		if (!vcn)
    207			return le;
    208
    209		if (*vcn == le_vcn)
    210			return le;
    211
    212		if (*vcn < le_vcn)
    213			return ret;
    214
    215		ret = le;
    216	}
    217
    218	return ret;
    219}
    220
    221/*
    222 * al_find_le_to_insert
    223 *
    224 * Find the first list entry which matches type, name and VCN.
    225 */
    226static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni,
    227						    enum ATTR_TYPE type,
    228						    const __le16 *name,
    229						    u8 name_len, CLST vcn)
    230{
    231	struct ATTR_LIST_ENTRY *le = NULL, *prev;
    232	u32 type_in = le32_to_cpu(type);
    233
    234	/* List entries are sorted by type, name and VCN. */
    235	while ((le = al_enumerate(ni, prev = le))) {
    236		int diff = le32_to_cpu(le->type) - type_in;
    237
    238		if (diff < 0)
    239			continue;
    240
    241		if (diff > 0)
    242			return le;
    243
    244		if (!le->vcn) {
    245			/*
    246			 * Compare entry names only for entry with vcn == 0.
    247			 */
    248			diff = ntfs_cmp_names(le_name(le), le->name_len, name,
    249					      name_len, ni->mi.sbi->upcase,
    250					      true);
    251			if (diff < 0)
    252				continue;
    253
    254			if (diff > 0)
    255				return le;
    256		}
    257
    258		if (le64_to_cpu(le->vcn) >= vcn)
    259			return le;
    260	}
    261
    262	return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le;
    263}
    264
    265/*
    266 * al_add_le
    267 *
    268 * Add an "attribute list entry" to the list.
    269 */
    270int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
    271	      u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref,
    272	      struct ATTR_LIST_ENTRY **new_le)
    273{
    274	int err;
    275	struct ATTRIB *attr;
    276	struct ATTR_LIST_ENTRY *le;
    277	size_t off;
    278	u16 sz;
    279	size_t asize, new_asize, old_size;
    280	u64 new_size;
    281	typeof(ni->attr_list) *al = &ni->attr_list;
    282
    283	/*
    284	 * Compute the size of the new 'le'
    285	 */
    286	sz = le_size(name_len);
    287	old_size = al->size;
    288	new_size = old_size + sz;
    289	asize = al_aligned(old_size);
    290	new_asize = al_aligned(new_size);
    291
    292	/* Scan forward to the point at which the new 'le' should be inserted. */
    293	le = al_find_le_to_insert(ni, type, name, name_len, svcn);
    294	off = PtrOffset(al->le, le);
    295
    296	if (new_size > asize) {
    297		void *ptr = kmalloc(new_asize, GFP_NOFS);
    298
    299		if (!ptr)
    300			return -ENOMEM;
    301
    302		memcpy(ptr, al->le, off);
    303		memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
    304		le = Add2Ptr(ptr, off);
    305		kfree(al->le);
    306		al->le = ptr;
    307	} else {
    308		memmove(Add2Ptr(le, sz), le, old_size - off);
    309	}
    310	*new_le = le;
    311
    312	al->size = new_size;
    313
    314	le->type = type;
    315	le->size = cpu_to_le16(sz);
    316	le->name_len = name_len;
    317	le->name_off = offsetof(struct ATTR_LIST_ENTRY, name);
    318	le->vcn = cpu_to_le64(svcn);
    319	le->ref = *ref;
    320	le->id = id;
    321	memcpy(le->name, name, sizeof(short) * name_len);
    322
    323	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
    324			    &new_size, true, &attr);
    325	if (err) {
    326		/* Undo memmove above. */
    327		memmove(le, Add2Ptr(le, sz), old_size - off);
    328		al->size = old_size;
    329		return err;
    330	}
    331
    332	al->dirty = true;
    333
    334	if (attr && attr->non_res) {
    335		err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
    336					al->size, 0);
    337		if (err)
    338			return err;
    339		al->dirty = false;
    340	}
    341
    342	return 0;
    343}
    344
    345/*
    346 * al_remove_le - Remove @le from attribute list.
    347 */
    348bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
    349{
    350	u16 size;
    351	size_t off;
    352	typeof(ni->attr_list) *al = &ni->attr_list;
    353
    354	if (!al_is_valid_le(ni, le))
    355		return false;
    356
    357	/* Save on stack the size of 'le' */
    358	size = le16_to_cpu(le->size);
    359	off = PtrOffset(al->le, le);
    360
    361	memmove(le, Add2Ptr(le, size), al->size - (off + size));
    362
    363	al->size -= size;
    364	al->dirty = true;
    365
    366	return true;
    367}
    368
    369/*
    370 * al_delete_le - Delete first le from the list which matches its parameters.
    371 */
    372bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
    373		  const __le16 *name, size_t name_len,
    374		  const struct MFT_REF *ref)
    375{
    376	u16 size;
    377	struct ATTR_LIST_ENTRY *le;
    378	size_t off;
    379	typeof(ni->attr_list) *al = &ni->attr_list;
    380
    381	/* Scan forward to the first le that matches the input. */
    382	le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
    383	if (!le)
    384		return false;
    385
    386	off = PtrOffset(al->le, le);
    387
    388next:
    389	if (off >= al->size)
    390		return false;
    391	if (le->type != type)
    392		return false;
    393	if (le->name_len != name_len)
    394		return false;
    395	if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
    396				       ni->mi.sbi->upcase, true))
    397		return false;
    398	if (le64_to_cpu(le->vcn) != vcn)
    399		return false;
    400
    401	/*
    402	 * The caller specified a segment reference, so we have to
    403	 * scan through the matching entries until we find that segment
    404	 * reference or we run of matching entries.
    405	 */
    406	if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
    407		off += le16_to_cpu(le->size);
    408		le = Add2Ptr(al->le, off);
    409		goto next;
    410	}
    411
    412	/* Save on stack the size of 'le'. */
    413	size = le16_to_cpu(le->size);
    414	/* Delete the le. */
    415	memmove(le, Add2Ptr(le, size), al->size - (off + size));
    416
    417	al->size -= size;
    418	al->dirty = true;
    419
    420	return true;
    421}
    422
    423int al_update(struct ntfs_inode *ni, int sync)
    424{
    425	int err;
    426	struct ATTRIB *attr;
    427	typeof(ni->attr_list) *al = &ni->attr_list;
    428
    429	if (!al->dirty || !al->size)
    430		return 0;
    431
    432	/*
    433	 * Attribute list increased on demand in al_add_le.
    434	 * Attribute list decreased here.
    435	 */
    436	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
    437			    false, &attr);
    438	if (err)
    439		goto out;
    440
    441	if (!attr->non_res) {
    442		memcpy(resident_data(attr), al->le, al->size);
    443	} else {
    444		err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
    445					al->size, sync);
    446		if (err)
    447			goto out;
    448
    449		attr->nres.valid_size = attr->nres.data_size;
    450	}
    451
    452	ni->mi.dirty = true;
    453	al->dirty = false;
    454
    455out:
    456	return err;
    457}