Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *
0004  * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
0005  *
0006  */
0007 
0008 #include <linux/fs.h>
0009 
0010 #include "debug.h"
0011 #include "ntfs.h"
0012 #include "ntfs_fs.h"
0013 
0014 /*
0015  * al_is_valid_le
0016  *
0017  * Return: True if @le is valid.
0018  */
0019 static inline bool al_is_valid_le(const struct ntfs_inode *ni,
0020                   struct ATTR_LIST_ENTRY *le)
0021 {
0022     if (!le || !ni->attr_list.le || !ni->attr_list.size)
0023         return false;
0024 
0025     return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <=
0026            ni->attr_list.size;
0027 }
0028 
0029 void al_destroy(struct ntfs_inode *ni)
0030 {
0031     run_close(&ni->attr_list.run);
0032     kfree(ni->attr_list.le);
0033     ni->attr_list.le = NULL;
0034     ni->attr_list.size = 0;
0035     ni->attr_list.dirty = false;
0036 }
0037 
0038 /*
0039  * ntfs_load_attr_list
0040  *
0041  * This method makes sure that the ATTRIB list, if present,
0042  * has been properly set up.
0043  */
0044 int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
0045 {
0046     int err;
0047     size_t lsize;
0048     void *le = NULL;
0049 
0050     if (ni->attr_list.size)
0051         return 0;
0052 
0053     if (!attr->non_res) {
0054         lsize = le32_to_cpu(attr->res.data_size);
0055         le = kmalloc(al_aligned(lsize), GFP_NOFS);
0056         if (!le) {
0057             err = -ENOMEM;
0058             goto out;
0059         }
0060         memcpy(le, resident_data(attr), lsize);
0061     } else if (attr->nres.svcn) {
0062         err = -EINVAL;
0063         goto out;
0064     } else {
0065         u16 run_off = le16_to_cpu(attr->nres.run_off);
0066 
0067         lsize = le64_to_cpu(attr->nres.data_size);
0068 
0069         run_init(&ni->attr_list.run);
0070 
0071         err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno,
0072                     0, le64_to_cpu(attr->nres.evcn), 0,
0073                     Add2Ptr(attr, run_off),
0074                     le32_to_cpu(attr->size) - run_off);
0075         if (err < 0)
0076             goto out;
0077 
0078         le = kmalloc(al_aligned(lsize), GFP_NOFS);
0079         if (!le) {
0080             err = -ENOMEM;
0081             goto out;
0082         }
0083 
0084         err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le,
0085                        lsize, NULL);
0086         if (err)
0087             goto out;
0088     }
0089 
0090     ni->attr_list.size = lsize;
0091     ni->attr_list.le = le;
0092 
0093     return 0;
0094 
0095 out:
0096     ni->attr_list.le = le;
0097     al_destroy(ni);
0098 
0099     return err;
0100 }
0101 
0102 /*
0103  * al_enumerate
0104  *
0105  * Return:
0106  * * The next list le.
0107  * * If @le is NULL then return the first le.
0108  */
0109 struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
0110                      struct ATTR_LIST_ENTRY *le)
0111 {
0112     size_t off;
0113     u16 sz;
0114 
0115     if (!le) {
0116         le = ni->attr_list.le;
0117     } else {
0118         sz = le16_to_cpu(le->size);
0119         if (sz < sizeof(struct ATTR_LIST_ENTRY)) {
0120             /* Impossible 'cause we should not return such le. */
0121             return NULL;
0122         }
0123         le = Add2Ptr(le, sz);
0124     }
0125 
0126     /* Check boundary. */
0127     off = PtrOffset(ni->attr_list.le, le);
0128     if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
0129         /* The regular end of list. */
0130         return NULL;
0131     }
0132 
0133     sz = le16_to_cpu(le->size);
0134 
0135     /* Check le for errors. */
0136     if (sz < sizeof(struct ATTR_LIST_ENTRY) ||
0137         off + sz > ni->attr_list.size ||
0138         sz < le->name_off + le->name_len * sizeof(short)) {
0139         return NULL;
0140     }
0141 
0142     return le;
0143 }
0144 
0145 /*
0146  * al_find_le
0147  *
0148  * Find the first le in the list which matches type, name and VCN.
0149  *
0150  * Return: NULL if not found.
0151  */
0152 struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni,
0153                    struct ATTR_LIST_ENTRY *le,
0154                    const struct ATTRIB *attr)
0155 {
0156     CLST svcn = attr_svcn(attr);
0157 
0158     return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len,
0159               &svcn);
0160 }
0161 
0162 /*
0163  * al_find_ex
0164  *
0165  * Find the first le in the list which matches type, name and VCN.
0166  *
0167  * Return: NULL if not found.
0168  */
0169 struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni,
0170                    struct ATTR_LIST_ENTRY *le,
0171                    enum ATTR_TYPE type, const __le16 *name,
0172                    u8 name_len, const CLST *vcn)
0173 {
0174     struct ATTR_LIST_ENTRY *ret = NULL;
0175     u32 type_in = le32_to_cpu(type);
0176 
0177     while ((le = al_enumerate(ni, le))) {
0178         u64 le_vcn;
0179         int diff = le32_to_cpu(le->type) - type_in;
0180 
0181         /* List entries are sorted by type, name and VCN. */
0182         if (diff < 0)
0183             continue;
0184 
0185         if (diff > 0)
0186             return ret;
0187 
0188         if (le->name_len != name_len)
0189             continue;
0190 
0191         le_vcn = le64_to_cpu(le->vcn);
0192         if (!le_vcn) {
0193             /*
0194              * Compare entry names only for entry with vcn == 0.
0195              */
0196             diff = ntfs_cmp_names(le_name(le), name_len, name,
0197                           name_len, ni->mi.sbi->upcase,
0198                           true);
0199             if (diff < 0)
0200                 continue;
0201 
0202             if (diff > 0)
0203                 return ret;
0204         }
0205 
0206         if (!vcn)
0207             return le;
0208 
0209         if (*vcn == le_vcn)
0210             return le;
0211 
0212         if (*vcn < le_vcn)
0213             return ret;
0214 
0215         ret = le;
0216     }
0217 
0218     return ret;
0219 }
0220 
0221 /*
0222  * al_find_le_to_insert
0223  *
0224  * Find the first list entry which matches type, name and VCN.
0225  */
0226 static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni,
0227                             enum ATTR_TYPE type,
0228                             const __le16 *name,
0229                             u8 name_len, CLST vcn)
0230 {
0231     struct ATTR_LIST_ENTRY *le = NULL, *prev;
0232     u32 type_in = le32_to_cpu(type);
0233 
0234     /* List entries are sorted by type, name and VCN. */
0235     while ((le = al_enumerate(ni, prev = le))) {
0236         int diff = le32_to_cpu(le->type) - type_in;
0237 
0238         if (diff < 0)
0239             continue;
0240 
0241         if (diff > 0)
0242             return le;
0243 
0244         if (!le->vcn) {
0245             /*
0246              * Compare entry names only for entry with vcn == 0.
0247              */
0248             diff = ntfs_cmp_names(le_name(le), le->name_len, name,
0249                           name_len, ni->mi.sbi->upcase,
0250                           true);
0251             if (diff < 0)
0252                 continue;
0253 
0254             if (diff > 0)
0255                 return le;
0256         }
0257 
0258         if (le64_to_cpu(le->vcn) >= vcn)
0259             return le;
0260     }
0261 
0262     return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le;
0263 }
0264 
0265 /*
0266  * al_add_le
0267  *
0268  * Add an "attribute list entry" to the list.
0269  */
0270 int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
0271           u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref,
0272           struct ATTR_LIST_ENTRY **new_le)
0273 {
0274     int err;
0275     struct ATTRIB *attr;
0276     struct ATTR_LIST_ENTRY *le;
0277     size_t off;
0278     u16 sz;
0279     size_t asize, new_asize, old_size;
0280     u64 new_size;
0281     typeof(ni->attr_list) *al = &ni->attr_list;
0282 
0283     /*
0284      * Compute the size of the new 'le'
0285      */
0286     sz = le_size(name_len);
0287     old_size = al->size;
0288     new_size = old_size + sz;
0289     asize = al_aligned(old_size);
0290     new_asize = al_aligned(new_size);
0291 
0292     /* Scan forward to the point at which the new 'le' should be inserted. */
0293     le = al_find_le_to_insert(ni, type, name, name_len, svcn);
0294     off = PtrOffset(al->le, le);
0295 
0296     if (new_size > asize) {
0297         void *ptr = kmalloc(new_asize, GFP_NOFS);
0298 
0299         if (!ptr)
0300             return -ENOMEM;
0301 
0302         memcpy(ptr, al->le, off);
0303         memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
0304         le = Add2Ptr(ptr, off);
0305         kfree(al->le);
0306         al->le = ptr;
0307     } else {
0308         memmove(Add2Ptr(le, sz), le, old_size - off);
0309     }
0310     *new_le = le;
0311 
0312     al->size = new_size;
0313 
0314     le->type = type;
0315     le->size = cpu_to_le16(sz);
0316     le->name_len = name_len;
0317     le->name_off = offsetof(struct ATTR_LIST_ENTRY, name);
0318     le->vcn = cpu_to_le64(svcn);
0319     le->ref = *ref;
0320     le->id = id;
0321     memcpy(le->name, name, sizeof(short) * name_len);
0322 
0323     err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
0324                 &new_size, true, &attr);
0325     if (err) {
0326         /* Undo memmove above. */
0327         memmove(le, Add2Ptr(le, sz), old_size - off);
0328         al->size = old_size;
0329         return err;
0330     }
0331 
0332     al->dirty = true;
0333 
0334     if (attr && attr->non_res) {
0335         err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
0336                     al->size, 0);
0337         if (err)
0338             return err;
0339         al->dirty = false;
0340     }
0341 
0342     return 0;
0343 }
0344 
0345 /*
0346  * al_remove_le - Remove @le from attribute list.
0347  */
0348 bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
0349 {
0350     u16 size;
0351     size_t off;
0352     typeof(ni->attr_list) *al = &ni->attr_list;
0353 
0354     if (!al_is_valid_le(ni, le))
0355         return false;
0356 
0357     /* Save on stack the size of 'le' */
0358     size = le16_to_cpu(le->size);
0359     off = PtrOffset(al->le, le);
0360 
0361     memmove(le, Add2Ptr(le, size), al->size - (off + size));
0362 
0363     al->size -= size;
0364     al->dirty = true;
0365 
0366     return true;
0367 }
0368 
0369 /*
0370  * al_delete_le - Delete first le from the list which matches its parameters.
0371  */
0372 bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
0373           const __le16 *name, size_t name_len,
0374           const struct MFT_REF *ref)
0375 {
0376     u16 size;
0377     struct ATTR_LIST_ENTRY *le;
0378     size_t off;
0379     typeof(ni->attr_list) *al = &ni->attr_list;
0380 
0381     /* Scan forward to the first le that matches the input. */
0382     le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
0383     if (!le)
0384         return false;
0385 
0386     off = PtrOffset(al->le, le);
0387 
0388 next:
0389     if (off >= al->size)
0390         return false;
0391     if (le->type != type)
0392         return false;
0393     if (le->name_len != name_len)
0394         return false;
0395     if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
0396                        ni->mi.sbi->upcase, true))
0397         return false;
0398     if (le64_to_cpu(le->vcn) != vcn)
0399         return false;
0400 
0401     /*
0402      * The caller specified a segment reference, so we have to
0403      * scan through the matching entries until we find that segment
0404      * reference or we run of matching entries.
0405      */
0406     if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
0407         off += le16_to_cpu(le->size);
0408         le = Add2Ptr(al->le, off);
0409         goto next;
0410     }
0411 
0412     /* Save on stack the size of 'le'. */
0413     size = le16_to_cpu(le->size);
0414     /* Delete the le. */
0415     memmove(le, Add2Ptr(le, size), al->size - (off + size));
0416 
0417     al->size -= size;
0418     al->dirty = true;
0419 
0420     return true;
0421 }
0422 
0423 int al_update(struct ntfs_inode *ni, int sync)
0424 {
0425     int err;
0426     struct ATTRIB *attr;
0427     typeof(ni->attr_list) *al = &ni->attr_list;
0428 
0429     if (!al->dirty || !al->size)
0430         return 0;
0431 
0432     /*
0433      * Attribute list increased on demand in al_add_le.
0434      * Attribute list decreased here.
0435      */
0436     err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
0437                 false, &attr);
0438     if (err)
0439         goto out;
0440 
0441     if (!attr->non_res) {
0442         memcpy(resident_data(attr), al->le, al->size);
0443     } else {
0444         err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
0445                     al->size, sync);
0446         if (err)
0447             goto out;
0448 
0449         attr->nres.valid_size = attr->nres.data_size;
0450     }
0451 
0452     ni->mi.dirty = true;
0453     al->dirty = false;
0454 
0455 out:
0456     return err;
0457 }