Merge tag 'riscv-for-linus-6.4-mw2' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / fs / ntfs3 / attrlist.c
CommitLineData
be71b5cb
KK
1// SPDX-License-Identifier: GPL-2.0
2/*
3 *
4 * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
5 *
6 */
7
be71b5cb 8#include <linux/fs.h>
be71b5cb
KK
9
10#include "debug.h"
11#include "ntfs.h"
12#include "ntfs_fs.h"
13
e8b8e97f
KA
14/*
15 * al_is_valid_le
16 *
17 * Return: True if @le is valid.
18 */
be71b5cb
KK
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);
195c52bd 32 kfree(ni->attr_list.le);
be71b5cb
KK
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);
195c52bd 55 le = kmalloc(al_aligned(lsize), GFP_NOFS);
be71b5cb
KK
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
6db62086
EL
71 if (run_off > le32_to_cpu(attr->size)) {
72 err = -EINVAL;
73 goto out;
74 }
75
be71b5cb
KK
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
195c52bd 83 le = kmalloc(al_aligned(lsize), GFP_NOFS);
be71b5cb
KK
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 *
e8b8e97f
KA
110 * Return:
111 * * The next list le.
112 * * If @le is NULL then return the first le.
be71b5cb
KK
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)) {
e8b8e97f 125 /* Impossible 'cause we should not return such le. */
be71b5cb
KK
126 return NULL;
127 }
128 le = Add2Ptr(le, sz);
129 }
130
e8b8e97f 131 /* Check boundary. */
be71b5cb
KK
132 off = PtrOffset(ni->attr_list.le, le);
133 if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
e8b8e97f 134 /* The regular end of list. */
be71b5cb
KK
135 return NULL;
136 }
137
138 sz = le16_to_cpu(le->size);
139
e8b8e97f 140 /* Check le for errors. */
be71b5cb
KK
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 *
e8b8e97f
KA
153 * Find the first le in the list which matches type, name and VCN.
154 *
155 * Return: NULL if not found.
be71b5cb
KK
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 *
e8b8e97f
KA
170 * Find the first le in the list which matches type, name and VCN.
171 *
172 * Return: NULL if not found.
be71b5cb
KK
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
e8b8e97f 186 /* List entries are sorted by type, name and VCN. */
be71b5cb
KK
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 /*
e8b8e97f 199 * Compare entry names only for entry with vcn == 0.
be71b5cb
KK
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 *
e8b8e97f 229 * Find the first list entry which matches type, name and VCN.
be71b5cb
KK
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
e8b8e97f 239 /* List entries are sorted by type, name and VCN. */
be71b5cb
KK
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 /*
e8b8e97f 251 * Compare entry names only for entry with vcn == 0.
be71b5cb
KK
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 *
e8b8e97f 273 * Add an "attribute list entry" to the list.
be71b5cb
KK
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;
78ab59fe 284 size_t asize, new_asize, old_size;
be71b5cb
KK
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);
78ab59fe
KK
292 old_size = al->size;
293 new_size = old_size + sz;
294 asize = al_aligned(old_size);
be71b5cb
KK
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) {
195c52bd 302 void *ptr = kmalloc(new_asize, GFP_NOFS);
be71b5cb
KK
303
304 if (!ptr)
305 return -ENOMEM;
306
307 memcpy(ptr, al->le, off);
78ab59fe 308 memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
be71b5cb 309 le = Add2Ptr(ptr, off);
195c52bd 310 kfree(al->le);
be71b5cb
KK
311 al->le = ptr;
312 } else {
78ab59fe 313 memmove(Add2Ptr(le, sz), le, old_size - off);
be71b5cb 314 }
78ab59fe 315 *new_le = le;
be71b5cb
KK
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
be71b5cb
KK
328 err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
329 &new_size, true, &attr);
78ab59fe
KK
330 if (err) {
331 /* Undo memmove above. */
332 memmove(le, Add2Ptr(le, sz), old_size - off);
333 al->size = old_size;
be71b5cb 334 return err;
78ab59fe
KK
335 }
336
337 al->dirty = true;
be71b5cb
KK
338
339 if (attr && attr->non_res) {
340 err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
63544672 341 al->size, 0);
be71b5cb
KK
342 if (err)
343 return err;
78ab59fe 344 al->dirty = false;
be71b5cb
KK
345 }
346
be71b5cb
KK
347 return 0;
348}
349
350/*
e8b8e97f 351 * al_remove_le - Remove @le from attribute list.
be71b5cb
KK
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/*
e8b8e97f 375 * al_delete_le - Delete first le from the list which matches its parameters.
be71b5cb
KK
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
e8b8e97f 386 /* Scan forward to the first le that matches the input. */
be71b5cb
KK
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
e8b8e97f 417 /* Save on stack the size of 'le'. */
be71b5cb 418 size = le16_to_cpu(le->size);
e8b8e97f 419 /* Delete the le. */
be71b5cb
KK
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
63544672 428int al_update(struct ntfs_inode *ni, int sync)
be71b5cb
KK
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 /*
e8b8e97f
KA
438 * Attribute list increased on demand in al_add_le.
439 * Attribute list decreased here.
be71b5cb
KK
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,
63544672 450 al->size, sync);
be71b5cb
KK
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}