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