Merge tag 'for-linus-5.19-rc1b-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / ntfs3 / attrlist.c
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/fs.h>
9
10 #include "debug.h"
11 #include "ntfs.h"
12 #include "ntfs_fs.h"
13
14 /*
15  * al_is_valid_le
16  *
17  * Return: True if @le is valid.
18  */
19 static 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
29 void al_destroy(struct ntfs_inode *ni)
30 {
31         run_close(&ni->attr_list.run);
32         kfree(ni->attr_list.le);
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  */
44 int 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);
55                 le = kmalloc(al_aligned(lsize), GFP_NOFS);
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
78                 le = kmalloc(al_aligned(lsize), GFP_NOFS);
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
95 out:
96         ni->attr_list.le = le;
97         al_destroy(ni);
98
99         return err;
100 }
101
102 /*
103  * al_enumerate
104  *
105  * Return:
106  * * The next list le.
107  * * If @le is NULL then return the first le.
108  */
109 struct 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)) {
120                         /* Impossible 'cause we should not return such le. */
121                         return NULL;
122                 }
123                 le = Add2Ptr(le, sz);
124         }
125
126         /* Check boundary. */
127         off = PtrOffset(ni->attr_list.le, le);
128         if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
129                 /* The regular end of list. */
130                 return NULL;
131         }
132
133         sz = le16_to_cpu(le->size);
134
135         /* Check le for errors. */
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  *
148  * Find the first le in the list which matches type, name and VCN.
149  *
150  * Return: NULL if not found.
151  */
152 struct 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  *
165  * Find the first le in the list which matches type, name and VCN.
166  *
167  * Return: NULL if not found.
168  */
169 struct 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
181                 /* List entries are sorted by type, name and VCN. */
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                         /*
194                          * Compare entry names only for entry with vcn == 0.
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  *
224  * Find the first list entry which matches type, name and VCN.
225  */
226 static 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
234         /* List entries are sorted by type, name and VCN. */
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                         /*
246                          * Compare entry names only for entry with vcn == 0.
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  *
268  * Add an "attribute list entry" to the list.
269  */
270 int 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;
279         size_t asize, new_asize, old_size;
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);
287         old_size = al->size;
288         new_size = old_size + sz;
289         asize = al_aligned(old_size);
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) {
297                 void *ptr = kmalloc(new_asize, GFP_NOFS);
298
299                 if (!ptr)
300                         return -ENOMEM;
301
302                 memcpy(ptr, al->le, off);
303                 memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
304                 le = Add2Ptr(ptr, off);
305                 kfree(al->le);
306                 al->le = ptr;
307         } else {
308                 memmove(Add2Ptr(le, sz), le, old_size - off);
309         }
310         *new_le = le;
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
323         err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
324                             &new_size, true, &attr);
325         if (err) {
326                 /* Undo memmove above. */
327                 memmove(le, Add2Ptr(le, sz), old_size - off);
328                 al->size = old_size;
329                 return err;
330         }
331
332         al->dirty = true;
333
334         if (attr && attr->non_res) {
335                 err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
336                                         al->size, 0);
337                 if (err)
338                         return err;
339                 al->dirty = false;
340         }
341
342         return 0;
343 }
344
345 /*
346  * al_remove_le - Remove @le from attribute list.
347  */
348 bool 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 /*
370  * al_delete_le - Delete first le from the list which matches its parameters.
371  */
372 bool 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
381         /* Scan forward to the first le that matches the input. */
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
388 next:
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
412         /* Save on stack the size of 'le'. */
413         size = le16_to_cpu(le->size);
414         /* Delete the le. */
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
423 int al_update(struct ntfs_inode *ni, int sync)
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         /*
433          * Attribute list increased on demand in al_add_le.
434          * Attribute list decreased here.
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,
445                                         al->size, sync);
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
455 out:
456         return err;
457 }