1 // SPDX-License-Identifier: GPL-2.0
4 * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
17 * Return: True if @le is valid.
19 static inline bool al_is_valid_le(const struct ntfs_inode
*ni
,
20 struct ATTR_LIST_ENTRY
*le
)
22 if (!le
|| !ni
->attr_list
.le
|| !ni
->attr_list
.size
)
25 return PtrOffset(ni
->attr_list
.le
, le
) + le16_to_cpu(le
->size
) <=
29 void al_destroy(struct ntfs_inode
*ni
)
31 run_close(&ni
->attr_list
.run
);
32 kvfree(ni
->attr_list
.le
);
33 ni
->attr_list
.le
= NULL
;
34 ni
->attr_list
.size
= 0;
35 ni
->attr_list
.dirty
= false;
41 * This method makes sure that the ATTRIB list, if present,
42 * has been properly set up.
44 int ntfs_load_attr_list(struct ntfs_inode
*ni
, struct ATTRIB
*attr
)
50 if (ni
->attr_list
.size
)
54 lsize
= le32_to_cpu(attr
->res
.data_size
);
55 /* attr is resident: lsize < record_size (1K or 4K) */
56 le
= kvmalloc(al_aligned(lsize
), GFP_KERNEL
);
61 memcpy(le
, resident_data(attr
), lsize
);
62 } else if (attr
->nres
.svcn
) {
66 u16 run_off
= le16_to_cpu(attr
->nres
.run_off
);
68 lsize
= le64_to_cpu(attr
->nres
.data_size
);
70 run_init(&ni
->attr_list
.run
);
72 if (run_off
> le32_to_cpu(attr
->size
)) {
77 err
= run_unpack_ex(&ni
->attr_list
.run
, ni
->mi
.sbi
, ni
->mi
.rno
,
78 0, le64_to_cpu(attr
->nres
.evcn
), 0,
79 Add2Ptr(attr
, run_off
),
80 le32_to_cpu(attr
->size
) - run_off
);
84 /* attr is nonresident.
86 * 1T (2^40) extremely fragmented file.
87 * cluster = 4K (2^12) => 2^28 fragments
88 * 2^9 fragments per one record => 2^19 records
89 * 2^5 bytes of ATTR_LIST_ENTRY per one record => 2^24 bytes.
91 * the result is 16M bytes per attribute list.
92 * Use kvmalloc to allocate in range [several Kbytes - dozen Mbytes]
94 le
= kvmalloc(al_aligned(lsize
), GFP_KERNEL
);
100 err
= ntfs_read_run_nb(ni
->mi
.sbi
, &ni
->attr_list
.run
, 0, le
,
106 ni
->attr_list
.size
= lsize
;
107 ni
->attr_list
.le
= le
;
112 ni
->attr_list
.le
= le
;
122 * * The next list le.
123 * * If @le is NULL then return the first le.
125 struct ATTR_LIST_ENTRY
*al_enumerate(struct ntfs_inode
*ni
,
126 struct ATTR_LIST_ENTRY
*le
)
130 const unsigned le_min_size
= le_size(0);
133 le
= ni
->attr_list
.le
;
135 sz
= le16_to_cpu(le
->size
);
136 if (sz
< le_min_size
) {
137 /* Impossible 'cause we should not return such le. */
140 le
= Add2Ptr(le
, sz
);
143 /* Check boundary. */
144 off
= PtrOffset(ni
->attr_list
.le
, le
);
145 if (off
+ le_min_size
> ni
->attr_list
.size
) {
146 /* The regular end of list. */
150 sz
= le16_to_cpu(le
->size
);
152 /* Check le for errors. */
153 if (sz
< le_min_size
|| off
+ sz
> ni
->attr_list
.size
||
154 sz
< le
->name_off
+ le
->name_len
* sizeof(short)) {
164 * Find the first le in the list which matches type, name and VCN.
166 * Return: NULL if not found.
168 struct ATTR_LIST_ENTRY
*al_find_le(struct ntfs_inode
*ni
,
169 struct ATTR_LIST_ENTRY
*le
,
170 const struct ATTRIB
*attr
)
172 CLST svcn
= attr_svcn(attr
);
174 return al_find_ex(ni
, le
, attr
->type
, attr_name(attr
), attr
->name_len
,
181 * Find the first le in the list which matches type, name and VCN.
183 * Return: NULL if not found.
185 struct ATTR_LIST_ENTRY
*al_find_ex(struct ntfs_inode
*ni
,
186 struct ATTR_LIST_ENTRY
*le
,
187 enum ATTR_TYPE type
, const __le16
*name
,
188 u8 name_len
, const CLST
*vcn
)
190 struct ATTR_LIST_ENTRY
*ret
= NULL
;
191 u32 type_in
= le32_to_cpu(type
);
193 while ((le
= al_enumerate(ni
, le
))) {
195 int diff
= le32_to_cpu(le
->type
) - type_in
;
197 /* List entries are sorted by type, name and VCN. */
204 if (le
->name_len
!= name_len
)
207 le_vcn
= le64_to_cpu(le
->vcn
);
210 * Compare entry names only for entry with vcn == 0.
212 diff
= ntfs_cmp_names(le_name(le
), name_len
, name
,
213 name_len
, ni
->mi
.sbi
->upcase
,
238 * al_find_le_to_insert
240 * Find the first list entry which matches type, name and VCN.
242 static struct ATTR_LIST_ENTRY
*al_find_le_to_insert(struct ntfs_inode
*ni
,
245 u8 name_len
, CLST vcn
)
247 struct ATTR_LIST_ENTRY
*le
= NULL
, *prev
;
248 u32 type_in
= le32_to_cpu(type
);
250 /* List entries are sorted by type, name and VCN. */
251 while ((le
= al_enumerate(ni
, prev
= le
))) {
252 int diff
= le32_to_cpu(le
->type
) - type_in
;
262 * Compare entry names only for entry with vcn == 0.
264 diff
= ntfs_cmp_names(le_name(le
), le
->name_len
, name
,
265 name_len
, ni
->mi
.sbi
->upcase
,
274 if (le64_to_cpu(le
->vcn
) >= vcn
)
278 return prev
? Add2Ptr(prev
, le16_to_cpu(prev
->size
)) : ni
->attr_list
.le
;
284 * Add an "attribute list entry" to the list.
286 int al_add_le(struct ntfs_inode
*ni
, enum ATTR_TYPE type
, const __le16
*name
,
287 u8 name_len
, CLST svcn
, __le16 id
, const struct MFT_REF
*ref
,
288 struct ATTR_LIST_ENTRY
**new_le
)
292 struct ATTR_LIST_ENTRY
*le
;
295 size_t asize
, new_asize
, old_size
;
297 typeof(ni
->attr_list
) *al
= &ni
->attr_list
;
300 * Compute the size of the new 'le'
302 sz
= le_size(name_len
);
304 new_size
= old_size
+ sz
;
305 asize
= al_aligned(old_size
);
306 new_asize
= al_aligned(new_size
);
308 /* Scan forward to the point at which the new 'le' should be inserted. */
309 le
= al_find_le_to_insert(ni
, type
, name
, name_len
, svcn
);
310 off
= PtrOffset(al
->le
, le
);
312 if (new_size
> asize
) {
313 void *ptr
= kmalloc(new_asize
, GFP_NOFS
);
318 memcpy(ptr
, al
->le
, off
);
319 memcpy(Add2Ptr(ptr
, off
+ sz
), le
, old_size
- off
);
320 le
= Add2Ptr(ptr
, off
);
324 memmove(Add2Ptr(le
, sz
), le
, old_size
- off
);
331 le
->size
= cpu_to_le16(sz
);
332 le
->name_len
= name_len
;
333 le
->name_off
= offsetof(struct ATTR_LIST_ENTRY
, name
);
334 le
->vcn
= cpu_to_le64(svcn
);
337 memcpy(le
->name
, name
, sizeof(short) * name_len
);
339 err
= attr_set_size(ni
, ATTR_LIST
, NULL
, 0, &al
->run
, new_size
,
340 &new_size
, true, &attr
);
342 /* Undo memmove above. */
343 memmove(le
, Add2Ptr(le
, sz
), old_size
- off
);
350 if (attr
&& attr
->non_res
) {
351 err
= ntfs_sb_write_run(ni
->mi
.sbi
, &al
->run
, 0, al
->le
,
362 * al_remove_le - Remove @le from attribute list.
364 bool al_remove_le(struct ntfs_inode
*ni
, struct ATTR_LIST_ENTRY
*le
)
368 typeof(ni
->attr_list
) *al
= &ni
->attr_list
;
370 if (!al_is_valid_le(ni
, le
))
373 /* Save on stack the size of 'le' */
374 size
= le16_to_cpu(le
->size
);
375 off
= PtrOffset(al
->le
, le
);
377 memmove(le
, Add2Ptr(le
, size
), al
->size
- (off
+ size
));
385 int al_update(struct ntfs_inode
*ni
, int sync
)
389 typeof(ni
->attr_list
) *al
= &ni
->attr_list
;
391 if (!al
->dirty
|| !al
->size
)
395 * Attribute list increased on demand in al_add_le.
396 * Attribute list decreased here.
398 err
= attr_set_size(ni
, ATTR_LIST
, NULL
, 0, &al
->run
, al
->size
, NULL
,
403 if (!attr
->non_res
) {
404 memcpy(resident_data(attr
), al
->le
, al
->size
);
406 err
= ntfs_sb_write_run(ni
->mi
.sbi
, &al
->run
, 0, al
->le
,
411 attr
->nres
.valid_size
= attr
->nres
.data_size
;