162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/fs.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "debug.h" 1162306a36Sopenharmony_ci#include "ntfs.h" 1262306a36Sopenharmony_ci#include "ntfs_fs.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* 1562306a36Sopenharmony_ci * al_is_valid_le 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Return: True if @le is valid. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_cistatic inline bool al_is_valid_le(const struct ntfs_inode *ni, 2062306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci if (!le || !ni->attr_list.le || !ni->attr_list.size) 2362306a36Sopenharmony_ci return false; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <= 2662306a36Sopenharmony_ci ni->attr_list.size; 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_civoid al_destroy(struct ntfs_inode *ni) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci run_close(&ni->attr_list.run); 3262306a36Sopenharmony_ci kvfree(ni->attr_list.le); 3362306a36Sopenharmony_ci ni->attr_list.le = NULL; 3462306a36Sopenharmony_ci ni->attr_list.size = 0; 3562306a36Sopenharmony_ci ni->attr_list.dirty = false; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * ntfs_load_attr_list 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * This method makes sure that the ATTRIB list, if present, 4262306a36Sopenharmony_ci * has been properly set up. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ciint ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int err; 4762306a36Sopenharmony_ci size_t lsize; 4862306a36Sopenharmony_ci void *le = NULL; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (ni->attr_list.size) 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci if (!attr->non_res) { 5462306a36Sopenharmony_ci lsize = le32_to_cpu(attr->res.data_size); 5562306a36Sopenharmony_ci /* attr is resident: lsize < record_size (1K or 4K) */ 5662306a36Sopenharmony_ci le = kvmalloc(al_aligned(lsize), GFP_KERNEL); 5762306a36Sopenharmony_ci if (!le) { 5862306a36Sopenharmony_ci err = -ENOMEM; 5962306a36Sopenharmony_ci goto out; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci memcpy(le, resident_data(attr), lsize); 6262306a36Sopenharmony_ci } else if (attr->nres.svcn) { 6362306a36Sopenharmony_ci err = -EINVAL; 6462306a36Sopenharmony_ci goto out; 6562306a36Sopenharmony_ci } else { 6662306a36Sopenharmony_ci u16 run_off = le16_to_cpu(attr->nres.run_off); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci lsize = le64_to_cpu(attr->nres.data_size); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci run_init(&ni->attr_list.run); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (run_off > le32_to_cpu(attr->size)) { 7362306a36Sopenharmony_ci err = -EINVAL; 7462306a36Sopenharmony_ci goto out; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno, 7862306a36Sopenharmony_ci 0, le64_to_cpu(attr->nres.evcn), 0, 7962306a36Sopenharmony_ci Add2Ptr(attr, run_off), 8062306a36Sopenharmony_ci le32_to_cpu(attr->size) - run_off); 8162306a36Sopenharmony_ci if (err < 0) 8262306a36Sopenharmony_ci goto out; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* attr is nonresident. 8562306a36Sopenharmony_ci * The worst case: 8662306a36Sopenharmony_ci * 1T (2^40) extremely fragmented file. 8762306a36Sopenharmony_ci * cluster = 4K (2^12) => 2^28 fragments 8862306a36Sopenharmony_ci * 2^9 fragments per one record => 2^19 records 8962306a36Sopenharmony_ci * 2^5 bytes of ATTR_LIST_ENTRY per one record => 2^24 bytes. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * the result is 16M bytes per attribute list. 9262306a36Sopenharmony_ci * Use kvmalloc to allocate in range [several Kbytes - dozen Mbytes] 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci le = kvmalloc(al_aligned(lsize), GFP_KERNEL); 9562306a36Sopenharmony_ci if (!le) { 9662306a36Sopenharmony_ci err = -ENOMEM; 9762306a36Sopenharmony_ci goto out; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le, 10162306a36Sopenharmony_ci lsize, NULL); 10262306a36Sopenharmony_ci if (err) 10362306a36Sopenharmony_ci goto out; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ni->attr_list.size = lsize; 10762306a36Sopenharmony_ci ni->attr_list.le = le; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciout: 11262306a36Sopenharmony_ci ni->attr_list.le = le; 11362306a36Sopenharmony_ci al_destroy(ni); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return err; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * al_enumerate 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Return: 12262306a36Sopenharmony_ci * * The next list le. 12362306a36Sopenharmony_ci * * If @le is NULL then return the first le. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistruct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, 12662306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci size_t off; 12962306a36Sopenharmony_ci u16 sz; 13062306a36Sopenharmony_ci const unsigned le_min_size = le_size(0); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!le) { 13362306a36Sopenharmony_ci le = ni->attr_list.le; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci sz = le16_to_cpu(le->size); 13662306a36Sopenharmony_ci if (sz < le_min_size) { 13762306a36Sopenharmony_ci /* Impossible 'cause we should not return such le. */ 13862306a36Sopenharmony_ci return NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci le = Add2Ptr(le, sz); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Check boundary. */ 14462306a36Sopenharmony_ci off = PtrOffset(ni->attr_list.le, le); 14562306a36Sopenharmony_ci if (off + le_min_size > ni->attr_list.size) { 14662306a36Sopenharmony_ci /* The regular end of list. */ 14762306a36Sopenharmony_ci return NULL; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci sz = le16_to_cpu(le->size); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Check le for errors. */ 15362306a36Sopenharmony_ci if (sz < le_min_size || off + sz > ni->attr_list.size || 15462306a36Sopenharmony_ci sz < le->name_off + le->name_len * sizeof(short)) { 15562306a36Sopenharmony_ci return NULL; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return le; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* 16262306a36Sopenharmony_ci * al_find_le 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * Find the first le in the list which matches type, name and VCN. 16562306a36Sopenharmony_ci * 16662306a36Sopenharmony_ci * Return: NULL if not found. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistruct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, 16962306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le, 17062306a36Sopenharmony_ci const struct ATTRIB *attr) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci CLST svcn = attr_svcn(attr); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len, 17562306a36Sopenharmony_ci &svcn); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * al_find_ex 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * Find the first le in the list which matches type, name and VCN. 18262306a36Sopenharmony_ci * 18362306a36Sopenharmony_ci * Return: NULL if not found. 18462306a36Sopenharmony_ci */ 18562306a36Sopenharmony_cistruct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, 18662306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le, 18762306a36Sopenharmony_ci enum ATTR_TYPE type, const __le16 *name, 18862306a36Sopenharmony_ci u8 name_len, const CLST *vcn) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *ret = NULL; 19162306a36Sopenharmony_ci u32 type_in = le32_to_cpu(type); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci while ((le = al_enumerate(ni, le))) { 19462306a36Sopenharmony_ci u64 le_vcn; 19562306a36Sopenharmony_ci int diff = le32_to_cpu(le->type) - type_in; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* List entries are sorted by type, name and VCN. */ 19862306a36Sopenharmony_ci if (diff < 0) 19962306a36Sopenharmony_ci continue; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (diff > 0) 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (le->name_len != name_len) 20562306a36Sopenharmony_ci continue; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci le_vcn = le64_to_cpu(le->vcn); 20862306a36Sopenharmony_ci if (!le_vcn) { 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Compare entry names only for entry with vcn == 0. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci diff = ntfs_cmp_names(le_name(le), name_len, name, 21362306a36Sopenharmony_ci name_len, ni->mi.sbi->upcase, 21462306a36Sopenharmony_ci true); 21562306a36Sopenharmony_ci if (diff < 0) 21662306a36Sopenharmony_ci continue; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (diff > 0) 21962306a36Sopenharmony_ci return ret; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!vcn) 22362306a36Sopenharmony_ci return le; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (*vcn == le_vcn) 22662306a36Sopenharmony_ci return le; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (*vcn < le_vcn) 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci ret = le; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * al_find_le_to_insert 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Find the first list entry which matches type, name and VCN. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, 24362306a36Sopenharmony_ci enum ATTR_TYPE type, 24462306a36Sopenharmony_ci const __le16 *name, 24562306a36Sopenharmony_ci u8 name_len, CLST vcn) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le = NULL, *prev; 24862306a36Sopenharmony_ci u32 type_in = le32_to_cpu(type); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* List entries are sorted by type, name and VCN. */ 25162306a36Sopenharmony_ci while ((le = al_enumerate(ni, prev = le))) { 25262306a36Sopenharmony_ci int diff = le32_to_cpu(le->type) - type_in; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (diff < 0) 25562306a36Sopenharmony_ci continue; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (diff > 0) 25862306a36Sopenharmony_ci return le; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!le->vcn) { 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * Compare entry names only for entry with vcn == 0. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci diff = ntfs_cmp_names(le_name(le), le->name_len, name, 26562306a36Sopenharmony_ci name_len, ni->mi.sbi->upcase, 26662306a36Sopenharmony_ci true); 26762306a36Sopenharmony_ci if (diff < 0) 26862306a36Sopenharmony_ci continue; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (diff > 0) 27162306a36Sopenharmony_ci return le; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (le64_to_cpu(le->vcn) >= vcn) 27562306a36Sopenharmony_ci return le; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * al_add_le 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * Add an "attribute list entry" to the list. 28562306a36Sopenharmony_ci */ 28662306a36Sopenharmony_ciint al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, 28762306a36Sopenharmony_ci u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, 28862306a36Sopenharmony_ci struct ATTR_LIST_ENTRY **new_le) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci int err; 29162306a36Sopenharmony_ci struct ATTRIB *attr; 29262306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 29362306a36Sopenharmony_ci size_t off; 29462306a36Sopenharmony_ci u16 sz; 29562306a36Sopenharmony_ci size_t asize, new_asize, old_size; 29662306a36Sopenharmony_ci u64 new_size; 29762306a36Sopenharmony_ci typeof(ni->attr_list) *al = &ni->attr_list; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * Compute the size of the new 'le' 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci sz = le_size(name_len); 30362306a36Sopenharmony_ci old_size = al->size; 30462306a36Sopenharmony_ci new_size = old_size + sz; 30562306a36Sopenharmony_ci asize = al_aligned(old_size); 30662306a36Sopenharmony_ci new_asize = al_aligned(new_size); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Scan forward to the point at which the new 'le' should be inserted. */ 30962306a36Sopenharmony_ci le = al_find_le_to_insert(ni, type, name, name_len, svcn); 31062306a36Sopenharmony_ci off = PtrOffset(al->le, le); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (new_size > asize) { 31362306a36Sopenharmony_ci void *ptr = kmalloc(new_asize, GFP_NOFS); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!ptr) 31662306a36Sopenharmony_ci return -ENOMEM; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci memcpy(ptr, al->le, off); 31962306a36Sopenharmony_ci memcpy(Add2Ptr(ptr, off + sz), le, old_size - off); 32062306a36Sopenharmony_ci le = Add2Ptr(ptr, off); 32162306a36Sopenharmony_ci kvfree(al->le); 32262306a36Sopenharmony_ci al->le = ptr; 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci memmove(Add2Ptr(le, sz), le, old_size - off); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci *new_le = le; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci al->size = new_size; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci le->type = type; 33162306a36Sopenharmony_ci le->size = cpu_to_le16(sz); 33262306a36Sopenharmony_ci le->name_len = name_len; 33362306a36Sopenharmony_ci le->name_off = offsetof(struct ATTR_LIST_ENTRY, name); 33462306a36Sopenharmony_ci le->vcn = cpu_to_le64(svcn); 33562306a36Sopenharmony_ci le->ref = *ref; 33662306a36Sopenharmony_ci le->id = id; 33762306a36Sopenharmony_ci memcpy(le->name, name, sizeof(short) * name_len); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size, 34062306a36Sopenharmony_ci &new_size, true, &attr); 34162306a36Sopenharmony_ci if (err) { 34262306a36Sopenharmony_ci /* Undo memmove above. */ 34362306a36Sopenharmony_ci memmove(le, Add2Ptr(le, sz), old_size - off); 34462306a36Sopenharmony_ci al->size = old_size; 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci al->dirty = true; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (attr && attr->non_res) { 35162306a36Sopenharmony_ci err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, 35262306a36Sopenharmony_ci al->size, 0); 35362306a36Sopenharmony_ci if (err) 35462306a36Sopenharmony_ci return err; 35562306a36Sopenharmony_ci al->dirty = false; 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/* 36262306a36Sopenharmony_ci * al_remove_le - Remove @le from attribute list. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cibool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci u16 size; 36762306a36Sopenharmony_ci size_t off; 36862306a36Sopenharmony_ci typeof(ni->attr_list) *al = &ni->attr_list; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (!al_is_valid_le(ni, le)) 37162306a36Sopenharmony_ci return false; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* Save on stack the size of 'le' */ 37462306a36Sopenharmony_ci size = le16_to_cpu(le->size); 37562306a36Sopenharmony_ci off = PtrOffset(al->le, le); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci memmove(le, Add2Ptr(le, size), al->size - (off + size)); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci al->size -= size; 38062306a36Sopenharmony_ci al->dirty = true; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return true; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci/* 38662306a36Sopenharmony_ci * al_delete_le - Delete first le from the list which matches its parameters. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cibool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, 38962306a36Sopenharmony_ci const __le16 *name, u8 name_len, const struct MFT_REF *ref) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci u16 size; 39262306a36Sopenharmony_ci struct ATTR_LIST_ENTRY *le; 39362306a36Sopenharmony_ci size_t off; 39462306a36Sopenharmony_ci typeof(ni->attr_list) *al = &ni->attr_list; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Scan forward to the first le that matches the input. */ 39762306a36Sopenharmony_ci le = al_find_ex(ni, NULL, type, name, name_len, &vcn); 39862306a36Sopenharmony_ci if (!le) 39962306a36Sopenharmony_ci return false; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci off = PtrOffset(al->le, le); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cinext: 40462306a36Sopenharmony_ci if (off >= al->size) 40562306a36Sopenharmony_ci return false; 40662306a36Sopenharmony_ci if (le->type != type) 40762306a36Sopenharmony_ci return false; 40862306a36Sopenharmony_ci if (le->name_len != name_len) 40962306a36Sopenharmony_ci return false; 41062306a36Sopenharmony_ci if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len, 41162306a36Sopenharmony_ci ni->mi.sbi->upcase, true)) 41262306a36Sopenharmony_ci return false; 41362306a36Sopenharmony_ci if (le64_to_cpu(le->vcn) != vcn) 41462306a36Sopenharmony_ci return false; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * The caller specified a segment reference, so we have to 41862306a36Sopenharmony_ci * scan through the matching entries until we find that segment 41962306a36Sopenharmony_ci * reference or we run of matching entries. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci if (ref && memcmp(ref, &le->ref, sizeof(*ref))) { 42262306a36Sopenharmony_ci off += le16_to_cpu(le->size); 42362306a36Sopenharmony_ci le = Add2Ptr(al->le, off); 42462306a36Sopenharmony_ci goto next; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Save on stack the size of 'le'. */ 42862306a36Sopenharmony_ci size = le16_to_cpu(le->size); 42962306a36Sopenharmony_ci /* Delete the le. */ 43062306a36Sopenharmony_ci memmove(le, Add2Ptr(le, size), al->size - (off + size)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci al->size -= size; 43362306a36Sopenharmony_ci al->dirty = true; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return true; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciint al_update(struct ntfs_inode *ni, int sync) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci int err; 44162306a36Sopenharmony_ci struct ATTRIB *attr; 44262306a36Sopenharmony_ci typeof(ni->attr_list) *al = &ni->attr_list; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (!al->dirty || !al->size) 44562306a36Sopenharmony_ci return 0; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Attribute list increased on demand in al_add_le. 44962306a36Sopenharmony_ci * Attribute list decreased here. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL, 45262306a36Sopenharmony_ci false, &attr); 45362306a36Sopenharmony_ci if (err) 45462306a36Sopenharmony_ci goto out; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (!attr->non_res) { 45762306a36Sopenharmony_ci memcpy(resident_data(attr), al->le, al->size); 45862306a36Sopenharmony_ci } else { 45962306a36Sopenharmony_ci err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, 46062306a36Sopenharmony_ci al->size, sync); 46162306a36Sopenharmony_ci if (err) 46262306a36Sopenharmony_ci goto out; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci attr->nres.valid_size = attr->nres.data_size; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ni->mi.dirty = true; 46862306a36Sopenharmony_ci al->dirty = false; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciout: 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci} 473