blob: fa32399eb5171ce25f037fbd1bd8e483a37b839c [file] [log] [blame]
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +03001// 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
Kari Argillandere8b8e972021-08-03 14:57:09 +030017/*
18 * al_is_valid_le
19 *
20 * Return: True if @le is valid.
21 */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +030022static 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
32void al_destroy(struct ntfs_inode *ni)
33{
34 run_close(&ni->attr_list.run);
Kari Argillander195c52b2021-08-24 21:37:07 +030035 kfree(ni->attr_list.le);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +030036 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 */
47int 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);
Kari Argillander195c52b2021-08-24 21:37:07 +030058 le = kmalloc(al_aligned(lsize), GFP_NOFS);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +030059 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
Kari Argillander195c52b2021-08-24 21:37:07 +030081 le = kmalloc(al_aligned(lsize), GFP_NOFS);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +030082 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
98out:
99 ni->attr_list.le = le;
100 al_destroy(ni);
101
102 return err;
103}
104
105/*
106 * al_enumerate
107 *
Kari Argillandere8b8e972021-08-03 14:57:09 +0300108 * Return:
109 * * The next list le.
110 * * If @le is NULL then return the first le.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300111 */
112struct 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)) {
Kari Argillandere8b8e972021-08-03 14:57:09 +0300123 /* Impossible 'cause we should not return such le. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300124 return NULL;
125 }
126 le = Add2Ptr(le, sz);
127 }
128
Kari Argillandere8b8e972021-08-03 14:57:09 +0300129 /* Check boundary. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300130 off = PtrOffset(ni->attr_list.le, le);
131 if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
Kari Argillandere8b8e972021-08-03 14:57:09 +0300132 /* The regular end of list. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300133 return NULL;
134 }
135
136 sz = le16_to_cpu(le->size);
137
Kari Argillandere8b8e972021-08-03 14:57:09 +0300138 /* Check le for errors. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300139 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 *
Kari Argillandere8b8e972021-08-03 14:57:09 +0300151 * Find the first le in the list which matches type, name and VCN.
152 *
153 * Return: NULL if not found.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300154 */
155struct 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 *
Kari Argillandere8b8e972021-08-03 14:57:09 +0300168 * Find the first le in the list which matches type, name and VCN.
169 *
170 * Return: NULL if not found.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300171 */
172struct 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
Kari Argillandere8b8e972021-08-03 14:57:09 +0300184 /* List entries are sorted by type, name and VCN. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300185 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 /*
Kari Argillandere8b8e972021-08-03 14:57:09 +0300197 * Compare entry names only for entry with vcn == 0.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300198 */
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 *
Kari Argillandere8b8e972021-08-03 14:57:09 +0300227 * Find the first list entry which matches type, name and VCN.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300228 */
229static 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
Kari Argillandere8b8e972021-08-03 14:57:09 +0300237 /* List entries are sorted by type, name and VCN. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300238 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 /*
Kari Argillandere8b8e972021-08-03 14:57:09 +0300249 * Compare entry names only for entry with vcn == 0.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300250 */
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 *
Kari Argillandere8b8e972021-08-03 14:57:09 +0300271 * Add an "attribute list entry" to the list.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300272 */
273int 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;
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300282 size_t asize, new_asize, old_size;
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300283 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);
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300290 old_size = al->size;
291 new_size = old_size + sz;
292 asize = al_aligned(old_size);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300293 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) {
Kari Argillander195c52b2021-08-24 21:37:07 +0300300 void *ptr = kmalloc(new_asize, GFP_NOFS);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300301
302 if (!ptr)
303 return -ENOMEM;
304
305 memcpy(ptr, al->le, off);
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300306 memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300307 le = Add2Ptr(ptr, off);
Kari Argillander195c52b2021-08-24 21:37:07 +0300308 kfree(al->le);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300309 al->le = ptr;
310 } else {
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300311 memmove(Add2Ptr(le, sz), le, old_size - off);
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300312 }
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300313 *new_le = le;
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300314
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
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300326 err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
327 &new_size, true, &attr);
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300328 if (err) {
329 /* Undo memmove above. */
330 memmove(le, Add2Ptr(le, sz), old_size - off);
331 al->size = old_size;
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300332 return err;
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300333 }
334
335 al->dirty = true;
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300336
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;
Konstantin Komarov78ab59f2021-08-31 18:52:39 +0300342 al->dirty = false;
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300343 }
344
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300345 return 0;
346}
347
348/*
Kari Argillandere8b8e972021-08-03 14:57:09 +0300349 * al_remove_le - Remove @le from attribute list.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300350 */
351bool 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/*
Kari Argillandere8b8e972021-08-03 14:57:09 +0300373 * al_delete_le - Delete first le from the list which matches its parameters.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300374 */
375bool 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
Kari Argillandere8b8e972021-08-03 14:57:09 +0300384 /* Scan forward to the first le that matches the input. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300385 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
391next:
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
Kari Argillandere8b8e972021-08-03 14:57:09 +0300415 /* Save on stack the size of 'le'. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300416 size = le16_to_cpu(le->size);
Kari Argillandere8b8e972021-08-03 14:57:09 +0300417 /* Delete the le. */
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300418 memmove(le, Add2Ptr(le, size), al->size - (off + size));
419
420 al->size -= size;
421 al->dirty = true;
422
423 return true;
424}
425
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300426int 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 /*
Kari Argillandere8b8e972021-08-03 14:57:09 +0300436 * Attribute list increased on demand in al_add_le.
437 * Attribute list decreased here.
Konstantin Komarovbe71b5c2021-08-13 17:21:30 +0300438 */
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
458out:
459 return err;
460}