162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "debug.h"
1162306a36Sopenharmony_ci#include "ntfs.h"
1262306a36Sopenharmony_ci#include "ntfs_fs.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type,
1562306a36Sopenharmony_ci			       const __le16 *name, u8 name_len,
1662306a36Sopenharmony_ci			       const u16 *upcase)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	/* First, compare the type codes. */
1962306a36Sopenharmony_ci	int diff = le32_to_cpu(left->type) - le32_to_cpu(type);
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	if (diff)
2262306a36Sopenharmony_ci		return diff;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/* They have the same type code, so we have to compare the names. */
2562306a36Sopenharmony_ci	return ntfs_cmp_names(attr_name(left), left->name_len, name, name_len,
2662306a36Sopenharmony_ci			      upcase, true);
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * mi_new_attt_id
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Return: Unused attribute id that is less than mrec->next_attr_id.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistatic __le16 mi_new_attt_id(struct mft_inode *mi)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	u16 free_id, max_id, t16;
3762306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
3862306a36Sopenharmony_ci	struct ATTRIB *attr;
3962306a36Sopenharmony_ci	__le16 id;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	id = rec->next_attr_id;
4262306a36Sopenharmony_ci	free_id = le16_to_cpu(id);
4362306a36Sopenharmony_ci	if (free_id < 0x7FFF) {
4462306a36Sopenharmony_ci		rec->next_attr_id = cpu_to_le16(free_id + 1);
4562306a36Sopenharmony_ci		return id;
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* One record can store up to 1024/24 ~= 42 attributes. */
4962306a36Sopenharmony_ci	free_id = 0;
5062306a36Sopenharmony_ci	max_id = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	attr = NULL;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (;;) {
5562306a36Sopenharmony_ci		attr = mi_enum_attr(mi, attr);
5662306a36Sopenharmony_ci		if (!attr) {
5762306a36Sopenharmony_ci			rec->next_attr_id = cpu_to_le16(max_id + 1);
5862306a36Sopenharmony_ci			mi->dirty = true;
5962306a36Sopenharmony_ci			return cpu_to_le16(free_id);
6062306a36Sopenharmony_ci		}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci		t16 = le16_to_cpu(attr->id);
6362306a36Sopenharmony_ci		if (t16 == free_id) {
6462306a36Sopenharmony_ci			free_id += 1;
6562306a36Sopenharmony_ci			attr = NULL;
6662306a36Sopenharmony_ci		} else if (max_id < t16)
6762306a36Sopenharmony_ci			max_id = t16;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciint mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	int err;
7462306a36Sopenharmony_ci	struct mft_inode *m = kzalloc(sizeof(struct mft_inode), GFP_NOFS);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!m)
7762306a36Sopenharmony_ci		return -ENOMEM;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	err = mi_init(m, sbi, rno);
8062306a36Sopenharmony_ci	if (err) {
8162306a36Sopenharmony_ci		kfree(m);
8262306a36Sopenharmony_ci		return err;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	err = mi_read(m, false);
8662306a36Sopenharmony_ci	if (err) {
8762306a36Sopenharmony_ci		mi_put(m);
8862306a36Sopenharmony_ci		return err;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	*mi = m;
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_civoid mi_put(struct mft_inode *mi)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	mi_clear(mi);
9862306a36Sopenharmony_ci	kfree(mi);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciint mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	mi->sbi = sbi;
10462306a36Sopenharmony_ci	mi->rno = rno;
10562306a36Sopenharmony_ci	mi->mrec = kmalloc(sbi->record_size, GFP_NOFS);
10662306a36Sopenharmony_ci	if (!mi->mrec)
10762306a36Sopenharmony_ci		return -ENOMEM;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/*
11362306a36Sopenharmony_ci * mi_read - Read MFT data.
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_ciint mi_read(struct mft_inode *mi, bool is_mft)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int err;
11862306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
11962306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = mi->sbi;
12062306a36Sopenharmony_ci	u32 bpr = sbi->record_size;
12162306a36Sopenharmony_ci	u64 vbo = (u64)mi->rno << sbi->record_bits;
12262306a36Sopenharmony_ci	struct ntfs_inode *mft_ni = sbi->mft.ni;
12362306a36Sopenharmony_ci	struct runs_tree *run = mft_ni ? &mft_ni->file.run : NULL;
12462306a36Sopenharmony_ci	struct rw_semaphore *rw_lock = NULL;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (is_mounted(sbi)) {
12762306a36Sopenharmony_ci		if (!is_mft && mft_ni) {
12862306a36Sopenharmony_ci			rw_lock = &mft_ni->file.run_lock;
12962306a36Sopenharmony_ci			down_read(rw_lock);
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
13462306a36Sopenharmony_ci	if (rw_lock)
13562306a36Sopenharmony_ci		up_read(rw_lock);
13662306a36Sopenharmony_ci	if (!err)
13762306a36Sopenharmony_ci		goto ok;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (err == -E_NTFS_FIXUP) {
14062306a36Sopenharmony_ci		mi->dirty = true;
14162306a36Sopenharmony_ci		goto ok;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (err != -ENOENT)
14562306a36Sopenharmony_ci		goto out;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (rw_lock) {
14862306a36Sopenharmony_ci		ni_lock(mft_ni);
14962306a36Sopenharmony_ci		down_write(rw_lock);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci	err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, run,
15262306a36Sopenharmony_ci				 vbo >> sbi->cluster_bits);
15362306a36Sopenharmony_ci	if (rw_lock) {
15462306a36Sopenharmony_ci		up_write(rw_lock);
15562306a36Sopenharmony_ci		ni_unlock(mft_ni);
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci	if (err)
15862306a36Sopenharmony_ci		goto out;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (rw_lock)
16162306a36Sopenharmony_ci		down_read(rw_lock);
16262306a36Sopenharmony_ci	err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
16362306a36Sopenharmony_ci	if (rw_lock)
16462306a36Sopenharmony_ci		up_read(rw_lock);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (err == -E_NTFS_FIXUP) {
16762306a36Sopenharmony_ci		mi->dirty = true;
16862306a36Sopenharmony_ci		goto ok;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci	if (err)
17162306a36Sopenharmony_ci		goto out;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ciok:
17462306a36Sopenharmony_ci	/* Check field 'total' only here. */
17562306a36Sopenharmony_ci	if (le32_to_cpu(rec->total) != bpr) {
17662306a36Sopenharmony_ci		err = -EINVAL;
17762306a36Sopenharmony_ci		goto out;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return 0;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciout:
18362306a36Sopenharmony_ci	if (err == -E_NTFS_CORRUPT) {
18462306a36Sopenharmony_ci		ntfs_err(sbi->sb, "mft corrupted");
18562306a36Sopenharmony_ci		ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
18662306a36Sopenharmony_ci		err = -EINVAL;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return err;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * mi_enum_attr - start/continue attributes enumeration in record.
19462306a36Sopenharmony_ci *
19562306a36Sopenharmony_ci * NOTE: mi->mrec - memory of size sbi->record_size
19662306a36Sopenharmony_ci * here we sure that mi->mrec->total == sbi->record_size (see mi_read)
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_cistruct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	const struct MFT_REC *rec = mi->mrec;
20162306a36Sopenharmony_ci	u32 used = le32_to_cpu(rec->used);
20262306a36Sopenharmony_ci	u32 t32, off, asize, prev_type;
20362306a36Sopenharmony_ci	u16 t16;
20462306a36Sopenharmony_ci	u64 data_size, alloc_size, tot_size;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (!attr) {
20762306a36Sopenharmony_ci		u32 total = le32_to_cpu(rec->total);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		off = le16_to_cpu(rec->attr_off);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		if (used > total)
21262306a36Sopenharmony_ci			return NULL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci		if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 ||
21562306a36Sopenharmony_ci		    !IS_ALIGNED(off, 4)) {
21662306a36Sopenharmony_ci			return NULL;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		/* Skip non-resident records. */
22062306a36Sopenharmony_ci		if (!is_rec_inuse(rec))
22162306a36Sopenharmony_ci			return NULL;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		prev_type = 0;
22462306a36Sopenharmony_ci		attr = Add2Ptr(rec, off);
22562306a36Sopenharmony_ci	} else {
22662306a36Sopenharmony_ci		/* Check if input attr inside record. */
22762306a36Sopenharmony_ci		off = PtrOffset(rec, attr);
22862306a36Sopenharmony_ci		if (off >= used)
22962306a36Sopenharmony_ci			return NULL;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		asize = le32_to_cpu(attr->size);
23262306a36Sopenharmony_ci		if (asize < SIZEOF_RESIDENT) {
23362306a36Sopenharmony_ci			/* Impossible 'cause we should not return such attribute. */
23462306a36Sopenharmony_ci			return NULL;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		/* Overflow check. */
23862306a36Sopenharmony_ci		if (off + asize < off)
23962306a36Sopenharmony_ci			return NULL;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci		prev_type = le32_to_cpu(attr->type);
24262306a36Sopenharmony_ci		attr = Add2Ptr(attr, asize);
24362306a36Sopenharmony_ci		off += asize;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	asize = le32_to_cpu(attr->size);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Can we use the first field (attr->type). */
24962306a36Sopenharmony_ci	if (off + 8 > used) {
25062306a36Sopenharmony_ci		static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8);
25162306a36Sopenharmony_ci		return NULL;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (attr->type == ATTR_END) {
25562306a36Sopenharmony_ci		/* End of enumeration. */
25662306a36Sopenharmony_ci		return NULL;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* 0x100 is last known attribute for now. */
26062306a36Sopenharmony_ci	t32 = le32_to_cpu(attr->type);
26162306a36Sopenharmony_ci	if (!t32 || (t32 & 0xf) || (t32 > 0x100))
26262306a36Sopenharmony_ci		return NULL;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* attributes in record must be ordered by type */
26562306a36Sopenharmony_ci	if (t32 < prev_type)
26662306a36Sopenharmony_ci		return NULL;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Check overflow and boundary. */
26962306a36Sopenharmony_ci	if (off + asize < off || off + asize > used)
27062306a36Sopenharmony_ci		return NULL;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Check size of attribute. */
27362306a36Sopenharmony_ci	if (!attr->non_res) {
27462306a36Sopenharmony_ci		/* Check resident fields. */
27562306a36Sopenharmony_ci		if (asize < SIZEOF_RESIDENT)
27662306a36Sopenharmony_ci			return NULL;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		t16 = le16_to_cpu(attr->res.data_off);
27962306a36Sopenharmony_ci		if (t16 > asize)
28062306a36Sopenharmony_ci			return NULL;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (le32_to_cpu(attr->res.data_size) > asize - t16)
28362306a36Sopenharmony_ci			return NULL;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci		t32 = sizeof(short) * attr->name_len;
28662306a36Sopenharmony_ci		if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
28762306a36Sopenharmony_ci			return NULL;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		return attr;
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Check nonresident fields. */
29362306a36Sopenharmony_ci	if (attr->non_res != 1)
29462306a36Sopenharmony_ci		return NULL;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	t16 = le16_to_cpu(attr->nres.run_off);
29762306a36Sopenharmony_ci	if (t16 > asize)
29862306a36Sopenharmony_ci		return NULL;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	t32 = sizeof(short) * attr->name_len;
30162306a36Sopenharmony_ci	if (t32 && le16_to_cpu(attr->name_off) + t32 > t16)
30262306a36Sopenharmony_ci		return NULL;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* Check start/end vcn. */
30562306a36Sopenharmony_ci	if (le64_to_cpu(attr->nres.svcn) > le64_to_cpu(attr->nres.evcn) + 1)
30662306a36Sopenharmony_ci		return NULL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	data_size = le64_to_cpu(attr->nres.data_size);
30962306a36Sopenharmony_ci	if (le64_to_cpu(attr->nres.valid_size) > data_size)
31062306a36Sopenharmony_ci		return NULL;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	alloc_size = le64_to_cpu(attr->nres.alloc_size);
31362306a36Sopenharmony_ci	if (data_size > alloc_size)
31462306a36Sopenharmony_ci		return NULL;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	t32 = mi->sbi->cluster_mask;
31762306a36Sopenharmony_ci	if (alloc_size & t32)
31862306a36Sopenharmony_ci		return NULL;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!attr->nres.svcn && is_attr_ext(attr)) {
32162306a36Sopenharmony_ci		/* First segment of sparse/compressed attribute */
32262306a36Sopenharmony_ci		if (asize + 8 < SIZEOF_NONRESIDENT_EX)
32362306a36Sopenharmony_ci			return NULL;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		tot_size = le64_to_cpu(attr->nres.total_size);
32662306a36Sopenharmony_ci		if (tot_size & t32)
32762306a36Sopenharmony_ci			return NULL;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		if (tot_size > alloc_size)
33062306a36Sopenharmony_ci			return NULL;
33162306a36Sopenharmony_ci	} else {
33262306a36Sopenharmony_ci		if (asize + 8 < SIZEOF_NONRESIDENT)
33362306a36Sopenharmony_ci			return NULL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		if (attr->nres.c_unit)
33662306a36Sopenharmony_ci			return NULL;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	return attr;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/*
34362306a36Sopenharmony_ci * mi_find_attr - Find the attribute by type and name and id.
34462306a36Sopenharmony_ci */
34562306a36Sopenharmony_cistruct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr,
34662306a36Sopenharmony_ci			    enum ATTR_TYPE type, const __le16 *name,
34762306a36Sopenharmony_ci			    u8 name_len, const __le16 *id)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	u32 type_in = le32_to_cpu(type);
35062306a36Sopenharmony_ci	u32 atype;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cinext_attr:
35362306a36Sopenharmony_ci	attr = mi_enum_attr(mi, attr);
35462306a36Sopenharmony_ci	if (!attr)
35562306a36Sopenharmony_ci		return NULL;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	atype = le32_to_cpu(attr->type);
35862306a36Sopenharmony_ci	if (atype > type_in)
35962306a36Sopenharmony_ci		return NULL;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (atype < type_in)
36262306a36Sopenharmony_ci		goto next_attr;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (attr->name_len != name_len)
36562306a36Sopenharmony_ci		goto next_attr;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	if (name_len && memcmp(attr_name(attr), name, name_len * sizeof(short)))
36862306a36Sopenharmony_ci		goto next_attr;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (id && *id != attr->id)
37162306a36Sopenharmony_ci		goto next_attr;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	return attr;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ciint mi_write(struct mft_inode *mi, int wait)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct MFT_REC *rec;
37962306a36Sopenharmony_ci	int err;
38062306a36Sopenharmony_ci	struct ntfs_sb_info *sbi;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (!mi->dirty)
38362306a36Sopenharmony_ci		return 0;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	sbi = mi->sbi;
38662306a36Sopenharmony_ci	rec = mi->mrec;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	err = ntfs_write_bh(sbi, &rec->rhdr, &mi->nb, wait);
38962306a36Sopenharmony_ci	if (err)
39062306a36Sopenharmony_ci		return err;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	if (mi->rno < sbi->mft.recs_mirr)
39362306a36Sopenharmony_ci		sbi->flags |= NTFS_FLAGS_MFTMIRR;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	mi->dirty = false;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciint mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
40162306a36Sopenharmony_ci		  __le16 flags, bool is_mft)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	int err;
40462306a36Sopenharmony_ci	u16 seq = 1;
40562306a36Sopenharmony_ci	struct MFT_REC *rec;
40662306a36Sopenharmony_ci	u64 vbo = (u64)rno << sbi->record_bits;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	err = mi_init(mi, sbi, rno);
40962306a36Sopenharmony_ci	if (err)
41062306a36Sopenharmony_ci		return err;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	rec = mi->mrec;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (rno == MFT_REC_MFT) {
41562306a36Sopenharmony_ci		;
41662306a36Sopenharmony_ci	} else if (rno < MFT_REC_FREE) {
41762306a36Sopenharmony_ci		seq = rno;
41862306a36Sopenharmony_ci	} else if (rno >= sbi->mft.used) {
41962306a36Sopenharmony_ci		;
42062306a36Sopenharmony_ci	} else if (mi_read(mi, is_mft)) {
42162306a36Sopenharmony_ci		;
42262306a36Sopenharmony_ci	} else if (rec->rhdr.sign == NTFS_FILE_SIGNATURE) {
42362306a36Sopenharmony_ci		/* Record is reused. Update its sequence number. */
42462306a36Sopenharmony_ci		seq = le16_to_cpu(rec->seq) + 1;
42562306a36Sopenharmony_ci		if (!seq)
42662306a36Sopenharmony_ci			seq = 1;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	memcpy(rec, sbi->new_rec, sbi->record_size);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	rec->seq = cpu_to_le16(seq);
43262306a36Sopenharmony_ci	rec->flags = RECORD_FLAG_IN_USE | flags;
43362306a36Sopenharmony_ci	if (MFTRECORD_FIXUP_OFFSET == MFTRECORD_FIXUP_OFFSET_3)
43462306a36Sopenharmony_ci		rec->mft_record = cpu_to_le32(rno);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	mi->dirty = true;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (!mi->nb.nbufs) {
43962306a36Sopenharmony_ci		struct ntfs_inode *ni = sbi->mft.ni;
44062306a36Sopenharmony_ci		bool lock = false;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		if (is_mounted(sbi) && !is_mft) {
44362306a36Sopenharmony_ci			down_read(&ni->file.run_lock);
44462306a36Sopenharmony_ci			lock = true;
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		err = ntfs_get_bh(sbi, &ni->file.run, vbo, sbi->record_size,
44862306a36Sopenharmony_ci				  &mi->nb);
44962306a36Sopenharmony_ci		if (lock)
45062306a36Sopenharmony_ci			up_read(&ni->file.run_lock);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return err;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/*
45762306a36Sopenharmony_ci * mi_insert_attr - Reserve space for new attribute.
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * Return: Not full constructed attribute or NULL if not possible to create.
46062306a36Sopenharmony_ci */
46162306a36Sopenharmony_cistruct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
46262306a36Sopenharmony_ci			      const __le16 *name, u8 name_len, u32 asize,
46362306a36Sopenharmony_ci			      u16 name_off)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	size_t tail;
46662306a36Sopenharmony_ci	struct ATTRIB *attr;
46762306a36Sopenharmony_ci	__le16 id;
46862306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
46962306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = mi->sbi;
47062306a36Sopenharmony_ci	u32 used = le32_to_cpu(rec->used);
47162306a36Sopenharmony_ci	const u16 *upcase = sbi->upcase;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* Can we insert mi attribute? */
47462306a36Sopenharmony_ci	if (used + asize > sbi->record_size)
47562306a36Sopenharmony_ci		return NULL;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	/*
47862306a36Sopenharmony_ci	 * Scan through the list of attributes to find the point
47962306a36Sopenharmony_ci	 * at which we should insert it.
48062306a36Sopenharmony_ci	 */
48162306a36Sopenharmony_ci	attr = NULL;
48262306a36Sopenharmony_ci	while ((attr = mi_enum_attr(mi, attr))) {
48362306a36Sopenharmony_ci		int diff = compare_attr(attr, type, name, name_len, upcase);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		if (diff < 0)
48662306a36Sopenharmony_ci			continue;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		if (!diff && !is_attr_indexed(attr))
48962306a36Sopenharmony_ci			return NULL;
49062306a36Sopenharmony_ci		break;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (!attr) {
49462306a36Sopenharmony_ci		/* Append. */
49562306a36Sopenharmony_ci		tail = 8;
49662306a36Sopenharmony_ci		attr = Add2Ptr(rec, used - 8);
49762306a36Sopenharmony_ci	} else {
49862306a36Sopenharmony_ci		/* Insert before 'attr'. */
49962306a36Sopenharmony_ci		tail = used - PtrOffset(rec, attr);
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	id = mi_new_attt_id(mi);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	memmove(Add2Ptr(attr, asize), attr, tail);
50562306a36Sopenharmony_ci	memset(attr, 0, asize);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	attr->type = type;
50862306a36Sopenharmony_ci	attr->size = cpu_to_le32(asize);
50962306a36Sopenharmony_ci	attr->name_len = name_len;
51062306a36Sopenharmony_ci	attr->name_off = cpu_to_le16(name_off);
51162306a36Sopenharmony_ci	attr->id = id;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	memmove(Add2Ptr(attr, name_off), name, name_len * sizeof(short));
51462306a36Sopenharmony_ci	rec->used = cpu_to_le32(used + asize);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	mi->dirty = true;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return attr;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci/*
52262306a36Sopenharmony_ci * mi_remove_attr - Remove the attribute from record.
52362306a36Sopenharmony_ci *
52462306a36Sopenharmony_ci * NOTE: The source attr will point to next attribute.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_cibool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
52762306a36Sopenharmony_ci		    struct ATTRIB *attr)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
53062306a36Sopenharmony_ci	u32 aoff = PtrOffset(rec, attr);
53162306a36Sopenharmony_ci	u32 used = le32_to_cpu(rec->used);
53262306a36Sopenharmony_ci	u32 asize = le32_to_cpu(attr->size);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	if (aoff + asize > used)
53562306a36Sopenharmony_ci		return false;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (ni && is_attr_indexed(attr)) {
53862306a36Sopenharmony_ci		u16 links = le16_to_cpu(ni->mi.mrec->hard_links);
53962306a36Sopenharmony_ci		struct ATTR_FILE_NAME *fname =
54062306a36Sopenharmony_ci			attr->type != ATTR_NAME ?
54162306a36Sopenharmony_ci				NULL :
54262306a36Sopenharmony_ci				resident_data_ex(attr,
54362306a36Sopenharmony_ci						 SIZEOF_ATTRIBUTE_FILENAME);
54462306a36Sopenharmony_ci		if (fname && fname->type == FILE_NAME_DOS) {
54562306a36Sopenharmony_ci			/* Do not decrease links count deleting DOS name. */
54662306a36Sopenharmony_ci		} else if (!links) {
54762306a36Sopenharmony_ci			/* minor error. Not critical. */
54862306a36Sopenharmony_ci		} else {
54962306a36Sopenharmony_ci			ni->mi.mrec->hard_links = cpu_to_le16(links - 1);
55062306a36Sopenharmony_ci			ni->mi.dirty = true;
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	used -= asize;
55562306a36Sopenharmony_ci	memmove(attr, Add2Ptr(attr, asize), used - aoff);
55662306a36Sopenharmony_ci	rec->used = cpu_to_le32(used);
55762306a36Sopenharmony_ci	mi->dirty = true;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	return true;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci/* bytes = "new attribute size" - "old attribute size" */
56362306a36Sopenharmony_cibool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes)
56462306a36Sopenharmony_ci{
56562306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
56662306a36Sopenharmony_ci	u32 aoff = PtrOffset(rec, attr);
56762306a36Sopenharmony_ci	u32 total, used = le32_to_cpu(rec->used);
56862306a36Sopenharmony_ci	u32 nsize, asize = le32_to_cpu(attr->size);
56962306a36Sopenharmony_ci	u32 rsize = le32_to_cpu(attr->res.data_size);
57062306a36Sopenharmony_ci	int tail = (int)(used - aoff - asize);
57162306a36Sopenharmony_ci	int dsize;
57262306a36Sopenharmony_ci	char *next;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (tail < 0 || aoff >= used)
57562306a36Sopenharmony_ci		return false;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (!bytes)
57862306a36Sopenharmony_ci		return true;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	total = le32_to_cpu(rec->total);
58162306a36Sopenharmony_ci	next = Add2Ptr(attr, asize);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	if (bytes > 0) {
58462306a36Sopenharmony_ci		dsize = ALIGN(bytes, 8);
58562306a36Sopenharmony_ci		if (used + dsize > total)
58662306a36Sopenharmony_ci			return false;
58762306a36Sopenharmony_ci		nsize = asize + dsize;
58862306a36Sopenharmony_ci		/* Move tail */
58962306a36Sopenharmony_ci		memmove(next + dsize, next, tail);
59062306a36Sopenharmony_ci		memset(next, 0, dsize);
59162306a36Sopenharmony_ci		used += dsize;
59262306a36Sopenharmony_ci		rsize += dsize;
59362306a36Sopenharmony_ci	} else {
59462306a36Sopenharmony_ci		dsize = ALIGN(-bytes, 8);
59562306a36Sopenharmony_ci		if (dsize > asize)
59662306a36Sopenharmony_ci			return false;
59762306a36Sopenharmony_ci		nsize = asize - dsize;
59862306a36Sopenharmony_ci		memmove(next - dsize, next, tail);
59962306a36Sopenharmony_ci		used -= dsize;
60062306a36Sopenharmony_ci		rsize -= dsize;
60162306a36Sopenharmony_ci	}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	rec->used = cpu_to_le32(used);
60462306a36Sopenharmony_ci	attr->size = cpu_to_le32(nsize);
60562306a36Sopenharmony_ci	if (!attr->non_res)
60662306a36Sopenharmony_ci		attr->res.data_size = cpu_to_le32(rsize);
60762306a36Sopenharmony_ci	mi->dirty = true;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return true;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/*
61362306a36Sopenharmony_ci * Pack runs in MFT record.
61462306a36Sopenharmony_ci * If failed record is not changed.
61562306a36Sopenharmony_ci */
61662306a36Sopenharmony_ciint mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
61762306a36Sopenharmony_ci		 struct runs_tree *run, CLST len)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	int err = 0;
62062306a36Sopenharmony_ci	struct ntfs_sb_info *sbi = mi->sbi;
62162306a36Sopenharmony_ci	u32 new_run_size;
62262306a36Sopenharmony_ci	CLST plen;
62362306a36Sopenharmony_ci	struct MFT_REC *rec = mi->mrec;
62462306a36Sopenharmony_ci	CLST svcn = le64_to_cpu(attr->nres.svcn);
62562306a36Sopenharmony_ci	u32 used = le32_to_cpu(rec->used);
62662306a36Sopenharmony_ci	u32 aoff = PtrOffset(rec, attr);
62762306a36Sopenharmony_ci	u32 asize = le32_to_cpu(attr->size);
62862306a36Sopenharmony_ci	char *next = Add2Ptr(attr, asize);
62962306a36Sopenharmony_ci	u16 run_off = le16_to_cpu(attr->nres.run_off);
63062306a36Sopenharmony_ci	u32 run_size = asize - run_off;
63162306a36Sopenharmony_ci	u32 tail = used - aoff - asize;
63262306a36Sopenharmony_ci	u32 dsize = sbi->record_size - used;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	/* Make a maximum gap in current record. */
63562306a36Sopenharmony_ci	memmove(next + dsize, next, tail);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/* Pack as much as possible. */
63862306a36Sopenharmony_ci	err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size + dsize,
63962306a36Sopenharmony_ci		       &plen);
64062306a36Sopenharmony_ci	if (err < 0) {
64162306a36Sopenharmony_ci		memmove(next, next + dsize, tail);
64262306a36Sopenharmony_ci		return err;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	new_run_size = ALIGN(err, 8);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	memmove(next + new_run_size - run_size, next + dsize, tail);
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	attr->size = cpu_to_le32(asize + new_run_size - run_size);
65062306a36Sopenharmony_ci	attr->nres.evcn = cpu_to_le64(svcn + plen - 1);
65162306a36Sopenharmony_ci	rec->used = cpu_to_le32(used + new_run_size - run_size);
65262306a36Sopenharmony_ci	mi->dirty = true;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return 0;
65562306a36Sopenharmony_ci}
656