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