1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * 4 * Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved. 5 * 6 */ 7 8 #include <linux/fs.h> 9 10 #include "debug.h" 11 #include "ntfs.h" 12 #include "ntfs_fs.h" 13 14 /* 15 * al_is_valid_le 16 * 17 * Return: True if @le is valid. 18 */ 19 static inline bool al_is_valid_le(const struct ntfs_inode *ni, 20 struct ATTR_LIST_ENTRY *le) 21 { 22 if (!le || !ni->attr_list.le || !ni->attr_list.size) 23 return false; 24 25 return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <= 26 ni->attr_list.size; 27 } 28 29 void al_destroy(struct ntfs_inode *ni) 30 { 31 run_close(&ni->attr_list.run); 32 kfree(ni->attr_list.le); 33 ni->attr_list.le = NULL; 34 ni->attr_list.size = 0; 35 ni->attr_list.dirty = false; 36 } 37 38 /* 39 * ntfs_load_attr_list 40 * 41 * This method makes sure that the ATTRIB list, if present, 42 * has been properly set up. 43 */ 44 int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr) 45 { 46 int err; 47 size_t lsize; 48 void *le = NULL; 49 50 if (ni->attr_list.size) 51 return 0; 52 53 if (!attr->non_res) { 54 lsize = le32_to_cpu(attr->res.data_size); 55 /* attr is resident: lsize < record_size (1K or 4K) */ 56 le = kvmalloc(al_aligned(lsize), GFP_KERNEL); 57 if (!le) { 58 err = -ENOMEM; 59 goto out; 60 } 61 memcpy(le, resident_data(attr), lsize); 62 } else if (attr->nres.svcn) { 63 err = -EINVAL; 64 goto out; 65 } else { 66 u16 run_off = le16_to_cpu(attr->nres.run_off); 67 68 lsize = le64_to_cpu(attr->nres.data_size); 69 70 run_init(&ni->attr_list.run); 71 72 if (run_off > le32_to_cpu(attr->size)) { 73 err = -EINVAL; 74 goto out; 75 } 76 77 err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno, 78 0, le64_to_cpu(attr->nres.evcn), 0, 79 Add2Ptr(attr, run_off), 80 le32_to_cpu(attr->size) - run_off); 81 if (err < 0) 82 goto out; 83 84 /* attr is nonresident. 85 * The worst case: 86 * 1T (2^40) extremely fragmented file. 87 * cluster = 4K (2^12) => 2^28 fragments 88 * 2^9 fragments per one record => 2^19 records 89 * 2^5 bytes of ATTR_LIST_ENTRY per one record => 2^24 bytes. 90 * 91 * the result is 16M bytes per attribute list. 92 * Use kvmalloc to allocate in range [several Kbytes - dozen Mbytes] 93 */ 94 le = kvmalloc(al_aligned(lsize), GFP_KERNEL); 95 if (!le) { 96 err = -ENOMEM; 97 goto out; 98 } 99 100 err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le, 101 lsize, NULL); 102 if (err) 103 goto out; 104 } 105 106 ni->attr_list.size = lsize; 107 ni->attr_list.le = le; 108 109 return 0; 110 111 out: 112 ni->attr_list.le = le; 113 al_destroy(ni); 114 115 return err; 116 } 117 118 /* 119 * al_enumerate 120 * 121 * Return: 122 * * The next list le. 123 * * If @le is NULL then return the first le. 124 */ 125 struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni, 126 struct ATTR_LIST_ENTRY *le) 127 { 128 size_t off; 129 u16 sz; 130 131 if (!le) { 132 le = ni->attr_list.le; 133 } else { 134 sz = le16_to_cpu(le->size); 135 if (sz < sizeof(struct ATTR_LIST_ENTRY)) { 136 /* Impossible 'cause we should not return such le. */ 137 return NULL; 138 } 139 le = Add2Ptr(le, sz); 140 } 141 142 /* Check boundary. */ 143 off = PtrOffset(ni->attr_list.le, le); 144 if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) { 145 /* The regular end of list. */ 146 return NULL; 147 } 148 149 sz = le16_to_cpu(le->size); 150 151 /* Check le for errors. */ 152 if (sz < sizeof(struct ATTR_LIST_ENTRY) || 153 off + sz > ni->attr_list.size || 154 sz < le->name_off + le->name_len * sizeof(short)) { 155 return NULL; 156 } 157 158 return le; 159 } 160 161 /* 162 * al_find_le 163 * 164 * Find the first le in the list which matches type, name and VCN. 165 * 166 * Return: NULL if not found. 167 */ 168 struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni, 169 struct ATTR_LIST_ENTRY *le, 170 const struct ATTRIB *attr) 171 { 172 CLST svcn = attr_svcn(attr); 173 174 return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len, 175 &svcn); 176 } 177 178 /* 179 * al_find_ex 180 * 181 * Find the first le in the list which matches type, name and VCN. 182 * 183 * Return: NULL if not found. 184 */ 185 struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni, 186 struct ATTR_LIST_ENTRY *le, 187 enum ATTR_TYPE type, const __le16 *name, 188 u8 name_len, const CLST *vcn) 189 { 190 struct ATTR_LIST_ENTRY *ret = NULL; 191 u32 type_in = le32_to_cpu(type); 192 193 while ((le = al_enumerate(ni, le))) { 194 u64 le_vcn; 195 int diff = le32_to_cpu(le->type) - type_in; 196 197 /* List entries are sorted by type, name and VCN. */ 198 if (diff < 0) 199 continue; 200 201 if (diff > 0) 202 return ret; 203 204 if (le->name_len != name_len) 205 continue; 206 207 le_vcn = le64_to_cpu(le->vcn); 208 if (!le_vcn) { 209 /* 210 * Compare entry names only for entry with vcn == 0. 211 */ 212 diff = ntfs_cmp_names(le_name(le), name_len, name, 213 name_len, ni->mi.sbi->upcase, 214 true); 215 if (diff < 0) 216 continue; 217 218 if (diff > 0) 219 return ret; 220 } 221 222 if (!vcn) 223 return le; 224 225 if (*vcn == le_vcn) 226 return le; 227 228 if (*vcn < le_vcn) 229 return ret; 230 231 ret = le; 232 } 233 234 return ret; 235 } 236 237 /* 238 * al_find_le_to_insert 239 * 240 * Find the first list entry which matches type, name and VCN. 241 */ 242 static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni, 243 enum ATTR_TYPE type, 244 const __le16 *name, 245 u8 name_len, CLST vcn) 246 { 247 struct ATTR_LIST_ENTRY *le = NULL, *prev; 248 u32 type_in = le32_to_cpu(type); 249 250 /* List entries are sorted by type, name and VCN. */ 251 while ((le = al_enumerate(ni, prev = le))) { 252 int diff = le32_to_cpu(le->type) - type_in; 253 254 if (diff < 0) 255 continue; 256 257 if (diff > 0) 258 return le; 259 260 if (!le->vcn) { 261 /* 262 * Compare entry names only for entry with vcn == 0. 263 */ 264 diff = ntfs_cmp_names(le_name(le), le->name_len, name, 265 name_len, ni->mi.sbi->upcase, 266 true); 267 if (diff < 0) 268 continue; 269 270 if (diff > 0) 271 return le; 272 } 273 274 if (le64_to_cpu(le->vcn) >= vcn) 275 return le; 276 } 277 278 return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le; 279 } 280 281 /* 282 * al_add_le 283 * 284 * Add an "attribute list entry" to the list. 285 */ 286 int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name, 287 u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref, 288 struct ATTR_LIST_ENTRY **new_le) 289 { 290 int err; 291 struct ATTRIB *attr; 292 struct ATTR_LIST_ENTRY *le; 293 size_t off; 294 u16 sz; 295 size_t asize, new_asize, old_size; 296 u64 new_size; 297 typeof(ni->attr_list) *al = &ni->attr_list; 298 299 /* 300 * Compute the size of the new 'le' 301 */ 302 sz = le_size(name_len); 303 old_size = al->size; 304 new_size = old_size + sz; 305 asize = al_aligned(old_size); 306 new_asize = al_aligned(new_size); 307 308 /* Scan forward to the point at which the new 'le' should be inserted. */ 309 le = al_find_le_to_insert(ni, type, name, name_len, svcn); 310 off = PtrOffset(al->le, le); 311 312 if (new_size > asize) { 313 void *ptr = kmalloc(new_asize, GFP_NOFS); 314 315 if (!ptr) 316 return -ENOMEM; 317 318 memcpy(ptr, al->le, off); 319 memcpy(Add2Ptr(ptr, off + sz), le, old_size - off); 320 le = Add2Ptr(ptr, off); 321 kfree(al->le); 322 al->le = ptr; 323 } else { 324 memmove(Add2Ptr(le, sz), le, old_size - off); 325 } 326 *new_le = le; 327 328 al->size = new_size; 329 330 le->type = type; 331 le->size = cpu_to_le16(sz); 332 le->name_len = name_len; 333 le->name_off = offsetof(struct ATTR_LIST_ENTRY, name); 334 le->vcn = cpu_to_le64(svcn); 335 le->ref = *ref; 336 le->id = id; 337 memcpy(le->name, name, sizeof(short) * name_len); 338 339 err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size, 340 &new_size, true, &attr); 341 if (err) { 342 /* Undo memmove above. */ 343 memmove(le, Add2Ptr(le, sz), old_size - off); 344 al->size = old_size; 345 return err; 346 } 347 348 al->dirty = true; 349 350 if (attr && attr->non_res) { 351 err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, 352 al->size, 0); 353 if (err) 354 return err; 355 al->dirty = false; 356 } 357 358 return 0; 359 } 360 361 /* 362 * al_remove_le - Remove @le from attribute list. 363 */ 364 bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le) 365 { 366 u16 size; 367 size_t off; 368 typeof(ni->attr_list) *al = &ni->attr_list; 369 370 if (!al_is_valid_le(ni, le)) 371 return false; 372 373 /* Save on stack the size of 'le' */ 374 size = le16_to_cpu(le->size); 375 off = PtrOffset(al->le, le); 376 377 memmove(le, Add2Ptr(le, size), al->size - (off + size)); 378 379 al->size -= size; 380 al->dirty = true; 381 382 return true; 383 } 384 385 /* 386 * al_delete_le - Delete first le from the list which matches its parameters. 387 */ 388 bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn, 389 const __le16 *name, u8 name_len, const struct MFT_REF *ref) 390 { 391 u16 size; 392 struct ATTR_LIST_ENTRY *le; 393 size_t off; 394 typeof(ni->attr_list) *al = &ni->attr_list; 395 396 /* Scan forward to the first le that matches the input. */ 397 le = al_find_ex(ni, NULL, type, name, name_len, &vcn); 398 if (!le) 399 return false; 400 401 off = PtrOffset(al->le, le); 402 403 next: 404 if (off >= al->size) 405 return false; 406 if (le->type != type) 407 return false; 408 if (le->name_len != name_len) 409 return false; 410 if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len, 411 ni->mi.sbi->upcase, true)) 412 return false; 413 if (le64_to_cpu(le->vcn) != vcn) 414 return false; 415 416 /* 417 * The caller specified a segment reference, so we have to 418 * scan through the matching entries until we find that segment 419 * reference or we run of matching entries. 420 */ 421 if (ref && memcmp(ref, &le->ref, sizeof(*ref))) { 422 off += le16_to_cpu(le->size); 423 le = Add2Ptr(al->le, off); 424 goto next; 425 } 426 427 /* Save on stack the size of 'le'. */ 428 size = le16_to_cpu(le->size); 429 /* Delete the le. */ 430 memmove(le, Add2Ptr(le, size), al->size - (off + size)); 431 432 al->size -= size; 433 al->dirty = true; 434 435 return true; 436 } 437 438 int al_update(struct ntfs_inode *ni, int sync) 439 { 440 int err; 441 struct ATTRIB *attr; 442 typeof(ni->attr_list) *al = &ni->attr_list; 443 444 if (!al->dirty || !al->size) 445 return 0; 446 447 /* 448 * Attribute list increased on demand in al_add_le. 449 * Attribute list decreased here. 450 */ 451 err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL, 452 false, &attr); 453 if (err) 454 goto out; 455 456 if (!attr->non_res) { 457 memcpy(resident_data(attr), al->le, al->size); 458 } else { 459 err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le, 460 al->size, sync); 461 if (err) 462 goto out; 463 464 attr->nres.valid_size = attr->nres.data_size; 465 } 466 467 ni->mi.dirty = true; 468 al->dirty = false; 469 470 out: 471 return err; 472 } 473