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