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