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