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