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