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