1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Attribute list attribute handling code.
4 * Part of this file is based on code from the NTFS-3G.
5 *
6 * Copyright (c) 2004-2005 Anton Altaparmakov
7 * Copyright (c) 2004-2005 Yura Pakhuchiy
8 * Copyright (c) 2006 Szabolcs Szakacsits
9 * Copyright (c) 2025 LG Electronics Co., Ltd.
10 */
11
12 #include "mft.h"
13 #include "attrib.h"
14 #include "attrlist.h"
15 #include "lcnalloc.h"
16
17 #define NTFS_MAX_ATTR_LIST_SIZE (256 * 1024)
18
19 /*
20 * ntfs_attrlist_need - check whether inode need attribute list
21 * @ni: opened ntfs inode for which perform check
22 *
23 * Check whether all are attributes belong to one MFT record, in that case
24 * attribute list is not needed.
25 *
26 * Return 1 if inode need attribute list, 0 if not, or -errno on error.
27 */
ntfs_attrlist_need(struct ntfs_inode * ni)28 int ntfs_attrlist_need(struct ntfs_inode *ni)
29 {
30 struct attr_list_entry *ale;
31
32 if (!ni) {
33 ntfs_debug("Invalid arguments.\n");
34 return -EINVAL;
35 }
36 ntfs_debug("Entering for inode 0x%llx.\n", (long long) ni->mft_no);
37
38 if (!NInoAttrList(ni)) {
39 ntfs_debug("Inode haven't got attribute list.\n");
40 return -EINVAL;
41 }
42
43 if (!ni->attr_list) {
44 ntfs_debug("Corrupt in-memory struct.\n");
45 return -EINVAL;
46 }
47
48 ale = (struct attr_list_entry *)ni->attr_list;
49 while ((u8 *)ale < ni->attr_list + ni->attr_list_size) {
50 if (MREF_LE(ale->mft_reference) != ni->mft_no)
51 return 1;
52 ale = (struct attr_list_entry *)((u8 *)ale + le16_to_cpu(ale->length));
53 }
54 return 0;
55 }
56
57 /*
58 * Repack the $MFT/$ATTRIBUTE_LIST data into one run.
59 *
60 * The mapping pairs for an $ATTRIBUTE_LIST must remain in the base MFT
61 * record. Once that record has no room left, extending a fragmented list
62 * can require one more mapping-pairs byte than the record can hold. There
63 * is no attribute that can legally be moved out in that state: $STANDARD_
64 * INFORMATION, $ATTRIBUTE_LIST, and the first $MFT/$DATA extent all have to
65 * stay in the base record. Move the list data to one contiguous run. The
66 * caller supplies the minimum allocation size so a recovery can use the
67 * smallest useful run while normal updates can still request the maximum
68 * legal list size as a reserve.
69 */
ntfs_attrlist_repack(struct inode * attr_vi,struct ntfs_inode * attr_ni,s64 min_alloc_size,struct ntfs_inode * locked_ni)70 static int ntfs_attrlist_repack(struct inode *attr_vi,
71 struct ntfs_inode *attr_ni, s64 min_alloc_size,
72 struct ntfs_inode *locked_ni)
73 {
74 struct ntfs_volume *vol = attr_ni->vol;
75 struct runlist_element *old_rl, *new_rl;
76 u8 *data = NULL;
77 s64 data_size, alloc_size, nr_clusters, written;
78 s64 old_alloc_size;
79 size_t old_rl_count, new_rl_count;
80 unsigned long flags;
81 int err, restore_err;
82 if (attr_ni->mft_no != FILE_MFT || !NInoNonResident(attr_ni) ||
83 min_alloc_size < 0)
84 return -EINVAL;
85 /* The buffered I/O below can reacquire the attribute runlist lock. */
86 if (attr_ni == locked_ni)
87 return -ENOSPC;
88
89 err = ntfs_attr_map_whole_runlist(attr_ni);
90 if (err)
91 return err;
92
93 data_size = attr_ni->data_size;
94 if (data_size < 0)
95 return -EIO;
96
97 if (data_size) {
98 data = kvmalloc(data_size, GFP_NOFS);
99 if (!data)
100 return -ENOMEM;
101
102 written = ntfs_inode_attr_pread(attr_vi, 0, data_size, data);
103 if (written != data_size) {
104 err = written < 0 ? (int)written : -EIO;
105 goto out_free_data;
106 }
107 }
108
109 old_alloc_size = attr_ni->allocated_size;
110 alloc_size = max_t(s64, old_alloc_size, min_alloc_size);
111 nr_clusters = ntfs_bytes_to_cluster(vol,
112 alloc_size + vol->cluster_size - 1);
113 if (nr_clusters <= 0) {
114 err = -EFBIG;
115 goto out_free_data;
116 }
117
118 /* A single run keeps the mapping pairs at the minimum size. */
119 new_rl = ntfs_cluster_alloc(vol, 0, nr_clusters, -1, DATA_ZONE,
120 true, true, false);
121 if (IS_ERR(new_rl)) {
122 err = PTR_ERR(new_rl);
123 goto out_free_data;
124 }
125
126 new_rl_count = 0;
127 if (new_rl->vcn == 0 && new_rl->length == nr_clusters &&
128 !new_rl[1].length)
129 new_rl_count = 2;
130
131 if (new_rl_count != 2) {
132 ntfs_cluster_free_from_rl(vol, new_rl);
133 kvfree(new_rl);
134 err = -ENOSPC;
135 goto out_free_data;
136 }
137 old_rl = attr_ni->runlist.rl;
138 old_rl_count = attr_ni->runlist.count;
139 down_write(&attr_ni->runlist.lock);
140 attr_ni->runlist.rl = new_rl;
141 attr_ni->runlist.count = new_rl_count;
142 up_write(&attr_ni->runlist.lock);
143
144 write_lock_irqsave(&attr_ni->size_lock, flags);
145 attr_ni->allocated_size = ntfs_cluster_to_bytes(vol, nr_clusters);
146 write_unlock_irqrestore(&attr_ni->size_lock, flags);
147
148 /* Populate the replacement extent before publishing its mapping pairs. */
149 if (data_size) {
150 written = ntfs_inode_attr_pwrite(attr_vi, 0, data_size, data, true);
151 if (written != data_size) {
152 err = written < 0 ? (int)written : -EIO;
153 goto restore_old_runlist;
154 }
155 }
156
157 err = ntfs_attr_update_mapping_pairs_locked(attr_ni, 0, locked_ni);
158 if (err)
159 goto restore_old_runlist;
160
161 /* The new mapping is now authoritative; release the old data runs. */
162 if (ntfs_cluster_free_from_rl(vol, old_rl)) {
163 ntfs_error(vol->sb,
164 "Failed to free old ATTRIBUTE_LIST extent: inode %#llx",
165 (long long)attr_ni->mft_no);
166 NVolSetErrors(vol);
167 }
168 kvfree(old_rl);
169 kvfree(data);
170 return 0;
171
172 restore_old_runlist:
173 down_write(&attr_ni->runlist.lock);
174 attr_ni->runlist.rl = old_rl;
175 attr_ni->runlist.count = old_rl_count;
176 up_write(&attr_ni->runlist.lock);
177
178 write_lock_irqsave(&attr_ni->size_lock, flags);
179 attr_ni->allocated_size = old_alloc_size;
180 write_unlock_irqrestore(&attr_ni->size_lock, flags);
181
182 restore_err = ntfs_attr_update_mapping_pairs_locked(
183 attr_ni, 0, locked_ni);
184 if (restore_err) {
185 ntfs_error(vol->sb, "Failed to restore ATTRIBUTE_LIST mapping pairs (%d)",
186 restore_err);
187 NVolSetErrors(vol);
188 }
189
190 ntfs_cluster_free_from_rl(vol, new_rl);
191 kvfree(new_rl);
192 err = err ? err : restore_err;
193
194 out_free_data:
195 kvfree(data);
196 return err;
197 }
198
ntfs_attrlist_update_locked(struct ntfs_inode * base_ni,struct ntfs_inode * locked_ni)199 int ntfs_attrlist_update_locked(struct ntfs_inode *base_ni,
200 struct ntfs_inode *locked_ni)
201 {
202 struct inode *attr_vi;
203 struct ntfs_inode *attr_ni;
204 s64 written;
205 int err, retry_err;
206
207 /*
208 * generic_shutdown_super() clears SB_ACTIVE before evicting cached
209 * inodes. Do not look up the attribute-list inode after SB_ACTIVE has
210 * been cleared; it may already be I_FREEING, and waiting on it can
211 * self-deadlock.
212 */
213 if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE))
214 return -EIO;
215
216 attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0);
217 if (IS_ERR(attr_vi)) {
218 err = PTR_ERR(attr_vi);
219 return err;
220 }
221 attr_ni = NTFS_I(attr_vi);
222 /* Truncation and page-cache writes can reacquire this runlist lock. */
223 if (attr_ni == locked_ni) {
224 iput(attr_vi);
225 return -ENOSPC;
226 }
227
228 err = ntfs_attr_truncate_i_locked(
229 attr_ni, base_ni->attr_list_size, HOLES_NO, locked_ni);
230 if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT &&
231 NInoNonResident(attr_ni)) {
232 retry_err = ntfs_attrlist_repack(attr_vi, attr_ni,
233 base_ni->attr_list_size, locked_ni);
234 if (retry_err) {
235 ntfs_error(base_ni->vol->sb, "Failed to repack attribute list");
236 iput(attr_vi);
237 return retry_err;
238 }
239
240 retry_err = ntfs_attr_truncate_i_locked(
241 attr_ni, base_ni->attr_list_size,
242 HOLES_NO, locked_ni);
243 if (retry_err) {
244 ntfs_error(base_ni->vol->sb,
245 "Failed to resize attribute list after repack");
246 iput(attr_vi);
247 return retry_err;
248 }
249 } else if (err) {
250 iput(attr_vi);
251 ntfs_error(base_ni->vol->sb,
252 "Failed to truncate attribute list of inode %#llx",
253 (long long)base_ni->mft_no);
254 return err;
255 }
256
257 /*
258 * Reserve the maximum legal list size while the MFT metadata area is
259 * still easy to allocate contiguously. This prevents a later list entry
260 * from needing another mapping-pairs byte in the full base MFT record.
261 * Failure to obtain the optional reserve must not reject the current
262 * metadata update; the repack retry above remains available if needed.
263 */
264 if (base_ni->mft_no == FILE_MFT && NInoNonResident(attr_ni) &&
265 attr_ni->allocated_size < NTFS_MAX_ATTR_LIST_SIZE) {
266 retry_err = ntfs_attr_expand_locked(
267 attr_ni, base_ni->attr_list_size,
268 NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
269 if (retry_err == -ENOSPC) {
270 retry_err = ntfs_attrlist_repack(
271 attr_vi, attr_ni,
272 NTFS_MAX_ATTR_LIST_SIZE, locked_ni);
273 if (retry_err == -ENOSPC)
274 retry_err = 0;
275 }
276 if (retry_err) {
277 ntfs_error(base_ni->vol->sb,
278 "Failed to reserve attribute list space");
279 iput(attr_vi);
280 return retry_err;
281 }
282 }
283
284 i_size_write(attr_vi, base_ni->attr_list_size);
285
286 if (NInoNonResident(attr_ni) && !NInoAttrListNonResident(base_ni))
287 NInoSetAttrListNonResident(base_ni);
288
289 written = ntfs_inode_attr_pwrite(attr_vi, 0, base_ni->attr_list_size,
290 base_ni->attr_list, false);
291 if (written != base_ni->attr_list_size) {
292 err = written < 0 ? (int)written : -EIO;
293 iput(attr_vi);
294 ntfs_error(base_ni->vol->sb,
295 "Failed to write attribute list of inode %#llx",
296 (long long)base_ni->mft_no);
297 return err;
298 }
299
300 NInoSetAttrListDirty(base_ni);
301 iput(attr_vi);
302 return 0;
303 }
304
ntfs_attrlist_update(struct ntfs_inode * base_ni)305 int ntfs_attrlist_update(struct ntfs_inode *base_ni)
306 {
307 return ntfs_attrlist_update_locked(base_ni, NULL);
308 }
309
310 /*
311 * ntfs_attrlist_entry_add - add an attribute list attribute entry
312 * @ni: opened ntfs inode, which contains that attribute
313 * @attr: attribute record to add to attribute list
314 *
315 * Return 0 on success and -errno on error.
316 */
ntfs_attrlist_entry_add(struct ntfs_inode * ni,struct attr_record * attr)317 int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr)
318 {
319 struct attr_list_entry *ale;
320 __le64 mref;
321 struct ntfs_attr_search_ctx *ctx;
322 u8 *new_al;
323 int entry_len, entry_offset, err;
324 struct mft_record *ni_mrec;
325 u8 *old_al;
326 __le64 lowest_vcn;
327
328 if (!ni || !attr) {
329 ntfs_debug("Invalid arguments.\n");
330 return -EINVAL;
331 }
332
333 ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n",
334 ni->mft_no, (unsigned int) le32_to_cpu(attr->type));
335
336 ni_mrec = map_mft_record(ni);
337 if (IS_ERR(ni_mrec)) {
338 ntfs_debug("Invalid arguments.\n");
339 return -EIO;
340 }
341
342 mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni_mrec->sequence_number));
343 unmap_mft_record(ni);
344
345 if (ni->nr_extents == -1)
346 ni = ni->ext.base_ntfs_ino;
347
348 if (!NInoAttrList(ni)) {
349 ntfs_debug("Attribute list isn't present.\n");
350 return -ENOENT;
351 }
352
353 /* Determine size and allocate memory for new attribute list. */
354 entry_len = (sizeof(struct attr_list_entry) + sizeof(__le16) *
355 attr->name_length + 7) & ~7;
356 new_al = kvzalloc(ni->attr_list_size + entry_len, GFP_NOFS);
357 if (!new_al)
358 return -ENOMEM;
359
360 /* Find place for the new entry. */
361 ctx = ntfs_attr_get_search_ctx(ni, NULL);
362 if (!ctx) {
363 err = -ENOMEM;
364 ntfs_error(ni->vol->sb, "Failed to get search context");
365 goto err_out;
366 }
367 if (attr->non_resident)
368 lowest_vcn = attr->data.non_resident.lowest_vcn;
369 else
370 lowest_vcn = 0;
371
372 err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *)
373 ((u8 *)attr + le16_to_cpu(attr->name_offset)) :
374 AT_UNNAMED, attr->name_length, CASE_SENSITIVE,
375 le64_to_cpu(lowest_vcn),
376 (attr->non_resident) ? NULL : ((u8 *)attr +
377 le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ?
378 0 : le32_to_cpu(attr->data.resident.value_length), ctx);
379 if (!err) {
380 /* Found some extent, check it to be before new extent. */
381 if (ctx->al_entry->lowest_vcn == lowest_vcn) {
382 err = -EEXIST;
383 ntfs_debug("Such attribute already present in the attribute list.\n");
384 ntfs_attr_put_search_ctx(ctx);
385 goto err_out;
386 }
387 /* Add new entry after this extent. */
388 ale = (struct attr_list_entry *)((u8 *)ctx->al_entry +
389 le16_to_cpu(ctx->al_entry->length));
390 } else {
391 /* Check for real errors. */
392 if (err != -ENOENT) {
393 ntfs_debug("Attribute lookup failed.\n");
394 ntfs_attr_put_search_ctx(ctx);
395 goto err_out;
396 }
397 /* No previous extents found. */
398 ale = ctx->al_entry;
399 }
400 /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */
401 ntfs_attr_put_search_ctx(ctx);
402
403 /* Determine new entry offset. */
404 entry_offset = ((u8 *)ale - ni->attr_list);
405 /* Set pointer to new entry. */
406 ale = (struct attr_list_entry *)(new_al + entry_offset);
407 memset(ale, 0, entry_len);
408 /* Form new entry. */
409 ale->type = attr->type;
410 ale->length = cpu_to_le16(entry_len);
411 ale->name_length = attr->name_length;
412 ale->name_offset = offsetof(struct attr_list_entry, name);
413 if (attr->non_resident)
414 ale->lowest_vcn = attr->data.non_resident.lowest_vcn;
415 else
416 ale->lowest_vcn = 0;
417 ale->mft_reference = mref;
418 ale->instance = attr->instance;
419 memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset),
420 attr->name_length * sizeof(__le16));
421
422 /* Copy entries from old attribute list to new. */
423 memcpy(new_al, ni->attr_list, entry_offset);
424 memcpy(new_al + entry_offset + entry_len, ni->attr_list +
425 entry_offset, ni->attr_list_size - entry_offset);
426
427 /* Set new runlist. */
428 old_al = ni->attr_list;
429 ni->attr_list = new_al;
430 ni->attr_list_size = ni->attr_list_size + entry_len;
431
432 err = ntfs_attrlist_update(ni);
433 if (err) {
434 ni->attr_list = old_al;
435 ni->attr_list_size -= entry_len;
436 goto err_out;
437 }
438 kvfree(old_al);
439 return 0;
440 err_out:
441 kvfree(new_al);
442 return err;
443 }
444
445 /*
446 * ntfs_attrlist_entry_rm - remove an attribute list attribute entry
447 * @ctx: attribute search context describing the attribute list entry
448 *
449 * Remove the attribute list entry @ctx->al_entry from the attribute list.
450 *
451 * Return 0 on success and -errno on error.
452 */
ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx * ctx)453 int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx)
454 {
455 u8 *new_al;
456 int new_al_len;
457 struct ntfs_inode *base_ni;
458 struct attr_list_entry *ale;
459
460 if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) {
461 ntfs_debug("Invalid arguments.\n");
462 return -EINVAL;
463 }
464
465 if (ctx->base_ntfs_ino)
466 base_ni = ctx->base_ntfs_ino;
467 else
468 base_ni = ctx->ntfs_ino;
469 ale = ctx->al_entry;
470
471 ntfs_debug("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n",
472 (long long)ctx->ntfs_ino->mft_no,
473 (unsigned int)le32_to_cpu(ctx->al_entry->type),
474 (long long)le64_to_cpu(ctx->al_entry->lowest_vcn));
475
476 if (!NInoAttrList(base_ni)) {
477 ntfs_debug("Attribute list isn't present.\n");
478 return -ENOENT;
479 }
480
481 /* Allocate memory for new attribute list. */
482 new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length);
483 new_al = kvzalloc(new_al_len, GFP_NOFS);
484 if (!new_al)
485 return -ENOMEM;
486
487 /* Copy entries from old attribute list to new. */
488 memcpy(new_al, base_ni->attr_list, (u8 *)ale - base_ni->attr_list);
489 memcpy(new_al + ((u8 *)ale - base_ni->attr_list), (u8 *)ale + le16_to_cpu(
490 ale->length), new_al_len - ((u8 *)ale - base_ni->attr_list));
491
492 /* Set new runlist. */
493 kvfree(base_ni->attr_list);
494 base_ni->attr_list = new_al;
495 base_ni->attr_list_size = new_al_len;
496
497 return ntfs_attrlist_update(base_ni);
498 }
499