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 ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n", 123 (long long) ni->mft_no, 124 (unsigned int) le32_to_cpu(attr->type)); 125 126 if (!ni || !attr) { 127 ntfs_debug("Invalid arguments.\n"); 128 return -EINVAL; 129 } 130 131 ni_mrec = map_mft_record(ni); 132 if (IS_ERR(ni_mrec)) { 133 ntfs_debug("Invalid arguments.\n"); 134 return -EIO; 135 } 136 137 mref = MK_LE_MREF(ni->mft_no, le16_to_cpu(ni_mrec->sequence_number)); 138 unmap_mft_record(ni); 139 140 if (ni->nr_extents == -1) 141 ni = ni->ext.base_ntfs_ino; 142 143 if (!NInoAttrList(ni)) { 144 ntfs_debug("Attribute list isn't present.\n"); 145 return -ENOENT; 146 } 147 148 /* Determine size and allocate memory for new attribute list. */ 149 entry_len = (sizeof(struct attr_list_entry) + sizeof(__le16) * 150 attr->name_length + 7) & ~7; 151 new_al = kvzalloc(ni->attr_list_size + entry_len, GFP_NOFS); 152 if (!new_al) 153 return -ENOMEM; 154 155 /* Find place for the new entry. */ 156 ctx = ntfs_attr_get_search_ctx(ni, NULL); 157 if (!ctx) { 158 err = -ENOMEM; 159 ntfs_error(ni->vol->sb, "Failed to get search context"); 160 goto err_out; 161 } 162 163 err = ntfs_attr_lookup(attr->type, (attr->name_length) ? (__le16 *) 164 ((u8 *)attr + le16_to_cpu(attr->name_offset)) : 165 AT_UNNAMED, attr->name_length, CASE_SENSITIVE, 166 (attr->non_resident) ? le64_to_cpu(attr->data.non_resident.lowest_vcn) : 167 0, (attr->non_resident) ? NULL : ((u8 *)attr + 168 le16_to_cpu(attr->data.resident.value_offset)), (attr->non_resident) ? 169 0 : le32_to_cpu(attr->data.resident.value_length), ctx); 170 if (!err) { 171 /* Found some extent, check it to be before new extent. */ 172 if (ctx->al_entry->lowest_vcn == attr->data.non_resident.lowest_vcn) { 173 err = -EEXIST; 174 ntfs_debug("Such attribute already present in the attribute list.\n"); 175 ntfs_attr_put_search_ctx(ctx); 176 goto err_out; 177 } 178 /* Add new entry after this extent. */ 179 ale = (struct attr_list_entry *)((u8 *)ctx->al_entry + 180 le16_to_cpu(ctx->al_entry->length)); 181 } else { 182 /* Check for real errors. */ 183 if (err != -ENOENT) { 184 ntfs_debug("Attribute lookup failed.\n"); 185 ntfs_attr_put_search_ctx(ctx); 186 goto err_out; 187 } 188 /* No previous extents found. */ 189 ale = ctx->al_entry; 190 } 191 /* Don't need it anymore, @ctx->al_entry points to @ni->attr_list. */ 192 ntfs_attr_put_search_ctx(ctx); 193 194 /* Determine new entry offset. */ 195 entry_offset = ((u8 *)ale - ni->attr_list); 196 /* Set pointer to new entry. */ 197 ale = (struct attr_list_entry *)(new_al + entry_offset); 198 memset(ale, 0, entry_len); 199 /* Form new entry. */ 200 ale->type = attr->type; 201 ale->length = cpu_to_le16(entry_len); 202 ale->name_length = attr->name_length; 203 ale->name_offset = offsetof(struct attr_list_entry, name); 204 if (attr->non_resident) 205 ale->lowest_vcn = attr->data.non_resident.lowest_vcn; 206 else 207 ale->lowest_vcn = 0; 208 ale->mft_reference = mref; 209 ale->instance = attr->instance; 210 memcpy(ale->name, (u8 *)attr + le16_to_cpu(attr->name_offset), 211 attr->name_length * sizeof(__le16)); 212 213 /* Copy entries from old attribute list to new. */ 214 memcpy(new_al, ni->attr_list, entry_offset); 215 memcpy(new_al + entry_offset + entry_len, ni->attr_list + 216 entry_offset, ni->attr_list_size - entry_offset); 217 218 /* Set new runlist. */ 219 old_al = ni->attr_list; 220 ni->attr_list = new_al; 221 ni->attr_list_size = ni->attr_list_size + entry_len; 222 223 err = ntfs_attrlist_update(ni); 224 if (err) { 225 ni->attr_list = old_al; 226 ni->attr_list_size -= entry_len; 227 goto err_out; 228 } 229 kvfree(old_al); 230 return 0; 231 err_out: 232 kvfree(new_al); 233 return err; 234 } 235 236 /* 237 * ntfs_attrlist_entry_rm - remove an attribute list attribute entry 238 * @ctx: attribute search context describing the attribute list entry 239 * 240 * Remove the attribute list entry @ctx->al_entry from the attribute list. 241 * 242 * Return 0 on success and -errno on error. 243 */ 244 int ntfs_attrlist_entry_rm(struct ntfs_attr_search_ctx *ctx) 245 { 246 u8 *new_al; 247 int new_al_len; 248 struct ntfs_inode *base_ni; 249 struct attr_list_entry *ale; 250 251 if (!ctx || !ctx->ntfs_ino || !ctx->al_entry) { 252 ntfs_debug("Invalid arguments.\n"); 253 return -EINVAL; 254 } 255 256 if (ctx->base_ntfs_ino) 257 base_ni = ctx->base_ntfs_ino; 258 else 259 base_ni = ctx->ntfs_ino; 260 ale = ctx->al_entry; 261 262 ntfs_debug("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld.\n", 263 (long long)ctx->ntfs_ino->mft_no, 264 (unsigned int)le32_to_cpu(ctx->al_entry->type), 265 (long long)le64_to_cpu(ctx->al_entry->lowest_vcn)); 266 267 if (!NInoAttrList(base_ni)) { 268 ntfs_debug("Attribute list isn't present.\n"); 269 return -ENOENT; 270 } 271 272 /* Allocate memory for new attribute list. */ 273 new_al_len = base_ni->attr_list_size - le16_to_cpu(ale->length); 274 new_al = kvzalloc(new_al_len, GFP_NOFS); 275 if (!new_al) 276 return -ENOMEM; 277 278 /* Copy entries from old attribute list to new. */ 279 memcpy(new_al, base_ni->attr_list, (u8 *)ale - base_ni->attr_list); 280 memcpy(new_al + ((u8 *)ale - base_ni->attr_list), (u8 *)ale + le16_to_cpu( 281 ale->length), new_al_len - ((u8 *)ale - base_ni->attr_list)); 282 283 /* Set new runlist. */ 284 kvfree(base_ni->attr_list); 285 base_ni->attr_list = new_al; 286 base_ni->attr_list_size = new_al_len; 287 288 return ntfs_attrlist_update(base_ni); 289 } 290