Merge branch 'for-5.16-vsprintf-pgp' into for-linus
[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/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
17 /*
18  * al_is_valid_le
19  *
20  * Return: True if @le is valid.
21  */
22 static 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
32 void al_destroy(struct ntfs_inode *ni)
33 {
34         run_close(&ni->attr_list.run);
35         kfree(ni->attr_list.le);
36         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  */
47 int 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);
58                 le = kmalloc(al_aligned(lsize), GFP_NOFS);
59                 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
81                 le = kmalloc(al_aligned(lsize), GFP_NOFS);
82                 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
98 out:
99         ni->attr_list.le = le;
100         al_destroy(ni);
101
102         return err;
103 }
104
105 /*
106  * al_enumerate
107  *
108  * Return:
109  * * The next list le.
110  * * If @le is NULL then return the first le.
111  */
112 struct 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)) {
123                         /* Impossible 'cause we should not return such le. */
124                         return NULL;
125                 }
126                 le = Add2Ptr(le, sz);
127         }
128
129         /* Check boundary. */
130         off = PtrOffset(ni->attr_list.le, le);
131         if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
132                 /* The regular end of list. */
133                 return NULL;
134         }
135
136         sz = le16_to_cpu(le->size);
137
138         /* Check le for errors. */
139         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  *
151  * Find the first le in the list which matches type, name and VCN.
152  *
153  * Return: NULL if not found.
154  */
155 struct 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  *
168  * Find the first le in the list which matches type, name and VCN.
169  *
170  * Return: NULL if not found.
171  */
172 struct 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
184                 /* List entries are sorted by type, name and VCN. */
185                 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                         /*
197                          * Compare entry names only for entry with vcn == 0.
198                          */
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  *
227  * Find the first list entry which matches type, name and VCN.
228  */
229 static 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
237         /* List entries are sorted by type, name and VCN. */
238         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                         /*
249                          * Compare entry names only for entry with vcn == 0.
250                          */
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  *
271  * Add an "attribute list entry" to the list.
272  */
273 int 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;
282         size_t asize, new_asize, old_size;
283         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);
290         old_size = al->size;
291         new_size = old_size + sz;
292         asize = al_aligned(old_size);
293         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) {
300                 void *ptr = kmalloc(new_asize, GFP_NOFS);
301
302                 if (!ptr)
303                         return -ENOMEM;
304
305                 memcpy(ptr, al->le, off);
306                 memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
307                 le = Add2Ptr(ptr, off);
308                 kfree(al->le);
309                 al->le = ptr;
310         } else {
311                 memmove(Add2Ptr(le, sz), le, old_size - off);
312         }
313         *new_le = le;
314
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
326         err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
327                             &new_size, true, &attr);
328         if (err) {
329                 /* Undo memmove above. */
330                 memmove(le, Add2Ptr(le, sz), old_size - off);
331                 al->size = old_size;
332                 return err;
333         }
334
335         al->dirty = true;
336
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;
342                 al->dirty = false;
343         }
344
345         return 0;
346 }
347
348 /*
349  * al_remove_le - Remove @le from attribute list.
350  */
351 bool 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 /*
373  * al_delete_le - Delete first le from the list which matches its parameters.
374  */
375 bool 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
384         /* Scan forward to the first le that matches the input. */
385         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
391 next:
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
415         /* Save on stack the size of 'le'. */
416         size = le16_to_cpu(le->size);
417         /* Delete the le. */
418         memmove(le, Add2Ptr(le, size), al->size - (off + size));
419
420         al->size -= size;
421         al->dirty = true;
422
423         return true;
424 }
425
426 int 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         /*
436          * Attribute list increased on demand in al_add_le.
437          * Attribute list decreased here.
438          */
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
458 out:
459         return err;
460 }