xref: /openbmc/linux/fs/ntfs3/attrlist.c (revision be71b5cba2e6485e8959da7a9f9a44461a1bb074)
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 */
18 static 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 
28 void al_destroy(struct ntfs_inode *ni)
29 {
30 	run_close(&ni->attr_list.run);
31 	ntfs_free(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  */
43 int 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 = ntfs_malloc(al_aligned(lsize));
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 = ntfs_malloc(al_aligned(lsize));
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 
94 out:
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  */
107 struct 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  */
149 struct 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  */
165 struct 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  */
222 static 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  */
266 int 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 = ntfs_malloc(new_asize);
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 		ntfs_free(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  */
342 bool 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  */
368 bool 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 
384 next:
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  */
422 int 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 
454 out:
455 	return err;
456 }
457