Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  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		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);
 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
100out:
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 */
114struct 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 */
157struct 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 */
174struct 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 */
231static 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 */
275int 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 */
353bool 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 */
377bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
378		  const __le16 *name, size_t name_len,
379		  const struct MFT_REF *ref)
380{
381	u16 size;
382	struct ATTR_LIST_ENTRY *le;
383	size_t off;
384	typeof(ni->attr_list) *al = &ni->attr_list;
385
386	/* Scan forward to the first le that matches the input. */
387	le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
388	if (!le)
389		return false;
390
391	off = PtrOffset(al->le, le);
392
393next:
394	if (off >= al->size)
395		return false;
396	if (le->type != type)
397		return false;
398	if (le->name_len != name_len)
399		return false;
400	if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
401				       ni->mi.sbi->upcase, true))
402		return false;
403	if (le64_to_cpu(le->vcn) != vcn)
404		return false;
405
406	/*
407	 * The caller specified a segment reference, so we have to
408	 * scan through the matching entries until we find that segment
409	 * reference or we run of matching entries.
410	 */
411	if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
412		off += le16_to_cpu(le->size);
413		le = Add2Ptr(al->le, off);
414		goto next;
415	}
416
417	/* Save on stack the size of 'le'. */
418	size = le16_to_cpu(le->size);
419	/* Delete the le. */
420	memmove(le, Add2Ptr(le, size), al->size - (off + size));
421
422	al->size -= size;
423	al->dirty = true;
424
425	return true;
426}
427
428int al_update(struct ntfs_inode *ni, int sync)
429{
430	int err;
431	struct ATTRIB *attr;
432	typeof(ni->attr_list) *al = &ni->attr_list;
433
434	if (!al->dirty || !al->size)
435		return 0;
436
437	/*
438	 * Attribute list increased on demand in al_add_le.
439	 * Attribute list decreased here.
440	 */
441	err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
442			    false, &attr);
443	if (err)
444		goto out;
445
446	if (!attr->non_res) {
447		memcpy(resident_data(attr), al->le, al->size);
448	} else {
449		err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
450					al->size, sync);
451		if (err)
452			goto out;
453
454		attr->nres.valid_size = attr->nres.data_size;
455	}
456
457	ni->mi.dirty = true;
458	al->dirty = false;
459
460out:
461	return err;
462}