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