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