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