File Index
Files and directories
Sign in
FILE

attrlist.c

File preview with syntax highlighting.

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