0001
0002
0003
0004
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
0016
0017
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
0040
0041
0042
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
0104
0105
0106
0107
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
0121 return NULL;
0122 }
0123 le = Add2Ptr(le, sz);
0124 }
0125
0126
0127 off = PtrOffset(ni->attr_list.le, le);
0128 if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
0129
0130 return NULL;
0131 }
0132
0133 sz = le16_to_cpu(le->size);
0134
0135
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
0147
0148
0149
0150
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
0164
0165
0166
0167
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
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
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
0223
0224
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
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
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
0267
0268
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
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
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
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
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
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
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
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
0403
0404
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
0413 size = le16_to_cpu(le->size);
0414
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
0434
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 }