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