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