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 16 /* 17 * ntfs_attrlist_need - check whether inode need attribute list 18 * @ni: opened ntfs inode for which perform check 19 * 20 * Check whether all are attributes belong to one MFT record, in that case 21 * attribute list is not needed. 22 * 23 * Return 1 if inode need attribute list, 0 if not, or -errno on error. 24 */ 25 int ntfs_attrlist_need(struct ntfs_inode *ni) 26 { 27 struct attr_list_entry *ale; 28 29 if (!ni) { 30 ntfs_debug("Invalid arguments.\n"); 31 return -EINVAL; 32 } 33 ntfs_debug("Entering for inode 0x%llx.\n", (long long) ni->mft_no); 34 35 if (!NInoAttrList(ni)) { 36 ntfs_debug("Inode haven't got attribute list.\n"); 37 return -EINVAL; 38 } 39 40 if (!ni->attr_list) { 41 ntfs_debug("Corrupt in-memory struct.\n"); 42 return -EINVAL; 43 } 44 45 ale = (struct attr_list_entry *)ni->attr_list; 46 while ((u8 *)ale < ni->attr_list + ni->attr_list_size) { 47 if (MREF_LE(ale->mft_reference) != ni->mft_no) 48 return 1; 49 ale = (struct attr_list_entry *)((u8 *)ale + le16_to_cpu(ale->length)); 50 } 51 return 0; 52 } 53 54 int ntfs_attrlist_update(struct ntfs_inode *base_ni) 55 { 56 struct inode *attr_vi; 57 struct ntfs_inode *attr_ni; 58 int err; 59 60 /* 61 * generic_shutdown_super() clears SB_ACTIVE before evicting cached 62 * inodes. Do not look up the attribute-list inode after SB_ACTIVE has 63 * been cleared; it may already be I_FREEING, and waiting on it can 64 * self-deadlock. 65 */ 66 if (!(VFS_I(base_ni)->i_sb->s_flags & SB_ACTIVE)) 67 return -EIO; 68 69 attr_vi = ntfs_attr_iget(VFS_I(base_ni), AT_ATTRIBUTE_LIST, AT_UNNAMED, 0); 70 if (IS_ERR(attr_vi)) { 71 err = PTR_ERR(attr_vi); 72 return err; 73 } 74 attr_ni = NTFS_I(attr_vi); 75 76 err = ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO); 77 if (err == -ENOSPC && attr_ni->mft_no == FILE_MFT) { 78 err = ntfs_attr_truncate(attr_ni, 0); 79 if (err || ntfs_attr_truncate_i(attr_ni, base_ni->attr_list_size, HOLES_NO) != 0) { 80 iput(attr_vi); 81 ntfs_error(base_ni->vol->sb, 82 "Failed to truncate attribute list of inode %#llx", 83 (long long)base_ni->mft_no); 84 return -EIO; 85 } 86 } else if (err) { 87 iput(attr_vi); 88 ntfs_error(base_ni->vol->sb, 89 "Failed to truncate attribute list of inode %#llx", 90 (long long)base_ni->mft_no); 91 return -EIO; 92 } 93 94 i_size_write(attr_vi, base_ni->attr_list_size); 95 96 if (NInoNonResident(attr_ni) && !NInoAttrListNonResident(base_ni)) 97 NInoSetAttrListNonResident(base_ni); 98 99 if (ntfs_inode_attr_pwrite(attr_vi, 0, base_ni->attr_list_size, 100 base_ni->attr_list, false) != 101 base_ni->attr_list_size) { 102 iput(attr_vi); 103 ntfs_error(base_ni->vol->sb, 104 "Failed to write attribute list of inode %#llx", 105 (long long)base_ni->mft_no); 106 return -EIO; 107 } 108 109 NInoSetAttrListDirty(base_ni); 110 iput(attr_vi); 111 return 0; 112 } 113 114 /* 115 * ntfs_attrlist_entry_add - add an attribute list attribute entry 116 * @ni: opened ntfs inode, which contains that attribute 117 * @attr: attribute record to add to attribute list 118 * 119 * Return 0 on success and -errno on error. 120 */ 121 int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr) 122 { 123 struct attr_list_entry *ale; 124 __le64 mref; 125 struct ntfs_attr_search_ctx *ctx; 126 u8 *new_al; 127 int entry_len, entry_offset, err; 128 struct mft_record *ni_mrec; 129 u8 *old_al; 130 __le64 lowest_vcn; 131 132 if (!ni || !attr) { 133 ntfs_debug("Invalid arguments.\n"); 134 return -EINVAL; 135 } 136 137 ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", 138 ni->mft_no, (unsigned int) le32_to_cpu(attr->type)); 139 140 ni_mrec = map_mft_record(ni); 141 if (IS_ERR(ni_mrec)) { 142 ntfs_debug("Invalid arguments.\n"); 143 return -EIO; 144 } 145 146 mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni_mrec->sequence_number)); 147 unmap_mft_record(ni); 148 149 if (ni->nr_extents == -1) 150 ni = ni->ext.base_ntfs_ino; 151 152 if (!NInoAttrList(ni)) { 153 ntfs_debug("Attribute list isn't present.\n"); 154 return -ENOENT; 155 } 156 157 /* Determine size and allocate memory for new attribute list. */ 158 entry_len = (sizeof(struct attr_list_entry) + sizeof(__le16) * 159 attr->name_length + 7) & ~7; 160 new_al = kvzalloc(ni->attr_list_size + entry_len, GFP_NOFS); 161 if (!new_al) 162 return -ENOMEM; 163 164 /* Find place for the new entry. */ 165 ctx = ntfs_attr_get_search_ctx(ni, NULL); 166 if (!ctx) { 167 err = -ENOMEM; 168 ntfs_error(ni->vol->sb, "Failed to get search context"); 169 goto err_out; 170 } 171 if (attr->non_resident) 172 lowest_vcn = attr->data.non_resident.lowest_vcn; 173 else 174 lowest_vcn = 0; 175 176 err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *) 177 ((u8 *)attr + le16_to_cpu(attr->name_offset)) : 178 AT_UNNAMED, attr->name_length, CASE_SENSITIVE, 179 le64_to_cpu(lowest_vcn), 180 (attr->non_resident) ? NULL : ((u8 *)attr + 181 le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ? 182 0 : le32_to_cpu(attr->data.resident.value_length), ctx); 183 if (!err) { 184 /* Found some extent, check it to be before new extent. */ 185 if (ctx->al_entry->lowest_vcn == lowest_vcn) { 186 err = -EEXIST; 187 ntfs_debug("Such attribute already present in the attribute list.\n"); 188 ntfs_attr_put_search_ctx(ctx); 189 goto err_out; 190 } 191 /* Add new entry after this extent. */ 192 ale = (struct attr_list_entry *)((u8 *)ctx->al_entry + 193 le16_to_cpu(ctx->al_entry->length)); 194 } else { 195 /* Check for real errors. */ 196 if (err != -ENOENT) { 197 ntfs_debug("Attribute lookup failed.\n"); 198 ntfs_attr_put_search_ctx(ctx); 199 goto err_out; 200 } 201 /* No previous extents found. */ 202 ale = ctx->al_entry; 203 } 204 /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ 205 ntfs_attr_put_search_ctx(ctx); 206 207 /* Determine new entry offset. */ 208 entry_offset = ((u8 *)ale - ni->attr_list); 209 /* Set pointer to new entry. */ 210 ale = (struct attr_list_entry *)(new_al + entry_offset); 211 memset(ale, 0, entry_len); 212 /* Form new entry. */ 213 ale->type = attr->type; 214 ale->length = cpu_to_le16(entry_len); 215 ale->name_length = attr->name_length; 216 ale->name_offset = offsetof(struct attr_list_entry, name); 217 if (attr->non_resident) 218 ale->lowest_vcn = attr->data.non_resident.lowest_vcn; 219 else 220 ale->lowest_vcn = 0; 221 ale->mft_reference = mref; 222 ale->instance = attr->instance; 223 memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), 224 attr->name_length * sizeof(__le16)); 225 226 /* Copy entries from old attribute list to new. */ 227 memcpy(new_al, ni->attr_list, entry_offset); 228 memcpy(new_al + entry_offset + entry_len, ni->attr_list + 229 entry_offset, ni->attr_list_size - entry_offset); 230 231 /* Set new runlist. */ 232 old_al = ni->attr_list; 233 ni->attr_list = new_al; 234 ni->attr_list_size = ni->attr_list_size + entry_len; 235 236 err = ntfs_attrlist_update(ni); 237 if (err) { 238 ni->attr_list = old_al; 239 ni->attr_list_size -= entry_len; 240 goto err_out; 241 } 242 kvfree(old_al); 243 return 0; 244 err_out: 245 kvfree(new_al); 246 return err; 247 } 248 249 /* 250 * ntfs_attrlist_entry_rm - remove an attribute list attribute entry 251 * @ctx: attribute search context describing the attribute list entry 252 * 253 * Remove the attribute list entry @ctx->al_entry from the attribute list. 254 * 255 * Return 0 on success and -errno on error. 256 */ 257 int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx) 258 { 259 u8 *new_al; 260 int new_al_len; 261 struct ntfs_inode *base_ni; 262 struct attr_list_entry *ale; 263 264 if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { 265 ntfs_debug("Invalid arguments.\n"); 266 return -EINVAL; 267 } 268 269 if (ctx->base_ntfs_ino) 270 base_ni = ctx->base_ntfs_ino; 271 else 272 base_ni = ctx->ntfs_ino; 273 ale = ctx->al_entry; 274 275 ntfs_debug("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", 276 (long long)ctx->ntfs_ino->mft_no, 277 (unsigned int)le32_to_cpu(ctx->al_entry->type), 278 (long long)le64_to_cpu(ctx->al_entry->lowest_vcn)); 279 280 if (!NInoAttrList(base_ni)) { 281 ntfs_debug("Attribute list isn't present.\n"); 282 return -ENOENT; 283 } 284 285 /* Allocate memory for new attribute list. */ 286 new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); 287 new_al = kvzalloc(new_al_len, GFP_NOFS); 288 if (!new_al) 289 return -ENOMEM; 290 291 /* Copy entries from old attribute list to new. */ 292 memcpy(new_al, base_ni->attr_list, (u8 *)ale - base_ni->attr_list); 293 memcpy(new_al + ((u8 *)ale - base_ni->attr_list), (u8 *)ale + le16_to_cpu( 294 ale->length), new_al_len - ((u8 *)ale - base_ni->attr_list)); 295 296 /* Set new runlist. */ 297 kvfree(base_ni->attr_list); 298 base_ni->attr_list = new_al; 299 base_ni->attr_list_size = new_al_len; 300 301 return ntfs_attrlist_update(base_ni); 302 } 303