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