| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- *
- * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
- *
- */
- #include <linux/fs.h>
- #include "debug.h"
- #include "ntfs.h"
- #include "ntfs_fs.h"
- /*
- * al_is_valid_le
- *
- * Return: True if @le is valid.
- */
- static inline bool al_is_valid_le(const struct ntfs_inode *ni,
- struct ATTR_LIST_ENTRY *le)
- {
- if (!le || !ni->attr_list.le || !ni->attr_list.size)
- return false;
- return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <=
- ni->attr_list.size;
- }
- void al_destroy(struct ntfs_inode *ni)
- {
- run_close(&ni->attr_list.run);
- kvfree(ni->attr_list.le);
- ni->attr_list.le = NULL;
- ni->attr_list.size = 0;
- ni->attr_list.dirty = false;
- }
- /*
- * ntfs_load_attr_list
- *
- * This method makes sure that the ATTRIB list, if present,
- * has been properly set up.
- */
- int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
- {
- int err;
- size_t lsize;
- void *le = NULL;
- if (ni->attr_list.size)
- return 0;
- if (!attr->non_res) {
- lsize = le32_to_cpu(attr->res.data_size);
- if (!lsize) {
- err = -EINVAL;
- goto out;
- }
- /* attr is resident: lsize < record_size (1K or 4K) */
- le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
- if (!le) {
- err = -ENOMEM;
- goto out;
- }
- memcpy(le, resident_data(attr), lsize);
- } else if (attr->nres.svcn) {
- err = -EINVAL;
- goto out;
- } else {
- u16 run_off = le16_to_cpu(attr->nres.run_off);
- lsize = le64_to_cpu(attr->nres.data_size);
- if (!lsize) {
- err = -EINVAL;
- goto out;
- }
- run_init(&ni->attr_list.run);
- if (run_off > le32_to_cpu(attr->size)) {
- err = -EINVAL;
- goto out;
- }
- err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno,
- 0, le64_to_cpu(attr->nres.evcn), 0,
- Add2Ptr(attr, run_off),
- le32_to_cpu(attr->size) - run_off);
- if (err < 0)
- goto out;
- /* attr is nonresident.
- * The worst case:
- * 1T (2^40) extremely fragmented file.
- * cluster = 4K (2^12) => 2^28 fragments
- * 2^9 fragments per one record => 2^19 records
- * 2^5 bytes of ATTR_LIST_ENTRY per one record => 2^24 bytes.
- *
- * the result is 16M bytes per attribute list.
- * Use kvmalloc to allocate in range [several Kbytes - dozen Mbytes]
- */
- le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
- if (!le) {
- err = -ENOMEM;
- goto out;
- }
- err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le,
- lsize, NULL);
- if (err)
- goto out;
- }
- ni->attr_list.size = lsize;
- ni->attr_list.le = le;
- return 0;
- out:
- ni->attr_list.le = le;
- al_destroy(ni);
- return err;
- }
- /*
- * al_enumerate
- *
- * Return:
- * * The next list le.
- * * If @le is NULL then return the first le.
- */
- struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
- struct ATTR_LIST_ENTRY *le)
- {
- size_t off;
- u16 sz;
- const unsigned le_min_size = le_size(0);
- if (!le) {
- le = ni->attr_list.le;
- } else {
- sz = le16_to_cpu(le->size);
- if (sz < le_min_size) {
- /* Impossible 'cause we should not return such le. */
- return NULL;
- }
- le = Add2Ptr(le, sz);
- }
- /* Check boundary. */
- off = PtrOffset(ni->attr_list.le, le);
- if (off + le_min_size > ni->attr_list.size) {
- /* The regular end of list. */
- return NULL;
- }
- sz = le16_to_cpu(le->size);
- /* Check le for errors. */
- if (sz < le_min_size || off + sz > ni->attr_list.size ||
- sz < le->name_off + le->name_len * sizeof(short)) {
- return NULL;
- }
- return le;
- }
- /*
- * al_find_le
- *
- * Find the first le in the list which matches type, name and VCN.
- *
- * Return: NULL if not found.
- */
- struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni,
- struct ATTR_LIST_ENTRY *le,
- const struct ATTRIB *attr)
- {
- CLST svcn = attr_svcn(attr);
- return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len,
- &svcn);
- }
- /*
- * al_find_ex
- *
- * Find the first le in the list which matches type, name and VCN.
- *
- * Return: NULL if not found.
- */
- struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni,
- struct ATTR_LIST_ENTRY *le,
- enum ATTR_TYPE type, const __le16 *name,
- u8 name_len, const CLST *vcn)
- {
- struct ATTR_LIST_ENTRY *ret = NULL;
- u32 type_in = le32_to_cpu(type);
- while ((le = al_enumerate(ni, le))) {
- u64 le_vcn;
- int diff = le32_to_cpu(le->type) - type_in;
- /* List entries are sorted by type, name and VCN. */
- if (diff < 0)
- continue;
- if (diff > 0)
- return ret;
- if (le->name_len != name_len)
- continue;
- le_vcn = le64_to_cpu(le->vcn);
- if (!le_vcn) {
- /*
- * Compare entry names only for entry with vcn == 0.
- */
- diff = ntfs_cmp_names(le_name(le), name_len, name,
- name_len, ni->mi.sbi->upcase,
- true);
- if (diff < 0)
- continue;
- if (diff > 0)
- return ret;
- }
- if (!vcn)
- return le;
- if (*vcn == le_vcn)
- return le;
- if (*vcn < le_vcn)
- return ret;
- ret = le;
- }
- return ret;
- }
- /*
- * al_find_le_to_insert
- *
- * Find the first list entry which matches type, name and VCN.
- */
- static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni,
- enum ATTR_TYPE type,
- const __le16 *name,
- u8 name_len, CLST vcn)
- {
- struct ATTR_LIST_ENTRY *le = NULL, *prev;
- u32 type_in = le32_to_cpu(type);
- /* List entries are sorted by type, name and VCN. */
- while ((le = al_enumerate(ni, prev = le))) {
- int diff = le32_to_cpu(le->type) - type_in;
- if (diff < 0)
- continue;
- if (diff > 0)
- return le;
- if (!le->vcn) {
- /*
- * Compare entry names only for entry with vcn == 0.
- */
- diff = ntfs_cmp_names(le_name(le), le->name_len, name,
- name_len, ni->mi.sbi->upcase,
- true);
- if (diff < 0)
- continue;
- if (diff > 0)
- return le;
- }
- if (le64_to_cpu(le->vcn) >= vcn)
- return le;
- }
- return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le;
- }
- /*
- * al_add_le
- *
- * Add an "attribute list entry" to the list.
- */
- int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
- u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref,
- struct ATTR_LIST_ENTRY **new_le)
- {
- int err;
- struct ATTRIB *attr;
- struct ATTR_LIST_ENTRY *le;
- size_t off;
- u16 sz;
- size_t asize, new_asize, old_size;
- u64 new_size;
- typeof(ni->attr_list) *al = &ni->attr_list;
- /*
- * Compute the size of the new 'le'
- */
- sz = le_size(name_len);
- old_size = al->size;
- new_size = old_size + sz;
- asize = al_aligned(old_size);
- new_asize = al_aligned(new_size);
- /* Scan forward to the point at which the new 'le' should be inserted. */
- le = al_find_le_to_insert(ni, type, name, name_len, svcn);
- off = PtrOffset(al->le, le);
- if (new_size > asize) {
- void *ptr = kmalloc(new_asize, GFP_NOFS);
- if (!ptr)
- return -ENOMEM;
- memcpy(ptr, al->le, off);
- memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
- le = Add2Ptr(ptr, off);
- kvfree(al->le);
- al->le = ptr;
- } else {
- memmove(Add2Ptr(le, sz), le, old_size - off);
- }
- *new_le = le;
- al->size = new_size;
- le->type = type;
- le->size = cpu_to_le16(sz);
- le->name_len = name_len;
- le->name_off = offsetof(struct ATTR_LIST_ENTRY, name);
- le->vcn = cpu_to_le64(svcn);
- le->ref = *ref;
- le->id = id;
- memcpy(le->name, name, sizeof(short) * name_len);
- err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
- &new_size, true, &attr, false);
- if (err) {
- /* Undo memmove above. */
- memmove(le, Add2Ptr(le, sz), old_size - off);
- al->size = old_size;
- return err;
- }
- al->dirty = true;
- if (attr && attr->non_res) {
- err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
- al->size, 0);
- if (err)
- return err;
- al->dirty = false;
- }
- return 0;
- }
- /*
- * al_remove_le - Remove @le from attribute list.
- */
- bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
- {
- u16 size;
- size_t off;
- typeof(ni->attr_list) *al = &ni->attr_list;
- if (!al_is_valid_le(ni, le))
- return false;
- /* Save on stack the size of 'le' */
- size = le16_to_cpu(le->size);
- off = PtrOffset(al->le, le);
- memmove(le, Add2Ptr(le, size), al->size - (off + size));
- al->size -= size;
- al->dirty = true;
- return true;
- }
- int al_update(struct ntfs_inode *ni, int sync)
- {
- int err;
- struct ATTRIB *attr;
- typeof(ni->attr_list) *al = &ni->attr_list;
- if (!al->dirty || !al->size)
- return 0;
- /*
- * Attribute list increased on demand in al_add_le.
- * Attribute list decreased here.
- */
- err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
- false, &attr, false);
- if (err)
- goto out;
- if (!attr->non_res) {
- memcpy(resident_data(attr), al->le, al->size);
- } else {
- err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
- al->size, sync);
- if (err)
- goto out;
- attr->nres.valid_size = attr->nres.data_size;
- }
- ni->mi.dirty = true;
- al->dirty = false;
- out:
- return err;
- }
|