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