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