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