1 /* Copyright (c) Mark Harmstone 2016-17 2 * 3 * This file is part of WinBtrfs. 4 * 5 * WinBtrfs is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU Lesser General Public Licence as published by 7 * the Free Software Foundation, either version 3 of the Licence, or 8 * (at your option) any later version. 9 * 10 * WinBtrfs is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public Licence for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public Licence 16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include "btrfs_drv.h" 19 #include "xxhash.h" 20 #include "crc32c.h" 21 #include <ata.h> 22 #include <ntddscsi.h> 23 #include <ntddstor.h> 24 25 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node)) 26 27 // #define DEBUG_WRITE_LOOPS 28 29 typedef struct { 30 KEVENT Event; 31 IO_STATUS_BLOCK iosb; 32 } write_context; 33 34 typedef struct { 35 EXTENT_ITEM_TREE eit; 36 uint8_t type; 37 TREE_BLOCK_REF tbr; 38 } EXTENT_ITEM_TREE2; 39 40 typedef struct { 41 EXTENT_ITEM ei; 42 uint8_t type; 43 TREE_BLOCK_REF tbr; 44 } EXTENT_ITEM_SKINNY_METADATA; 45 46 static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp); 47 static NTSTATUS update_tree_extents(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback); 48 49 #ifndef _MSC_VER // not in mingw yet 50 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000 51 #endif 52 53 _Function_class_(IO_COMPLETION_ROUTINE) 54 static NTSTATUS __stdcall write_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 55 write_context* context = conptr; 56 57 UNUSED(DeviceObject); 58 59 context->iosb = Irp->IoStatus; 60 KeSetEvent(&context->Event, 0, false); 61 62 return STATUS_MORE_PROCESSING_REQUIRED; 63 } 64 65 NTSTATUS write_data_phys(_In_ PDEVICE_OBJECT device, _In_ PFILE_OBJECT fileobj, _In_ uint64_t address, 66 _In_reads_bytes_(length) void* data, _In_ uint32_t length) { 67 NTSTATUS Status; 68 LARGE_INTEGER offset; 69 PIRP Irp; 70 PIO_STACK_LOCATION IrpSp; 71 write_context context; 72 73 TRACE("(%p, %I64x, %p, %x)\n", device, address, data, length); 74 75 RtlZeroMemory(&context, sizeof(write_context)); 76 77 KeInitializeEvent(&context.Event, NotificationEvent, false); 78 79 offset.QuadPart = address; 80 81 Irp = IoAllocateIrp(device->StackSize, false); 82 83 if (!Irp) { 84 ERR("IoAllocateIrp failed\n"); 85 return STATUS_INSUFFICIENT_RESOURCES; 86 } 87 88 IrpSp = IoGetNextIrpStackLocation(Irp); 89 IrpSp->MajorFunction = IRP_MJ_WRITE; 90 IrpSp->FileObject = fileobj; 91 92 if (device->Flags & DO_BUFFERED_IO) { 93 Irp->AssociatedIrp.SystemBuffer = data; 94 95 Irp->Flags = IRP_BUFFERED_IO; 96 } else if (device->Flags & DO_DIRECT_IO) { 97 Irp->MdlAddress = IoAllocateMdl(data, length, false, false, NULL); 98 if (!Irp->MdlAddress) { 99 DbgPrint("IoAllocateMdl failed\n"); 100 Status = STATUS_INSUFFICIENT_RESOURCES; 101 goto exit; 102 } 103 104 Status = STATUS_SUCCESS; 105 106 _SEH2_TRY { 107 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess); 108 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 109 Status = _SEH2_GetExceptionCode(); 110 } _SEH2_END; 111 112 if (!NT_SUCCESS(Status)) { 113 ERR("MmProbeAndLockPages threw exception %08lx\n", Status); 114 IoFreeMdl(Irp->MdlAddress); 115 goto exit; 116 } 117 } else { 118 Irp->UserBuffer = data; 119 } 120 121 IrpSp->Parameters.Write.Length = length; 122 IrpSp->Parameters.Write.ByteOffset = offset; 123 124 Irp->UserIosb = &context.iosb; 125 126 Irp->UserEvent = &context.Event; 127 128 IoSetCompletionRoutine(Irp, write_completion, &context, true, true, true); 129 130 Status = IoCallDriver(device, Irp); 131 132 if (Status == STATUS_PENDING) { 133 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); 134 Status = context.iosb.Status; 135 } 136 137 if (!NT_SUCCESS(Status)) { 138 ERR("IoCallDriver returned %08lx\n", Status); 139 } 140 141 if (device->Flags & DO_DIRECT_IO) { 142 MmUnlockPages(Irp->MdlAddress); 143 IoFreeMdl(Irp->MdlAddress); 144 } 145 146 exit: 147 IoFreeIrp(Irp); 148 149 return Status; 150 } 151 152 static void add_trim_entry(device* dev, uint64_t address, uint64_t size) { 153 space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG); 154 if (!s) { 155 ERR("out of memory\n"); 156 return; 157 } 158 159 s->address = address; 160 s->size = size; 161 dev->num_trim_entries++; 162 163 InsertTailList(&dev->trim_list, &s->list_entry); 164 } 165 166 static void clean_space_cache_chunk(device_extension* Vcb, chunk* c) { 167 ULONG type; 168 169 if (Vcb->trim && !Vcb->options.no_trim) { 170 if (c->chunk_item->type & BLOCK_FLAG_DUPLICATE) 171 type = BLOCK_FLAG_DUPLICATE; 172 else if (c->chunk_item->type & BLOCK_FLAG_RAID0) 173 type = BLOCK_FLAG_RAID0; 174 else if (c->chunk_item->type & BLOCK_FLAG_RAID1) 175 type = BLOCK_FLAG_DUPLICATE; 176 else if (c->chunk_item->type & BLOCK_FLAG_RAID10) 177 type = BLOCK_FLAG_RAID10; 178 else if (c->chunk_item->type & BLOCK_FLAG_RAID5) 179 type = BLOCK_FLAG_RAID5; 180 else if (c->chunk_item->type & BLOCK_FLAG_RAID6) 181 type = BLOCK_FLAG_RAID6; 182 else if (c->chunk_item->type & BLOCK_FLAG_RAID1C3) 183 type = BLOCK_FLAG_DUPLICATE; 184 else if (c->chunk_item->type & BLOCK_FLAG_RAID1C4) 185 type = BLOCK_FLAG_DUPLICATE; 186 else // SINGLE 187 type = BLOCK_FLAG_DUPLICATE; 188 } 189 190 while (!IsListEmpty(&c->deleting)) { 191 space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry); 192 193 if (Vcb->trim && !Vcb->options.no_trim && (!Vcb->options.no_barrier || !(c->chunk_item->type & BLOCK_FLAG_METADATA))) { 194 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 195 196 if (type == BLOCK_FLAG_DUPLICATE) { 197 uint16_t i; 198 199 for (i = 0; i < c->chunk_item->num_stripes; i++) { 200 if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim) 201 add_trim_entry(c->devices[i], s->address - c->offset + cis[i].offset, s->size); 202 } 203 } else if (type == BLOCK_FLAG_RAID0) { 204 uint64_t startoff, endoff; 205 uint16_t startoffstripe, endoffstripe, i; 206 207 get_raid0_offset(s->address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe); 208 get_raid0_offset(s->address - c->offset + s->size - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe); 209 210 for (i = 0; i < c->chunk_item->num_stripes; i++) { 211 if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim) { 212 uint64_t stripestart, stripeend; 213 214 if (startoffstripe > i) 215 stripestart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 216 else if (startoffstripe == i) 217 stripestart = startoff; 218 else 219 stripestart = startoff - (startoff % c->chunk_item->stripe_length); 220 221 if (endoffstripe > i) 222 stripeend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 223 else if (endoffstripe == i) 224 stripeend = endoff + 1; 225 else 226 stripeend = endoff - (endoff % c->chunk_item->stripe_length); 227 228 if (stripestart != stripeend) 229 add_trim_entry(c->devices[i], stripestart + cis[i].offset, stripeend - stripestart); 230 } 231 } 232 } else if (type == BLOCK_FLAG_RAID10) { 233 uint64_t startoff, endoff; 234 uint16_t sub_stripes, startoffstripe, endoffstripe, i; 235 236 sub_stripes = max(1, c->chunk_item->sub_stripes); 237 238 get_raid0_offset(s->address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &startoff, &startoffstripe); 239 get_raid0_offset(s->address - c->offset + s->size - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / sub_stripes, &endoff, &endoffstripe); 240 241 startoffstripe *= sub_stripes; 242 endoffstripe *= sub_stripes; 243 244 for (i = 0; i < c->chunk_item->num_stripes; i += sub_stripes) { 245 ULONG j; 246 uint64_t stripestart, stripeend; 247 248 if (startoffstripe > i) 249 stripestart = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 250 else if (startoffstripe == i) 251 stripestart = startoff; 252 else 253 stripestart = startoff - (startoff % c->chunk_item->stripe_length); 254 255 if (endoffstripe > i) 256 stripeend = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 257 else if (endoffstripe == i) 258 stripeend = endoff + 1; 259 else 260 stripeend = endoff - (endoff % c->chunk_item->stripe_length); 261 262 if (stripestart != stripeend) { 263 for (j = 0; j < sub_stripes; j++) { 264 if (c->devices[i+j] && c->devices[i+j]->devobj && !c->devices[i+j]->readonly && c->devices[i+j]->trim) 265 add_trim_entry(c->devices[i+j], stripestart + cis[i+j].offset, stripeend - stripestart); 266 } 267 } 268 } 269 } 270 // FIXME - RAID5(?), RAID6(?) 271 } 272 273 RemoveEntryList(&s->list_entry); 274 ExFreePool(s); 275 } 276 } 277 278 typedef struct { 279 DEVICE_MANAGE_DATA_SET_ATTRIBUTES* dmdsa; 280 ATA_PASS_THROUGH_EX apte; 281 PIRP Irp; 282 IO_STATUS_BLOCK iosb; 283 #ifdef DEBUG_TRIM_EMULATION 284 PMDL mdl; 285 void* buf; 286 #endif 287 } ioctl_context_stripe; 288 289 typedef struct { 290 KEVENT Event; 291 LONG left; 292 ioctl_context_stripe* stripes; 293 } ioctl_context; 294 295 _Function_class_(IO_COMPLETION_ROUTINE) 296 static NTSTATUS __stdcall ioctl_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 297 ioctl_context* context = (ioctl_context*)conptr; 298 LONG left2 = InterlockedDecrement(&context->left); 299 300 UNUSED(DeviceObject); 301 UNUSED(Irp); 302 303 if (left2 == 0) 304 KeSetEvent(&context->Event, 0, false); 305 306 return STATUS_MORE_PROCESSING_REQUIRED; 307 } 308 309 #ifdef DEBUG_TRIM_EMULATION 310 static void trim_emulation(device* dev) { 311 LIST_ENTRY* le; 312 ioctl_context context; 313 unsigned int i = 0, count = 0; 314 315 le = dev->trim_list.Flink; 316 while (le != &dev->trim_list) { 317 count++; 318 le = le->Flink; 319 } 320 321 context.left = count; 322 323 KeInitializeEvent(&context.Event, NotificationEvent, false); 324 325 context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(ioctl_context_stripe) * context.left, ALLOC_TAG); 326 if (!context.stripes) { 327 ERR("out of memory\n"); 328 return; 329 } 330 331 RtlZeroMemory(context.stripes, sizeof(ioctl_context_stripe) * context.left); 332 333 i = 0; 334 le = dev->trim_list.Flink; 335 while (le != &dev->trim_list) { 336 ioctl_context_stripe* stripe = &context.stripes[i]; 337 space* s = CONTAINING_RECORD(le, space, list_entry); 338 339 WARN("(%I64x, %I64x)\n", s->address, s->size); 340 341 stripe->Irp = IoAllocateIrp(dev->devobj->StackSize, false); 342 343 if (!stripe->Irp) { 344 ERR("IoAllocateIrp failed\n"); 345 } else { 346 PIO_STACK_LOCATION IrpSp = IoGetNextIrpStackLocation(stripe->Irp); 347 IrpSp->MajorFunction = IRP_MJ_WRITE; 348 IrpSp->FileObject = dev->fileobj; 349 350 stripe->buf = ExAllocatePoolWithTag(NonPagedPool, (uint32_t)s->size, ALLOC_TAG); 351 352 if (!stripe->buf) { 353 ERR("out of memory\n"); 354 } else { 355 RtlZeroMemory(stripe->buf, (uint32_t)s->size); // FIXME - randomize instead? 356 357 stripe->mdl = IoAllocateMdl(stripe->buf, (uint32_t)s->size, false, false, NULL); 358 359 if (!stripe->mdl) { 360 ERR("IoAllocateMdl failed\n"); 361 } else { 362 MmBuildMdlForNonPagedPool(stripe->mdl); 363 364 stripe->Irp->MdlAddress = stripe->mdl; 365 366 IrpSp->Parameters.Write.ByteOffset.QuadPart = s->address; 367 IrpSp->Parameters.Write.Length = s->size; 368 369 stripe->Irp->UserIosb = &stripe->iosb; 370 371 IoSetCompletionRoutine(stripe->Irp, ioctl_completion, &context, true, true, true); 372 373 IoCallDriver(dev->devobj, stripe->Irp); 374 } 375 } 376 } 377 378 i++; 379 380 le = le->Flink; 381 } 382 383 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); 384 385 for (i = 0; i < count; i++) { 386 ioctl_context_stripe* stripe = &context.stripes[i]; 387 388 if (stripe->mdl) 389 IoFreeMdl(stripe->mdl); 390 391 if (stripe->buf) 392 ExFreePool(stripe->buf); 393 } 394 395 ExFreePool(context.stripes); 396 } 397 #endif 398 399 static void clean_space_cache(device_extension* Vcb) { 400 LIST_ENTRY* le; 401 chunk* c; 402 #ifndef DEBUG_TRIM_EMULATION 403 ULONG num; 404 #endif 405 406 TRACE("(%p)\n", Vcb); 407 408 ExAcquireResourceSharedLite(&Vcb->chunk_lock, true); 409 410 le = Vcb->chunks.Flink; 411 while (le != &Vcb->chunks) { 412 c = CONTAINING_RECORD(le, chunk, list_entry); 413 414 if (c->space_changed) { 415 acquire_chunk_lock(c, Vcb); 416 417 if (c->space_changed) 418 clean_space_cache_chunk(Vcb, c); 419 420 c->space_changed = false; 421 422 release_chunk_lock(c, Vcb); 423 } 424 425 le = le->Flink; 426 } 427 428 ExReleaseResourceLite(&Vcb->chunk_lock); 429 430 if (Vcb->trim && !Vcb->options.no_trim) { 431 #ifndef DEBUG_TRIM_EMULATION 432 ioctl_context context; 433 ULONG total_num; 434 435 context.left = 0; 436 437 le = Vcb->devices.Flink; 438 while (le != &Vcb->devices) { 439 device* dev = CONTAINING_RECORD(le, device, list_entry); 440 441 if (dev->devobj && !dev->readonly && dev->trim && dev->num_trim_entries > 0) 442 context.left++; 443 444 le = le->Flink; 445 } 446 447 if (context.left == 0) 448 return; 449 450 total_num = context.left; 451 num = 0; 452 453 KeInitializeEvent(&context.Event, NotificationEvent, false); 454 455 context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(ioctl_context_stripe) * context.left, ALLOC_TAG); 456 if (!context.stripes) { 457 ERR("out of memory\n"); 458 return; 459 } 460 461 RtlZeroMemory(context.stripes, sizeof(ioctl_context_stripe) * context.left); 462 #endif 463 464 le = Vcb->devices.Flink; 465 while (le != &Vcb->devices) { 466 device* dev = CONTAINING_RECORD(le, device, list_entry); 467 468 if (dev->devobj && !dev->readonly && dev->trim && dev->num_trim_entries > 0) { 469 #ifdef DEBUG_TRIM_EMULATION 470 trim_emulation(dev); 471 #else 472 LIST_ENTRY* le2; 473 ioctl_context_stripe* stripe = &context.stripes[num]; 474 DEVICE_DATA_SET_RANGE* ranges; 475 ULONG datalen = (ULONG)sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(uint64_t)) + (dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE)), i; 476 PIO_STACK_LOCATION IrpSp; 477 478 stripe->dmdsa = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG); 479 if (!stripe->dmdsa) { 480 ERR("out of memory\n"); 481 goto nextdev; 482 } 483 484 stripe->dmdsa->Size = sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES); 485 stripe->dmdsa->Action = DeviceDsmAction_Trim; 486 stripe->dmdsa->Flags = DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED; 487 stripe->dmdsa->ParameterBlockOffset = 0; 488 stripe->dmdsa->ParameterBlockLength = 0; 489 stripe->dmdsa->DataSetRangesOffset = (ULONG)sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES), sizeof(uint64_t)); 490 stripe->dmdsa->DataSetRangesLength = dev->num_trim_entries * sizeof(DEVICE_DATA_SET_RANGE); 491 492 ranges = (DEVICE_DATA_SET_RANGE*)((uint8_t*)stripe->dmdsa + stripe->dmdsa->DataSetRangesOffset); 493 494 i = 0; 495 496 le2 = dev->trim_list.Flink; 497 while (le2 != &dev->trim_list) { 498 space* s = CONTAINING_RECORD(le2, space, list_entry); 499 500 ranges[i].StartingOffset = s->address; 501 ranges[i].LengthInBytes = s->size; 502 i++; 503 504 le2 = le2->Flink; 505 } 506 507 stripe->Irp = IoAllocateIrp(dev->devobj->StackSize, false); 508 509 if (!stripe->Irp) { 510 ERR("IoAllocateIrp failed\n"); 511 goto nextdev; 512 } 513 514 IrpSp = IoGetNextIrpStackLocation(stripe->Irp); 515 IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; 516 IrpSp->FileObject = dev->fileobj; 517 518 IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES; 519 IrpSp->Parameters.DeviceIoControl.InputBufferLength = datalen; 520 IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0; 521 522 stripe->Irp->AssociatedIrp.SystemBuffer = stripe->dmdsa; 523 stripe->Irp->Flags |= IRP_BUFFERED_IO; 524 stripe->Irp->UserBuffer = NULL; 525 stripe->Irp->UserIosb = &stripe->iosb; 526 527 IoSetCompletionRoutine(stripe->Irp, ioctl_completion, &context, true, true, true); 528 529 IoCallDriver(dev->devobj, stripe->Irp); 530 531 nextdev: 532 #endif 533 while (!IsListEmpty(&dev->trim_list)) { 534 space* s = CONTAINING_RECORD(RemoveHeadList(&dev->trim_list), space, list_entry); 535 ExFreePool(s); 536 } 537 538 dev->num_trim_entries = 0; 539 540 #ifndef DEBUG_TRIM_EMULATION 541 num++; 542 #endif 543 } 544 545 le = le->Flink; 546 } 547 548 #ifndef DEBUG_TRIM_EMULATION 549 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); 550 551 for (num = 0; num < total_num; num++) { 552 if (context.stripes[num].dmdsa) 553 ExFreePool(context.stripes[num].dmdsa); 554 555 if (context.stripes[num].Irp) 556 IoFreeIrp(context.stripes[num].Irp); 557 } 558 559 ExFreePool(context.stripes); 560 #endif 561 } 562 } 563 564 static bool trees_consistent(device_extension* Vcb) { 565 ULONG maxsize = Vcb->superblock.node_size - sizeof(tree_header); 566 LIST_ENTRY* le; 567 568 le = Vcb->trees.Flink; 569 while (le != &Vcb->trees) { 570 tree* t = CONTAINING_RECORD(le, tree, list_entry); 571 572 if (t->write) { 573 if (t->header.num_items == 0 && t->parent) { 574 #ifdef DEBUG_WRITE_LOOPS 575 ERR("empty tree found, looping again\n"); 576 #endif 577 return false; 578 } 579 580 if (t->size > maxsize) { 581 #ifdef DEBUG_WRITE_LOOPS 582 ERR("overlarge tree found (%u > %u), looping again\n", t->size, maxsize); 583 #endif 584 return false; 585 } 586 587 if (!t->has_new_address) { 588 #ifdef DEBUG_WRITE_LOOPS 589 ERR("tree found without new address, looping again\n"); 590 #endif 591 return false; 592 } 593 } 594 595 le = le->Flink; 596 } 597 598 return true; 599 } 600 601 static NTSTATUS add_parents(device_extension* Vcb, PIRP Irp) { 602 ULONG level; 603 LIST_ENTRY* le; 604 605 for (level = 0; level <= 255; level++) { 606 bool nothing_found = true; 607 608 TRACE("level = %lu\n", level); 609 610 le = Vcb->trees.Flink; 611 while (le != &Vcb->trees) { 612 tree* t = CONTAINING_RECORD(le, tree, list_entry); 613 614 if (t->write && t->header.level == level) { 615 TRACE("tree %p: root = %I64x, level = %x, parent = %p\n", t, t->header.tree_id, t->header.level, t->parent); 616 617 nothing_found = false; 618 619 if (t->parent) { 620 if (!t->parent->write) 621 TRACE("adding tree %p (level %x)\n", t->parent, t->header.level); 622 623 t->parent->write = true; 624 } else if (t->root != Vcb->root_root && t->root != Vcb->chunk_root) { 625 KEY searchkey; 626 traverse_ptr tp; 627 NTSTATUS Status; 628 #ifdef __REACTOS__ 629 tree* t2; 630 #endif 631 632 searchkey.obj_id = t->root->id; 633 searchkey.obj_type = TYPE_ROOT_ITEM; 634 searchkey.offset = 0xffffffffffffffff; 635 636 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 637 if (!NT_SUCCESS(Status)) { 638 ERR("error - find_item returned %08lx\n", Status); 639 return Status; 640 } 641 642 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 643 ERR("could not find ROOT_ITEM for tree %I64x\n", searchkey.obj_id); 644 return STATUS_INTERNAL_ERROR; 645 } 646 647 if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, delete and create new entry 648 ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG); 649 650 if (!ri) { 651 ERR("out of memory\n"); 652 return STATUS_INSUFFICIENT_RESOURCES; 653 } 654 655 RtlCopyMemory(ri, &t->root->root_item, sizeof(ROOT_ITEM)); 656 657 Status = delete_tree_item(Vcb, &tp); 658 if (!NT_SUCCESS(Status)) { 659 ERR("delete_tree_item returned %08lx\n", Status); 660 ExFreePool(ri); 661 return Status; 662 } 663 664 Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp); 665 if (!NT_SUCCESS(Status)) { 666 ERR("insert_tree_item returned %08lx\n", Status); 667 ExFreePool(ri); 668 return Status; 669 } 670 } 671 672 #ifndef __REACTOS__ 673 tree* t2 = tp.tree; 674 #else 675 t2 = tp.tree; 676 #endif 677 while (t2) { 678 t2->write = true; 679 680 t2 = t2->parent; 681 } 682 } 683 } 684 685 le = le->Flink; 686 } 687 688 if (nothing_found) 689 break; 690 } 691 692 return STATUS_SUCCESS; 693 } 694 695 static void add_parents_to_cache(tree* t) { 696 while (t->parent) { 697 t = t->parent; 698 t->write = true; 699 } 700 } 701 702 static bool insert_tree_extent_skinny(device_extension* Vcb, uint8_t level, uint64_t root_id, chunk* c, uint64_t address, PIRP Irp, LIST_ENTRY* rollback) { 703 NTSTATUS Status; 704 EXTENT_ITEM_SKINNY_METADATA* eism; 705 traverse_ptr insert_tp; 706 707 eism = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_SKINNY_METADATA), ALLOC_TAG); 708 if (!eism) { 709 ERR("out of memory\n"); 710 return false; 711 } 712 713 eism->ei.refcount = 1; 714 eism->ei.generation = Vcb->superblock.generation; 715 eism->ei.flags = EXTENT_ITEM_TREE_BLOCK; 716 eism->type = TYPE_TREE_BLOCK_REF; 717 eism->tbr.offset = root_id; 718 719 Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, sizeof(EXTENT_ITEM_SKINNY_METADATA), &insert_tp, Irp); 720 if (!NT_SUCCESS(Status)) { 721 ERR("insert_tree_item returned %08lx\n", Status); 722 ExFreePool(eism); 723 return false; 724 } 725 726 acquire_chunk_lock(c, Vcb); 727 728 space_list_subtract(c, false, address, Vcb->superblock.node_size, rollback); 729 730 release_chunk_lock(c, Vcb); 731 732 add_parents_to_cache(insert_tp.tree); 733 734 return true; 735 } 736 737 bool find_metadata_address_in_chunk(device_extension* Vcb, chunk* c, uint64_t* address) { 738 LIST_ENTRY* le; 739 space* s; 740 741 TRACE("(%p, %I64x, %p)\n", Vcb, c->offset, address); 742 743 if (Vcb->superblock.node_size > c->chunk_item->size - c->used) 744 return false; 745 746 if (!c->cache_loaded) { 747 NTSTATUS Status = load_cache_chunk(Vcb, c, NULL); 748 749 if (!NT_SUCCESS(Status)) { 750 ERR("load_cache_chunk returned %08lx\n", Status); 751 return false; 752 } 753 } 754 755 if (IsListEmpty(&c->space_size)) 756 return false; 757 758 if (!c->last_alloc_set) { 759 s = CONTAINING_RECORD(c->space.Blink, space, list_entry); 760 761 c->last_alloc = s->address; 762 c->last_alloc_set = true; 763 764 if (s->size >= Vcb->superblock.node_size) { 765 *address = s->address; 766 c->last_alloc += Vcb->superblock.node_size; 767 return true; 768 } 769 } 770 771 le = c->space.Flink; 772 while (le != &c->space) { 773 s = CONTAINING_RECORD(le, space, list_entry); 774 775 if (s->address <= c->last_alloc && s->address + s->size >= c->last_alloc + Vcb->superblock.node_size) { 776 *address = c->last_alloc; 777 c->last_alloc += Vcb->superblock.node_size; 778 return true; 779 } 780 781 le = le->Flink; 782 } 783 784 le = c->space_size.Flink; 785 while (le != &c->space_size) { 786 s = CONTAINING_RECORD(le, space, list_entry_size); 787 788 if (s->size == Vcb->superblock.node_size) { 789 *address = s->address; 790 c->last_alloc = s->address + Vcb->superblock.node_size; 791 return true; 792 } else if (s->size < Vcb->superblock.node_size) { 793 if (le == c->space_size.Flink) 794 return false; 795 796 s = CONTAINING_RECORD(le->Blink, space, list_entry_size); 797 798 *address = s->address; 799 c->last_alloc = s->address + Vcb->superblock.node_size; 800 801 return true; 802 } 803 804 le = le->Flink; 805 } 806 807 s = CONTAINING_RECORD(c->space_size.Blink, space, list_entry_size); 808 809 if (s->size > Vcb->superblock.node_size) { 810 *address = s->address; 811 c->last_alloc = s->address + Vcb->superblock.node_size; 812 return true; 813 } 814 815 return false; 816 } 817 818 static bool insert_tree_extent(device_extension* Vcb, uint8_t level, uint64_t root_id, chunk* c, uint64_t* new_address, PIRP Irp, LIST_ENTRY* rollback) { 819 NTSTATUS Status; 820 uint64_t address; 821 EXTENT_ITEM_TREE2* eit2; 822 traverse_ptr insert_tp; 823 824 TRACE("(%p, %x, %I64x, %p, %p, %p, %p)\n", Vcb, level, root_id, c, new_address, Irp, rollback); 825 826 if (!find_metadata_address_in_chunk(Vcb, c, &address)) 827 return false; 828 829 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) { 830 bool b = insert_tree_extent_skinny(Vcb, level, root_id, c, address, Irp, rollback); 831 832 if (b) 833 *new_address = address; 834 835 return b; 836 } 837 838 eit2 = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_ITEM_TREE2), ALLOC_TAG); 839 if (!eit2) { 840 ERR("out of memory\n"); 841 return false; 842 } 843 844 eit2->eit.extent_item.refcount = 1; 845 eit2->eit.extent_item.generation = Vcb->superblock.generation; 846 eit2->eit.extent_item.flags = EXTENT_ITEM_TREE_BLOCK; 847 eit2->eit.level = level; 848 eit2->type = TYPE_TREE_BLOCK_REF; 849 eit2->tbr.offset = root_id; 850 851 Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_EXTENT_ITEM, Vcb->superblock.node_size, eit2, sizeof(EXTENT_ITEM_TREE2), &insert_tp, Irp); 852 if (!NT_SUCCESS(Status)) { 853 ERR("insert_tree_item returned %08lx\n", Status); 854 ExFreePool(eit2); 855 return false; 856 } 857 858 acquire_chunk_lock(c, Vcb); 859 860 space_list_subtract(c, false, address, Vcb->superblock.node_size, rollback); 861 862 release_chunk_lock(c, Vcb); 863 864 add_parents_to_cache(insert_tp.tree); 865 866 *new_address = address; 867 868 return true; 869 } 870 871 NTSTATUS get_tree_new_address(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { 872 NTSTATUS Status; 873 chunk *origchunk = NULL, *c; 874 LIST_ENTRY* le; 875 uint64_t flags, addr; 876 877 if (t->root->id == BTRFS_ROOT_CHUNK) 878 flags = Vcb->system_flags; 879 else 880 flags = Vcb->metadata_flags; 881 882 if (t->has_address) { 883 origchunk = get_chunk_from_address(Vcb, t->header.address); 884 885 if (origchunk && !origchunk->readonly && !origchunk->reloc && origchunk->chunk_item->type == flags && 886 insert_tree_extent(Vcb, t->header.level, t->root->id, origchunk, &addr, Irp, rollback)) { 887 t->new_address = addr; 888 t->has_new_address = true; 889 return STATUS_SUCCESS; 890 } 891 } 892 893 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, true); 894 895 le = Vcb->chunks.Flink; 896 while (le != &Vcb->chunks) { 897 c = CONTAINING_RECORD(le, chunk, list_entry); 898 899 if (!c->readonly && !c->reloc) { 900 acquire_chunk_lock(c, Vcb); 901 902 if (c != origchunk && c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= Vcb->superblock.node_size) { 903 if (insert_tree_extent(Vcb, t->header.level, t->root->id, c, &addr, Irp, rollback)) { 904 release_chunk_lock(c, Vcb); 905 ExReleaseResourceLite(&Vcb->chunk_lock); 906 t->new_address = addr; 907 t->has_new_address = true; 908 return STATUS_SUCCESS; 909 } 910 } 911 912 release_chunk_lock(c, Vcb); 913 } 914 915 le = le->Flink; 916 } 917 918 // allocate new chunk if necessary 919 920 Status = alloc_chunk(Vcb, flags, &c, false); 921 922 if (!NT_SUCCESS(Status)) { 923 ERR("alloc_chunk returned %08lx\n", Status); 924 ExReleaseResourceLite(&Vcb->chunk_lock); 925 return Status; 926 } 927 928 acquire_chunk_lock(c, Vcb); 929 930 if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) { 931 if (insert_tree_extent(Vcb, t->header.level, t->root->id, c, &addr, Irp, rollback)) { 932 release_chunk_lock(c, Vcb); 933 ExReleaseResourceLite(&Vcb->chunk_lock); 934 t->new_address = addr; 935 t->has_new_address = true; 936 return STATUS_SUCCESS; 937 } 938 } 939 940 release_chunk_lock(c, Vcb); 941 942 ExReleaseResourceLite(&Vcb->chunk_lock); 943 944 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb->superblock.node_size); 945 946 return STATUS_DISK_FULL; 947 } 948 949 static NTSTATUS reduce_tree_extent(device_extension* Vcb, uint64_t address, tree* t, uint64_t parent_root, uint8_t level, PIRP Irp, LIST_ENTRY* rollback) { 950 NTSTATUS Status; 951 uint64_t rc, root; 952 953 TRACE("(%p, %I64x, %p)\n", Vcb, address, t); 954 955 rc = get_extent_refcount(Vcb, address, Vcb->superblock.node_size, Irp); 956 if (rc == 0) { 957 ERR("error - refcount for extent %I64x was 0\n", address); 958 return STATUS_INTERNAL_ERROR; 959 } 960 961 if (!t || t->parent) 962 root = parent_root; 963 else 964 root = t->header.tree_id; 965 966 Status = decrease_extent_refcount_tree(Vcb, address, Vcb->superblock.node_size, root, level, Irp); 967 if (!NT_SUCCESS(Status)) { 968 ERR("decrease_extent_refcount_tree returned %08lx\n", Status); 969 return Status; 970 } 971 972 if (rc == 1) { 973 chunk* c = get_chunk_from_address(Vcb, address); 974 975 if (c) { 976 acquire_chunk_lock(c, Vcb); 977 978 if (!c->cache_loaded) { 979 Status = load_cache_chunk(Vcb, c, NULL); 980 981 if (!NT_SUCCESS(Status)) { 982 ERR("load_cache_chunk returned %08lx\n", Status); 983 release_chunk_lock(c, Vcb); 984 return Status; 985 } 986 } 987 988 c->used -= Vcb->superblock.node_size; 989 990 space_list_add(c, address, Vcb->superblock.node_size, rollback); 991 992 release_chunk_lock(c, Vcb); 993 } else 994 ERR("could not find chunk for address %I64x\n", address); 995 } 996 997 return STATUS_SUCCESS; 998 } 999 1000 static NTSTATUS add_changed_extent_ref_edr(changed_extent* ce, EXTENT_DATA_REF* edr, bool old) { 1001 LIST_ENTRY *le2, *list; 1002 changed_extent_ref* cer; 1003 1004 list = old ? &ce->old_refs : &ce->refs; 1005 1006 le2 = list->Flink; 1007 while (le2 != list) { 1008 cer = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 1009 1010 if (cer->type == TYPE_EXTENT_DATA_REF && cer->edr.root == edr->root && cer->edr.objid == edr->objid && cer->edr.offset == edr->offset) { 1011 cer->edr.count += edr->count; 1012 goto end; 1013 } 1014 1015 le2 = le2->Flink; 1016 } 1017 1018 cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); 1019 if (!cer) { 1020 ERR("out of memory\n"); 1021 return STATUS_INSUFFICIENT_RESOURCES; 1022 } 1023 1024 cer->type = TYPE_EXTENT_DATA_REF; 1025 RtlCopyMemory(&cer->edr, edr, sizeof(EXTENT_DATA_REF)); 1026 InsertTailList(list, &cer->list_entry); 1027 1028 end: 1029 if (old) 1030 ce->old_count += edr->count; 1031 else 1032 ce->count += edr->count; 1033 1034 return STATUS_SUCCESS; 1035 } 1036 1037 static NTSTATUS add_changed_extent_ref_sdr(changed_extent* ce, SHARED_DATA_REF* sdr, bool old) { 1038 LIST_ENTRY *le2, *list; 1039 changed_extent_ref* cer; 1040 1041 list = old ? &ce->old_refs : &ce->refs; 1042 1043 le2 = list->Flink; 1044 while (le2 != list) { 1045 cer = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 1046 1047 if (cer->type == TYPE_SHARED_DATA_REF && cer->sdr.offset == sdr->offset) { 1048 cer->sdr.count += sdr->count; 1049 goto end; 1050 } 1051 1052 le2 = le2->Flink; 1053 } 1054 1055 cer = ExAllocatePoolWithTag(PagedPool, sizeof(changed_extent_ref), ALLOC_TAG); 1056 if (!cer) { 1057 ERR("out of memory\n"); 1058 return STATUS_INSUFFICIENT_RESOURCES; 1059 } 1060 1061 cer->type = TYPE_SHARED_DATA_REF; 1062 RtlCopyMemory(&cer->sdr, sdr, sizeof(SHARED_DATA_REF)); 1063 InsertTailList(list, &cer->list_entry); 1064 1065 end: 1066 if (old) 1067 ce->old_count += sdr->count; 1068 else 1069 ce->count += sdr->count; 1070 1071 return STATUS_SUCCESS; 1072 } 1073 1074 static bool shared_tree_is_unique(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { 1075 KEY searchkey; 1076 traverse_ptr tp; 1077 NTSTATUS Status; 1078 1079 if (!t->updated_extents && t->has_address) { 1080 Status = update_tree_extents(Vcb, t, Irp, rollback); 1081 if (!NT_SUCCESS(Status)) { 1082 ERR("update_tree_extents returned %08lx\n", Status); 1083 return false; 1084 } 1085 } 1086 1087 searchkey.obj_id = t->header.address; 1088 searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM; 1089 searchkey.offset = 0xffffffffffffffff; 1090 1091 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 1092 if (!NT_SUCCESS(Status)) { 1093 ERR("error - find_item returned %08lx\n", Status); 1094 return false; 1095 } 1096 1097 if (tp.item->key.obj_id == t->header.address && (tp.item->key.obj_type == TYPE_METADATA_ITEM || tp.item->key.obj_type == TYPE_EXTENT_ITEM)) 1098 return false; 1099 else 1100 return true; 1101 } 1102 1103 static NTSTATUS update_tree_extents(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { 1104 NTSTATUS Status; 1105 uint64_t rc = get_extent_refcount(Vcb, t->header.address, Vcb->superblock.node_size, Irp); 1106 uint64_t flags = get_extent_flags(Vcb, t->header.address, Irp); 1107 1108 if (rc == 0) { 1109 ERR("refcount for extent %I64x was 0\n", t->header.address); 1110 return STATUS_INTERNAL_ERROR; 1111 } 1112 1113 if (flags & EXTENT_ITEM_SHARED_BACKREFS || t->header.flags & HEADER_FLAG_SHARED_BACKREF || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) { 1114 TREE_BLOCK_REF tbr; 1115 bool unique = rc > 1 ? false : (t->parent ? shared_tree_is_unique(Vcb, t->parent, Irp, rollback) : false); 1116 1117 if (t->header.level == 0) { 1118 LIST_ENTRY* le; 1119 1120 le = t->itemlist.Flink; 1121 while (le != &t->itemlist) { 1122 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 1123 1124 if (!td->inserted && td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { 1125 EXTENT_DATA* ed = (EXTENT_DATA*)td->data; 1126 1127 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { 1128 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 1129 1130 if (ed2->size > 0) { 1131 EXTENT_DATA_REF edr; 1132 changed_extent* ce = NULL; 1133 chunk* c = get_chunk_from_address(Vcb, ed2->address); 1134 1135 if (c) { 1136 LIST_ENTRY* le2; 1137 1138 le2 = c->changed_extents.Flink; 1139 while (le2 != &c->changed_extents) { 1140 changed_extent* ce2 = CONTAINING_RECORD(le2, changed_extent, list_entry); 1141 1142 if (ce2->address == ed2->address) { 1143 ce = ce2; 1144 break; 1145 } 1146 1147 le2 = le2->Flink; 1148 } 1149 } 1150 1151 edr.root = t->root->id; 1152 edr.objid = td->key.obj_id; 1153 edr.offset = td->key.offset - ed2->offset; 1154 edr.count = 1; 1155 1156 if (ce) { 1157 Status = add_changed_extent_ref_edr(ce, &edr, true); 1158 if (!NT_SUCCESS(Status)) { 1159 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1160 return Status; 1161 } 1162 1163 Status = add_changed_extent_ref_edr(ce, &edr, false); 1164 if (!NT_SUCCESS(Status)) { 1165 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1166 return Status; 1167 } 1168 } 1169 1170 Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp); 1171 if (!NT_SUCCESS(Status)) { 1172 ERR("increase_extent_refcount returned %08lx\n", Status); 1173 return Status; 1174 } 1175 1176 if ((flags & EXTENT_ITEM_SHARED_BACKREFS && unique) || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) { 1177 uint64_t sdrrc = find_extent_shared_data_refcount(Vcb, ed2->address, t->header.address, Irp); 1178 1179 if (sdrrc > 0) { 1180 SHARED_DATA_REF sdr; 1181 1182 sdr.offset = t->header.address; 1183 sdr.count = 1; 1184 1185 Status = decrease_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, 1186 t->header.address, ce ? ce->superseded : false, Irp); 1187 if (!NT_SUCCESS(Status)) { 1188 ERR("decrease_extent_refcount returned %08lx\n", Status); 1189 return Status; 1190 } 1191 1192 if (ce) { 1193 LIST_ENTRY* le2; 1194 1195 le2 = ce->refs.Flink; 1196 while (le2 != &ce->refs) { 1197 changed_extent_ref* cer = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 1198 1199 if (cer->type == TYPE_SHARED_DATA_REF && cer->sdr.offset == sdr.offset) { 1200 ce->count--; 1201 cer->sdr.count--; 1202 break; 1203 } 1204 1205 le2 = le2->Flink; 1206 } 1207 1208 le2 = ce->old_refs.Flink; 1209 while (le2 != &ce->old_refs) { 1210 changed_extent_ref* cer = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 1211 1212 if (cer->type == TYPE_SHARED_DATA_REF && cer->sdr.offset == sdr.offset) { 1213 ce->old_count--; 1214 1215 if (cer->sdr.count > 1) 1216 cer->sdr.count--; 1217 else { 1218 RemoveEntryList(&cer->list_entry); 1219 ExFreePool(cer); 1220 } 1221 1222 break; 1223 } 1224 1225 le2 = le2->Flink; 1226 } 1227 } 1228 } 1229 } 1230 1231 // FIXME - clear shared flag if unique? 1232 } 1233 } 1234 } 1235 1236 le = le->Flink; 1237 } 1238 } else { 1239 LIST_ENTRY* le; 1240 1241 le = t->itemlist.Flink; 1242 while (le != &t->itemlist) { 1243 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 1244 1245 if (!td->inserted) { 1246 tbr.offset = t->root->id; 1247 1248 Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, 1249 &tbr, &td->key, t->header.level - 1, Irp); 1250 if (!NT_SUCCESS(Status)) { 1251 ERR("increase_extent_refcount returned %08lx\n", Status); 1252 return Status; 1253 } 1254 1255 if (unique || !(t->header.flags & HEADER_FLAG_MIXED_BACKREF)) { 1256 uint64_t sbrrc = find_extent_shared_tree_refcount(Vcb, td->treeholder.address, t->header.address, Irp); 1257 1258 if (sbrrc > 0) { 1259 SHARED_BLOCK_REF sbr; 1260 1261 sbr.offset = t->header.address; 1262 1263 Status = decrease_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0, 1264 t->header.address, false, Irp); 1265 if (!NT_SUCCESS(Status)) { 1266 ERR("decrease_extent_refcount returned %08lx\n", Status); 1267 return Status; 1268 } 1269 } 1270 } 1271 1272 // FIXME - clear shared flag if unique? 1273 } 1274 1275 le = le->Flink; 1276 } 1277 } 1278 1279 if (unique) { 1280 uint64_t sbrrc = find_extent_shared_tree_refcount(Vcb, t->header.address, t->parent->header.address, Irp); 1281 1282 if (sbrrc == 1) { 1283 SHARED_BLOCK_REF sbr; 1284 1285 sbr.offset = t->parent->header.address; 1286 1287 Status = decrease_extent_refcount(Vcb, t->header.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, NULL, 0, 1288 t->parent->header.address, false, Irp); 1289 if (!NT_SUCCESS(Status)) { 1290 ERR("decrease_extent_refcount returned %08lx\n", Status); 1291 return Status; 1292 } 1293 } 1294 } 1295 1296 if (t->parent) 1297 tbr.offset = t->parent->header.tree_id; 1298 else 1299 tbr.offset = t->header.tree_id; 1300 1301 Status = increase_extent_refcount(Vcb, t->header.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, 1302 t->parent ? &t->paritem->key : NULL, t->header.level, Irp); 1303 if (!NT_SUCCESS(Status)) { 1304 ERR("increase_extent_refcount returned %08lx\n", Status); 1305 return Status; 1306 } 1307 1308 // FIXME - clear shared flag if unique? 1309 1310 t->header.flags &= ~HEADER_FLAG_SHARED_BACKREF; 1311 } 1312 1313 if (rc > 1 || t->header.tree_id == t->root->id) { 1314 Status = reduce_tree_extent(Vcb, t->header.address, t, t->parent ? t->parent->header.tree_id : t->header.tree_id, t->header.level, Irp, rollback); 1315 1316 if (!NT_SUCCESS(Status)) { 1317 ERR("reduce_tree_extent returned %08lx\n", Status); 1318 return Status; 1319 } 1320 } 1321 1322 t->has_address = false; 1323 1324 if ((rc > 1 || t->header.tree_id != t->root->id) && !(flags & EXTENT_ITEM_SHARED_BACKREFS)) { 1325 if (t->header.tree_id == t->root->id) { 1326 flags |= EXTENT_ITEM_SHARED_BACKREFS; 1327 update_extent_flags(Vcb, t->header.address, flags, Irp); 1328 } 1329 1330 if (t->header.level > 0) { 1331 LIST_ENTRY* le; 1332 1333 le = t->itemlist.Flink; 1334 while (le != &t->itemlist) { 1335 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 1336 1337 if (!td->inserted) { 1338 if (t->header.tree_id == t->root->id) { 1339 SHARED_BLOCK_REF sbr; 1340 1341 sbr.offset = t->header.address; 1342 1343 Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_SHARED_BLOCK_REF, &sbr, &td->key, t->header.level - 1, Irp); 1344 } else { 1345 TREE_BLOCK_REF tbr; 1346 1347 tbr.offset = t->root->id; 1348 1349 Status = increase_extent_refcount(Vcb, td->treeholder.address, Vcb->superblock.node_size, TYPE_TREE_BLOCK_REF, &tbr, &td->key, t->header.level - 1, Irp); 1350 } 1351 1352 if (!NT_SUCCESS(Status)) { 1353 ERR("increase_extent_refcount returned %08lx\n", Status); 1354 return Status; 1355 } 1356 } 1357 1358 le = le->Flink; 1359 } 1360 } else { 1361 LIST_ENTRY* le; 1362 1363 le = t->itemlist.Flink; 1364 while (le != &t->itemlist) { 1365 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 1366 1367 if (!td->inserted && td->key.obj_type == TYPE_EXTENT_DATA && td->size >= sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { 1368 EXTENT_DATA* ed = (EXTENT_DATA*)td->data; 1369 1370 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { 1371 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 1372 1373 if (ed2->size > 0) { 1374 changed_extent* ce = NULL; 1375 chunk* c = get_chunk_from_address(Vcb, ed2->address); 1376 1377 if (c) { 1378 LIST_ENTRY* le2; 1379 1380 le2 = c->changed_extents.Flink; 1381 while (le2 != &c->changed_extents) { 1382 changed_extent* ce2 = CONTAINING_RECORD(le2, changed_extent, list_entry); 1383 1384 if (ce2->address == ed2->address) { 1385 ce = ce2; 1386 break; 1387 } 1388 1389 le2 = le2->Flink; 1390 } 1391 } 1392 1393 if (t->header.tree_id == t->root->id) { 1394 SHARED_DATA_REF sdr; 1395 1396 sdr.offset = t->header.address; 1397 sdr.count = 1; 1398 1399 if (ce) { 1400 Status = add_changed_extent_ref_sdr(ce, &sdr, true); 1401 if (!NT_SUCCESS(Status)) { 1402 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1403 return Status; 1404 } 1405 1406 Status = add_changed_extent_ref_sdr(ce, &sdr, false); 1407 if (!NT_SUCCESS(Status)) { 1408 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1409 return Status; 1410 } 1411 } 1412 1413 Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_SHARED_DATA_REF, &sdr, NULL, 0, Irp); 1414 } else { 1415 EXTENT_DATA_REF edr; 1416 1417 edr.root = t->root->id; 1418 edr.objid = td->key.obj_id; 1419 edr.offset = td->key.offset - ed2->offset; 1420 edr.count = 1; 1421 1422 if (ce) { 1423 Status = add_changed_extent_ref_edr(ce, &edr, true); 1424 if (!NT_SUCCESS(Status)) { 1425 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1426 return Status; 1427 } 1428 1429 Status = add_changed_extent_ref_edr(ce, &edr, false); 1430 if (!NT_SUCCESS(Status)) { 1431 ERR("add_changed_extent_ref_edr returned %08lx\n", Status); 1432 return Status; 1433 } 1434 } 1435 1436 Status = increase_extent_refcount(Vcb, ed2->address, ed2->size, TYPE_EXTENT_DATA_REF, &edr, NULL, 0, Irp); 1437 } 1438 1439 if (!NT_SUCCESS(Status)) { 1440 ERR("increase_extent_refcount returned %08lx\n", Status); 1441 return Status; 1442 } 1443 } 1444 } 1445 } 1446 1447 le = le->Flink; 1448 } 1449 } 1450 } 1451 1452 t->updated_extents = true; 1453 t->header.tree_id = t->root->id; 1454 1455 return STATUS_SUCCESS; 1456 } 1457 1458 static NTSTATUS allocate_tree_extents(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { 1459 LIST_ENTRY* le; 1460 NTSTATUS Status; 1461 bool changed = false; 1462 uint8_t max_level = 0, level; 1463 1464 TRACE("(%p)\n", Vcb); 1465 1466 le = Vcb->trees.Flink; 1467 while (le != &Vcb->trees) { 1468 tree* t = CONTAINING_RECORD(le, tree, list_entry); 1469 1470 if (t->write && !t->has_new_address) { 1471 chunk* c; 1472 1473 if (t->has_address) { 1474 c = get_chunk_from_address(Vcb, t->header.address); 1475 1476 if (c) { 1477 if (!c->cache_loaded) { 1478 acquire_chunk_lock(c, Vcb); 1479 1480 if (!c->cache_loaded) { 1481 Status = load_cache_chunk(Vcb, c, NULL); 1482 1483 if (!NT_SUCCESS(Status)) { 1484 ERR("load_cache_chunk returned %08lx\n", Status); 1485 release_chunk_lock(c, Vcb); 1486 return Status; 1487 } 1488 } 1489 1490 release_chunk_lock(c, Vcb); 1491 } 1492 } 1493 } 1494 1495 Status = get_tree_new_address(Vcb, t, Irp, rollback); 1496 if (!NT_SUCCESS(Status)) { 1497 ERR("get_tree_new_address returned %08lx\n", Status); 1498 return Status; 1499 } 1500 1501 TRACE("allocated extent %I64x\n", t->new_address); 1502 1503 c = get_chunk_from_address(Vcb, t->new_address); 1504 1505 if (c) 1506 c->used += Vcb->superblock.node_size; 1507 else { 1508 ERR("could not find chunk for address %I64x\n", t->new_address); 1509 return STATUS_INTERNAL_ERROR; 1510 } 1511 1512 changed = true; 1513 1514 if (t->header.level > max_level) 1515 max_level = t->header.level; 1516 } 1517 1518 le = le->Flink; 1519 } 1520 1521 if (!changed) 1522 return STATUS_SUCCESS; 1523 1524 level = max_level; 1525 do { 1526 le = Vcb->trees.Flink; 1527 while (le != &Vcb->trees) { 1528 tree* t = CONTAINING_RECORD(le, tree, list_entry); 1529 1530 if (t->write && !t->updated_extents && t->has_address && t->header.level == level) { 1531 Status = update_tree_extents(Vcb, t, Irp, rollback); 1532 if (!NT_SUCCESS(Status)) { 1533 ERR("update_tree_extents returned %08lx\n", Status); 1534 return Status; 1535 } 1536 } 1537 1538 le = le->Flink; 1539 } 1540 1541 if (level == 0) 1542 break; 1543 1544 level--; 1545 } while (true); 1546 1547 return STATUS_SUCCESS; 1548 } 1549 1550 static NTSTATUS update_root_root(device_extension* Vcb, bool no_cache, PIRP Irp, LIST_ENTRY* rollback) { 1551 LIST_ENTRY* le; 1552 NTSTATUS Status; 1553 1554 TRACE("(%p)\n", Vcb); 1555 1556 le = Vcb->trees.Flink; 1557 while (le != &Vcb->trees) { 1558 tree* t = CONTAINING_RECORD(le, tree, list_entry); 1559 1560 if (t->write && !t->parent) { 1561 if (t->root != Vcb->root_root && t->root != Vcb->chunk_root) { 1562 KEY searchkey; 1563 traverse_ptr tp; 1564 1565 searchkey.obj_id = t->root->id; 1566 searchkey.obj_type = TYPE_ROOT_ITEM; 1567 searchkey.offset = 0xffffffffffffffff; 1568 1569 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 1570 if (!NT_SUCCESS(Status)) { 1571 ERR("error - find_item returned %08lx\n", Status); 1572 return Status; 1573 } 1574 1575 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 1576 ERR("could not find ROOT_ITEM for tree %I64x\n", searchkey.obj_id); 1577 return STATUS_INTERNAL_ERROR; 1578 } 1579 1580 TRACE("updating the address for root %I64x to %I64x\n", searchkey.obj_id, t->new_address); 1581 1582 t->root->root_item.block_number = t->new_address; 1583 t->root->root_item.root_level = t->header.level; 1584 t->root->root_item.generation = Vcb->superblock.generation; 1585 t->root->root_item.generation2 = Vcb->superblock.generation; 1586 1587 // item is guaranteed to be at least sizeof(ROOT_ITEM), due to add_parents 1588 1589 RtlCopyMemory(tp.item->data, &t->root->root_item, sizeof(ROOT_ITEM)); 1590 } 1591 1592 t->root->treeholder.address = t->new_address; 1593 t->root->treeholder.generation = Vcb->superblock.generation; 1594 } 1595 1596 le = le->Flink; 1597 } 1598 1599 if (!no_cache && !(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE)) { 1600 ExAcquireResourceSharedLite(&Vcb->chunk_lock, true); 1601 Status = update_chunk_caches(Vcb, Irp, rollback); 1602 ExReleaseResourceLite(&Vcb->chunk_lock); 1603 1604 if (!NT_SUCCESS(Status)) { 1605 ERR("update_chunk_caches returned %08lx\n", Status); 1606 return Status; 1607 } 1608 } 1609 1610 return STATUS_SUCCESS; 1611 } 1612 1613 NTSTATUS do_tree_writes(device_extension* Vcb, LIST_ENTRY* tree_writes, bool no_free) { 1614 chunk* c; 1615 LIST_ENTRY* le; 1616 tree_write* tw; 1617 NTSTATUS Status; 1618 ULONG i, num_bits; 1619 write_data_context* wtc; 1620 ULONG bit_num = 0; 1621 bool raid56 = false; 1622 1623 // merge together runs 1624 c = NULL; 1625 le = tree_writes->Flink; 1626 while (le != tree_writes) { 1627 tw = CONTAINING_RECORD(le, tree_write, list_entry); 1628 1629 if (!c || tw->address < c->offset || tw->address >= c->offset + c->chunk_item->size) 1630 c = get_chunk_from_address(Vcb, tw->address); 1631 else { 1632 tree_write* tw2 = CONTAINING_RECORD(le->Blink, tree_write, list_entry); 1633 1634 if (tw->address == tw2->address + tw2->length) { 1635 uint8_t* data = ExAllocatePoolWithTag(NonPagedPool, tw2->length + tw->length, ALLOC_TAG); 1636 1637 if (!data) { 1638 ERR("out of memory\n"); 1639 return STATUS_INSUFFICIENT_RESOURCES; 1640 } 1641 1642 RtlCopyMemory(data, tw2->data, tw2->length); 1643 RtlCopyMemory(&data[tw2->length], tw->data, tw->length); 1644 1645 if (!no_free || tw2->allocated) 1646 ExFreePool(tw2->data); 1647 1648 tw2->data = data; 1649 tw2->length += tw->length; 1650 tw2->allocated = true; 1651 1652 if (!no_free || tw->allocated) 1653 ExFreePool(tw->data); 1654 1655 RemoveEntryList(&tw->list_entry); 1656 ExFreePool(tw); 1657 1658 le = tw2->list_entry.Flink; 1659 continue; 1660 } 1661 } 1662 1663 tw->c = c; 1664 1665 if (c->chunk_item->type & (BLOCK_FLAG_RAID5 | BLOCK_FLAG_RAID6)) 1666 raid56 = true; 1667 1668 le = le->Flink; 1669 } 1670 1671 num_bits = 0; 1672 1673 le = tree_writes->Flink; 1674 while (le != tree_writes) { 1675 tw = CONTAINING_RECORD(le, tree_write, list_entry); 1676 1677 num_bits++; 1678 1679 le = le->Flink; 1680 } 1681 1682 wtc = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_context) * num_bits, ALLOC_TAG); 1683 if (!wtc) { 1684 ERR("out of memory\n"); 1685 return STATUS_INSUFFICIENT_RESOURCES; 1686 } 1687 1688 le = tree_writes->Flink; 1689 1690 while (le != tree_writes) { 1691 tw = CONTAINING_RECORD(le, tree_write, list_entry); 1692 1693 TRACE("address: %I64x, size: %x\n", tw->address, tw->length); 1694 1695 KeInitializeEvent(&wtc[bit_num].Event, NotificationEvent, false); 1696 InitializeListHead(&wtc[bit_num].stripes); 1697 wtc[bit_num].need_wait = false; 1698 wtc[bit_num].stripes_left = 0; 1699 wtc[bit_num].parity1 = wtc[bit_num].parity2 = wtc[bit_num].scratch = NULL; 1700 wtc[bit_num].mdl = wtc[bit_num].parity1_mdl = wtc[bit_num].parity2_mdl = NULL; 1701 1702 Status = write_data(Vcb, tw->address, tw->data, tw->length, &wtc[bit_num], NULL, NULL, false, 0, HighPagePriority); 1703 if (!NT_SUCCESS(Status)) { 1704 ERR("write_data returned %08lx\n", Status); 1705 1706 for (i = 0; i < num_bits; i++) { 1707 free_write_data_stripes(&wtc[i]); 1708 } 1709 ExFreePool(wtc); 1710 1711 return Status; 1712 } 1713 1714 bit_num++; 1715 1716 le = le->Flink; 1717 } 1718 1719 for (i = 0; i < num_bits; i++) { 1720 if (wtc[i].stripes.Flink != &wtc[i].stripes) { 1721 // launch writes and wait 1722 le = wtc[i].stripes.Flink; 1723 while (le != &wtc[i].stripes) { 1724 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 1725 1726 if (stripe->status != WriteDataStatus_Ignore) { 1727 wtc[i].need_wait = true; 1728 IoCallDriver(stripe->device->devobj, stripe->Irp); 1729 } 1730 1731 le = le->Flink; 1732 } 1733 } 1734 } 1735 1736 for (i = 0; i < num_bits; i++) { 1737 if (wtc[i].need_wait) 1738 KeWaitForSingleObject(&wtc[i].Event, Executive, KernelMode, false, NULL); 1739 } 1740 1741 for (i = 0; i < num_bits; i++) { 1742 le = wtc[i].stripes.Flink; 1743 while (le != &wtc[i].stripes) { 1744 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 1745 1746 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { 1747 Status = stripe->iosb.Status; 1748 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS); 1749 break; 1750 } 1751 1752 le = le->Flink; 1753 } 1754 1755 free_write_data_stripes(&wtc[i]); 1756 } 1757 1758 ExFreePool(wtc); 1759 1760 if (raid56) { 1761 c = NULL; 1762 1763 le = tree_writes->Flink; 1764 while (le != tree_writes) { 1765 tw = CONTAINING_RECORD(le, tree_write, list_entry); 1766 1767 if (tw->c != c) { 1768 c = tw->c; 1769 1770 ExAcquireResourceExclusiveLite(&c->partial_stripes_lock, true); 1771 1772 while (!IsListEmpty(&c->partial_stripes)) { 1773 partial_stripe* ps = CONTAINING_RECORD(RemoveHeadList(&c->partial_stripes), partial_stripe, list_entry); 1774 1775 Status = flush_partial_stripe(Vcb, c, ps); 1776 1777 if (ps->bmparr) 1778 ExFreePool(ps->bmparr); 1779 1780 ExFreePool(ps); 1781 1782 if (!NT_SUCCESS(Status)) { 1783 ERR("flush_partial_stripe returned %08lx\n", Status); 1784 ExReleaseResourceLite(&c->partial_stripes_lock); 1785 return Status; 1786 } 1787 } 1788 1789 ExReleaseResourceLite(&c->partial_stripes_lock); 1790 } 1791 1792 le = le->Flink; 1793 } 1794 } 1795 1796 return STATUS_SUCCESS; 1797 } 1798 1799 void calc_tree_checksum(device_extension* Vcb, tree_header* th) { 1800 switch (Vcb->superblock.csum_type) { 1801 case CSUM_TYPE_CRC32C: 1802 *((uint32_t*)th) = ~calc_crc32c(0xffffffff, (uint8_t*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); 1803 break; 1804 1805 case CSUM_TYPE_XXHASH: 1806 *((uint64_t*)th) = XXH64((uint8_t*)&th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum), 0); 1807 break; 1808 1809 case CSUM_TYPE_SHA256: 1810 calc_sha256((uint8_t*)th, &th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); 1811 break; 1812 1813 case CSUM_TYPE_BLAKE2: 1814 blake2b((uint8_t*)th, BLAKE2_HASH_SIZE, &th->fs_uuid, Vcb->superblock.node_size - sizeof(th->csum)); 1815 break; 1816 } 1817 } 1818 1819 static NTSTATUS write_trees(device_extension* Vcb, PIRP Irp) { 1820 ULONG level; 1821 uint8_t *data, *body; 1822 NTSTATUS Status; 1823 LIST_ENTRY* le; 1824 LIST_ENTRY tree_writes; 1825 tree_write* tw; 1826 1827 TRACE("(%p)\n", Vcb); 1828 1829 InitializeListHead(&tree_writes); 1830 1831 for (level = 0; level <= 255; level++) { 1832 bool nothing_found = true; 1833 1834 TRACE("level = %lu\n", level); 1835 1836 le = Vcb->trees.Flink; 1837 while (le != &Vcb->trees) { 1838 tree* t = CONTAINING_RECORD(le, tree, list_entry); 1839 1840 if (t->write && t->header.level == level) { 1841 KEY firstitem, searchkey; 1842 LIST_ENTRY* le2; 1843 traverse_ptr tp; 1844 1845 if (!t->has_new_address) { 1846 ERR("error - tried to write tree with no new address\n"); 1847 return STATUS_INTERNAL_ERROR; 1848 } 1849 1850 le2 = t->itemlist.Flink; 1851 while (le2 != &t->itemlist) { 1852 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry); 1853 if (!td->ignore) { 1854 firstitem = td->key; 1855 break; 1856 } 1857 le2 = le2->Flink; 1858 } 1859 1860 if (t->parent) { 1861 t->paritem->key = firstitem; 1862 t->paritem->treeholder.address = t->new_address; 1863 t->paritem->treeholder.generation = Vcb->superblock.generation; 1864 } 1865 1866 if (!(Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)) { 1867 EXTENT_ITEM_TREE* eit; 1868 1869 searchkey.obj_id = t->new_address; 1870 searchkey.obj_type = TYPE_EXTENT_ITEM; 1871 searchkey.offset = Vcb->superblock.node_size; 1872 1873 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 1874 if (!NT_SUCCESS(Status)) { 1875 ERR("error - find_item returned %08lx\n", Status); 1876 return Status; 1877 } 1878 1879 if (keycmp(searchkey, tp.item->key)) { 1880 ERR("could not find %I64x,%x,%I64x in extent_root (found %I64x,%x,%I64x instead)\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 1881 return STATUS_INTERNAL_ERROR; 1882 } 1883 1884 if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) { 1885 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM_TREE)); 1886 return STATUS_INTERNAL_ERROR; 1887 } 1888 1889 eit = (EXTENT_ITEM_TREE*)tp.item->data; 1890 eit->firstitem = firstitem; 1891 } 1892 1893 nothing_found = false; 1894 } 1895 1896 le = le->Flink; 1897 } 1898 1899 if (nothing_found) 1900 break; 1901 } 1902 1903 TRACE("allocated tree extents\n"); 1904 1905 le = Vcb->trees.Flink; 1906 while (le != &Vcb->trees) { 1907 tree* t = CONTAINING_RECORD(le, tree, list_entry); 1908 LIST_ENTRY* le2; 1909 #ifdef DEBUG_PARANOID 1910 uint32_t num_items = 0, size = 0; 1911 bool crash = false; 1912 #endif 1913 1914 if (t->write) { 1915 #ifdef DEBUG_PARANOID 1916 bool first = true; 1917 KEY lastkey; 1918 1919 le2 = t->itemlist.Flink; 1920 while (le2 != &t->itemlist) { 1921 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry); 1922 if (!td->ignore) { 1923 num_items++; 1924 1925 if (!first) { 1926 if (keycmp(td->key, lastkey) == 0) { 1927 ERR("(%I64x,%x,%I64x): duplicate key\n", td->key.obj_id, td->key.obj_type, td->key.offset); 1928 crash = true; 1929 } else if (keycmp(td->key, lastkey) == -1) { 1930 ERR("(%I64x,%x,%I64x): key out of order\n", td->key.obj_id, td->key.obj_type, td->key.offset); 1931 crash = true; 1932 } 1933 } else 1934 first = false; 1935 1936 lastkey = td->key; 1937 1938 if (t->header.level == 0) 1939 size += td->size; 1940 } 1941 le2 = le2->Flink; 1942 } 1943 1944 if (t->header.level == 0) 1945 size += num_items * sizeof(leaf_node); 1946 else 1947 size += num_items * sizeof(internal_node); 1948 1949 if (num_items != t->header.num_items) { 1950 ERR("tree %I64x, level %x: num_items was %x, expected %x\n", t->root->id, t->header.level, num_items, t->header.num_items); 1951 crash = true; 1952 } 1953 1954 if (size != t->size) { 1955 ERR("tree %I64x, level %x: size was %x, expected %x\n", t->root->id, t->header.level, size, t->size); 1956 crash = true; 1957 } 1958 1959 if (t->header.num_items == 0 && t->parent) { 1960 ERR("tree %I64x, level %x: tried to write empty tree with parent\n", t->root->id, t->header.level); 1961 crash = true; 1962 } 1963 1964 if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) { 1965 ERR("tree %I64x, level %x: tried to write overlarge tree (%x > %Ix)\n", t->root->id, t->header.level, t->size, Vcb->superblock.node_size - sizeof(tree_header)); 1966 crash = true; 1967 } 1968 1969 if (crash) { 1970 ERR("tree %p\n", t); 1971 le2 = t->itemlist.Flink; 1972 while (le2 != &t->itemlist) { 1973 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry); 1974 if (!td->ignore) { 1975 ERR("%I64x,%x,%I64x inserted=%u\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->inserted); 1976 } 1977 le2 = le2->Flink; 1978 } 1979 int3; 1980 } 1981 #endif 1982 t->header.address = t->new_address; 1983 t->header.generation = Vcb->superblock.generation; 1984 t->header.tree_id = t->root->id; 1985 t->header.flags |= HEADER_FLAG_MIXED_BACKREF; 1986 t->header.fs_uuid = Vcb->superblock.metadata_uuid; 1987 t->has_address = true; 1988 1989 data = ExAllocatePoolWithTag(NonPagedPool, Vcb->superblock.node_size, ALLOC_TAG); 1990 if (!data) { 1991 ERR("out of memory\n"); 1992 Status = STATUS_INSUFFICIENT_RESOURCES; 1993 goto end; 1994 } 1995 1996 body = data + sizeof(tree_header); 1997 1998 RtlCopyMemory(data, &t->header, sizeof(tree_header)); 1999 RtlZeroMemory(body, Vcb->superblock.node_size - sizeof(tree_header)); 2000 2001 if (t->header.level == 0) { 2002 leaf_node* itemptr = (leaf_node*)body; 2003 int i = 0; 2004 uint8_t* dataptr = data + Vcb->superblock.node_size; 2005 2006 le2 = t->itemlist.Flink; 2007 while (le2 != &t->itemlist) { 2008 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry); 2009 if (!td->ignore) { 2010 dataptr = dataptr - td->size; 2011 2012 itemptr[i].key = td->key; 2013 itemptr[i].offset = (uint32_t)((uint8_t*)dataptr - (uint8_t*)body); 2014 itemptr[i].size = td->size; 2015 i++; 2016 2017 if (td->size > 0) 2018 RtlCopyMemory(dataptr, td->data, td->size); 2019 } 2020 2021 le2 = le2->Flink; 2022 } 2023 } else { 2024 internal_node* itemptr = (internal_node*)body; 2025 int i = 0; 2026 2027 le2 = t->itemlist.Flink; 2028 while (le2 != &t->itemlist) { 2029 tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry); 2030 if (!td->ignore) { 2031 itemptr[i].key = td->key; 2032 itemptr[i].address = td->treeholder.address; 2033 itemptr[i].generation = td->treeholder.generation; 2034 i++; 2035 } 2036 2037 le2 = le2->Flink; 2038 } 2039 } 2040 2041 calc_tree_checksum(Vcb, (tree_header*)data); 2042 2043 tw = ExAllocatePoolWithTag(PagedPool, sizeof(tree_write), ALLOC_TAG); 2044 if (!tw) { 2045 ERR("out of memory\n"); 2046 ExFreePool(data); 2047 Status = STATUS_INSUFFICIENT_RESOURCES; 2048 goto end; 2049 } 2050 2051 tw->address = t->new_address; 2052 tw->length = Vcb->superblock.node_size; 2053 tw->data = data; 2054 tw->allocated = false; 2055 2056 if (IsListEmpty(&tree_writes)) 2057 InsertTailList(&tree_writes, &tw->list_entry); 2058 else { 2059 bool inserted = false; 2060 2061 le2 = tree_writes.Flink; 2062 while (le2 != &tree_writes) { 2063 tree_write* tw2 = CONTAINING_RECORD(le2, tree_write, list_entry); 2064 2065 if (tw2->address > tw->address) { 2066 InsertHeadList(le2->Blink, &tw->list_entry); 2067 inserted = true; 2068 break; 2069 } 2070 2071 le2 = le2->Flink; 2072 } 2073 2074 if (!inserted) 2075 InsertTailList(&tree_writes, &tw->list_entry); 2076 } 2077 } 2078 2079 le = le->Flink; 2080 } 2081 2082 Status = do_tree_writes(Vcb, &tree_writes, false); 2083 if (!NT_SUCCESS(Status)) { 2084 ERR("do_tree_writes returned %08lx\n", Status); 2085 goto end; 2086 } 2087 2088 Status = STATUS_SUCCESS; 2089 2090 end: 2091 while (!IsListEmpty(&tree_writes)) { 2092 le = RemoveHeadList(&tree_writes); 2093 tw = CONTAINING_RECORD(le, tree_write, list_entry); 2094 2095 if (tw->data) 2096 ExFreePool(tw->data); 2097 2098 ExFreePool(tw); 2099 } 2100 2101 return Status; 2102 } 2103 2104 static void update_backup_superblock(device_extension* Vcb, superblock_backup* sb, PIRP Irp) { 2105 KEY searchkey; 2106 traverse_ptr tp; 2107 2108 RtlZeroMemory(sb, sizeof(superblock_backup)); 2109 2110 sb->root_tree_addr = Vcb->superblock.root_tree_addr; 2111 sb->root_tree_generation = Vcb->superblock.generation; 2112 sb->root_level = Vcb->superblock.root_level; 2113 2114 sb->chunk_tree_addr = Vcb->superblock.chunk_tree_addr; 2115 sb->chunk_tree_generation = Vcb->superblock.chunk_root_generation; 2116 sb->chunk_root_level = Vcb->superblock.chunk_root_level; 2117 2118 searchkey.obj_id = BTRFS_ROOT_EXTENT; 2119 searchkey.obj_type = TYPE_ROOT_ITEM; 2120 searchkey.offset = 0xffffffffffffffff; 2121 2122 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp))) { 2123 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { 2124 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; 2125 2126 sb->extent_tree_addr = ri->block_number; 2127 sb->extent_tree_generation = ri->generation; 2128 sb->extent_root_level = ri->root_level; 2129 } 2130 } 2131 2132 searchkey.obj_id = BTRFS_ROOT_FSTREE; 2133 2134 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp))) { 2135 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { 2136 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; 2137 2138 sb->fs_tree_addr = ri->block_number; 2139 sb->fs_tree_generation = ri->generation; 2140 sb->fs_root_level = ri->root_level; 2141 } 2142 } 2143 2144 searchkey.obj_id = BTRFS_ROOT_DEVTREE; 2145 2146 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp))) { 2147 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { 2148 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; 2149 2150 sb->dev_root_addr = ri->block_number; 2151 sb->dev_root_generation = ri->generation; 2152 sb->dev_root_level = ri->root_level; 2153 } 2154 } 2155 2156 searchkey.obj_id = BTRFS_ROOT_CHECKSUM; 2157 2158 if (NT_SUCCESS(find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp))) { 2159 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type && tp.item->size >= sizeof(ROOT_ITEM)) { 2160 ROOT_ITEM* ri = (ROOT_ITEM*)tp.item->data; 2161 2162 sb->csum_root_addr = ri->block_number; 2163 sb->csum_root_generation = ri->generation; 2164 sb->csum_root_level = ri->root_level; 2165 } 2166 } 2167 2168 sb->total_bytes = Vcb->superblock.total_bytes; 2169 sb->bytes_used = Vcb->superblock.bytes_used; 2170 sb->num_devices = Vcb->superblock.num_devices; 2171 } 2172 2173 typedef struct { 2174 void* context; 2175 uint8_t* buf; 2176 PMDL mdl; 2177 device* device; 2178 NTSTATUS Status; 2179 PIRP Irp; 2180 LIST_ENTRY list_entry; 2181 } write_superblocks_stripe; 2182 2183 typedef struct _write_superblocks_context { 2184 KEVENT Event; 2185 LIST_ENTRY stripes; 2186 LONG left; 2187 } write_superblocks_context; 2188 2189 _Function_class_(IO_COMPLETION_ROUTINE) 2190 static NTSTATUS __stdcall write_superblock_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 2191 write_superblocks_stripe* stripe = conptr; 2192 write_superblocks_context* context = stripe->context; 2193 2194 UNUSED(DeviceObject); 2195 2196 stripe->Status = Irp->IoStatus.Status; 2197 2198 if (InterlockedDecrement(&context->left) == 0) 2199 KeSetEvent(&context->Event, 0, false); 2200 2201 return STATUS_MORE_PROCESSING_REQUIRED; 2202 } 2203 2204 static void calc_superblock_checksum(superblock* sb) { 2205 switch (sb->csum_type) { 2206 case CSUM_TYPE_CRC32C: 2207 *(uint32_t*)sb = ~calc_crc32c(0xffffffff, (uint8_t*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); 2208 break; 2209 2210 case CSUM_TYPE_XXHASH: 2211 *(uint64_t*)sb = XXH64(&sb->uuid, sizeof(superblock) - sizeof(sb->checksum), 0); 2212 break; 2213 2214 case CSUM_TYPE_SHA256: 2215 calc_sha256((uint8_t*)sb, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 2216 break; 2217 2218 case CSUM_TYPE_BLAKE2: 2219 blake2b((uint8_t*)sb, BLAKE2_HASH_SIZE, &sb->uuid, sizeof(superblock) - sizeof(sb->checksum)); 2220 break; 2221 } 2222 } 2223 2224 static NTSTATUS write_superblock(device_extension* Vcb, device* device, write_superblocks_context* context) { 2225 unsigned int i = 0; 2226 2227 // All the documentation says that the Linux driver only writes one superblock 2228 // if it thinks a disk is an SSD, but this doesn't seem to be the case! 2229 2230 while (superblock_addrs[i] > 0 && device->devitem.num_bytes >= superblock_addrs[i] + sizeof(superblock)) { 2231 ULONG sblen = (ULONG)sector_align(sizeof(superblock), Vcb->superblock.sector_size); 2232 superblock* sb; 2233 write_superblocks_stripe* stripe; 2234 PIO_STACK_LOCATION IrpSp; 2235 2236 sb = ExAllocatePoolWithTag(NonPagedPool, sblen, ALLOC_TAG); 2237 if (!sb) { 2238 ERR("out of memory\n"); 2239 return STATUS_INSUFFICIENT_RESOURCES; 2240 } 2241 2242 RtlCopyMemory(sb, &Vcb->superblock, sizeof(superblock)); 2243 2244 if (sblen > sizeof(superblock)) 2245 RtlZeroMemory((uint8_t*)sb + sizeof(superblock), sblen - sizeof(superblock)); 2246 2247 RtlCopyMemory(&sb->dev_item, &device->devitem, sizeof(DEV_ITEM)); 2248 sb->sb_phys_addr = superblock_addrs[i]; 2249 2250 calc_superblock_checksum(sb); 2251 2252 stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_superblocks_stripe), ALLOC_TAG); 2253 if (!stripe) { 2254 ERR("out of memory\n"); 2255 ExFreePool(sb); 2256 return STATUS_INSUFFICIENT_RESOURCES; 2257 } 2258 2259 stripe->buf = (uint8_t*)sb; 2260 2261 stripe->Irp = IoAllocateIrp(device->devobj->StackSize, false); 2262 if (!stripe->Irp) { 2263 ERR("IoAllocateIrp failed\n"); 2264 ExFreePool(stripe); 2265 ExFreePool(sb); 2266 return STATUS_INSUFFICIENT_RESOURCES; 2267 } 2268 2269 IrpSp = IoGetNextIrpStackLocation(stripe->Irp); 2270 IrpSp->MajorFunction = IRP_MJ_WRITE; 2271 IrpSp->FileObject = device->fileobj; 2272 2273 if (i == 0) 2274 IrpSp->Flags |= SL_WRITE_THROUGH; 2275 2276 if (device->devobj->Flags & DO_BUFFERED_IO) { 2277 stripe->Irp->AssociatedIrp.SystemBuffer = sb; 2278 stripe->mdl = NULL; 2279 2280 stripe->Irp->Flags = IRP_BUFFERED_IO; 2281 } else if (device->devobj->Flags & DO_DIRECT_IO) { 2282 stripe->mdl = IoAllocateMdl(sb, sblen, false, false, NULL); 2283 if (!stripe->mdl) { 2284 ERR("IoAllocateMdl failed\n"); 2285 IoFreeIrp(stripe->Irp); 2286 ExFreePool(stripe); 2287 ExFreePool(sb); 2288 return STATUS_INSUFFICIENT_RESOURCES; 2289 } 2290 2291 stripe->Irp->MdlAddress = stripe->mdl; 2292 2293 MmBuildMdlForNonPagedPool(stripe->mdl); 2294 } else { 2295 stripe->Irp->UserBuffer = sb; 2296 stripe->mdl = NULL; 2297 } 2298 2299 IrpSp->Parameters.Write.Length = sblen; 2300 IrpSp->Parameters.Write.ByteOffset.QuadPart = superblock_addrs[i]; 2301 2302 IoSetCompletionRoutine(stripe->Irp, write_superblock_completion, stripe, true, true, true); 2303 2304 stripe->context = context; 2305 stripe->device = device; 2306 InsertTailList(&context->stripes, &stripe->list_entry); 2307 2308 context->left++; 2309 2310 i++; 2311 } 2312 2313 if (i == 0) 2314 ERR("no superblocks written!\n"); 2315 2316 return STATUS_SUCCESS; 2317 } 2318 2319 static NTSTATUS write_superblocks(device_extension* Vcb, PIRP Irp) { 2320 uint64_t i; 2321 NTSTATUS Status; 2322 LIST_ENTRY* le; 2323 write_superblocks_context context; 2324 2325 TRACE("(%p)\n", Vcb); 2326 2327 le = Vcb->trees.Flink; 2328 while (le != &Vcb->trees) { 2329 tree* t = CONTAINING_RECORD(le, tree, list_entry); 2330 2331 if (t->write && !t->parent) { 2332 if (t->root == Vcb->root_root) { 2333 Vcb->superblock.root_tree_addr = t->new_address; 2334 Vcb->superblock.root_level = t->header.level; 2335 } else if (t->root == Vcb->chunk_root) { 2336 Vcb->superblock.chunk_tree_addr = t->new_address; 2337 Vcb->superblock.chunk_root_generation = t->header.generation; 2338 Vcb->superblock.chunk_root_level = t->header.level; 2339 } 2340 } 2341 2342 le = le->Flink; 2343 } 2344 2345 for (i = 0; i < BTRFS_NUM_BACKUP_ROOTS - 1; i++) { 2346 RtlCopyMemory(&Vcb->superblock.backup[i], &Vcb->superblock.backup[i+1], sizeof(superblock_backup)); 2347 } 2348 2349 update_backup_superblock(Vcb, &Vcb->superblock.backup[BTRFS_NUM_BACKUP_ROOTS - 1], Irp); 2350 2351 KeInitializeEvent(&context.Event, NotificationEvent, false); 2352 InitializeListHead(&context.stripes); 2353 context.left = 0; 2354 2355 le = Vcb->devices.Flink; 2356 while (le != &Vcb->devices) { 2357 device* dev = CONTAINING_RECORD(le, device, list_entry); 2358 2359 if (dev->devobj && !dev->readonly) { 2360 Status = write_superblock(Vcb, dev, &context); 2361 if (!NT_SUCCESS(Status)) { 2362 ERR("write_superblock returned %08lx\n", Status); 2363 goto end; 2364 } 2365 } 2366 2367 le = le->Flink; 2368 } 2369 2370 if (IsListEmpty(&context.stripes)) { 2371 ERR("error - not writing any superblocks\n"); 2372 Status = STATUS_INTERNAL_ERROR; 2373 goto end; 2374 } 2375 2376 le = context.stripes.Flink; 2377 while (le != &context.stripes) { 2378 write_superblocks_stripe* stripe = CONTAINING_RECORD(le, write_superblocks_stripe, list_entry); 2379 2380 IoCallDriver(stripe->device->devobj, stripe->Irp); 2381 2382 le = le->Flink; 2383 } 2384 2385 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); 2386 2387 le = context.stripes.Flink; 2388 while (le != &context.stripes) { 2389 write_superblocks_stripe* stripe = CONTAINING_RECORD(le, write_superblocks_stripe, list_entry); 2390 2391 if (!NT_SUCCESS(stripe->Status)) { 2392 ERR("device %I64x returned %08lx\n", stripe->device->devitem.dev_id, stripe->Status); 2393 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS); 2394 Status = stripe->Status; 2395 goto end; 2396 } 2397 2398 le = le->Flink; 2399 } 2400 2401 Status = STATUS_SUCCESS; 2402 2403 end: 2404 while (!IsListEmpty(&context.stripes)) { 2405 write_superblocks_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&context.stripes), write_superblocks_stripe, list_entry); 2406 2407 if (stripe->mdl) { 2408 if (stripe->mdl->MdlFlags & MDL_PAGES_LOCKED) 2409 MmUnlockPages(stripe->mdl); 2410 2411 IoFreeMdl(stripe->mdl); 2412 } 2413 2414 if (stripe->Irp) 2415 IoFreeIrp(stripe->Irp); 2416 2417 if (stripe->buf) 2418 ExFreePool(stripe->buf); 2419 2420 ExFreePool(stripe); 2421 } 2422 2423 return Status; 2424 } 2425 2426 static NTSTATUS flush_changed_extent(device_extension* Vcb, chunk* c, changed_extent* ce, PIRP Irp, LIST_ENTRY* rollback) { 2427 LIST_ENTRY *le, *le2; 2428 NTSTATUS Status; 2429 uint64_t old_size; 2430 2431 if (ce->count == 0 && ce->old_count == 0) { 2432 while (!IsListEmpty(&ce->refs)) { 2433 changed_extent_ref* cer = CONTAINING_RECORD(RemoveHeadList(&ce->refs), changed_extent_ref, list_entry); 2434 ExFreePool(cer); 2435 } 2436 2437 while (!IsListEmpty(&ce->old_refs)) { 2438 changed_extent_ref* cer = CONTAINING_RECORD(RemoveHeadList(&ce->old_refs), changed_extent_ref, list_entry); 2439 ExFreePool(cer); 2440 } 2441 2442 goto end; 2443 } 2444 2445 le = ce->refs.Flink; 2446 while (le != &ce->refs) { 2447 changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry); 2448 uint32_t old_count = 0; 2449 2450 if (cer->type == TYPE_EXTENT_DATA_REF) { 2451 le2 = ce->old_refs.Flink; 2452 while (le2 != &ce->old_refs) { 2453 changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 2454 2455 if (cer2->type == TYPE_EXTENT_DATA_REF && cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) { 2456 old_count = cer2->edr.count; 2457 break; 2458 } 2459 2460 le2 = le2->Flink; 2461 } 2462 2463 old_size = ce->old_count > 0 ? ce->old_size : ce->size; 2464 2465 if (cer->edr.count > old_count) { 2466 Status = increase_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, cer->edr.count - old_count, Irp); 2467 2468 if (!NT_SUCCESS(Status)) { 2469 ERR("increase_extent_refcount_data returned %08lx\n", Status); 2470 return Status; 2471 } 2472 } 2473 } else if (cer->type == TYPE_SHARED_DATA_REF) { 2474 le2 = ce->old_refs.Flink; 2475 while (le2 != &ce->old_refs) { 2476 changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 2477 2478 if (cer2->type == TYPE_SHARED_DATA_REF && cer2->sdr.offset == cer->sdr.offset) { 2479 RemoveEntryList(&cer2->list_entry); 2480 ExFreePool(cer2); 2481 break; 2482 } 2483 2484 le2 = le2->Flink; 2485 } 2486 } 2487 2488 le = le->Flink; 2489 } 2490 2491 le = ce->refs.Flink; 2492 while (le != &ce->refs) { 2493 changed_extent_ref* cer = CONTAINING_RECORD(le, changed_extent_ref, list_entry); 2494 LIST_ENTRY* le3 = le->Flink; 2495 uint32_t old_count = 0; 2496 2497 if (cer->type == TYPE_EXTENT_DATA_REF) { 2498 le2 = ce->old_refs.Flink; 2499 while (le2 != &ce->old_refs) { 2500 changed_extent_ref* cer2 = CONTAINING_RECORD(le2, changed_extent_ref, list_entry); 2501 2502 if (cer2->type == TYPE_EXTENT_DATA_REF && cer2->edr.root == cer->edr.root && cer2->edr.objid == cer->edr.objid && cer2->edr.offset == cer->edr.offset) { 2503 old_count = cer2->edr.count; 2504 2505 RemoveEntryList(&cer2->list_entry); 2506 ExFreePool(cer2); 2507 break; 2508 } 2509 2510 le2 = le2->Flink; 2511 } 2512 2513 old_size = ce->old_count > 0 ? ce->old_size : ce->size; 2514 2515 if (cer->edr.count < old_count) { 2516 Status = decrease_extent_refcount_data(Vcb, ce->address, old_size, cer->edr.root, cer->edr.objid, cer->edr.offset, 2517 old_count - cer->edr.count, ce->superseded, Irp); 2518 2519 if (!NT_SUCCESS(Status)) { 2520 ERR("decrease_extent_refcount_data returned %08lx\n", Status); 2521 return Status; 2522 } 2523 } 2524 2525 if (ce->size != ce->old_size && ce->old_count > 0) { 2526 KEY searchkey; 2527 traverse_ptr tp; 2528 void* data; 2529 2530 searchkey.obj_id = ce->address; 2531 searchkey.obj_type = TYPE_EXTENT_ITEM; 2532 searchkey.offset = ce->old_size; 2533 2534 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 2535 if (!NT_SUCCESS(Status)) { 2536 ERR("error - find_item returned %08lx\n", Status); 2537 return Status; 2538 } 2539 2540 if (keycmp(searchkey, tp.item->key)) { 2541 ERR("could not find (%I64x,%x,%I64x) in extent tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 2542 return STATUS_INTERNAL_ERROR; 2543 } 2544 2545 if (tp.item->size > 0) { 2546 data = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); 2547 2548 if (!data) { 2549 ERR("out of memory\n"); 2550 return STATUS_INSUFFICIENT_RESOURCES; 2551 } 2552 2553 RtlCopyMemory(data, tp.item->data, tp.item->size); 2554 } else 2555 data = NULL; 2556 2557 Status = insert_tree_item(Vcb, Vcb->extent_root, ce->address, TYPE_EXTENT_ITEM, ce->size, data, tp.item->size, NULL, Irp); 2558 if (!NT_SUCCESS(Status)) { 2559 ERR("insert_tree_item returned %08lx\n", Status); 2560 if (data) ExFreePool(data); 2561 return Status; 2562 } 2563 2564 Status = delete_tree_item(Vcb, &tp); 2565 if (!NT_SUCCESS(Status)) { 2566 ERR("delete_tree_item returned %08lx\n", Status); 2567 return Status; 2568 } 2569 } 2570 } 2571 2572 RemoveEntryList(&cer->list_entry); 2573 ExFreePool(cer); 2574 2575 le = le3; 2576 } 2577 2578 #ifdef DEBUG_PARANOID 2579 if (!IsListEmpty(&ce->old_refs)) 2580 WARN("old_refs not empty\n"); 2581 #endif 2582 2583 end: 2584 if (ce->count == 0 && !ce->superseded) { 2585 c->used -= ce->size; 2586 space_list_add(c, ce->address, ce->size, rollback); 2587 } 2588 2589 RemoveEntryList(&ce->list_entry); 2590 ExFreePool(ce); 2591 2592 return STATUS_SUCCESS; 2593 } 2594 2595 void add_checksum_entry(device_extension* Vcb, uint64_t address, ULONG length, void* csum, PIRP Irp) { 2596 KEY searchkey; 2597 traverse_ptr tp, next_tp; 2598 NTSTATUS Status; 2599 uint64_t startaddr, endaddr; 2600 ULONG len; 2601 RTL_BITMAP bmp; 2602 ULONG* bmparr; 2603 ULONG runlength, index; 2604 2605 TRACE("(%p, %I64x, %lx, %p, %p)\n", Vcb, address, length, csum, Irp); 2606 2607 searchkey.obj_id = EXTENT_CSUM_ID; 2608 searchkey.obj_type = TYPE_EXTENT_CSUM; 2609 searchkey.offset = address; 2610 2611 // FIXME - create checksum_root if it doesn't exist at all 2612 2613 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp); 2614 if (Status == STATUS_NOT_FOUND) { // tree is completely empty 2615 if (csum) { // not deleted 2616 ULONG length2 = length; 2617 uint64_t off = address; 2618 void* data = csum; 2619 2620 do { 2621 uint16_t il = (uint16_t)min(length2, MAX_CSUM_SIZE / Vcb->csum_size); 2622 2623 void* checksums = ExAllocatePoolWithTag(PagedPool, il * Vcb->csum_size, ALLOC_TAG); 2624 if (!checksums) { 2625 ERR("out of memory\n"); 2626 return; 2627 } 2628 2629 RtlCopyMemory(checksums, data, il * Vcb->csum_size); 2630 2631 Status = insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, checksums, 2632 il * Vcb->csum_size, NULL, Irp); 2633 if (!NT_SUCCESS(Status)) { 2634 ERR("insert_tree_item returned %08lx\n", Status); 2635 ExFreePool(checksums); 2636 return; 2637 } 2638 2639 length2 -= il; 2640 2641 if (length2 > 0) { 2642 off += il * Vcb->superblock.sector_size; 2643 data = (uint8_t*)data + (il * Vcb->csum_size); 2644 } 2645 } while (length2 > 0); 2646 } 2647 } else if (!NT_SUCCESS(Status)) { 2648 ERR("find_item returned %08lx\n", Status); 2649 return; 2650 } else { 2651 uint32_t tplen; 2652 void* checksums; 2653 2654 // FIXME - check entry is TYPE_EXTENT_CSUM? 2655 2656 if (tp.item->key.offset < address && tp.item->key.offset + (tp.item->size * Vcb->superblock.sector_size / Vcb->csum_size) >= address) 2657 startaddr = tp.item->key.offset; 2658 else 2659 startaddr = address; 2660 2661 searchkey.obj_id = EXTENT_CSUM_ID; 2662 searchkey.obj_type = TYPE_EXTENT_CSUM; 2663 searchkey.offset = address + (length * Vcb->superblock.sector_size); 2664 2665 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp); 2666 if (!NT_SUCCESS(Status)) { 2667 ERR("find_item returned %08lx\n", Status); 2668 return; 2669 } 2670 2671 tplen = tp.item->size / Vcb->csum_size; 2672 2673 if (tp.item->key.offset + (tplen * Vcb->superblock.sector_size) >= address + (length * Vcb->superblock.sector_size)) 2674 endaddr = tp.item->key.offset + (tplen * Vcb->superblock.sector_size); 2675 else 2676 endaddr = address + (length * Vcb->superblock.sector_size); 2677 2678 TRACE("cs starts at %I64x (%lx sectors)\n", address, length); 2679 TRACE("startaddr = %I64x\n", startaddr); 2680 TRACE("endaddr = %I64x\n", endaddr); 2681 2682 len = (ULONG)((endaddr - startaddr) / Vcb->superblock.sector_size); 2683 2684 checksums = ExAllocatePoolWithTag(PagedPool, Vcb->csum_size * len, ALLOC_TAG); 2685 if (!checksums) { 2686 ERR("out of memory\n"); 2687 return; 2688 } 2689 2690 bmparr = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * ((len/8)+1), ALLOC_TAG); 2691 if (!bmparr) { 2692 ERR("out of memory\n"); 2693 ExFreePool(checksums); 2694 return; 2695 } 2696 2697 RtlInitializeBitMap(&bmp, bmparr, len); 2698 RtlSetAllBits(&bmp); 2699 2700 searchkey.obj_id = EXTENT_CSUM_ID; 2701 searchkey.obj_type = TYPE_EXTENT_CSUM; 2702 searchkey.offset = address; 2703 2704 Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, false, Irp); 2705 if (!NT_SUCCESS(Status)) { 2706 ERR("find_item returned %08lx\n", Status); 2707 ExFreePool(checksums); 2708 ExFreePool(bmparr); 2709 return; 2710 } 2711 2712 // set bit = free space, cleared bit = allocated sector 2713 2714 while (tp.item->key.offset < endaddr) { 2715 if (tp.item->key.offset >= startaddr) { 2716 if (tp.item->size > 0) { 2717 ULONG itemlen = (ULONG)min((len - (tp.item->key.offset - startaddr) / Vcb->superblock.sector_size) * Vcb->csum_size, tp.item->size); 2718 2719 RtlCopyMemory((uint8_t*)checksums + ((tp.item->key.offset - startaddr) * Vcb->csum_size / Vcb->superblock.sector_size), 2720 tp.item->data, itemlen); 2721 RtlClearBits(&bmp, (ULONG)((tp.item->key.offset - startaddr) / Vcb->superblock.sector_size), itemlen / Vcb->csum_size); 2722 } 2723 2724 Status = delete_tree_item(Vcb, &tp); 2725 if (!NT_SUCCESS(Status)) { 2726 ERR("delete_tree_item returned %08lx\n", Status); 2727 ExFreePool(checksums); 2728 ExFreePool(bmparr); 2729 return; 2730 } 2731 } 2732 2733 if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) { 2734 tp = next_tp; 2735 } else 2736 break; 2737 } 2738 2739 if (!csum) { // deleted 2740 RtlSetBits(&bmp, (ULONG)((address - startaddr) / Vcb->superblock.sector_size), length); 2741 } else { 2742 RtlCopyMemory((uint8_t*)checksums + ((address - startaddr) * Vcb->csum_size / Vcb->superblock.sector_size), 2743 csum, length * Vcb->csum_size); 2744 RtlClearBits(&bmp, (ULONG)((address - startaddr) / Vcb->superblock.sector_size), length); 2745 } 2746 2747 runlength = RtlFindFirstRunClear(&bmp, &index); 2748 2749 while (runlength != 0) { 2750 if (index >= len) 2751 break; 2752 2753 if (index + runlength >= len) { 2754 runlength = len - index; 2755 2756 if (runlength == 0) 2757 break; 2758 } 2759 2760 do { 2761 uint16_t rl; 2762 uint64_t off; 2763 void* data; 2764 2765 if (runlength * Vcb->csum_size > MAX_CSUM_SIZE) 2766 rl = (uint16_t)(MAX_CSUM_SIZE / Vcb->csum_size); 2767 else 2768 rl = (uint16_t)runlength; 2769 2770 data = ExAllocatePoolWithTag(PagedPool, Vcb->csum_size * rl, ALLOC_TAG); 2771 if (!data) { 2772 ERR("out of memory\n"); 2773 ExFreePool(bmparr); 2774 ExFreePool(checksums); 2775 return; 2776 } 2777 2778 RtlCopyMemory(data, (uint8_t*)checksums + (Vcb->csum_size * index), Vcb->csum_size * rl); 2779 2780 off = startaddr + UInt32x32To64(index, Vcb->superblock.sector_size); 2781 2782 Status = insert_tree_item(Vcb, Vcb->checksum_root, EXTENT_CSUM_ID, TYPE_EXTENT_CSUM, off, data, Vcb->csum_size * rl, NULL, Irp); 2783 if (!NT_SUCCESS(Status)) { 2784 ERR("insert_tree_item returned %08lx\n", Status); 2785 ExFreePool(data); 2786 ExFreePool(bmparr); 2787 ExFreePool(checksums); 2788 return; 2789 } 2790 2791 runlength -= rl; 2792 index += rl; 2793 } while (runlength > 0); 2794 2795 runlength = RtlFindNextForwardRunClear(&bmp, index, &index); 2796 } 2797 2798 ExFreePool(bmparr); 2799 ExFreePool(checksums); 2800 } 2801 } 2802 2803 static NTSTATUS update_chunk_usage(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { 2804 LIST_ENTRY *le = Vcb->chunks.Flink, *le2; 2805 chunk* c; 2806 KEY searchkey; 2807 traverse_ptr tp; 2808 BLOCK_GROUP_ITEM* bgi; 2809 NTSTATUS Status; 2810 2811 TRACE("(%p)\n", Vcb); 2812 2813 ExAcquireResourceSharedLite(&Vcb->chunk_lock, true); 2814 2815 while (le != &Vcb->chunks) { 2816 c = CONTAINING_RECORD(le, chunk, list_entry); 2817 2818 acquire_chunk_lock(c, Vcb); 2819 2820 if (!c->cache_loaded && (!IsListEmpty(&c->changed_extents) || c->used != c->oldused)) { 2821 Status = load_cache_chunk(Vcb, c, NULL); 2822 2823 if (!NT_SUCCESS(Status)) { 2824 ERR("load_cache_chunk returned %08lx\n", Status); 2825 release_chunk_lock(c, Vcb); 2826 goto end; 2827 } 2828 } 2829 2830 le2 = c->changed_extents.Flink; 2831 while (le2 != &c->changed_extents) { 2832 LIST_ENTRY* le3 = le2->Flink; 2833 changed_extent* ce = CONTAINING_RECORD(le2, changed_extent, list_entry); 2834 2835 Status = flush_changed_extent(Vcb, c, ce, Irp, rollback); 2836 if (!NT_SUCCESS(Status)) { 2837 ERR("flush_changed_extent returned %08lx\n", Status); 2838 release_chunk_lock(c, Vcb); 2839 goto end; 2840 } 2841 2842 le2 = le3; 2843 } 2844 2845 // This is usually done by update_chunks, but we have to check again in case any new chunks 2846 // have been allocated since. 2847 if (c->created) { 2848 Status = create_chunk(Vcb, c, Irp); 2849 if (!NT_SUCCESS(Status)) { 2850 ERR("create_chunk returned %08lx\n", Status); 2851 release_chunk_lock(c, Vcb); 2852 goto end; 2853 } 2854 } 2855 2856 if (c->old_cache) { 2857 if (c->old_cache->dirty) { 2858 LIST_ENTRY batchlist; 2859 2860 InitializeListHead(&batchlist); 2861 2862 Status = flush_fcb(c->old_cache, false, &batchlist, Irp); 2863 if (!NT_SUCCESS(Status)) { 2864 ERR("flush_fcb returned %08lx\n", Status); 2865 release_chunk_lock(c, Vcb); 2866 clear_batch_list(Vcb, &batchlist); 2867 goto end; 2868 } 2869 2870 Status = commit_batch_list(Vcb, &batchlist, Irp); 2871 if (!NT_SUCCESS(Status)) { 2872 ERR("commit_batch_list returned %08lx\n", Status); 2873 release_chunk_lock(c, Vcb); 2874 goto end; 2875 } 2876 } 2877 2878 free_fcb(c->old_cache); 2879 2880 if (c->old_cache->refcount == 0) 2881 reap_fcb(c->old_cache); 2882 2883 c->old_cache = NULL; 2884 } 2885 2886 if (c->used != c->oldused) { 2887 #ifdef __REACTOS__ 2888 uint64_t old_phys_used, phys_used; 2889 #endif 2890 searchkey.obj_id = c->offset; 2891 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; 2892 searchkey.offset = c->chunk_item->size; 2893 2894 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 2895 if (!NT_SUCCESS(Status)) { 2896 ERR("error - find_item returned %08lx\n", Status); 2897 release_chunk_lock(c, Vcb); 2898 goto end; 2899 } 2900 2901 if (keycmp(searchkey, tp.item->key)) { 2902 ERR("could not find (%I64x,%x,%I64x) in extent_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 2903 Status = STATUS_INTERNAL_ERROR; 2904 release_chunk_lock(c, Vcb); 2905 goto end; 2906 } 2907 2908 if (tp.item->size < sizeof(BLOCK_GROUP_ITEM)) { 2909 ERR("(%I64x,%x,%I64x) was %u bytes, expected %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(BLOCK_GROUP_ITEM)); 2910 Status = STATUS_INTERNAL_ERROR; 2911 release_chunk_lock(c, Vcb); 2912 goto end; 2913 } 2914 2915 bgi = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); 2916 if (!bgi) { 2917 ERR("out of memory\n"); 2918 Status = STATUS_INSUFFICIENT_RESOURCES; 2919 release_chunk_lock(c, Vcb); 2920 goto end; 2921 } 2922 2923 RtlCopyMemory(bgi, tp.item->data, tp.item->size); 2924 bgi->used = c->used; 2925 2926 #ifdef DEBUG_PARANOID 2927 if (bgi->used & 0x8000000000000000) { 2928 ERR("refusing to write BLOCK_GROUP_ITEM with negative usage value (%I64x)", bgi->used); 2929 int3; 2930 } 2931 #endif 2932 2933 TRACE("adjusting usage of chunk %I64x to %I64x\n", c->offset, c->used); 2934 2935 Status = delete_tree_item(Vcb, &tp); 2936 if (!NT_SUCCESS(Status)) { 2937 ERR("delete_tree_item returned %08lx\n", Status); 2938 ExFreePool(bgi); 2939 release_chunk_lock(c, Vcb); 2940 goto end; 2941 } 2942 2943 Status = insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, bgi, tp.item->size, NULL, Irp); 2944 if (!NT_SUCCESS(Status)) { 2945 ERR("insert_tree_item returned %08lx\n", Status); 2946 ExFreePool(bgi); 2947 release_chunk_lock(c, Vcb); 2948 goto end; 2949 } 2950 2951 #ifndef __REACTOS__ 2952 uint64_t old_phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused); 2953 uint64_t phys_used = chunk_estimate_phys_size(Vcb, c, c->used); 2954 #else 2955 old_phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused); 2956 phys_used = chunk_estimate_phys_size(Vcb, c, c->used); 2957 #endif 2958 2959 if (Vcb->superblock.bytes_used + phys_used > old_phys_used) 2960 Vcb->superblock.bytes_used += phys_used - old_phys_used; 2961 else 2962 Vcb->superblock.bytes_used = 0; 2963 2964 c->oldused = c->used; 2965 } 2966 2967 release_chunk_lock(c, Vcb); 2968 2969 le = le->Flink; 2970 } 2971 2972 Status = STATUS_SUCCESS; 2973 2974 end: 2975 ExReleaseResourceLite(&Vcb->chunk_lock); 2976 2977 return Status; 2978 } 2979 2980 static void get_first_item(tree* t, KEY* key) { 2981 LIST_ENTRY* le; 2982 2983 le = t->itemlist.Flink; 2984 while (le != &t->itemlist) { 2985 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 2986 2987 *key = td->key; 2988 return; 2989 } 2990 } 2991 2992 static NTSTATUS split_tree_at(device_extension* Vcb, tree* t, tree_data* newfirstitem, uint32_t numitems, uint32_t size) { 2993 tree *nt, *pt; 2994 tree_data* td; 2995 tree_data* oldlastitem; 2996 2997 TRACE("splitting tree in %I64x at (%I64x,%x,%I64x)\n", t->root->id, newfirstitem->key.obj_id, newfirstitem->key.obj_type, newfirstitem->key.offset); 2998 2999 nt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG); 3000 if (!nt) { 3001 ERR("out of memory\n"); 3002 return STATUS_INSUFFICIENT_RESOURCES; 3003 } 3004 3005 if (t->header.level > 0) { 3006 nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG); 3007 if (!nt->nonpaged) { 3008 ERR("out of memory\n"); 3009 ExFreePool(nt); 3010 return STATUS_INSUFFICIENT_RESOURCES; 3011 } 3012 3013 ExInitializeFastMutex(&nt->nonpaged->mutex); 3014 } else 3015 nt->nonpaged = NULL; 3016 3017 RtlCopyMemory(&nt->header, &t->header, sizeof(tree_header)); 3018 nt->header.address = 0; 3019 nt->header.generation = Vcb->superblock.generation; 3020 nt->header.num_items = t->header.num_items - numitems; 3021 nt->header.flags = HEADER_FLAG_MIXED_BACKREF | HEADER_FLAG_WRITTEN; 3022 3023 nt->has_address = false; 3024 nt->Vcb = Vcb; 3025 nt->parent = t->parent; 3026 3027 #ifdef DEBUG_PARANOID 3028 if (nt->parent && nt->parent->header.level <= nt->header.level) int3; 3029 #endif 3030 3031 nt->root = t->root; 3032 nt->new_address = 0; 3033 nt->has_new_address = false; 3034 nt->updated_extents = false; 3035 nt->uniqueness_determined = true; 3036 nt->is_unique = true; 3037 nt->list_entry_hash.Flink = NULL; 3038 nt->buf = NULL; 3039 InitializeListHead(&nt->itemlist); 3040 3041 oldlastitem = CONTAINING_RECORD(newfirstitem->list_entry.Blink, tree_data, list_entry); 3042 3043 nt->itemlist.Flink = &newfirstitem->list_entry; 3044 nt->itemlist.Blink = t->itemlist.Blink; 3045 nt->itemlist.Flink->Blink = &nt->itemlist; 3046 nt->itemlist.Blink->Flink = &nt->itemlist; 3047 3048 t->itemlist.Blink = &oldlastitem->list_entry; 3049 t->itemlist.Blink->Flink = &t->itemlist; 3050 3051 nt->size = t->size - size; 3052 t->size = size; 3053 t->header.num_items = numitems; 3054 nt->write = true; 3055 3056 InsertTailList(&Vcb->trees, &nt->list_entry); 3057 3058 if (nt->header.level > 0) { 3059 LIST_ENTRY* le = nt->itemlist.Flink; 3060 3061 while (le != &nt->itemlist) { 3062 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry); 3063 3064 if (td2->treeholder.tree) { 3065 td2->treeholder.tree->parent = nt; 3066 #ifdef DEBUG_PARANOID 3067 if (td2->treeholder.tree->parent && td2->treeholder.tree->parent->header.level <= td2->treeholder.tree->header.level) int3; 3068 #endif 3069 } 3070 3071 le = le->Flink; 3072 } 3073 } else { 3074 LIST_ENTRY* le = nt->itemlist.Flink; 3075 3076 while (le != &nt->itemlist) { 3077 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry); 3078 3079 if (!td2->inserted && td2->data) { 3080 uint8_t* data = ExAllocatePoolWithTag(PagedPool, td2->size, ALLOC_TAG); 3081 3082 if (!data) { 3083 ERR("out of memory\n"); 3084 return STATUS_INSUFFICIENT_RESOURCES; 3085 } 3086 3087 RtlCopyMemory(data, td2->data, td2->size); 3088 td2->data = data; 3089 td2->inserted = true; 3090 } 3091 3092 le = le->Flink; 3093 } 3094 } 3095 3096 if (nt->parent) { 3097 td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside); 3098 if (!td) { 3099 ERR("out of memory\n"); 3100 return STATUS_INSUFFICIENT_RESOURCES; 3101 } 3102 3103 td->key = newfirstitem->key; 3104 3105 InsertHeadList(&t->paritem->list_entry, &td->list_entry); 3106 3107 td->ignore = false; 3108 td->inserted = true; 3109 td->treeholder.tree = nt; 3110 nt->paritem = td; 3111 3112 nt->parent->header.num_items++; 3113 nt->parent->size += sizeof(internal_node); 3114 3115 goto end; 3116 } 3117 3118 TRACE("adding new tree parent\n"); 3119 3120 if (nt->header.level == 255) { 3121 ERR("cannot add parent to tree at level 255\n"); 3122 return STATUS_INTERNAL_ERROR; 3123 } 3124 3125 pt = ExAllocatePoolWithTag(PagedPool, sizeof(tree), ALLOC_TAG); 3126 if (!pt) { 3127 ERR("out of memory\n"); 3128 return STATUS_INSUFFICIENT_RESOURCES; 3129 } 3130 3131 pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG); 3132 if (!pt->nonpaged) { 3133 ERR("out of memory\n"); 3134 ExFreePool(pt); 3135 return STATUS_INSUFFICIENT_RESOURCES; 3136 } 3137 3138 ExInitializeFastMutex(&pt->nonpaged->mutex); 3139 3140 RtlCopyMemory(&pt->header, &nt->header, sizeof(tree_header)); 3141 pt->header.address = 0; 3142 pt->header.num_items = 2; 3143 pt->header.level = nt->header.level + 1; 3144 pt->header.flags = HEADER_FLAG_MIXED_BACKREF | HEADER_FLAG_WRITTEN; 3145 3146 pt->has_address = false; 3147 pt->Vcb = Vcb; 3148 pt->parent = NULL; 3149 pt->paritem = NULL; 3150 pt->root = t->root; 3151 pt->new_address = 0; 3152 pt->has_new_address = false; 3153 pt->updated_extents = false; 3154 pt->size = pt->header.num_items * sizeof(internal_node); 3155 pt->uniqueness_determined = true; 3156 pt->is_unique = true; 3157 pt->list_entry_hash.Flink = NULL; 3158 pt->buf = NULL; 3159 InitializeListHead(&pt->itemlist); 3160 3161 InsertTailList(&Vcb->trees, &pt->list_entry); 3162 3163 td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside); 3164 if (!td) { 3165 ERR("out of memory\n"); 3166 return STATUS_INSUFFICIENT_RESOURCES; 3167 } 3168 3169 get_first_item(t, &td->key); 3170 td->ignore = false; 3171 td->inserted = false; 3172 td->treeholder.address = 0; 3173 td->treeholder.generation = Vcb->superblock.generation; 3174 td->treeholder.tree = t; 3175 InsertTailList(&pt->itemlist, &td->list_entry); 3176 t->paritem = td; 3177 3178 td = ExAllocateFromPagedLookasideList(&Vcb->tree_data_lookaside); 3179 if (!td) { 3180 ERR("out of memory\n"); 3181 return STATUS_INSUFFICIENT_RESOURCES; 3182 } 3183 3184 td->key = newfirstitem->key; 3185 td->ignore = false; 3186 td->inserted = false; 3187 td->treeholder.address = 0; 3188 td->treeholder.generation = Vcb->superblock.generation; 3189 td->treeholder.tree = nt; 3190 InsertTailList(&pt->itemlist, &td->list_entry); 3191 nt->paritem = td; 3192 3193 pt->write = true; 3194 3195 t->root->treeholder.tree = pt; 3196 3197 t->parent = pt; 3198 nt->parent = pt; 3199 3200 #ifdef DEBUG_PARANOID 3201 if (t->parent && t->parent->header.level <= t->header.level) int3; 3202 if (nt->parent && nt->parent->header.level <= nt->header.level) int3; 3203 #endif 3204 3205 end: 3206 t->root->root_item.bytes_used += Vcb->superblock.node_size; 3207 3208 return STATUS_SUCCESS; 3209 } 3210 3211 static NTSTATUS split_tree(device_extension* Vcb, tree* t) { 3212 LIST_ENTRY* le; 3213 uint32_t size, ds, numitems; 3214 3215 size = 0; 3216 numitems = 0; 3217 3218 // FIXME - naïve implementation: maximizes number of filled trees 3219 3220 le = t->itemlist.Flink; 3221 while (le != &t->itemlist) { 3222 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 3223 3224 if (!td->ignore) { 3225 if (t->header.level == 0) 3226 ds = sizeof(leaf_node) + td->size; 3227 else 3228 ds = sizeof(internal_node); 3229 3230 if (numitems == 0 && ds > Vcb->superblock.node_size - sizeof(tree_header)) { 3231 ERR("(%I64x,%x,%I64x) in tree %I64x is too large (%x > %Ix)\n", 3232 td->key.obj_id, td->key.obj_type, td->key.offset, t->root->id, 3233 ds, Vcb->superblock.node_size - sizeof(tree_header)); 3234 return STATUS_INTERNAL_ERROR; 3235 } 3236 3237 // FIXME - move back if previous item was deleted item with same key 3238 if (size + ds > Vcb->superblock.node_size - sizeof(tree_header)) 3239 return split_tree_at(Vcb, t, td, numitems, size); 3240 3241 size += ds; 3242 numitems++; 3243 } 3244 3245 le = le->Flink; 3246 } 3247 3248 return STATUS_SUCCESS; 3249 } 3250 3251 bool is_tree_unique(device_extension* Vcb, tree* t, PIRP Irp) { 3252 KEY searchkey; 3253 traverse_ptr tp; 3254 NTSTATUS Status; 3255 bool ret = false; 3256 EXTENT_ITEM* ei; 3257 uint8_t* type; 3258 3259 if (t->uniqueness_determined) 3260 return t->is_unique; 3261 3262 if (t->parent && !is_tree_unique(Vcb, t->parent, Irp)) 3263 goto end; 3264 3265 if (t->has_address) { 3266 searchkey.obj_id = t->header.address; 3267 searchkey.obj_type = Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA ? TYPE_METADATA_ITEM : TYPE_EXTENT_ITEM; 3268 searchkey.offset = 0xffffffffffffffff; 3269 3270 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 3271 if (!NT_SUCCESS(Status)) { 3272 ERR("error - find_item returned %08lx\n", Status); 3273 goto end; 3274 } 3275 3276 if (tp.item->key.obj_id != t->header.address || (tp.item->key.obj_type != TYPE_METADATA_ITEM && tp.item->key.obj_type != TYPE_EXTENT_ITEM)) 3277 goto end; 3278 3279 if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && tp.item->size == sizeof(EXTENT_ITEM_V0)) 3280 goto end; 3281 3282 if (tp.item->size < sizeof(EXTENT_ITEM)) 3283 goto end; 3284 3285 ei = (EXTENT_ITEM*)tp.item->data; 3286 3287 if (ei->refcount > 1) 3288 goto end; 3289 3290 if (tp.item->key.obj_type == TYPE_EXTENT_ITEM && ei->flags & EXTENT_ITEM_TREE_BLOCK) { 3291 EXTENT_ITEM2* ei2; 3292 3293 if (tp.item->size < sizeof(EXTENT_ITEM) + sizeof(EXTENT_ITEM2)) 3294 goto end; 3295 3296 ei2 = (EXTENT_ITEM2*)&ei[1]; 3297 type = (uint8_t*)&ei2[1]; 3298 } else 3299 type = (uint8_t*)&ei[1]; 3300 3301 if (type >= tp.item->data + tp.item->size || *type != TYPE_TREE_BLOCK_REF) 3302 goto end; 3303 } 3304 3305 ret = true; 3306 3307 end: 3308 t->is_unique = ret; 3309 t->uniqueness_determined = true; 3310 3311 return ret; 3312 } 3313 3314 static NTSTATUS try_tree_amalgamate(device_extension* Vcb, tree* t, bool* done, bool* done_deletions, PIRP Irp, LIST_ENTRY* rollback) { 3315 LIST_ENTRY* le; 3316 tree_data* nextparitem = NULL; 3317 NTSTATUS Status; 3318 tree *next_tree, *par; 3319 3320 *done = false; 3321 3322 TRACE("trying to amalgamate tree in root %I64x, level %x (size %u)\n", t->root->id, t->header.level, t->size); 3323 3324 // FIXME - doesn't capture everything, as it doesn't ascend 3325 le = t->paritem->list_entry.Flink; 3326 while (le != &t->parent->itemlist) { 3327 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 3328 3329 if (!td->ignore) { 3330 nextparitem = td; 3331 break; 3332 } 3333 3334 le = le->Flink; 3335 } 3336 3337 if (!nextparitem) 3338 return STATUS_SUCCESS; 3339 3340 TRACE("nextparitem: key = %I64x,%x,%I64x\n", nextparitem->key.obj_id, nextparitem->key.obj_type, nextparitem->key.offset); 3341 3342 if (!nextparitem->treeholder.tree) { 3343 Status = do_load_tree(Vcb, &nextparitem->treeholder, t->root, t->parent, nextparitem, NULL); 3344 if (!NT_SUCCESS(Status)) { 3345 ERR("do_load_tree returned %08lx\n", Status); 3346 return Status; 3347 } 3348 } 3349 3350 if (!is_tree_unique(Vcb, nextparitem->treeholder.tree, Irp)) 3351 return STATUS_SUCCESS; 3352 3353 next_tree = nextparitem->treeholder.tree; 3354 3355 if (!next_tree->updated_extents && next_tree->has_address) { 3356 Status = update_tree_extents(Vcb, next_tree, Irp, rollback); 3357 if (!NT_SUCCESS(Status)) { 3358 ERR("update_tree_extents returned %08lx\n", Status); 3359 return Status; 3360 } 3361 } 3362 3363 if (t->size + next_tree->size <= Vcb->superblock.node_size - sizeof(tree_header)) { 3364 // merge two trees into one 3365 3366 t->header.num_items += next_tree->header.num_items; 3367 t->size += next_tree->size; 3368 3369 if (next_tree->header.level > 0) { 3370 le = next_tree->itemlist.Flink; 3371 3372 while (le != &next_tree->itemlist) { 3373 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry); 3374 3375 if (td2->treeholder.tree) { 3376 td2->treeholder.tree->parent = t; 3377 #ifdef DEBUG_PARANOID 3378 if (td2->treeholder.tree->parent && td2->treeholder.tree->parent->header.level <= td2->treeholder.tree->header.level) int3; 3379 #endif 3380 } 3381 3382 td2->inserted = true; 3383 le = le->Flink; 3384 } 3385 } else { 3386 le = next_tree->itemlist.Flink; 3387 3388 while (le != &next_tree->itemlist) { 3389 tree_data* td2 = CONTAINING_RECORD(le, tree_data, list_entry); 3390 3391 if (!td2->inserted && td2->data) { 3392 uint8_t* data = ExAllocatePoolWithTag(PagedPool, td2->size, ALLOC_TAG); 3393 3394 if (!data) { 3395 ERR("out of memory\n"); 3396 return STATUS_INSUFFICIENT_RESOURCES; 3397 } 3398 3399 RtlCopyMemory(data, td2->data, td2->size); 3400 td2->data = data; 3401 td2->inserted = true; 3402 } 3403 3404 le = le->Flink; 3405 } 3406 } 3407 3408 t->itemlist.Blink->Flink = next_tree->itemlist.Flink; 3409 t->itemlist.Blink->Flink->Blink = t->itemlist.Blink; 3410 t->itemlist.Blink = next_tree->itemlist.Blink; 3411 t->itemlist.Blink->Flink = &t->itemlist; 3412 3413 next_tree->itemlist.Flink = next_tree->itemlist.Blink = &next_tree->itemlist; 3414 3415 next_tree->header.num_items = 0; 3416 next_tree->size = 0; 3417 3418 if (next_tree->has_new_address) { // delete associated EXTENT_ITEM 3419 Status = reduce_tree_extent(Vcb, next_tree->new_address, next_tree, next_tree->parent->header.tree_id, next_tree->header.level, Irp, rollback); 3420 3421 if (!NT_SUCCESS(Status)) { 3422 ERR("reduce_tree_extent returned %08lx\n", Status); 3423 return Status; 3424 } 3425 } else if (next_tree->has_address) { 3426 Status = reduce_tree_extent(Vcb, next_tree->header.address, next_tree, next_tree->parent->header.tree_id, next_tree->header.level, Irp, rollback); 3427 3428 if (!NT_SUCCESS(Status)) { 3429 ERR("reduce_tree_extent returned %08lx\n", Status); 3430 return Status; 3431 } 3432 } 3433 3434 if (!nextparitem->ignore) { 3435 nextparitem->ignore = true; 3436 next_tree->parent->header.num_items--; 3437 next_tree->parent->size -= sizeof(internal_node); 3438 3439 *done_deletions = true; 3440 } 3441 3442 par = next_tree->parent; 3443 while (par) { 3444 par->write = true; 3445 par = par->parent; 3446 } 3447 3448 RemoveEntryList(&nextparitem->list_entry); 3449 ExFreePool(next_tree->paritem); 3450 next_tree->paritem = NULL; 3451 3452 next_tree->root->root_item.bytes_used -= Vcb->superblock.node_size; 3453 3454 free_tree(next_tree); 3455 3456 *done = true; 3457 } else { 3458 // rebalance by moving items from second tree into first 3459 ULONG avg_size = (t->size + next_tree->size) / 2; 3460 KEY firstitem = {0, 0, 0}; 3461 bool changed = false; 3462 3463 TRACE("attempting rebalance\n"); 3464 3465 le = next_tree->itemlist.Flink; 3466 while (le != &next_tree->itemlist && t->size < avg_size && next_tree->header.num_items > 1) { 3467 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 3468 ULONG size; 3469 3470 if (!td->ignore) { 3471 if (next_tree->header.level == 0) 3472 size = sizeof(leaf_node) + td->size; 3473 else 3474 size = sizeof(internal_node); 3475 } else 3476 size = 0; 3477 3478 if (t->size + size < Vcb->superblock.node_size - sizeof(tree_header)) { 3479 RemoveEntryList(&td->list_entry); 3480 InsertTailList(&t->itemlist, &td->list_entry); 3481 3482 if (next_tree->header.level > 0 && td->treeholder.tree) { 3483 td->treeholder.tree->parent = t; 3484 #ifdef DEBUG_PARANOID 3485 if (td->treeholder.tree->parent && td->treeholder.tree->parent->header.level <= td->treeholder.tree->header.level) int3; 3486 #endif 3487 } else if (next_tree->header.level == 0 && !td->inserted && td->size > 0) { 3488 uint8_t* data = ExAllocatePoolWithTag(PagedPool, td->size, ALLOC_TAG); 3489 3490 if (!data) { 3491 ERR("out of memory\n"); 3492 return STATUS_INSUFFICIENT_RESOURCES; 3493 } 3494 3495 RtlCopyMemory(data, td->data, td->size); 3496 td->data = data; 3497 } 3498 3499 td->inserted = true; 3500 3501 if (!td->ignore) { 3502 next_tree->size -= size; 3503 t->size += size; 3504 next_tree->header.num_items--; 3505 t->header.num_items++; 3506 } 3507 3508 changed = true; 3509 } else 3510 break; 3511 3512 le = next_tree->itemlist.Flink; 3513 } 3514 3515 le = next_tree->itemlist.Flink; 3516 while (le != &next_tree->itemlist) { 3517 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 3518 3519 if (!td->ignore) { 3520 firstitem = td->key; 3521 break; 3522 } 3523 3524 le = le->Flink; 3525 } 3526 3527 // FIXME - once ascension is working, make this work with parent's parent, etc. 3528 if (next_tree->paritem) 3529 next_tree->paritem->key = firstitem; 3530 3531 par = next_tree; 3532 while (par) { 3533 par->write = true; 3534 par = par->parent; 3535 } 3536 3537 if (changed) 3538 *done = true; 3539 } 3540 3541 return STATUS_SUCCESS; 3542 } 3543 3544 static NTSTATUS update_extent_level(device_extension* Vcb, uint64_t address, tree* t, uint8_t level, PIRP Irp) { 3545 KEY searchkey; 3546 traverse_ptr tp; 3547 NTSTATUS Status; 3548 3549 if (Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA) { 3550 searchkey.obj_id = address; 3551 searchkey.obj_type = TYPE_METADATA_ITEM; 3552 searchkey.offset = t->header.level; 3553 3554 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 3555 if (!NT_SUCCESS(Status)) { 3556 ERR("error - find_item returned %08lx\n", Status); 3557 return Status; 3558 } 3559 3560 if (!keycmp(tp.item->key, searchkey)) { 3561 EXTENT_ITEM_SKINNY_METADATA* eism; 3562 3563 if (tp.item->size > 0) { 3564 eism = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); 3565 3566 if (!eism) { 3567 ERR("out of memory\n"); 3568 return STATUS_INSUFFICIENT_RESOURCES; 3569 } 3570 3571 RtlCopyMemory(eism, tp.item->data, tp.item->size); 3572 } else 3573 eism = NULL; 3574 3575 Status = delete_tree_item(Vcb, &tp); 3576 if (!NT_SUCCESS(Status)) { 3577 ERR("delete_tree_item returned %08lx\n", Status); 3578 if (eism) ExFreePool(eism); 3579 return Status; 3580 } 3581 3582 Status = insert_tree_item(Vcb, Vcb->extent_root, address, TYPE_METADATA_ITEM, level, eism, tp.item->size, NULL, Irp); 3583 if (!NT_SUCCESS(Status)) { 3584 ERR("insert_tree_item returned %08lx\n", Status); 3585 if (eism) ExFreePool(eism); 3586 return Status; 3587 } 3588 3589 return STATUS_SUCCESS; 3590 } 3591 } 3592 3593 searchkey.obj_id = address; 3594 searchkey.obj_type = TYPE_EXTENT_ITEM; 3595 searchkey.offset = 0xffffffffffffffff; 3596 3597 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 3598 if (!NT_SUCCESS(Status)) { 3599 ERR("error - find_item returned %08lx\n", Status); 3600 return Status; 3601 } 3602 3603 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 3604 EXTENT_ITEM_TREE* eit; 3605 3606 if (tp.item->size < sizeof(EXTENT_ITEM_TREE)) { 3607 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_ITEM_TREE)); 3608 return STATUS_INTERNAL_ERROR; 3609 } 3610 3611 eit = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG); 3612 3613 if (!eit) { 3614 ERR("out of memory\n"); 3615 return STATUS_INSUFFICIENT_RESOURCES; 3616 } 3617 3618 RtlCopyMemory(eit, tp.item->data, tp.item->size); 3619 3620 Status = delete_tree_item(Vcb, &tp); 3621 if (!NT_SUCCESS(Status)) { 3622 ERR("delete_tree_item returned %08lx\n", Status); 3623 ExFreePool(eit); 3624 return Status; 3625 } 3626 3627 eit->level = level; 3628 3629 Status = insert_tree_item(Vcb, Vcb->extent_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, eit, tp.item->size, NULL, Irp); 3630 if (!NT_SUCCESS(Status)) { 3631 ERR("insert_tree_item returned %08lx\n", Status); 3632 ExFreePool(eit); 3633 return Status; 3634 } 3635 3636 return STATUS_SUCCESS; 3637 } 3638 3639 ERR("could not find EXTENT_ITEM for address %I64x\n", address); 3640 3641 return STATUS_INTERNAL_ERROR; 3642 } 3643 3644 static NTSTATUS update_tree_extents_recursive(device_extension* Vcb, tree* t, PIRP Irp, LIST_ENTRY* rollback) { 3645 NTSTATUS Status; 3646 3647 if (t->parent && !t->parent->updated_extents && t->parent->has_address) { 3648 Status = update_tree_extents_recursive(Vcb, t->parent, Irp, rollback); 3649 if (!NT_SUCCESS(Status)) 3650 return Status; 3651 } 3652 3653 Status = update_tree_extents(Vcb, t, Irp, rollback); 3654 if (!NT_SUCCESS(Status)) { 3655 ERR("update_tree_extents returned %08lx\n", Status); 3656 return Status; 3657 } 3658 3659 return STATUS_SUCCESS; 3660 } 3661 3662 static NTSTATUS do_splits(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { 3663 ULONG level, max_level; 3664 uint32_t min_size; 3665 bool empty, done_deletions = false; 3666 NTSTATUS Status; 3667 tree* t; 3668 3669 TRACE("(%p)\n", Vcb); 3670 3671 max_level = 0; 3672 3673 for (level = 0; level <= 255; level++) { 3674 LIST_ENTRY *le, *nextle; 3675 3676 empty = true; 3677 3678 TRACE("doing level %lu\n", level); 3679 3680 le = Vcb->trees.Flink; 3681 3682 while (le != &Vcb->trees) { 3683 t = CONTAINING_RECORD(le, tree, list_entry); 3684 3685 nextle = le->Flink; 3686 3687 if (t->write && t->header.level == level) { 3688 empty = false; 3689 3690 if (t->header.num_items == 0) { 3691 if (t->parent) { 3692 done_deletions = true; 3693 3694 TRACE("deleting tree in root %I64x\n", t->root->id); 3695 3696 t->root->root_item.bytes_used -= Vcb->superblock.node_size; 3697 3698 if (t->has_new_address) { // delete associated EXTENT_ITEM 3699 Status = reduce_tree_extent(Vcb, t->new_address, t, t->parent->header.tree_id, t->header.level, Irp, rollback); 3700 3701 if (!NT_SUCCESS(Status)) { 3702 ERR("reduce_tree_extent returned %08lx\n", Status); 3703 return Status; 3704 } 3705 3706 t->has_new_address = false; 3707 } else if (t->has_address) { 3708 Status = reduce_tree_extent(Vcb,t->header.address, t, t->parent->header.tree_id, t->header.level, Irp, rollback); 3709 3710 if (!NT_SUCCESS(Status)) { 3711 ERR("reduce_tree_extent returned %08lx\n", Status); 3712 return Status; 3713 } 3714 3715 t->has_address = false; 3716 } 3717 3718 if (!t->paritem->ignore) { 3719 t->paritem->ignore = true; 3720 t->parent->header.num_items--; 3721 t->parent->size -= sizeof(internal_node); 3722 } 3723 3724 RemoveEntryList(&t->paritem->list_entry); 3725 ExFreePool(t->paritem); 3726 t->paritem = NULL; 3727 3728 free_tree(t); 3729 } else if (t->header.level != 0) { 3730 if (t->has_new_address) { 3731 Status = update_extent_level(Vcb, t->new_address, t, 0, Irp); 3732 3733 if (!NT_SUCCESS(Status)) { 3734 ERR("update_extent_level returned %08lx\n", Status); 3735 return Status; 3736 } 3737 } 3738 3739 t->header.level = 0; 3740 } 3741 } else if (t->size > Vcb->superblock.node_size - sizeof(tree_header)) { 3742 TRACE("splitting overlarge tree (%x > %Ix)\n", t->size, Vcb->superblock.node_size - sizeof(tree_header)); 3743 3744 if (!t->updated_extents && t->has_address) { 3745 Status = update_tree_extents_recursive(Vcb, t, Irp, rollback); 3746 if (!NT_SUCCESS(Status)) { 3747 ERR("update_tree_extents_recursive returned %08lx\n", Status); 3748 return Status; 3749 } 3750 } 3751 3752 Status = split_tree(Vcb, t); 3753 3754 if (!NT_SUCCESS(Status)) { 3755 ERR("split_tree returned %08lx\n", Status); 3756 return Status; 3757 } 3758 } 3759 } 3760 3761 le = nextle; 3762 } 3763 3764 if (!empty) { 3765 max_level = level; 3766 } else { 3767 TRACE("nothing found for level %lu\n", level); 3768 break; 3769 } 3770 } 3771 3772 min_size = (Vcb->superblock.node_size - sizeof(tree_header)) / 2; 3773 3774 for (level = 0; level <= max_level; level++) { 3775 LIST_ENTRY* le; 3776 3777 le = Vcb->trees.Flink; 3778 3779 while (le != &Vcb->trees) { 3780 t = CONTAINING_RECORD(le, tree, list_entry); 3781 3782 if (t->write && t->header.level == level && t->header.num_items > 0 && t->parent && t->size < min_size && 3783 t->root->id != BTRFS_ROOT_FREE_SPACE && is_tree_unique(Vcb, t, Irp)) { 3784 bool done; 3785 3786 do { 3787 Status = try_tree_amalgamate(Vcb, t, &done, &done_deletions, Irp, rollback); 3788 if (!NT_SUCCESS(Status)) { 3789 ERR("try_tree_amalgamate returned %08lx\n", Status); 3790 return Status; 3791 } 3792 } while (done && t->size < min_size); 3793 } 3794 3795 le = le->Flink; 3796 } 3797 } 3798 3799 // simplify trees if top tree only has one entry 3800 3801 if (done_deletions) { 3802 for (level = max_level; level > 0; level--) { 3803 LIST_ENTRY *le, *nextle; 3804 3805 le = Vcb->trees.Flink; 3806 while (le != &Vcb->trees) { 3807 nextle = le->Flink; 3808 t = CONTAINING_RECORD(le, tree, list_entry); 3809 3810 if (t->write && t->header.level == level) { 3811 if (!t->parent && t->header.num_items == 1) { 3812 LIST_ENTRY* le2 = t->itemlist.Flink; 3813 tree_data* td = NULL; 3814 tree* child_tree = NULL; 3815 3816 while (le2 != &t->itemlist) { 3817 td = CONTAINING_RECORD(le2, tree_data, list_entry); 3818 if (!td->ignore) 3819 break; 3820 le2 = le2->Flink; 3821 } 3822 3823 TRACE("deleting top-level tree in root %I64x with one item\n", t->root->id); 3824 3825 if (t->has_new_address) { // delete associated EXTENT_ITEM 3826 Status = reduce_tree_extent(Vcb, t->new_address, t, t->header.tree_id, t->header.level, Irp, rollback); 3827 3828 if (!NT_SUCCESS(Status)) { 3829 ERR("reduce_tree_extent returned %08lx\n", Status); 3830 return Status; 3831 } 3832 3833 t->has_new_address = false; 3834 } else if (t->has_address) { 3835 Status = reduce_tree_extent(Vcb,t->header.address, t, t->header.tree_id, t->header.level, Irp, rollback); 3836 3837 if (!NT_SUCCESS(Status)) { 3838 ERR("reduce_tree_extent returned %08lx\n", Status); 3839 return Status; 3840 } 3841 3842 t->has_address = false; 3843 } 3844 3845 if (!td->treeholder.tree) { // load first item if not already loaded 3846 KEY searchkey = {0,0,0}; 3847 traverse_ptr tp; 3848 3849 Status = find_item(Vcb, t->root, &tp, &searchkey, false, Irp); 3850 if (!NT_SUCCESS(Status)) { 3851 ERR("error - find_item returned %08lx\n", Status); 3852 return Status; 3853 } 3854 } 3855 3856 child_tree = td->treeholder.tree; 3857 3858 if (child_tree) { 3859 child_tree->parent = NULL; 3860 child_tree->paritem = NULL; 3861 } 3862 3863 t->root->root_item.bytes_used -= Vcb->superblock.node_size; 3864 3865 free_tree(t); 3866 3867 if (child_tree) 3868 child_tree->root->treeholder.tree = child_tree; 3869 } 3870 } 3871 3872 le = nextle; 3873 } 3874 } 3875 } 3876 3877 return STATUS_SUCCESS; 3878 } 3879 3880 static NTSTATUS remove_root_extents(device_extension* Vcb, root* r, tree_holder* th, uint8_t level, tree* parent, PIRP Irp, LIST_ENTRY* rollback) { 3881 NTSTATUS Status; 3882 3883 if (!th->tree) { 3884 uint8_t* buf; 3885 chunk* c; 3886 3887 buf = ExAllocatePoolWithTag(PagedPool, Vcb->superblock.node_size, ALLOC_TAG); 3888 if (!buf) { 3889 ERR("out of memory\n"); 3890 return STATUS_INSUFFICIENT_RESOURCES; 3891 } 3892 3893 Status = read_data(Vcb, th->address, Vcb->superblock.node_size, NULL, true, buf, NULL, 3894 &c, Irp, th->generation, false, NormalPagePriority); 3895 if (!NT_SUCCESS(Status)) { 3896 ERR("read_data returned 0x%08lx\n", Status); 3897 ExFreePool(buf); 3898 return Status; 3899 } 3900 3901 Status = load_tree(Vcb, th->address, buf, r, &th->tree); 3902 3903 if (!th->tree || th->tree->buf != buf) 3904 ExFreePool(buf); 3905 3906 if (!NT_SUCCESS(Status)) { 3907 ERR("load_tree(%I64x) returned %08lx\n", th->address, Status); 3908 return Status; 3909 } 3910 } 3911 3912 if (level > 0) { 3913 LIST_ENTRY* le = th->tree->itemlist.Flink; 3914 3915 while (le != &th->tree->itemlist) { 3916 tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry); 3917 3918 if (!td->ignore) { 3919 Status = remove_root_extents(Vcb, r, &td->treeholder, th->tree->header.level - 1, th->tree, Irp, rollback); 3920 3921 if (!NT_SUCCESS(Status)) { 3922 ERR("remove_root_extents returned %08lx\n", Status); 3923 return Status; 3924 } 3925 } 3926 3927 le = le->Flink; 3928 } 3929 } 3930 3931 if (th->tree && !th->tree->updated_extents && th->tree->has_address) { 3932 Status = update_tree_extents(Vcb, th->tree, Irp, rollback); 3933 if (!NT_SUCCESS(Status)) { 3934 ERR("update_tree_extents returned %08lx\n", Status); 3935 return Status; 3936 } 3937 } 3938 3939 if (!th->tree || th->tree->has_address) { 3940 Status = reduce_tree_extent(Vcb, th->address, NULL, parent ? parent->header.tree_id : r->id, level, Irp, rollback); 3941 3942 if (!NT_SUCCESS(Status)) { 3943 ERR("reduce_tree_extent(%I64x) returned %08lx\n", th->address, Status); 3944 return Status; 3945 } 3946 } 3947 3948 return STATUS_SUCCESS; 3949 } 3950 3951 static NTSTATUS drop_root(device_extension* Vcb, root* r, PIRP Irp, LIST_ENTRY* rollback) { 3952 NTSTATUS Status; 3953 KEY searchkey; 3954 traverse_ptr tp; 3955 3956 Status = remove_root_extents(Vcb, r, &r->treeholder, r->root_item.root_level, NULL, Irp, rollback); 3957 if (!NT_SUCCESS(Status)) { 3958 ERR("remove_root_extents returned %08lx\n", Status); 3959 return Status; 3960 } 3961 3962 // remove entries in uuid root (tree 9) 3963 if (Vcb->uuid_root) { 3964 RtlCopyMemory(&searchkey.obj_id, &r->root_item.uuid.uuid[0], sizeof(uint64_t)); 3965 searchkey.obj_type = TYPE_SUBVOL_UUID; 3966 RtlCopyMemory(&searchkey.offset, &r->root_item.uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 3967 3968 if (searchkey.obj_id != 0 || searchkey.offset != 0) { 3969 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 3970 if (!NT_SUCCESS(Status)) { 3971 WARN("find_item returned %08lx\n", Status); 3972 } else { 3973 if (!keycmp(tp.item->key, searchkey)) { 3974 Status = delete_tree_item(Vcb, &tp); 3975 if (!NT_SUCCESS(Status)) { 3976 ERR("delete_tree_item returned %08lx\n", Status); 3977 return Status; 3978 } 3979 } else 3980 WARN("could not find (%I64x,%x,%I64x) in uuid tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 3981 } 3982 } 3983 3984 if (r->root_item.rtransid > 0) { 3985 RtlCopyMemory(&searchkey.obj_id, &r->root_item.received_uuid.uuid[0], sizeof(uint64_t)); 3986 searchkey.obj_type = TYPE_SUBVOL_REC_UUID; 3987 RtlCopyMemory(&searchkey.offset, &r->root_item.received_uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 3988 3989 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 3990 if (!NT_SUCCESS(Status)) 3991 WARN("find_item returned %08lx\n", Status); 3992 else { 3993 if (!keycmp(tp.item->key, searchkey)) { 3994 if (tp.item->size == sizeof(uint64_t)) { 3995 uint64_t* id = (uint64_t*)tp.item->data; 3996 3997 if (*id == r->id) { 3998 Status = delete_tree_item(Vcb, &tp); 3999 if (!NT_SUCCESS(Status)) { 4000 ERR("delete_tree_item returned %08lx\n", Status); 4001 return Status; 4002 } 4003 } 4004 } else if (tp.item->size > sizeof(uint64_t)) { 4005 ULONG i; 4006 uint64_t* ids = (uint64_t*)tp.item->data; 4007 4008 for (i = 0; i < tp.item->size / sizeof(uint64_t); i++) { 4009 if (ids[i] == r->id) { 4010 uint64_t* ne; 4011 4012 ne = ExAllocatePoolWithTag(PagedPool, tp.item->size - sizeof(uint64_t), ALLOC_TAG); 4013 if (!ne) { 4014 ERR("out of memory\n"); 4015 return STATUS_INSUFFICIENT_RESOURCES; 4016 } 4017 4018 if (i > 0) 4019 RtlCopyMemory(ne, ids, sizeof(uint64_t) * i); 4020 4021 if ((i + 1) * sizeof(uint64_t) < tp.item->size) 4022 RtlCopyMemory(&ne[i], &ids[i + 1], tp.item->size - ((i + 1) * sizeof(uint64_t))); 4023 4024 Status = delete_tree_item(Vcb, &tp); 4025 if (!NT_SUCCESS(Status)) { 4026 ERR("delete_tree_item returned %08lx\n", Status); 4027 ExFreePool(ne); 4028 return Status; 4029 } 4030 4031 Status = insert_tree_item(Vcb, Vcb->uuid_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, 4032 ne, tp.item->size - sizeof(uint64_t), NULL, Irp); 4033 if (!NT_SUCCESS(Status)) { 4034 ERR("insert_tree_item returned %08lx\n", Status); 4035 ExFreePool(ne); 4036 return Status; 4037 } 4038 4039 break; 4040 } 4041 } 4042 } 4043 } else 4044 WARN("could not find (%I64x,%x,%I64x) in uuid tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 4045 } 4046 } 4047 } 4048 4049 // delete ROOT_ITEM 4050 4051 searchkey.obj_id = r->id; 4052 searchkey.obj_type = TYPE_ROOT_ITEM; 4053 searchkey.offset = 0xffffffffffffffff; 4054 4055 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 4056 if (!NT_SUCCESS(Status)) { 4057 ERR("find_item returned %08lx\n", Status); 4058 return Status; 4059 } 4060 4061 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 4062 Status = delete_tree_item(Vcb, &tp); 4063 4064 if (!NT_SUCCESS(Status)) { 4065 ERR("delete_tree_item returned %08lx\n", Status); 4066 return Status; 4067 } 4068 } else 4069 WARN("could not find (%I64x,%x,%I64x) in root_root\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 4070 4071 // delete items in tree cache 4072 4073 free_trees_root(Vcb, r); 4074 4075 return STATUS_SUCCESS; 4076 } 4077 4078 static NTSTATUS drop_roots(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { 4079 LIST_ENTRY *le = Vcb->drop_roots.Flink, *le2; 4080 NTSTATUS Status; 4081 4082 while (le != &Vcb->drop_roots) { 4083 root* r = CONTAINING_RECORD(le, root, list_entry); 4084 4085 le2 = le->Flink; 4086 4087 Status = drop_root(Vcb, r, Irp, rollback); 4088 if (!NT_SUCCESS(Status)) { 4089 ERR("drop_root(%I64x) returned %08lx\n", r->id, Status); 4090 return Status; 4091 } 4092 4093 le = le2; 4094 } 4095 4096 return STATUS_SUCCESS; 4097 } 4098 4099 NTSTATUS update_dev_item(device_extension* Vcb, device* device, PIRP Irp) { 4100 KEY searchkey; 4101 traverse_ptr tp; 4102 DEV_ITEM* di; 4103 NTSTATUS Status; 4104 4105 searchkey.obj_id = 1; 4106 searchkey.obj_type = TYPE_DEV_ITEM; 4107 searchkey.offset = device->devitem.dev_id; 4108 4109 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp); 4110 if (!NT_SUCCESS(Status)) { 4111 ERR("error - find_item returned %08lx\n", Status); 4112 return Status; 4113 } 4114 4115 if (keycmp(tp.item->key, searchkey)) { 4116 ERR("error - could not find DEV_ITEM for device %I64x\n", device->devitem.dev_id); 4117 return STATUS_INTERNAL_ERROR; 4118 } 4119 4120 Status = delete_tree_item(Vcb, &tp); 4121 if (!NT_SUCCESS(Status)) { 4122 ERR("delete_tree_item returned %08lx\n", Status); 4123 return Status; 4124 } 4125 4126 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG); 4127 if (!di) { 4128 ERR("out of memory\n"); 4129 return STATUS_INSUFFICIENT_RESOURCES; 4130 } 4131 4132 RtlCopyMemory(di, &device->devitem, sizeof(DEV_ITEM)); 4133 4134 Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, device->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp); 4135 if (!NT_SUCCESS(Status)) { 4136 ERR("insert_tree_item returned %08lx\n", Status); 4137 ExFreePool(di); 4138 return Status; 4139 } 4140 4141 return STATUS_SUCCESS; 4142 } 4143 4144 static void regen_bootstrap(device_extension* Vcb) { 4145 sys_chunk* sc2; 4146 USHORT i = 0; 4147 LIST_ENTRY* le; 4148 4149 i = 0; 4150 le = Vcb->sys_chunks.Flink; 4151 while (le != &Vcb->sys_chunks) { 4152 sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); 4153 4154 TRACE("%I64x,%x,%I64x\n", sc2->key.obj_id, sc2->key.obj_type, sc2->key.offset); 4155 4156 RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], &sc2->key, sizeof(KEY)); 4157 i += sizeof(KEY); 4158 4159 RtlCopyMemory(&Vcb->superblock.sys_chunk_array[i], sc2->data, sc2->size); 4160 i += sc2->size; 4161 4162 le = le->Flink; 4163 } 4164 } 4165 4166 static NTSTATUS add_to_bootstrap(device_extension* Vcb, uint64_t obj_id, uint8_t obj_type, uint64_t offset, void* data, uint16_t size) { 4167 sys_chunk* sc; 4168 LIST_ENTRY* le; 4169 4170 if (Vcb->superblock.n + sizeof(KEY) + size > SYS_CHUNK_ARRAY_SIZE) { 4171 ERR("error - bootstrap is full\n"); 4172 return STATUS_INTERNAL_ERROR; 4173 } 4174 4175 sc = ExAllocatePoolWithTag(PagedPool, sizeof(sys_chunk), ALLOC_TAG); 4176 if (!sc) { 4177 ERR("out of memory\n"); 4178 return STATUS_INSUFFICIENT_RESOURCES; 4179 } 4180 4181 sc->key.obj_id = obj_id; 4182 sc->key.obj_type = obj_type; 4183 sc->key.offset = offset; 4184 sc->size = size; 4185 sc->data = ExAllocatePoolWithTag(PagedPool, sc->size, ALLOC_TAG); 4186 if (!sc->data) { 4187 ERR("out of memory\n"); 4188 ExFreePool(sc); 4189 return STATUS_INSUFFICIENT_RESOURCES; 4190 } 4191 4192 RtlCopyMemory(sc->data, data, sc->size); 4193 4194 le = Vcb->sys_chunks.Flink; 4195 while (le != &Vcb->sys_chunks) { 4196 sys_chunk* sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); 4197 4198 if (keycmp(sc2->key, sc->key) == 1) 4199 break; 4200 4201 le = le->Flink; 4202 } 4203 InsertTailList(le, &sc->list_entry); 4204 4205 Vcb->superblock.n += sizeof(KEY) + size; 4206 4207 regen_bootstrap(Vcb); 4208 4209 return STATUS_SUCCESS; 4210 } 4211 4212 static NTSTATUS create_chunk(device_extension* Vcb, chunk* c, PIRP Irp) { 4213 CHUNK_ITEM* ci; 4214 CHUNK_ITEM_STRIPE* cis; 4215 BLOCK_GROUP_ITEM* bgi; 4216 uint16_t i, factor; 4217 NTSTATUS Status; 4218 4219 ci = ExAllocatePoolWithTag(PagedPool, c->size, ALLOC_TAG); 4220 if (!ci) { 4221 ERR("out of memory\n"); 4222 return STATUS_INSUFFICIENT_RESOURCES; 4223 } 4224 4225 RtlCopyMemory(ci, c->chunk_item, c->size); 4226 4227 Status = insert_tree_item(Vcb, Vcb->chunk_root, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size, NULL, Irp); 4228 if (!NT_SUCCESS(Status)) { 4229 ERR("insert_tree_item failed\n"); 4230 ExFreePool(ci); 4231 return Status; 4232 } 4233 4234 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) { 4235 Status = add_to_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset, ci, c->size); 4236 if (!NT_SUCCESS(Status)) { 4237 ERR("add_to_bootstrap returned %08lx\n", Status); 4238 return Status; 4239 } 4240 } 4241 4242 // add BLOCK_GROUP_ITEM to tree 2 4243 4244 bgi = ExAllocatePoolWithTag(PagedPool, sizeof(BLOCK_GROUP_ITEM), ALLOC_TAG); 4245 if (!bgi) { 4246 ERR("out of memory\n"); 4247 return STATUS_INSUFFICIENT_RESOURCES; 4248 } 4249 4250 bgi->used = c->used; 4251 bgi->chunk_tree = 0x100; 4252 bgi->flags = c->chunk_item->type; 4253 4254 Status = insert_tree_item(Vcb, Vcb->extent_root, c->offset, TYPE_BLOCK_GROUP_ITEM, c->chunk_item->size, bgi, sizeof(BLOCK_GROUP_ITEM), NULL, Irp); 4255 if (!NT_SUCCESS(Status)) { 4256 ERR("insert_tree_item failed\n"); 4257 ExFreePool(bgi); 4258 return Status; 4259 } 4260 4261 if (c->chunk_item->type & BLOCK_FLAG_RAID0) 4262 factor = c->chunk_item->num_stripes; 4263 else if (c->chunk_item->type & BLOCK_FLAG_RAID10) 4264 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes; 4265 else if (c->chunk_item->type & BLOCK_FLAG_RAID5) 4266 factor = c->chunk_item->num_stripes - 1; 4267 else if (c->chunk_item->type & BLOCK_FLAG_RAID6) 4268 factor = c->chunk_item->num_stripes - 2; 4269 else // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4 4270 factor = 1; 4271 4272 // add DEV_EXTENTs to tree 4 4273 4274 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 4275 4276 for (i = 0; i < c->chunk_item->num_stripes; i++) { 4277 DEV_EXTENT* de; 4278 4279 de = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_EXTENT), ALLOC_TAG); 4280 if (!de) { 4281 ERR("out of memory\n"); 4282 return STATUS_INSUFFICIENT_RESOURCES; 4283 } 4284 4285 de->chunktree = Vcb->chunk_root->id; 4286 de->objid = 0x100; 4287 de->address = c->offset; 4288 de->length = c->chunk_item->size / factor; 4289 de->chunktree_uuid = Vcb->chunk_root->treeholder.tree->header.chunk_tree_uuid; 4290 4291 Status = insert_tree_item(Vcb, Vcb->dev_root, c->devices[i]->devitem.dev_id, TYPE_DEV_EXTENT, cis[i].offset, de, sizeof(DEV_EXTENT), NULL, Irp); 4292 if (!NT_SUCCESS(Status)) { 4293 ERR("insert_tree_item returned %08lx\n", Status); 4294 ExFreePool(de); 4295 return Status; 4296 } 4297 4298 // FIXME - no point in calling this twice for the same device 4299 Status = update_dev_item(Vcb, c->devices[i], Irp); 4300 if (!NT_SUCCESS(Status)) { 4301 ERR("update_dev_item returned %08lx\n", Status); 4302 return Status; 4303 } 4304 } 4305 4306 c->created = false; 4307 c->oldused = c->used; 4308 4309 Vcb->superblock.bytes_used += chunk_estimate_phys_size(Vcb, c, c->used); 4310 4311 return STATUS_SUCCESS; 4312 } 4313 4314 static void remove_from_bootstrap(device_extension* Vcb, uint64_t obj_id, uint8_t obj_type, uint64_t offset) { 4315 sys_chunk* sc2; 4316 LIST_ENTRY* le; 4317 4318 le = Vcb->sys_chunks.Flink; 4319 while (le != &Vcb->sys_chunks) { 4320 sc2 = CONTAINING_RECORD(le, sys_chunk, list_entry); 4321 4322 if (sc2->key.obj_id == obj_id && sc2->key.obj_type == obj_type && sc2->key.offset == offset) { 4323 RemoveEntryList(&sc2->list_entry); 4324 4325 Vcb->superblock.n -= sizeof(KEY) + sc2->size; 4326 4327 ExFreePool(sc2->data); 4328 ExFreePool(sc2); 4329 regen_bootstrap(Vcb); 4330 return; 4331 } 4332 4333 le = le->Flink; 4334 } 4335 } 4336 4337 static NTSTATUS set_xattr(device_extension* Vcb, LIST_ENTRY* batchlist, root* subvol, uint64_t inode, char* name, uint16_t namelen, 4338 uint32_t crc32, uint8_t* data, uint16_t datalen) { 4339 NTSTATUS Status; 4340 uint16_t xasize; 4341 DIR_ITEM* xa; 4342 4343 TRACE("(%p, %I64x, %I64x, %.*s, %08x, %p, %u)\n", Vcb, subvol->id, inode, namelen, name, crc32, data, datalen); 4344 4345 xasize = (uint16_t)offsetof(DIR_ITEM, name[0]) + namelen + datalen; 4346 4347 xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG); 4348 if (!xa) { 4349 ERR("out of memory\n"); 4350 return STATUS_INSUFFICIENT_RESOURCES; 4351 } 4352 4353 xa->key.obj_id = 0; 4354 xa->key.obj_type = 0; 4355 xa->key.offset = 0; 4356 xa->transid = Vcb->superblock.generation; 4357 xa->m = datalen; 4358 xa->n = namelen; 4359 xa->type = BTRFS_TYPE_EA; 4360 RtlCopyMemory(xa->name, name, namelen); 4361 RtlCopyMemory(xa->name + namelen, data, datalen); 4362 4363 Status = insert_tree_item_batch(batchlist, Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, Batch_SetXattr); 4364 if (!NT_SUCCESS(Status)) { 4365 ERR("insert_tree_item_batch returned %08lx\n", Status); 4366 ExFreePool(xa); 4367 return Status; 4368 } 4369 4370 return STATUS_SUCCESS; 4371 } 4372 4373 static NTSTATUS delete_xattr(device_extension* Vcb, LIST_ENTRY* batchlist, root* subvol, uint64_t inode, char* name, 4374 uint16_t namelen, uint32_t crc32) { 4375 NTSTATUS Status; 4376 uint16_t xasize; 4377 DIR_ITEM* xa; 4378 4379 TRACE("(%p, %I64x, %I64x, %.*s, %08x)\n", Vcb, subvol->id, inode, namelen, name, crc32); 4380 4381 xasize = (uint16_t)offsetof(DIR_ITEM, name[0]) + namelen; 4382 4383 xa = ExAllocatePoolWithTag(PagedPool, xasize, ALLOC_TAG); 4384 if (!xa) { 4385 ERR("out of memory\n"); 4386 return STATUS_INSUFFICIENT_RESOURCES; 4387 } 4388 4389 xa->key.obj_id = 0; 4390 xa->key.obj_type = 0; 4391 xa->key.offset = 0; 4392 xa->transid = Vcb->superblock.generation; 4393 xa->m = 0; 4394 xa->n = namelen; 4395 xa->type = BTRFS_TYPE_EA; 4396 RtlCopyMemory(xa->name, name, namelen); 4397 4398 Status = insert_tree_item_batch(batchlist, Vcb, subvol, inode, TYPE_XATTR_ITEM, crc32, xa, xasize, Batch_DeleteXattr); 4399 if (!NT_SUCCESS(Status)) { 4400 ERR("insert_tree_item_batch returned %08lx\n", Status); 4401 ExFreePool(xa); 4402 return Status; 4403 } 4404 4405 return STATUS_SUCCESS; 4406 } 4407 4408 static NTSTATUS insert_sparse_extent(fcb* fcb, LIST_ENTRY* batchlist, uint64_t start, uint64_t length) { 4409 NTSTATUS Status; 4410 EXTENT_DATA* ed; 4411 EXTENT_DATA2* ed2; 4412 4413 TRACE("((%I64x, %I64x), %I64x, %I64x)\n", fcb->subvol->id, fcb->inode, start, length); 4414 4415 ed = ExAllocatePoolWithTag(PagedPool, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); 4416 if (!ed) { 4417 ERR("out of memory\n"); 4418 return STATUS_INSUFFICIENT_RESOURCES; 4419 } 4420 4421 ed->generation = fcb->Vcb->superblock.generation; 4422 ed->decoded_size = length; 4423 ed->compression = BTRFS_COMPRESSION_NONE; 4424 ed->encryption = BTRFS_ENCRYPTION_NONE; 4425 ed->encoding = BTRFS_ENCODING_NONE; 4426 ed->type = EXTENT_TYPE_REGULAR; 4427 4428 ed2 = (EXTENT_DATA2*)ed->data; 4429 ed2->address = 0; 4430 ed2->size = 0; 4431 ed2->offset = 0; 4432 ed2->num_bytes = length; 4433 4434 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, start, ed, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), Batch_Insert); 4435 if (!NT_SUCCESS(Status)) { 4436 ERR("insert_tree_item_batch returned %08lx\n", Status); 4437 ExFreePool(ed); 4438 return Status; 4439 } 4440 4441 return STATUS_SUCCESS; 4442 } 4443 4444 #ifdef _MSC_VER 4445 #pragma warning(push) 4446 #pragma warning(suppress: 28194) 4447 #endif 4448 NTSTATUS insert_tree_item_batch(LIST_ENTRY* batchlist, device_extension* Vcb, root* r, uint64_t objid, uint8_t objtype, uint64_t offset, 4449 _In_opt_ _When_(return >= 0, __drv_aliasesMem) void* data, uint16_t datalen, enum batch_operation operation) { 4450 LIST_ENTRY* le; 4451 batch_root* br = NULL; 4452 batch_item* bi; 4453 4454 le = batchlist->Flink; 4455 while (le != batchlist) { 4456 batch_root* br2 = CONTAINING_RECORD(le, batch_root, list_entry); 4457 4458 if (br2->r == r) { 4459 br = br2; 4460 break; 4461 } 4462 4463 le = le->Flink; 4464 } 4465 4466 if (!br) { 4467 br = ExAllocatePoolWithTag(PagedPool, sizeof(batch_root), ALLOC_TAG); 4468 if (!br) { 4469 ERR("out of memory\n"); 4470 return STATUS_INSUFFICIENT_RESOURCES; 4471 } 4472 4473 br->r = r; 4474 InitializeListHead(&br->items); 4475 InsertTailList(batchlist, &br->list_entry); 4476 } 4477 4478 bi = ExAllocateFromPagedLookasideList(&Vcb->batch_item_lookaside); 4479 if (!bi) { 4480 ERR("out of memory\n"); 4481 return STATUS_INSUFFICIENT_RESOURCES; 4482 } 4483 4484 bi->key.obj_id = objid; 4485 bi->key.obj_type = objtype; 4486 bi->key.offset = offset; 4487 bi->data = data; 4488 bi->datalen = datalen; 4489 bi->operation = operation; 4490 4491 le = br->items.Blink; 4492 while (le != &br->items) { 4493 batch_item* bi2 = CONTAINING_RECORD(le, batch_item, list_entry); 4494 int cmp = keycmp(bi2->key, bi->key); 4495 4496 if (cmp == -1 || (cmp == 0 && bi->operation >= bi2->operation)) { 4497 InsertHeadList(&bi2->list_entry, &bi->list_entry); 4498 return STATUS_SUCCESS; 4499 } 4500 4501 le = le->Blink; 4502 } 4503 4504 InsertHeadList(&br->items, &bi->list_entry); 4505 4506 return STATUS_SUCCESS; 4507 } 4508 #ifdef _MSC_VER 4509 #pragma warning(pop) 4510 #endif 4511 4512 typedef struct { 4513 uint64_t address; 4514 uint64_t length; 4515 uint64_t offset; 4516 bool changed; 4517 chunk* chunk; 4518 uint64_t skip_start; 4519 uint64_t skip_end; 4520 LIST_ENTRY list_entry; 4521 } extent_range; 4522 4523 static void rationalize_extents(fcb* fcb, PIRP Irp) { 4524 LIST_ENTRY* le; 4525 LIST_ENTRY extent_ranges; 4526 extent_range* er; 4527 bool changed = false, truncating = false; 4528 uint32_t num_extents = 0; 4529 4530 InitializeListHead(&extent_ranges); 4531 4532 le = fcb->extents.Flink; 4533 while (le != &fcb->extents) { 4534 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4535 4536 if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) { 4537 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4538 4539 if (ed2->size != 0) { 4540 LIST_ENTRY* le2; 4541 4542 le2 = extent_ranges.Flink; 4543 while (le2 != &extent_ranges) { 4544 extent_range* er2 = CONTAINING_RECORD(le2, extent_range, list_entry); 4545 4546 if (er2->address == ed2->address) { 4547 er2->skip_start = min(er2->skip_start, ed2->offset); 4548 er2->skip_end = min(er2->skip_end, ed2->size - ed2->offset - ed2->num_bytes); 4549 goto cont; 4550 } else if (er2->address > ed2->address) 4551 break; 4552 4553 le2 = le2->Flink; 4554 } 4555 4556 er = ExAllocatePoolWithTag(PagedPool, sizeof(extent_range), ALLOC_TAG); // FIXME - should be from lookaside? 4557 if (!er) { 4558 ERR("out of memory\n"); 4559 goto end; 4560 } 4561 4562 er->address = ed2->address; 4563 er->length = ed2->size; 4564 er->offset = ext->offset - ed2->offset; 4565 er->changed = false; 4566 er->chunk = NULL; 4567 er->skip_start = ed2->offset; 4568 er->skip_end = ed2->size - ed2->offset - ed2->num_bytes; 4569 4570 if (er->skip_start != 0 || er->skip_end != 0) 4571 truncating = true; 4572 4573 InsertHeadList(le2->Blink, &er->list_entry); 4574 num_extents++; 4575 } 4576 } 4577 4578 cont: 4579 le = le->Flink; 4580 } 4581 4582 if (num_extents == 0 || (num_extents == 1 && !truncating)) 4583 goto end; 4584 4585 le = extent_ranges.Flink; 4586 while (le != &extent_ranges) { 4587 er = CONTAINING_RECORD(le, extent_range, list_entry); 4588 4589 if (!er->chunk) { 4590 LIST_ENTRY* le2; 4591 4592 er->chunk = get_chunk_from_address(fcb->Vcb, er->address); 4593 4594 if (!er->chunk) { 4595 ERR("get_chunk_from_address(%I64x) failed\n", er->address); 4596 goto end; 4597 } 4598 4599 le2 = le->Flink; 4600 while (le2 != &extent_ranges) { 4601 extent_range* er2 = CONTAINING_RECORD(le2, extent_range, list_entry); 4602 4603 if (!er2->chunk && er2->address >= er->chunk->offset && er2->address < er->chunk->offset + er->chunk->chunk_item->size) 4604 er2->chunk = er->chunk; 4605 4606 le2 = le2->Flink; 4607 } 4608 } 4609 4610 le = le->Flink; 4611 } 4612 4613 if (truncating) { 4614 // truncate beginning or end of extent if unused 4615 4616 le = extent_ranges.Flink; 4617 while (le != &extent_ranges) { 4618 er = CONTAINING_RECORD(le, extent_range, list_entry); 4619 4620 if (er->skip_start > 0) { 4621 LIST_ENTRY* le2 = fcb->extents.Flink; 4622 while (le2 != &fcb->extents) { 4623 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 4624 4625 if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) { 4626 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4627 4628 if (ed2->size != 0 && ed2->address == er->address) { 4629 NTSTATUS Status; 4630 4631 Status = update_changed_extent_ref(fcb->Vcb, er->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4632 -1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, true, Irp); 4633 if (!NT_SUCCESS(Status)) { 4634 ERR("update_changed_extent_ref returned %08lx\n", Status); 4635 goto end; 4636 } 4637 4638 ext->extent_data.decoded_size -= er->skip_start; 4639 ed2->size -= er->skip_start; 4640 ed2->address += er->skip_start; 4641 ed2->offset -= er->skip_start; 4642 4643 add_changed_extent_ref(er->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4644 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM); 4645 } 4646 } 4647 4648 le2 = le2->Flink; 4649 } 4650 4651 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) 4652 add_checksum_entry(fcb->Vcb, er->address, (ULONG)(er->skip_start / fcb->Vcb->superblock.sector_size), NULL, NULL); 4653 4654 acquire_chunk_lock(er->chunk, fcb->Vcb); 4655 4656 if (!er->chunk->cache_loaded) { 4657 NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL); 4658 4659 if (!NT_SUCCESS(Status)) { 4660 ERR("load_cache_chunk returned %08lx\n", Status); 4661 release_chunk_lock(er->chunk, fcb->Vcb); 4662 goto end; 4663 } 4664 } 4665 4666 er->chunk->used -= er->skip_start; 4667 4668 space_list_add(er->chunk, er->address, er->skip_start, NULL); 4669 4670 release_chunk_lock(er->chunk, fcb->Vcb); 4671 4672 er->address += er->skip_start; 4673 er->length -= er->skip_start; 4674 } 4675 4676 if (er->skip_end > 0) { 4677 LIST_ENTRY* le2 = fcb->extents.Flink; 4678 while (le2 != &fcb->extents) { 4679 extent* ext = CONTAINING_RECORD(le2, extent, list_entry); 4680 4681 if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) { 4682 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4683 4684 if (ed2->size != 0 && ed2->address == er->address) { 4685 NTSTATUS Status; 4686 4687 Status = update_changed_extent_ref(fcb->Vcb, er->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4688 -1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, true, Irp); 4689 if (!NT_SUCCESS(Status)) { 4690 ERR("update_changed_extent_ref returned %08lx\n", Status); 4691 goto end; 4692 } 4693 4694 ext->extent_data.decoded_size -= er->skip_end; 4695 ed2->size -= er->skip_end; 4696 4697 add_changed_extent_ref(er->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4698 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM); 4699 } 4700 } 4701 4702 le2 = le2->Flink; 4703 } 4704 4705 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) 4706 add_checksum_entry(fcb->Vcb, er->address + er->length - er->skip_end, (ULONG)(er->skip_end / fcb->Vcb->superblock.sector_size), NULL, NULL); 4707 4708 acquire_chunk_lock(er->chunk, fcb->Vcb); 4709 4710 if (!er->chunk->cache_loaded) { 4711 NTSTATUS Status = load_cache_chunk(fcb->Vcb, er->chunk, NULL); 4712 4713 if (!NT_SUCCESS(Status)) { 4714 ERR("load_cache_chunk returned %08lx\n", Status); 4715 release_chunk_lock(er->chunk, fcb->Vcb); 4716 goto end; 4717 } 4718 } 4719 4720 er->chunk->used -= er->skip_end; 4721 4722 space_list_add(er->chunk, er->address + er->length - er->skip_end, er->skip_end, NULL); 4723 4724 release_chunk_lock(er->chunk, fcb->Vcb); 4725 4726 er->length -= er->skip_end; 4727 } 4728 4729 le = le->Flink; 4730 } 4731 } 4732 4733 if (num_extents < 2) 4734 goto end; 4735 4736 // merge together adjacent extents 4737 le = extent_ranges.Flink; 4738 while (le != &extent_ranges) { 4739 er = CONTAINING_RECORD(le, extent_range, list_entry); 4740 4741 if (le->Flink != &extent_ranges && er->length < MAX_EXTENT_SIZE) { 4742 extent_range* er2 = CONTAINING_RECORD(le->Flink, extent_range, list_entry); 4743 4744 if (er->chunk == er2->chunk) { 4745 if (er2->address == er->address + er->length && er2->offset >= er->offset + er->length) { 4746 if (er->length + er2->length <= MAX_EXTENT_SIZE) { 4747 er->length += er2->length; 4748 er->changed = true; 4749 4750 RemoveEntryList(&er2->list_entry); 4751 ExFreePool(er2); 4752 4753 changed = true; 4754 continue; 4755 } 4756 } 4757 } 4758 } 4759 4760 le = le->Flink; 4761 } 4762 4763 if (!changed) 4764 goto end; 4765 4766 le = fcb->extents.Flink; 4767 while (le != &fcb->extents) { 4768 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4769 4770 if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->unique) { 4771 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4772 4773 if (ed2->size != 0) { 4774 LIST_ENTRY* le2; 4775 4776 le2 = extent_ranges.Flink; 4777 while (le2 != &extent_ranges) { 4778 extent_range* er2 = CONTAINING_RECORD(le2, extent_range, list_entry); 4779 4780 if (ed2->address >= er2->address && ed2->address + ed2->size <= er2->address + er2->length && er2->changed) { 4781 NTSTATUS Status; 4782 4783 Status = update_changed_extent_ref(fcb->Vcb, er2->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4784 -1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM, true, Irp); 4785 if (!NT_SUCCESS(Status)) { 4786 ERR("update_changed_extent_ref returned %08lx\n", Status); 4787 goto end; 4788 } 4789 4790 ed2->offset += ed2->address - er2->address; 4791 ed2->address = er2->address; 4792 ed2->size = er2->length; 4793 ext->extent_data.decoded_size = ed2->size; 4794 4795 add_changed_extent_ref(er2->chunk, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 4796 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM); 4797 4798 break; 4799 } 4800 4801 le2 = le2->Flink; 4802 } 4803 } 4804 } 4805 4806 le = le->Flink; 4807 } 4808 4809 end: 4810 while (!IsListEmpty(&extent_ranges)) { 4811 le = RemoveHeadList(&extent_ranges); 4812 er = CONTAINING_RECORD(le, extent_range, list_entry); 4813 4814 ExFreePool(er); 4815 } 4816 } 4817 4818 NTSTATUS flush_fcb(fcb* fcb, bool cache, LIST_ENTRY* batchlist, PIRP Irp) { 4819 traverse_ptr tp; 4820 KEY searchkey; 4821 NTSTATUS Status; 4822 INODE_ITEM* ii; 4823 uint64_t ii_offset; 4824 #ifdef DEBUG_PARANOID 4825 uint64_t old_size = 0; 4826 bool extents_changed; 4827 #endif 4828 4829 if (fcb->ads) { 4830 if (fcb->deleted) { 4831 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adsxattr.Length, fcb->adshash); 4832 if (!NT_SUCCESS(Status)) { 4833 ERR("delete_xattr returned %08lx\n", Status); 4834 goto end; 4835 } 4836 } else { 4837 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, fcb->adsxattr.Buffer, fcb->adsxattr.Length, 4838 fcb->adshash, (uint8_t*)fcb->adsdata.Buffer, fcb->adsdata.Length); 4839 if (!NT_SUCCESS(Status)) { 4840 ERR("set_xattr returned %08lx\n", Status); 4841 goto end; 4842 } 4843 } 4844 4845 Status = STATUS_SUCCESS; 4846 goto end; 4847 } 4848 4849 if (fcb->deleted) { 4850 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0xffffffffffffffff, NULL, 0, Batch_DeleteInode); 4851 if (!NT_SUCCESS(Status)) { 4852 ERR("insert_tree_item_batch returned %08lx\n", Status); 4853 goto end; 4854 } 4855 4856 if (fcb->marked_as_orphan) { 4857 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, BTRFS_ORPHAN_INODE_OBJID, TYPE_ORPHAN_INODE, 4858 fcb->inode, NULL, 0, Batch_Delete); 4859 if (!NT_SUCCESS(Status)) { 4860 ERR("insert_tree_item_batch returned %08lx\n", Status); 4861 goto end; 4862 } 4863 } 4864 4865 Status = STATUS_SUCCESS; 4866 goto end; 4867 } 4868 4869 #ifdef DEBUG_PARANOID 4870 extents_changed = fcb->extents_changed; 4871 #endif 4872 4873 if (fcb->extents_changed) { 4874 LIST_ENTRY* le; 4875 bool prealloc = false, extents_inline = false; 4876 uint64_t last_end; 4877 4878 // delete ignored extent items 4879 le = fcb->extents.Flink; 4880 while (le != &fcb->extents) { 4881 LIST_ENTRY* le2 = le->Flink; 4882 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4883 4884 if (ext->ignore) { 4885 RemoveEntryList(&ext->list_entry); 4886 4887 if (ext->csum) 4888 ExFreePool(ext->csum); 4889 4890 ExFreePool(ext); 4891 } 4892 4893 le = le2; 4894 } 4895 4896 le = fcb->extents.Flink; 4897 while (le != &fcb->extents) { 4898 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4899 4900 if (ext->inserted && ext->csum && ext->extent_data.type == EXTENT_TYPE_REGULAR) { 4901 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4902 4903 if (ed2->size > 0) { // not sparse 4904 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) 4905 add_checksum_entry(fcb->Vcb, ed2->address + ed2->offset, (ULONG)(ed2->num_bytes / fcb->Vcb->superblock.sector_size), ext->csum, Irp); 4906 else 4907 add_checksum_entry(fcb->Vcb, ed2->address, (ULONG)(ed2->size / fcb->Vcb->superblock.sector_size), ext->csum, Irp); 4908 } 4909 } 4910 4911 le = le->Flink; 4912 } 4913 4914 if (!IsListEmpty(&fcb->extents)) { 4915 rationalize_extents(fcb, Irp); 4916 4917 // merge together adjacent EXTENT_DATAs pointing to same extent 4918 4919 le = fcb->extents.Flink; 4920 while (le != &fcb->extents) { 4921 LIST_ENTRY* le2 = le->Flink; 4922 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4923 4924 if ((ext->extent_data.type == EXTENT_TYPE_REGULAR || ext->extent_data.type == EXTENT_TYPE_PREALLOC) && le->Flink != &fcb->extents) { 4925 extent* nextext = CONTAINING_RECORD(le->Flink, extent, list_entry); 4926 4927 if (ext->extent_data.type == nextext->extent_data.type) { 4928 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ext->extent_data.data; 4929 EXTENT_DATA2* ned2 = (EXTENT_DATA2*)nextext->extent_data.data; 4930 4931 if (ed2->size != 0 && ed2->address == ned2->address && ed2->size == ned2->size && 4932 nextext->offset == ext->offset + ed2->num_bytes && ned2->offset == ed2->offset + ed2->num_bytes) { 4933 chunk* c; 4934 4935 if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE && ext->csum) { 4936 ULONG len = (ULONG)((ed2->num_bytes + ned2->num_bytes) / fcb->Vcb->superblock.sector_size); 4937 void* csum; 4938 4939 csum = ExAllocatePoolWithTag(NonPagedPool, len * fcb->Vcb->csum_size, ALLOC_TAG); 4940 if (!csum) { 4941 ERR("out of memory\n"); 4942 Status = STATUS_INSUFFICIENT_RESOURCES; 4943 goto end; 4944 } 4945 4946 RtlCopyMemory(csum, ext->csum, (ULONG)(ed2->num_bytes * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size)); 4947 RtlCopyMemory((uint8_t*)csum + (ed2->num_bytes * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size), nextext->csum, 4948 (ULONG)(ned2->num_bytes * fcb->Vcb->csum_size / fcb->Vcb->superblock.sector_size)); 4949 4950 ExFreePool(ext->csum); 4951 ext->csum = csum; 4952 } 4953 4954 ext->extent_data.generation = fcb->Vcb->superblock.generation; 4955 ed2->num_bytes += ned2->num_bytes; 4956 4957 RemoveEntryList(&nextext->list_entry); 4958 4959 if (nextext->csum) 4960 ExFreePool(nextext->csum); 4961 4962 ExFreePool(nextext); 4963 4964 c = get_chunk_from_address(fcb->Vcb, ed2->address); 4965 4966 if (!c) { 4967 ERR("get_chunk_from_address(%I64x) failed\n", ed2->address); 4968 } else { 4969 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1, 4970 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, false, Irp); 4971 if (!NT_SUCCESS(Status)) { 4972 ERR("update_changed_extent_ref returned %08lx\n", Status); 4973 goto end; 4974 } 4975 } 4976 4977 le2 = le; 4978 } 4979 } 4980 } 4981 4982 le = le2; 4983 } 4984 } 4985 4986 if (!fcb->created) { 4987 // delete existing EXTENT_DATA items 4988 4989 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, 0, NULL, 0, Batch_DeleteExtentData); 4990 if (!NT_SUCCESS(Status)) { 4991 ERR("insert_tree_item_batch returned %08lx\n", Status); 4992 goto end; 4993 } 4994 } 4995 4996 // add new EXTENT_DATAs 4997 4998 last_end = 0; 4999 5000 le = fcb->extents.Flink; 5001 while (le != &fcb->extents) { 5002 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 5003 EXTENT_DATA* ed; 5004 5005 ext->inserted = false; 5006 5007 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && ext->offset > last_end) { 5008 Status = insert_sparse_extent(fcb, batchlist, last_end, ext->offset - last_end); 5009 if (!NT_SUCCESS(Status)) { 5010 ERR("insert_sparse_extent returned %08lx\n", Status); 5011 goto end; 5012 } 5013 } 5014 5015 ed = ExAllocatePoolWithTag(PagedPool, ext->datalen, ALLOC_TAG); 5016 if (!ed) { 5017 ERR("out of memory\n"); 5018 Status = STATUS_INSUFFICIENT_RESOURCES; 5019 goto end; 5020 } 5021 5022 RtlCopyMemory(ed, &ext->extent_data, ext->datalen); 5023 5024 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_EXTENT_DATA, ext->offset, 5025 ed, ext->datalen, Batch_Insert); 5026 if (!NT_SUCCESS(Status)) { 5027 ERR("insert_tree_item_batch returned %08lx\n", Status); 5028 goto end; 5029 } 5030 5031 if (ed->type == EXTENT_TYPE_PREALLOC) 5032 prealloc = true; 5033 5034 if (ed->type == EXTENT_TYPE_INLINE) 5035 extents_inline = true; 5036 5037 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES)) { 5038 if (ed->type == EXTENT_TYPE_INLINE) 5039 last_end = ext->offset + ed->decoded_size; 5040 else { 5041 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 5042 5043 last_end = ext->offset + ed2->num_bytes; 5044 } 5045 } 5046 5047 le = le->Flink; 5048 } 5049 5050 if (!(fcb->Vcb->superblock.incompat_flags & BTRFS_INCOMPAT_FLAGS_NO_HOLES) && !extents_inline && 5051 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) > last_end) { 5052 Status = insert_sparse_extent(fcb, batchlist, last_end, sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size) - last_end); 5053 if (!NT_SUCCESS(Status)) { 5054 ERR("insert_sparse_extent returned %08lx\n", Status); 5055 goto end; 5056 } 5057 } 5058 5059 // update prealloc flag in INODE_ITEM 5060 5061 if (!prealloc) 5062 fcb->inode_item.flags &= ~BTRFS_INODE_PREALLOC; 5063 else 5064 fcb->inode_item.flags |= BTRFS_INODE_PREALLOC; 5065 5066 fcb->inode_item_changed = true; 5067 5068 fcb->extents_changed = false; 5069 } 5070 5071 if ((!fcb->created && fcb->inode_item_changed) || cache) { 5072 searchkey.obj_id = fcb->inode; 5073 searchkey.obj_type = TYPE_INODE_ITEM; 5074 searchkey.offset = 0xffffffffffffffff; 5075 5076 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, false, Irp); 5077 if (!NT_SUCCESS(Status)) { 5078 ERR("error - find_item returned %08lx\n", Status); 5079 goto end; 5080 } 5081 5082 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 5083 if (cache) { 5084 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); 5085 if (!ii) { 5086 ERR("out of memory\n"); 5087 Status = STATUS_INSUFFICIENT_RESOURCES; 5088 goto end; 5089 } 5090 5091 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); 5092 5093 Status = insert_tree_item(fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, 0, ii, sizeof(INODE_ITEM), NULL, Irp); 5094 if (!NT_SUCCESS(Status)) { 5095 ERR("insert_tree_item returned %08lx\n", Status); 5096 goto end; 5097 } 5098 5099 ii_offset = 0; 5100 } else { 5101 ERR("could not find INODE_ITEM for inode %I64x in subvol %I64x\n", fcb->inode, fcb->subvol->id); 5102 Status = STATUS_INTERNAL_ERROR; 5103 goto end; 5104 } 5105 } else { 5106 #ifdef DEBUG_PARANOID 5107 INODE_ITEM* ii2 = (INODE_ITEM*)tp.item->data; 5108 5109 old_size = ii2->st_size; 5110 #endif 5111 5112 ii_offset = tp.item->key.offset; 5113 } 5114 5115 if (!cache) { 5116 Status = delete_tree_item(fcb->Vcb, &tp); 5117 if (!NT_SUCCESS(Status)) { 5118 ERR("delete_tree_item returned %08lx\n", Status); 5119 goto end; 5120 } 5121 } else { 5122 searchkey.obj_id = fcb->inode; 5123 searchkey.obj_type = TYPE_INODE_ITEM; 5124 searchkey.offset = ii_offset; 5125 5126 Status = find_item(fcb->Vcb, fcb->subvol, &tp, &searchkey, false, Irp); 5127 if (!NT_SUCCESS(Status)) { 5128 ERR("error - find_item returned %08lx\n", Status); 5129 goto end; 5130 } 5131 5132 if (keycmp(tp.item->key, searchkey)) { 5133 ERR("could not find INODE_ITEM for inode %I64x in subvol %I64x\n", fcb->inode, fcb->subvol->id); 5134 Status = STATUS_INTERNAL_ERROR; 5135 goto end; 5136 } else 5137 RtlCopyMemory(tp.item->data, &fcb->inode_item, min(tp.item->size, sizeof(INODE_ITEM))); 5138 } 5139 5140 #ifdef DEBUG_PARANOID 5141 if (!extents_changed && fcb->type != BTRFS_TYPE_DIRECTORY && old_size != fcb->inode_item.st_size) { 5142 ERR("error - size has changed but extents not marked as changed\n"); 5143 int3; 5144 } 5145 #endif 5146 } else 5147 ii_offset = 0; 5148 5149 fcb->created = false; 5150 5151 if (!cache && fcb->inode_item_changed) { 5152 ii = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_ITEM), ALLOC_TAG); 5153 if (!ii) { 5154 ERR("out of memory\n"); 5155 Status = STATUS_INSUFFICIENT_RESOURCES; 5156 goto end; 5157 } 5158 5159 RtlCopyMemory(ii, &fcb->inode_item, sizeof(INODE_ITEM)); 5160 5161 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, fcb->inode, TYPE_INODE_ITEM, ii_offset, ii, sizeof(INODE_ITEM), 5162 Batch_Insert); 5163 if (!NT_SUCCESS(Status)) { 5164 ERR("insert_tree_item_batch returned %08lx\n", Status); 5165 goto end; 5166 } 5167 5168 fcb->inode_item_changed = false; 5169 } 5170 5171 if (fcb->sd_dirty) { 5172 if (!fcb->sd_deleted) { 5173 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_NTACL, sizeof(EA_NTACL) - 1, 5174 EA_NTACL_HASH, (uint8_t*)fcb->sd, (uint16_t)RtlLengthSecurityDescriptor(fcb->sd)); 5175 if (!NT_SUCCESS(Status)) { 5176 ERR("set_xattr returned %08lx\n", Status); 5177 goto end; 5178 } 5179 } else { 5180 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_NTACL, sizeof(EA_NTACL) - 1, EA_NTACL_HASH); 5181 if (!NT_SUCCESS(Status)) { 5182 ERR("delete_xattr returned %08lx\n", Status); 5183 goto end; 5184 } 5185 } 5186 5187 fcb->sd_deleted = false; 5188 fcb->sd_dirty = false; 5189 } 5190 5191 if (fcb->atts_changed) { 5192 if (!fcb->atts_deleted) { 5193 uint8_t val[16], *val2; 5194 ULONG atts = fcb->atts; 5195 5196 TRACE("inserting new DOSATTRIB xattr\n"); 5197 5198 if (fcb->inode == SUBVOL_ROOT_INODE) 5199 atts &= ~FILE_ATTRIBUTE_READONLY; 5200 5201 val2 = &val[sizeof(val) - 1]; 5202 5203 do { 5204 uint8_t c = atts % 16; 5205 *val2 = c <= 9 ? (c + '0') : (c - 0xa + 'a'); 5206 5207 val2--; 5208 atts >>= 4; 5209 } while (atts != 0); 5210 5211 *val2 = 'x'; 5212 val2--; 5213 *val2 = '0'; 5214 5215 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1, 5216 EA_DOSATTRIB_HASH, val2, (uint16_t)(val + sizeof(val) - val2)); 5217 if (!NT_SUCCESS(Status)) { 5218 ERR("set_xattr returned %08lx\n", Status); 5219 goto end; 5220 } 5221 } else { 5222 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_DOSATTRIB, sizeof(EA_DOSATTRIB) - 1, EA_DOSATTRIB_HASH); 5223 if (!NT_SUCCESS(Status)) { 5224 ERR("delete_xattr returned %08lx\n", Status); 5225 goto end; 5226 } 5227 } 5228 5229 fcb->atts_changed = false; 5230 fcb->atts_deleted = false; 5231 } 5232 5233 if (fcb->reparse_xattr_changed) { 5234 if (fcb->reparse_xattr.Buffer && fcb->reparse_xattr.Length > 0) { 5235 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_REPARSE, sizeof(EA_REPARSE) - 1, 5236 EA_REPARSE_HASH, (uint8_t*)fcb->reparse_xattr.Buffer, (uint16_t)fcb->reparse_xattr.Length); 5237 if (!NT_SUCCESS(Status)) { 5238 ERR("set_xattr returned %08lx\n", Status); 5239 goto end; 5240 } 5241 } else { 5242 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_REPARSE, sizeof(EA_REPARSE) - 1, EA_REPARSE_HASH); 5243 if (!NT_SUCCESS(Status)) { 5244 ERR("delete_xattr returned %08lx\n", Status); 5245 goto end; 5246 } 5247 } 5248 5249 fcb->reparse_xattr_changed = false; 5250 } 5251 5252 if (fcb->ea_changed) { 5253 if (fcb->ea_xattr.Buffer && fcb->ea_xattr.Length > 0) { 5254 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_EA, sizeof(EA_EA) - 1, 5255 EA_EA_HASH, (uint8_t*)fcb->ea_xattr.Buffer, (uint16_t)fcb->ea_xattr.Length); 5256 if (!NT_SUCCESS(Status)) { 5257 ERR("set_xattr returned %08lx\n", Status); 5258 goto end; 5259 } 5260 } else { 5261 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_EA, sizeof(EA_EA) - 1, EA_EA_HASH); 5262 if (!NT_SUCCESS(Status)) { 5263 ERR("delete_xattr returned %08lx\n", Status); 5264 goto end; 5265 } 5266 } 5267 5268 fcb->ea_changed = false; 5269 } 5270 5271 if (fcb->prop_compression_changed) { 5272 if (fcb->prop_compression == PropCompression_None) { 5273 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1, EA_PROP_COMPRESSION_HASH); 5274 if (!NT_SUCCESS(Status)) { 5275 ERR("delete_xattr returned %08lx\n", Status); 5276 goto end; 5277 } 5278 } else if (fcb->prop_compression == PropCompression_Zlib) { 5279 static const char zlib[] = "zlib"; 5280 5281 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1, 5282 EA_PROP_COMPRESSION_HASH, (uint8_t*)zlib, sizeof(zlib) - 1); 5283 if (!NT_SUCCESS(Status)) { 5284 ERR("set_xattr returned %08lx\n", Status); 5285 goto end; 5286 } 5287 } else if (fcb->prop_compression == PropCompression_LZO) { 5288 static const char lzo[] = "lzo"; 5289 5290 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1, 5291 EA_PROP_COMPRESSION_HASH, (uint8_t*)lzo, sizeof(lzo) - 1); 5292 if (!NT_SUCCESS(Status)) { 5293 ERR("set_xattr returned %08lx\n", Status); 5294 goto end; 5295 } 5296 } else if (fcb->prop_compression == PropCompression_ZSTD) { 5297 static const char zstd[] = "zstd"; 5298 5299 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_PROP_COMPRESSION, sizeof(EA_PROP_COMPRESSION) - 1, 5300 EA_PROP_COMPRESSION_HASH, (uint8_t*)zstd, sizeof(zstd) - 1); 5301 if (!NT_SUCCESS(Status)) { 5302 ERR("set_xattr returned %08lx\n", Status); 5303 goto end; 5304 } 5305 } 5306 5307 fcb->prop_compression_changed = false; 5308 } 5309 5310 if (fcb->xattrs_changed) { 5311 LIST_ENTRY* le; 5312 5313 le = fcb->xattrs.Flink; 5314 while (le != &fcb->xattrs) { 5315 xattr* xa = CONTAINING_RECORD(le, xattr, list_entry); 5316 LIST_ENTRY* le2 = le->Flink; 5317 5318 if (xa->dirty) { 5319 uint32_t hash = calc_crc32c(0xfffffffe, (uint8_t*)xa->data, xa->namelen); 5320 5321 if (xa->valuelen == 0) { 5322 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, xa->data, xa->namelen, hash); 5323 if (!NT_SUCCESS(Status)) { 5324 ERR("delete_xattr returned %08lx\n", Status); 5325 goto end; 5326 } 5327 5328 RemoveEntryList(&xa->list_entry); 5329 ExFreePool(xa); 5330 } else { 5331 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, xa->data, xa->namelen, 5332 hash, (uint8_t*)&xa->data[xa->namelen], xa->valuelen); 5333 if (!NT_SUCCESS(Status)) { 5334 ERR("set_xattr returned %08lx\n", Status); 5335 goto end; 5336 } 5337 5338 xa->dirty = false; 5339 } 5340 } 5341 5342 le = le2; 5343 } 5344 5345 fcb->xattrs_changed = false; 5346 } 5347 5348 if ((fcb->case_sensitive_set && !fcb->case_sensitive)) { 5349 Status = delete_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_CASE_SENSITIVE, 5350 sizeof(EA_CASE_SENSITIVE) - 1, EA_CASE_SENSITIVE_HASH); 5351 if (!NT_SUCCESS(Status)) { 5352 ERR("delete_xattr returned %08lx\n", Status); 5353 goto end; 5354 } 5355 5356 fcb->case_sensitive_set = false; 5357 } else if ((!fcb->case_sensitive_set && fcb->case_sensitive)) { 5358 Status = set_xattr(fcb->Vcb, batchlist, fcb->subvol, fcb->inode, EA_CASE_SENSITIVE, 5359 sizeof(EA_CASE_SENSITIVE) - 1, EA_CASE_SENSITIVE_HASH, (uint8_t*)"1", 1); 5360 if (!NT_SUCCESS(Status)) { 5361 ERR("set_xattr returned %08lx\n", Status); 5362 goto end; 5363 } 5364 5365 fcb->case_sensitive_set = true; 5366 } 5367 5368 if (fcb->inode_item.st_nlink == 0 && !fcb->marked_as_orphan) { // mark as orphan 5369 Status = insert_tree_item_batch(batchlist, fcb->Vcb, fcb->subvol, BTRFS_ORPHAN_INODE_OBJID, TYPE_ORPHAN_INODE, 5370 fcb->inode, NULL, 0, Batch_Insert); 5371 if (!NT_SUCCESS(Status)) { 5372 ERR("insert_tree_item_batch returned %08lx\n", Status); 5373 goto end; 5374 } 5375 5376 fcb->marked_as_orphan = true; 5377 } 5378 5379 Status = STATUS_SUCCESS; 5380 5381 end: 5382 if (fcb->dirty) { 5383 bool lock = false; 5384 5385 fcb->dirty = false; 5386 5387 if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->dirty_fcbs_lock)) { 5388 ExAcquireResourceExclusiveLite(&fcb->Vcb->dirty_fcbs_lock, true); 5389 lock = true; 5390 } 5391 5392 RemoveEntryList(&fcb->list_entry_dirty); 5393 5394 if (lock) 5395 ExReleaseResourceLite(&fcb->Vcb->dirty_fcbs_lock); 5396 } 5397 5398 return Status; 5399 } 5400 5401 void add_trim_entry_avoid_sb(device_extension* Vcb, device* dev, uint64_t address, uint64_t size) { 5402 int i; 5403 ULONG sblen = (ULONG)sector_align(sizeof(superblock), Vcb->superblock.sector_size); 5404 5405 i = 0; 5406 while (superblock_addrs[i] != 0) { 5407 if (superblock_addrs[i] + sblen >= address && superblock_addrs[i] < address + size) { 5408 if (superblock_addrs[i] > address) 5409 add_trim_entry(dev, address, superblock_addrs[i] - address); 5410 5411 if (size <= superblock_addrs[i] + sblen - address) 5412 return; 5413 5414 size -= superblock_addrs[i] + sblen - address; 5415 address = superblock_addrs[i] + sblen; 5416 } else if (superblock_addrs[i] > address + size) 5417 break; 5418 5419 i++; 5420 } 5421 5422 add_trim_entry(dev, address, size); 5423 } 5424 5425 static NTSTATUS drop_chunk(device_extension* Vcb, chunk* c, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) { 5426 NTSTATUS Status; 5427 KEY searchkey; 5428 traverse_ptr tp; 5429 uint64_t i, factor; 5430 #ifdef __REACTOS__ 5431 uint64_t phys_used; 5432 #endif 5433 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];; 5434 5435 TRACE("dropping chunk %I64x\n", c->offset); 5436 5437 if (c->chunk_item->type & BLOCK_FLAG_RAID0) 5438 factor = c->chunk_item->num_stripes; 5439 else if (c->chunk_item->type & BLOCK_FLAG_RAID10) 5440 factor = c->chunk_item->num_stripes / c->chunk_item->sub_stripes; 5441 else if (c->chunk_item->type & BLOCK_FLAG_RAID5) 5442 factor = c->chunk_item->num_stripes - 1; 5443 else if (c->chunk_item->type & BLOCK_FLAG_RAID6) 5444 factor = c->chunk_item->num_stripes - 2; 5445 else // SINGLE, DUPLICATE, RAID1, RAID1C3, RAID1C4 5446 factor = 1; 5447 5448 // do TRIM 5449 if (Vcb->trim && !Vcb->options.no_trim) { 5450 uint64_t len = c->chunk_item->size / factor; 5451 5452 for (i = 0; i < c->chunk_item->num_stripes; i++) { 5453 if (c->devices[i] && c->devices[i]->devobj && !c->devices[i]->readonly && c->devices[i]->trim) 5454 add_trim_entry_avoid_sb(Vcb, c->devices[i], cis[i].offset, len); 5455 } 5456 } 5457 5458 if (!c->cache) { 5459 Status = load_stored_free_space_cache(Vcb, c, true, Irp); 5460 5461 if (!NT_SUCCESS(Status) && Status != STATUS_NOT_FOUND) 5462 WARN("load_stored_free_space_cache returned %08lx\n", Status); 5463 } 5464 5465 // remove free space cache 5466 if (c->cache) { 5467 c->cache->deleted = true; 5468 5469 Status = excise_extents(Vcb, c->cache, 0, c->cache->inode_item.st_size, Irp, rollback); 5470 if (!NT_SUCCESS(Status)) { 5471 ERR("excise_extents returned %08lx\n", Status); 5472 return Status; 5473 } 5474 5475 Status = flush_fcb(c->cache, true, batchlist, Irp); 5476 5477 free_fcb(c->cache); 5478 5479 if (c->cache->refcount == 0) 5480 reap_fcb(c->cache); 5481 5482 if (!NT_SUCCESS(Status)) { 5483 ERR("flush_fcb returned %08lx\n", Status); 5484 return Status; 5485 } 5486 5487 searchkey.obj_id = FREE_SPACE_CACHE_ID; 5488 searchkey.obj_type = 0; 5489 searchkey.offset = c->offset; 5490 5491 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 5492 if (!NT_SUCCESS(Status)) { 5493 ERR("error - find_item returned %08lx\n", Status); 5494 return Status; 5495 } 5496 5497 if (!keycmp(tp.item->key, searchkey)) { 5498 Status = delete_tree_item(Vcb, &tp); 5499 if (!NT_SUCCESS(Status)) { 5500 ERR("delete_tree_item returned %08lx\n", Status); 5501 return Status; 5502 } 5503 } 5504 } 5505 5506 if (Vcb->space_root) { 5507 Status = insert_tree_item_batch(batchlist, Vcb, Vcb->space_root, c->offset, TYPE_FREE_SPACE_INFO, c->chunk_item->size, 5508 NULL, 0, Batch_DeleteFreeSpace); 5509 if (!NT_SUCCESS(Status)) { 5510 ERR("insert_tree_item_batch returned %08lx\n", Status); 5511 return Status; 5512 } 5513 } 5514 5515 for (i = 0; i < c->chunk_item->num_stripes; i++) { 5516 if (!c->created) { 5517 // remove DEV_EXTENTs from tree 4 5518 searchkey.obj_id = cis[i].dev_id; 5519 searchkey.obj_type = TYPE_DEV_EXTENT; 5520 searchkey.offset = cis[i].offset; 5521 5522 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp); 5523 if (!NT_SUCCESS(Status)) { 5524 ERR("error - find_item returned %08lx\n", Status); 5525 return Status; 5526 } 5527 5528 if (!keycmp(tp.item->key, searchkey)) { 5529 Status = delete_tree_item(Vcb, &tp); 5530 if (!NT_SUCCESS(Status)) { 5531 ERR("delete_tree_item returned %08lx\n", Status); 5532 return Status; 5533 } 5534 5535 if (tp.item->size >= sizeof(DEV_EXTENT)) { 5536 DEV_EXTENT* de = (DEV_EXTENT*)tp.item->data; 5537 5538 c->devices[i]->devitem.bytes_used -= de->length; 5539 5540 if (Vcb->balance.thread && Vcb->balance.shrinking && Vcb->balance.opts[0].devid == c->devices[i]->devitem.dev_id) { 5541 if (cis[i].offset < Vcb->balance.opts[0].drange_start && cis[i].offset + de->length > Vcb->balance.opts[0].drange_start) 5542 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, Vcb->balance.opts[0].drange_start - cis[i].offset, NULL, rollback); 5543 } else 5544 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, de->length, NULL, rollback); 5545 } 5546 } else 5547 WARN("could not find (%I64x,%x,%I64x) in dev tree\n", searchkey.obj_id, searchkey.obj_type, searchkey.offset); 5548 } else { 5549 uint64_t len = c->chunk_item->size / factor; 5550 5551 c->devices[i]->devitem.bytes_used -= len; 5552 5553 if (Vcb->balance.thread && Vcb->balance.shrinking && Vcb->balance.opts[0].devid == c->devices[i]->devitem.dev_id) { 5554 if (cis[i].offset < Vcb->balance.opts[0].drange_start && cis[i].offset + len > Vcb->balance.opts[0].drange_start) 5555 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, Vcb->balance.opts[0].drange_start - cis[i].offset, NULL, rollback); 5556 } else 5557 space_list_add2(&c->devices[i]->space, NULL, cis[i].offset, len, NULL, rollback); 5558 } 5559 } 5560 5561 // modify DEV_ITEMs in chunk tree 5562 for (i = 0; i < c->chunk_item->num_stripes; i++) { 5563 if (c->devices[i]) { 5564 uint64_t j; 5565 DEV_ITEM* di; 5566 5567 searchkey.obj_id = 1; 5568 searchkey.obj_type = TYPE_DEV_ITEM; 5569 searchkey.offset = c->devices[i]->devitem.dev_id; 5570 5571 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp); 5572 if (!NT_SUCCESS(Status)) { 5573 ERR("error - find_item returned %08lx\n", Status); 5574 return Status; 5575 } 5576 5577 if (!keycmp(tp.item->key, searchkey)) { 5578 Status = delete_tree_item(Vcb, &tp); 5579 if (!NT_SUCCESS(Status)) { 5580 ERR("delete_tree_item returned %08lx\n", Status); 5581 return Status; 5582 } 5583 5584 di = ExAllocatePoolWithTag(PagedPool, sizeof(DEV_ITEM), ALLOC_TAG); 5585 if (!di) { 5586 ERR("out of memory\n"); 5587 return STATUS_INSUFFICIENT_RESOURCES; 5588 } 5589 5590 RtlCopyMemory(di, &c->devices[i]->devitem, sizeof(DEV_ITEM)); 5591 5592 Status = insert_tree_item(Vcb, Vcb->chunk_root, 1, TYPE_DEV_ITEM, c->devices[i]->devitem.dev_id, di, sizeof(DEV_ITEM), NULL, Irp); 5593 if (!NT_SUCCESS(Status)) { 5594 ERR("insert_tree_item returned %08lx\n", Status); 5595 return Status; 5596 } 5597 } 5598 5599 for (j = i + 1; j < c->chunk_item->num_stripes; j++) { 5600 if (c->devices[j] == c->devices[i]) 5601 c->devices[j] = NULL; 5602 } 5603 } 5604 } 5605 5606 if (!c->created) { 5607 // remove CHUNK_ITEM from chunk tree 5608 searchkey.obj_id = 0x100; 5609 searchkey.obj_type = TYPE_CHUNK_ITEM; 5610 searchkey.offset = c->offset; 5611 5612 Status = find_item(Vcb, Vcb->chunk_root, &tp, &searchkey, false, Irp); 5613 if (!NT_SUCCESS(Status)) { 5614 ERR("error - find_item returned %08lx\n", Status); 5615 return Status; 5616 } 5617 5618 if (!keycmp(tp.item->key, searchkey)) { 5619 Status = delete_tree_item(Vcb, &tp); 5620 5621 if (!NT_SUCCESS(Status)) { 5622 ERR("delete_tree_item returned %08lx\n", Status); 5623 return Status; 5624 } 5625 } else 5626 WARN("could not find CHUNK_ITEM for chunk %I64x\n", c->offset); 5627 5628 // remove BLOCK_GROUP_ITEM from extent tree 5629 searchkey.obj_id = c->offset; 5630 searchkey.obj_type = TYPE_BLOCK_GROUP_ITEM; 5631 searchkey.offset = 0xffffffffffffffff; 5632 5633 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 5634 if (!NT_SUCCESS(Status)) { 5635 ERR("error - find_item returned %08lx\n", Status); 5636 return Status; 5637 } 5638 5639 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 5640 Status = delete_tree_item(Vcb, &tp); 5641 5642 if (!NT_SUCCESS(Status)) { 5643 ERR("delete_tree_item returned %08lx\n", Status); 5644 return Status; 5645 } 5646 } else 5647 WARN("could not find BLOCK_GROUP_ITEM for chunk %I64x\n", c->offset); 5648 } 5649 5650 if (c->chunk_item->type & BLOCK_FLAG_SYSTEM) 5651 remove_from_bootstrap(Vcb, 0x100, TYPE_CHUNK_ITEM, c->offset); 5652 5653 RemoveEntryList(&c->list_entry); 5654 5655 // clear raid56 incompat flag if dropping last RAID5/6 chunk 5656 5657 if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) { 5658 LIST_ENTRY* le; 5659 bool clear_flag = true; 5660 5661 le = Vcb->chunks.Flink; 5662 while (le != &Vcb->chunks) { 5663 chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry); 5664 5665 if (c2->chunk_item->type & BLOCK_FLAG_RAID5 || c2->chunk_item->type & BLOCK_FLAG_RAID6) { 5666 clear_flag = false; 5667 break; 5668 } 5669 5670 le = le->Flink; 5671 } 5672 5673 if (clear_flag) 5674 Vcb->superblock.incompat_flags &= ~BTRFS_INCOMPAT_FLAGS_RAID56; 5675 } 5676 5677 // clear raid1c34 incompat flag if dropping last RAID5/6 chunk 5678 5679 if (c->chunk_item->type & BLOCK_FLAG_RAID1C3 || c->chunk_item->type & BLOCK_FLAG_RAID1C4) { 5680 LIST_ENTRY* le; 5681 bool clear_flag = true; 5682 5683 le = Vcb->chunks.Flink; 5684 while (le != &Vcb->chunks) { 5685 chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry); 5686 5687 if (c2->chunk_item->type & BLOCK_FLAG_RAID1C3 || c2->chunk_item->type & BLOCK_FLAG_RAID1C4) { 5688 clear_flag = false; 5689 break; 5690 } 5691 5692 le = le->Flink; 5693 } 5694 5695 if (clear_flag) 5696 Vcb->superblock.incompat_flags &= ~BTRFS_INCOMPAT_FLAGS_RAID1C34; 5697 } 5698 5699 #ifndef __REACTOS__ 5700 uint64_t phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused); 5701 #else 5702 phys_used = chunk_estimate_phys_size(Vcb, c, c->oldused); 5703 #endif // __REACTOS__ 5704 5705 if (phys_used < Vcb->superblock.bytes_used) 5706 Vcb->superblock.bytes_used -= phys_used; 5707 else 5708 Vcb->superblock.bytes_used = 0; 5709 5710 ExFreePool(c->chunk_item); 5711 ExFreePool(c->devices); 5712 5713 while (!IsListEmpty(&c->space)) { 5714 space* s = CONTAINING_RECORD(c->space.Flink, space, list_entry); 5715 5716 RemoveEntryList(&s->list_entry); 5717 ExFreePool(s); 5718 } 5719 5720 while (!IsListEmpty(&c->deleting)) { 5721 space* s = CONTAINING_RECORD(c->deleting.Flink, space, list_entry); 5722 5723 RemoveEntryList(&s->list_entry); 5724 ExFreePool(s); 5725 } 5726 5727 release_chunk_lock(c, Vcb); 5728 5729 ExDeleteResourceLite(&c->partial_stripes_lock); 5730 ExDeleteResourceLite(&c->range_locks_lock); 5731 ExDeleteResourceLite(&c->lock); 5732 ExDeleteResourceLite(&c->changed_extents_lock); 5733 5734 ExFreePool(c); 5735 5736 return STATUS_SUCCESS; 5737 } 5738 5739 static NTSTATUS partial_stripe_read(device_extension* Vcb, chunk* c, partial_stripe* ps, uint64_t startoff, uint16_t parity, ULONG offset, ULONG len) { 5740 NTSTATUS Status; 5741 ULONG sl = (ULONG)(c->chunk_item->stripe_length / Vcb->superblock.sector_size); 5742 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 5743 5744 while (len > 0) { 5745 ULONG readlen = min(offset + len, offset + (sl - (offset % sl))) - offset; 5746 uint16_t stripe; 5747 5748 stripe = (parity + (offset / sl) + 1) % c->chunk_item->num_stripes; 5749 5750 if (c->devices[stripe]->devobj) { 5751 Status = sync_read_phys(c->devices[stripe]->devobj, c->devices[stripe]->fileobj, cis[stripe].offset + startoff + ((offset % sl) * Vcb->superblock.sector_size), 5752 readlen * Vcb->superblock.sector_size, ps->data + (offset * Vcb->superblock.sector_size), false); 5753 if (!NT_SUCCESS(Status)) { 5754 ERR("sync_read_phys returned %08lx\n", Status); 5755 return Status; 5756 } 5757 } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) { 5758 uint16_t i; 5759 uint8_t* scratch; 5760 5761 scratch = ExAllocatePoolWithTag(NonPagedPool, readlen * Vcb->superblock.sector_size, ALLOC_TAG); 5762 if (!scratch) { 5763 ERR("out of memory\n"); 5764 return STATUS_INSUFFICIENT_RESOURCES; 5765 } 5766 5767 for (i = 0; i < c->chunk_item->num_stripes; i++) { 5768 if (i != stripe) { 5769 if (!c->devices[i]->devobj) { 5770 ExFreePool(scratch); 5771 return STATUS_UNEXPECTED_IO_ERROR; 5772 } 5773 5774 if (i == 0 || (stripe == 0 && i == 1)) { 5775 Status = sync_read_phys(c->devices[i]->devobj, c->devices[i]->fileobj, cis[i].offset + startoff + ((offset % sl) * Vcb->superblock.sector_size), 5776 readlen * Vcb->superblock.sector_size, ps->data + (offset * Vcb->superblock.sector_size), false); 5777 if (!NT_SUCCESS(Status)) { 5778 ERR("sync_read_phys returned %08lx\n", Status); 5779 ExFreePool(scratch); 5780 return Status; 5781 } 5782 } else { 5783 Status = sync_read_phys(c->devices[i]->devobj, c->devices[i]->fileobj, cis[i].offset + startoff + ((offset % sl) * Vcb->superblock.sector_size), 5784 readlen * Vcb->superblock.sector_size, scratch, false); 5785 if (!NT_SUCCESS(Status)) { 5786 ERR("sync_read_phys returned %08lx\n", Status); 5787 ExFreePool(scratch); 5788 return Status; 5789 } 5790 5791 do_xor(ps->data + (offset * Vcb->superblock.sector_size), scratch, readlen * Vcb->superblock.sector_size); 5792 } 5793 } 5794 } 5795 5796 ExFreePool(scratch); 5797 } else { 5798 uint8_t* scratch; 5799 uint16_t k, i, logstripe, error_stripe, num_errors = 0; 5800 5801 scratch = ExAllocatePoolWithTag(NonPagedPool, (c->chunk_item->num_stripes + 2) * readlen * Vcb->superblock.sector_size, ALLOC_TAG); 5802 if (!scratch) { 5803 ERR("out of memory\n"); 5804 return STATUS_INSUFFICIENT_RESOURCES; 5805 } 5806 5807 i = (parity + 1) % c->chunk_item->num_stripes; 5808 for (k = 0; k < c->chunk_item->num_stripes; k++) { 5809 if (i != stripe) { 5810 if (c->devices[i]->devobj) { 5811 Status = sync_read_phys(c->devices[i]->devobj, c->devices[i]->fileobj, cis[i].offset + startoff + ((offset % sl) * Vcb->superblock.sector_size), 5812 readlen * Vcb->superblock.sector_size, scratch + (k * readlen * Vcb->superblock.sector_size), false); 5813 if (!NT_SUCCESS(Status)) { 5814 ERR("sync_read_phys returned %08lx\n", Status); 5815 num_errors++; 5816 error_stripe = k; 5817 } 5818 } else { 5819 num_errors++; 5820 error_stripe = k; 5821 } 5822 5823 if (num_errors > 1) { 5824 ExFreePool(scratch); 5825 return STATUS_UNEXPECTED_IO_ERROR; 5826 } 5827 } else 5828 logstripe = k; 5829 5830 i = (i + 1) % c->chunk_item->num_stripes; 5831 } 5832 5833 if (num_errors == 0 || error_stripe == c->chunk_item->num_stripes - 1) { 5834 for (k = 0; k < c->chunk_item->num_stripes - 1; k++) { 5835 if (k != logstripe) { 5836 if (k == 0 || (k == 1 && logstripe == 0)) { 5837 RtlCopyMemory(ps->data + (offset * Vcb->superblock.sector_size), scratch + (k * readlen * Vcb->superblock.sector_size), 5838 readlen * Vcb->superblock.sector_size); 5839 } else { 5840 do_xor(ps->data + (offset * Vcb->superblock.sector_size), scratch + (k * readlen * Vcb->superblock.sector_size), 5841 readlen * Vcb->superblock.sector_size); 5842 } 5843 } 5844 } 5845 } else { 5846 raid6_recover2(scratch, c->chunk_item->num_stripes, readlen * Vcb->superblock.sector_size, logstripe, 5847 error_stripe, scratch + (c->chunk_item->num_stripes * readlen * Vcb->superblock.sector_size)); 5848 5849 RtlCopyMemory(ps->data + (offset * Vcb->superblock.sector_size), scratch + (c->chunk_item->num_stripes * readlen * Vcb->superblock.sector_size), 5850 readlen * Vcb->superblock.sector_size); 5851 } 5852 5853 ExFreePool(scratch); 5854 } 5855 5856 offset += readlen; 5857 len -= readlen; 5858 } 5859 5860 return STATUS_SUCCESS; 5861 } 5862 5863 NTSTATUS flush_partial_stripe(device_extension* Vcb, chunk* c, partial_stripe* ps) { 5864 NTSTATUS Status; 5865 uint16_t parity2, stripe, startoffstripe; 5866 uint8_t* data; 5867 uint64_t startoff; 5868 ULONG runlength, index, last1; 5869 CHUNK_ITEM_STRIPE* cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 5870 LIST_ENTRY* le; 5871 uint16_t k, num_data_stripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2); 5872 uint64_t ps_length = num_data_stripes * c->chunk_item->stripe_length; 5873 ULONG stripe_length = (ULONG)c->chunk_item->stripe_length; 5874 5875 // FIXME - do writes asynchronously? 5876 5877 get_raid0_offset(ps->address - c->offset, stripe_length, num_data_stripes, &startoff, &startoffstripe); 5878 5879 parity2 = (((ps->address - c->offset) / ps_length) + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes; 5880 5881 // read data (or reconstruct if degraded) 5882 5883 runlength = RtlFindFirstRunClear(&ps->bmp, &index); 5884 last1 = 0; 5885 5886 while (runlength != 0) { 5887 if (index >= ps->bmplen) 5888 break; 5889 5890 if (index + runlength >= ps->bmplen) { 5891 runlength = ps->bmplen - index; 5892 5893 if (runlength == 0) 5894 break; 5895 } 5896 5897 if (index > last1) { 5898 Status = partial_stripe_read(Vcb, c, ps, startoff, parity2, last1, index - last1); 5899 if (!NT_SUCCESS(Status)) { 5900 ERR("partial_stripe_read returned %08lx\n", Status); 5901 return Status; 5902 } 5903 } 5904 5905 last1 = index + runlength; 5906 5907 runlength = RtlFindNextForwardRunClear(&ps->bmp, index + runlength, &index); 5908 } 5909 5910 if (last1 < ps_length / Vcb->superblock.sector_size) { 5911 Status = partial_stripe_read(Vcb, c, ps, startoff, parity2, last1, (ULONG)((ps_length / Vcb->superblock.sector_size) - last1)); 5912 if (!NT_SUCCESS(Status)) { 5913 ERR("partial_stripe_read returned %08lx\n", Status); 5914 return Status; 5915 } 5916 } 5917 5918 // set unallocated data to 0 5919 le = c->space.Flink; 5920 while (le != &c->space) { 5921 space* s = CONTAINING_RECORD(le, space, list_entry); 5922 5923 if (s->address + s->size > ps->address && s->address < ps->address + ps_length) { 5924 uint64_t start = max(ps->address, s->address); 5925 uint64_t end = min(ps->address + ps_length, s->address + s->size); 5926 5927 RtlZeroMemory(ps->data + start - ps->address, (ULONG)(end - start)); 5928 } else if (s->address >= ps->address + ps_length) 5929 break; 5930 5931 le = le->Flink; 5932 } 5933 5934 le = c->deleting.Flink; 5935 while (le != &c->deleting) { 5936 space* s = CONTAINING_RECORD(le, space, list_entry); 5937 5938 if (s->address + s->size > ps->address && s->address < ps->address + ps_length) { 5939 uint64_t start = max(ps->address, s->address); 5940 uint64_t end = min(ps->address + ps_length, s->address + s->size); 5941 5942 RtlZeroMemory(ps->data + start - ps->address, (ULONG)(end - start)); 5943 } else if (s->address >= ps->address + ps_length) 5944 break; 5945 5946 le = le->Flink; 5947 } 5948 5949 stripe = (parity2 + 1) % c->chunk_item->num_stripes; 5950 5951 data = ps->data; 5952 for (k = 0; k < num_data_stripes; k++) { 5953 if (c->devices[stripe]->devobj) { 5954 Status = write_data_phys(c->devices[stripe]->devobj, c->devices[stripe]->fileobj, cis[stripe].offset + startoff, data, stripe_length); 5955 if (!NT_SUCCESS(Status)) { 5956 ERR("write_data_phys returned %08lx\n", Status); 5957 return Status; 5958 } 5959 } 5960 5961 data += stripe_length; 5962 stripe = (stripe + 1) % c->chunk_item->num_stripes; 5963 } 5964 5965 // write parity 5966 if (c->chunk_item->type & BLOCK_FLAG_RAID5) { 5967 if (c->devices[parity2]->devobj) { 5968 uint16_t i; 5969 5970 for (i = 1; i < c->chunk_item->num_stripes - 1; i++) { 5971 do_xor(ps->data, ps->data + (i * stripe_length), stripe_length); 5972 } 5973 5974 Status = write_data_phys(c->devices[parity2]->devobj, c->devices[parity2]->fileobj, cis[parity2].offset + startoff, ps->data, stripe_length); 5975 if (!NT_SUCCESS(Status)) { 5976 ERR("write_data_phys returned %08lx\n", Status); 5977 return Status; 5978 } 5979 } 5980 } else { 5981 uint16_t parity1 = (parity2 + c->chunk_item->num_stripes - 1) % c->chunk_item->num_stripes; 5982 5983 if (c->devices[parity1]->devobj || c->devices[parity2]->devobj) { 5984 uint8_t* scratch; 5985 uint16_t i; 5986 5987 scratch = ExAllocatePoolWithTag(NonPagedPool, stripe_length * 2, ALLOC_TAG); 5988 if (!scratch) { 5989 ERR("out of memory\n"); 5990 return STATUS_INSUFFICIENT_RESOURCES; 5991 } 5992 5993 i = c->chunk_item->num_stripes - 3; 5994 5995 while (true) { 5996 if (i == c->chunk_item->num_stripes - 3) { 5997 RtlCopyMemory(scratch, ps->data + (i * stripe_length), stripe_length); 5998 RtlCopyMemory(scratch + stripe_length, ps->data + (i * stripe_length), stripe_length); 5999 } else { 6000 do_xor(scratch, ps->data + (i * stripe_length), stripe_length); 6001 6002 galois_double(scratch + stripe_length, stripe_length); 6003 do_xor(scratch + stripe_length, ps->data + (i * stripe_length), stripe_length); 6004 } 6005 6006 if (i == 0) 6007 break; 6008 6009 i--; 6010 } 6011 6012 if (c->devices[parity1]->devobj) { 6013 Status = write_data_phys(c->devices[parity1]->devobj, c->devices[parity1]->fileobj, cis[parity1].offset + startoff, scratch, stripe_length); 6014 if (!NT_SUCCESS(Status)) { 6015 ERR("write_data_phys returned %08lx\n", Status); 6016 ExFreePool(scratch); 6017 return Status; 6018 } 6019 } 6020 6021 if (c->devices[parity2]->devobj) { 6022 Status = write_data_phys(c->devices[parity2]->devobj, c->devices[parity2]->fileobj, cis[parity2].offset + startoff, 6023 scratch + stripe_length, stripe_length); 6024 if (!NT_SUCCESS(Status)) { 6025 ERR("write_data_phys returned %08lx\n", Status); 6026 ExFreePool(scratch); 6027 return Status; 6028 } 6029 } 6030 6031 ExFreePool(scratch); 6032 } 6033 } 6034 6035 return STATUS_SUCCESS; 6036 } 6037 6038 static NTSTATUS update_chunks(device_extension* Vcb, LIST_ENTRY* batchlist, PIRP Irp, LIST_ENTRY* rollback) { 6039 LIST_ENTRY *le, *le2; 6040 NTSTATUS Status; 6041 uint64_t used_minus_cache; 6042 6043 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, true); 6044 6045 // FIXME - do tree chunks before data chunks 6046 6047 le = Vcb->chunks.Flink; 6048 while (le != &Vcb->chunks) { 6049 chunk* c = CONTAINING_RECORD(le, chunk, list_entry); 6050 6051 le2 = le->Flink; 6052 6053 if (c->changed) { 6054 acquire_chunk_lock(c, Vcb); 6055 6056 // flush partial stripes 6057 if (!Vcb->readonly && (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6)) { 6058 ExAcquireResourceExclusiveLite(&c->partial_stripes_lock, true); 6059 6060 while (!IsListEmpty(&c->partial_stripes)) { 6061 partial_stripe* ps = CONTAINING_RECORD(RemoveHeadList(&c->partial_stripes), partial_stripe, list_entry); 6062 6063 Status = flush_partial_stripe(Vcb, c, ps); 6064 6065 if (ps->bmparr) 6066 ExFreePool(ps->bmparr); 6067 6068 ExFreePool(ps); 6069 6070 if (!NT_SUCCESS(Status)) { 6071 ERR("flush_partial_stripe returned %08lx\n", Status); 6072 ExReleaseResourceLite(&c->partial_stripes_lock); 6073 release_chunk_lock(c, Vcb); 6074 ExReleaseResourceLite(&Vcb->chunk_lock); 6075 return Status; 6076 } 6077 } 6078 6079 ExReleaseResourceLite(&c->partial_stripes_lock); 6080 } 6081 6082 if (c->list_entry_balance.Flink) { 6083 release_chunk_lock(c, Vcb); 6084 le = le2; 6085 continue; 6086 } 6087 6088 if (c->space_changed || c->created) { 6089 bool created = c->created; 6090 6091 used_minus_cache = c->used; 6092 6093 // subtract self-hosted cache 6094 if (used_minus_cache > 0 && c->chunk_item->type & BLOCK_FLAG_DATA && c->cache && c->cache->inode_item.st_size == c->used) { 6095 LIST_ENTRY* le3; 6096 6097 le3 = c->cache->extents.Flink; 6098 while (le3 != &c->cache->extents) { 6099 extent* ext = CONTAINING_RECORD(le3, extent, list_entry); 6100 EXTENT_DATA* ed = &ext->extent_data; 6101 6102 if (!ext->ignore) { 6103 if (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) { 6104 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 6105 6106 if (ed2->size != 0 && ed2->address >= c->offset && ed2->address + ed2->size <= c->offset + c->chunk_item->size) 6107 used_minus_cache -= ed2->size; 6108 } 6109 } 6110 6111 le3 = le3->Flink; 6112 } 6113 } 6114 6115 if (used_minus_cache == 0) { 6116 Status = drop_chunk(Vcb, c, batchlist, Irp, rollback); 6117 if (!NT_SUCCESS(Status)) { 6118 ERR("drop_chunk returned %08lx\n", Status); 6119 release_chunk_lock(c, Vcb); 6120 ExReleaseResourceLite(&Vcb->chunk_lock); 6121 return Status; 6122 } 6123 6124 // c is now freed, so avoid releasing non-existent lock 6125 le = le2; 6126 continue; 6127 } else if (c->created) { 6128 Status = create_chunk(Vcb, c, Irp); 6129 if (!NT_SUCCESS(Status)) { 6130 ERR("create_chunk returned %08lx\n", Status); 6131 release_chunk_lock(c, Vcb); 6132 ExReleaseResourceLite(&Vcb->chunk_lock); 6133 return Status; 6134 } 6135 } 6136 6137 if (used_minus_cache > 0 || created) 6138 release_chunk_lock(c, Vcb); 6139 } else 6140 release_chunk_lock(c, Vcb); 6141 } 6142 6143 le = le2; 6144 } 6145 6146 ExReleaseResourceLite(&Vcb->chunk_lock); 6147 6148 return STATUS_SUCCESS; 6149 } 6150 6151 static NTSTATUS delete_root_ref(device_extension* Vcb, uint64_t subvolid, uint64_t parsubvolid, uint64_t parinode, PANSI_STRING utf8, PIRP Irp) { 6152 KEY searchkey; 6153 traverse_ptr tp; 6154 NTSTATUS Status; 6155 6156 searchkey.obj_id = parsubvolid; 6157 searchkey.obj_type = TYPE_ROOT_REF; 6158 searchkey.offset = subvolid; 6159 6160 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6161 if (!NT_SUCCESS(Status)) { 6162 ERR("error - find_item returned %08lx\n", Status); 6163 return Status; 6164 } 6165 6166 if (!keycmp(searchkey, tp.item->key)) { 6167 if (tp.item->size < sizeof(ROOT_REF)) { 6168 ERR("(%I64x,%x,%I64x) was %u bytes, expected at least %Iu\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(ROOT_REF)); 6169 return STATUS_INTERNAL_ERROR; 6170 } else { 6171 ROOT_REF* rr; 6172 ULONG len; 6173 6174 rr = (ROOT_REF*)tp.item->data; 6175 len = tp.item->size; 6176 6177 do { 6178 uint16_t itemlen; 6179 6180 if (len < sizeof(ROOT_REF) || len < offsetof(ROOT_REF, name[0]) + rr->n) { 6181 ERR("(%I64x,%x,%I64x) was truncated\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 6182 break; 6183 } 6184 6185 itemlen = (uint16_t)offsetof(ROOT_REF, name[0]) + rr->n; 6186 6187 if (rr->dir == parinode && rr->n == utf8->Length && RtlCompareMemory(rr->name, utf8->Buffer, rr->n) == rr->n) { 6188 uint16_t newlen = tp.item->size - itemlen; 6189 6190 Status = delete_tree_item(Vcb, &tp); 6191 if (!NT_SUCCESS(Status)) { 6192 ERR("delete_tree_item returned %08lx\n", Status); 6193 return Status; 6194 } 6195 6196 if (newlen == 0) { 6197 TRACE("deleting (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 6198 } else { 6199 uint8_t *newrr = ExAllocatePoolWithTag(PagedPool, newlen, ALLOC_TAG), *rroff; 6200 6201 if (!newrr) { 6202 ERR("out of memory\n"); 6203 return STATUS_INSUFFICIENT_RESOURCES; 6204 } 6205 6206 TRACE("modifying (%I64x,%x,%I64x)\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); 6207 6208 if ((uint8_t*)rr > tp.item->data) { 6209 RtlCopyMemory(newrr, tp.item->data, (uint8_t*)rr - tp.item->data); 6210 rroff = newrr + ((uint8_t*)rr - tp.item->data); 6211 } else { 6212 rroff = newrr; 6213 } 6214 6215 if ((uint8_t*)&rr->name[rr->n] < tp.item->data + tp.item->size) 6216 RtlCopyMemory(rroff, &rr->name[rr->n], tp.item->size - ((uint8_t*)&rr->name[rr->n] - tp.item->data)); 6217 6218 Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, newrr, newlen, NULL, Irp); 6219 if (!NT_SUCCESS(Status)) { 6220 ERR("insert_tree_item returned %08lx\n", Status); 6221 ExFreePool(newrr); 6222 return Status; 6223 } 6224 } 6225 6226 break; 6227 } 6228 6229 if (len > itemlen) { 6230 len -= itemlen; 6231 rr = (ROOT_REF*)&rr->name[rr->n]; 6232 } else 6233 break; 6234 } while (len > 0); 6235 } 6236 } else { 6237 WARN("could not find ROOT_REF entry for subvol %I64x in %I64x\n", searchkey.offset, searchkey.obj_id); 6238 return STATUS_NOT_FOUND; 6239 } 6240 6241 return STATUS_SUCCESS; 6242 } 6243 6244 #ifdef _MSC_VER 6245 #pragma warning(push) 6246 #pragma warning(suppress: 28194) 6247 #endif 6248 static NTSTATUS add_root_ref(_In_ device_extension* Vcb, _In_ uint64_t subvolid, _In_ uint64_t parsubvolid, _In_ __drv_aliasesMem ROOT_REF* rr, _In_opt_ PIRP Irp) { 6249 KEY searchkey; 6250 traverse_ptr tp; 6251 NTSTATUS Status; 6252 6253 searchkey.obj_id = parsubvolid; 6254 searchkey.obj_type = TYPE_ROOT_REF; 6255 searchkey.offset = subvolid; 6256 6257 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6258 if (!NT_SUCCESS(Status)) { 6259 ERR("error - find_item returned %08lx\n", Status); 6260 return Status; 6261 } 6262 6263 if (!keycmp(searchkey, tp.item->key)) { 6264 uint16_t rrsize = tp.item->size + (uint16_t)offsetof(ROOT_REF, name[0]) + rr->n; 6265 uint8_t* rr2; 6266 6267 rr2 = ExAllocatePoolWithTag(PagedPool, rrsize, ALLOC_TAG); 6268 if (!rr2) { 6269 ERR("out of memory\n"); 6270 return STATUS_INSUFFICIENT_RESOURCES; 6271 } 6272 6273 if (tp.item->size > 0) 6274 RtlCopyMemory(rr2, tp.item->data, tp.item->size); 6275 6276 RtlCopyMemory(rr2 + tp.item->size, rr, offsetof(ROOT_REF, name[0]) + rr->n); 6277 ExFreePool(rr); 6278 6279 Status = delete_tree_item(Vcb, &tp); 6280 if (!NT_SUCCESS(Status)) { 6281 ERR("delete_tree_item returned %08lx\n", Status); 6282 ExFreePool(rr2); 6283 return Status; 6284 } 6285 6286 Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr2, rrsize, NULL, Irp); 6287 if (!NT_SUCCESS(Status)) { 6288 ERR("insert_tree_item returned %08lx\n", Status); 6289 ExFreePool(rr2); 6290 return Status; 6291 } 6292 } else { 6293 Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, rr, (uint16_t)offsetof(ROOT_REF, name[0]) + rr->n, NULL, Irp); 6294 if (!NT_SUCCESS(Status)) { 6295 ERR("insert_tree_item returned %08lx\n", Status); 6296 ExFreePool(rr); 6297 return Status; 6298 } 6299 } 6300 6301 return STATUS_SUCCESS; 6302 } 6303 #ifdef _MSC_VER 6304 #pragma warning(pop) 6305 #endif 6306 6307 static NTSTATUS update_root_backref(device_extension* Vcb, uint64_t subvolid, uint64_t parsubvolid, PIRP Irp) { 6308 KEY searchkey; 6309 traverse_ptr tp; 6310 uint8_t* data; 6311 uint16_t datalen; 6312 NTSTATUS Status; 6313 6314 searchkey.obj_id = parsubvolid; 6315 searchkey.obj_type = TYPE_ROOT_REF; 6316 searchkey.offset = subvolid; 6317 6318 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6319 if (!NT_SUCCESS(Status)) { 6320 ERR("error - find_item returned %08lx\n", Status); 6321 return Status; 6322 } 6323 6324 if (!keycmp(tp.item->key, searchkey) && tp.item->size > 0) { 6325 datalen = tp.item->size; 6326 6327 data = ExAllocatePoolWithTag(PagedPool, datalen, ALLOC_TAG); 6328 if (!data) { 6329 ERR("out of memory\n"); 6330 return STATUS_INSUFFICIENT_RESOURCES; 6331 } 6332 6333 RtlCopyMemory(data, tp.item->data, datalen); 6334 } else { 6335 datalen = 0; 6336 data = NULL; 6337 } 6338 6339 searchkey.obj_id = subvolid; 6340 searchkey.obj_type = TYPE_ROOT_BACKREF; 6341 searchkey.offset = parsubvolid; 6342 6343 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6344 if (!NT_SUCCESS(Status)) { 6345 ERR("error - find_item returned %08lx\n", Status); 6346 6347 if (datalen > 0) 6348 ExFreePool(data); 6349 6350 return Status; 6351 } 6352 6353 if (!keycmp(tp.item->key, searchkey)) { 6354 Status = delete_tree_item(Vcb, &tp); 6355 if (!NT_SUCCESS(Status)) { 6356 ERR("delete_tree_item returned %08lx\n", Status); 6357 6358 if (datalen > 0) 6359 ExFreePool(data); 6360 6361 return Status; 6362 } 6363 } 6364 6365 if (datalen > 0) { 6366 Status = insert_tree_item(Vcb, Vcb->root_root, subvolid, TYPE_ROOT_BACKREF, parsubvolid, data, datalen, NULL, Irp); 6367 if (!NT_SUCCESS(Status)) { 6368 ERR("insert_tree_item returned %08lx\n", Status); 6369 ExFreePool(data); 6370 return Status; 6371 } 6372 } 6373 6374 return STATUS_SUCCESS; 6375 } 6376 6377 static NTSTATUS add_root_item_to_cache(device_extension* Vcb, uint64_t root, PIRP Irp) { 6378 KEY searchkey; 6379 traverse_ptr tp; 6380 NTSTATUS Status; 6381 6382 searchkey.obj_id = root; 6383 searchkey.obj_type = TYPE_ROOT_ITEM; 6384 searchkey.offset = 0xffffffffffffffff; 6385 6386 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6387 if (!NT_SUCCESS(Status)) { 6388 ERR("error - find_item returned %08lx\n", Status); 6389 return Status; 6390 } 6391 6392 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 6393 ERR("could not find ROOT_ITEM for tree %I64x\n", searchkey.obj_id); 6394 return STATUS_INTERNAL_ERROR; 6395 } 6396 6397 if (tp.item->size < sizeof(ROOT_ITEM)) { // if not full length, create new entry with new bits zeroed 6398 ROOT_ITEM* ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG); 6399 if (!ri) { 6400 ERR("out of memory\n"); 6401 return STATUS_INSUFFICIENT_RESOURCES; 6402 } 6403 6404 if (tp.item->size > 0) 6405 RtlCopyMemory(ri, tp.item->data, tp.item->size); 6406 6407 RtlZeroMemory(((uint8_t*)ri) + tp.item->size, sizeof(ROOT_ITEM) - tp.item->size); 6408 6409 Status = delete_tree_item(Vcb, &tp); 6410 if (!NT_SUCCESS(Status)) { 6411 ERR("delete_tree_item returned %08lx\n", Status); 6412 ExFreePool(ri); 6413 return Status; 6414 } 6415 6416 Status = insert_tree_item(Vcb, Vcb->root_root, searchkey.obj_id, searchkey.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp); 6417 if (!NT_SUCCESS(Status)) { 6418 ERR("insert_tree_item returned %08lx\n", Status); 6419 ExFreePool(ri); 6420 return Status; 6421 } 6422 } else { 6423 tp.tree->write = true; 6424 } 6425 6426 return STATUS_SUCCESS; 6427 } 6428 6429 static NTSTATUS flush_fileref(file_ref* fileref, LIST_ENTRY* batchlist, PIRP Irp) { 6430 NTSTATUS Status; 6431 6432 // if fileref created and then immediately deleted, do nothing 6433 if (fileref->created && fileref->deleted) { 6434 fileref->dirty = false; 6435 return STATUS_SUCCESS; 6436 } 6437 6438 if (fileref->fcb->ads) { 6439 fileref->dirty = false; 6440 return STATUS_SUCCESS; 6441 } 6442 6443 if (fileref->created) { 6444 uint16_t disize; 6445 DIR_ITEM *di, *di2; 6446 uint32_t crc32; 6447 6448 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6449 6450 disize = (uint16_t)(offsetof(DIR_ITEM, name[0]) + fileref->dc->utf8.Length); 6451 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); 6452 if (!di) { 6453 ERR("out of memory\n"); 6454 return STATUS_INSUFFICIENT_RESOURCES; 6455 } 6456 6457 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { 6458 di->key.obj_id = fileref->fcb->inode; 6459 di->key.obj_type = TYPE_INODE_ITEM; 6460 di->key.offset = 0; 6461 } else { // subvolume 6462 di->key.obj_id = fileref->fcb->subvol->id; 6463 di->key.obj_type = TYPE_ROOT_ITEM; 6464 di->key.offset = 0xffffffffffffffff; 6465 } 6466 6467 di->transid = fileref->fcb->Vcb->superblock.generation; 6468 di->m = 0; 6469 di->n = (uint16_t)fileref->dc->utf8.Length; 6470 di->type = fileref->fcb->type; 6471 RtlCopyMemory(di->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6472 6473 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); 6474 if (!di2) { 6475 ERR("out of memory\n"); 6476 return STATUS_INSUFFICIENT_RESOURCES; 6477 } 6478 6479 RtlCopyMemory(di2, di, disize); 6480 6481 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, 6482 fileref->dc->index, di, disize, Batch_Insert); 6483 if (!NT_SUCCESS(Status)) { 6484 ERR("insert_tree_item_batch returned %08lx\n", Status); 6485 return Status; 6486 } 6487 6488 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32, 6489 di2, disize, Batch_DirItem); 6490 if (!NT_SUCCESS(Status)) { 6491 ERR("insert_tree_item_batch returned %08lx\n", Status); 6492 return Status; 6493 } 6494 6495 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { 6496 INODE_REF* ir; 6497 6498 ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->dc->utf8.Length, ALLOC_TAG); 6499 if (!ir) { 6500 ERR("out of memory\n"); 6501 return STATUS_INSUFFICIENT_RESOURCES; 6502 } 6503 6504 ir->index = fileref->dc->index; 6505 ir->n = fileref->dc->utf8.Length; 6506 RtlCopyMemory(ir->name, fileref->dc->utf8.Buffer, ir->n); 6507 6508 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode, 6509 ir, sizeof(INODE_REF) - 1 + ir->n, Batch_InodeRef); 6510 if (!NT_SUCCESS(Status)) { 6511 ERR("insert_tree_item_batch returned %08lx\n", Status); 6512 return Status; 6513 } 6514 } else if (fileref->fcb != fileref->fcb->Vcb->dummy_fcb) { 6515 ULONG rrlen; 6516 ROOT_REF* rr; 6517 6518 rrlen = sizeof(ROOT_REF) - 1 + fileref->dc->utf8.Length; 6519 6520 rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG); 6521 if (!rr) { 6522 ERR("out of memory\n"); 6523 return STATUS_INSUFFICIENT_RESOURCES; 6524 } 6525 6526 rr->dir = fileref->parent->fcb->inode; 6527 rr->index = fileref->dc->index; 6528 rr->n = fileref->dc->utf8.Length; 6529 RtlCopyMemory(rr->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6530 6531 Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp); 6532 if (!NT_SUCCESS(Status)) { 6533 ERR("add_root_ref returned %08lx\n", Status); 6534 return Status; 6535 } 6536 6537 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp); 6538 if (!NT_SUCCESS(Status)) { 6539 ERR("update_root_backref returned %08lx\n", Status); 6540 return Status; 6541 } 6542 } 6543 6544 fileref->created = false; 6545 } else if (fileref->deleted) { 6546 uint32_t crc32; 6547 ANSI_STRING* name; 6548 DIR_ITEM* di; 6549 6550 name = &fileref->oldutf8; 6551 6552 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)name->Buffer, name->Length); 6553 6554 di = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + name->Length, ALLOC_TAG); 6555 if (!di) { 6556 ERR("out of memory\n"); 6557 return STATUS_INSUFFICIENT_RESOURCES; 6558 } 6559 6560 di->m = 0; 6561 di->n = name->Length; 6562 RtlCopyMemory(di->name, name->Buffer, name->Length); 6563 6564 // delete DIR_ITEM (0x54) 6565 6566 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, 6567 crc32, di, sizeof(DIR_ITEM) - 1 + name->Length, Batch_DeleteDirItem); 6568 if (!NT_SUCCESS(Status)) { 6569 ERR("insert_tree_item_batch returned %08lx\n", Status); 6570 return Status; 6571 } 6572 6573 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { 6574 INODE_REF* ir; 6575 6576 // delete INODE_REF (0xc) 6577 6578 ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + name->Length, ALLOC_TAG); 6579 if (!ir) { 6580 ERR("out of memory\n"); 6581 return STATUS_INSUFFICIENT_RESOURCES; 6582 } 6583 6584 ir->index = fileref->oldindex; 6585 ir->n = name->Length; 6586 RtlCopyMemory(ir->name, name->Buffer, name->Length); 6587 6588 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, 6589 fileref->parent->fcb->inode, ir, sizeof(INODE_REF) - 1 + name->Length, Batch_DeleteInodeRef); 6590 if (!NT_SUCCESS(Status)) { 6591 ERR("insert_tree_item_batch returned %08lx\n", Status); 6592 return Status; 6593 } 6594 } else if (fileref->fcb != fileref->fcb->Vcb->dummy_fcb) { // subvolume 6595 Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, name, Irp); 6596 if (!NT_SUCCESS(Status)) { 6597 ERR("delete_root_ref returned %08lx\n", Status); 6598 return Status; 6599 } 6600 6601 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp); 6602 if (!NT_SUCCESS(Status)) { 6603 ERR("update_root_backref returned %08lx\n", Status); 6604 return Status; 6605 } 6606 } 6607 6608 // delete DIR_INDEX (0x60) 6609 6610 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, 6611 fileref->oldindex, NULL, 0, Batch_Delete); 6612 if (!NT_SUCCESS(Status)) { 6613 ERR("insert_tree_item_batch returned %08lx\n", Status); 6614 return Status; 6615 } 6616 6617 if (fileref->oldutf8.Buffer) { 6618 ExFreePool(fileref->oldutf8.Buffer); 6619 fileref->oldutf8.Buffer = NULL; 6620 } 6621 } else { // rename or change type 6622 PANSI_STRING oldutf8 = fileref->oldutf8.Buffer ? &fileref->oldutf8 : &fileref->dc->utf8; 6623 uint32_t crc32, oldcrc32; 6624 uint16_t disize; 6625 DIR_ITEM *olddi, *di, *di2; 6626 6627 crc32 = calc_crc32c(0xfffffffe, (uint8_t*)fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6628 6629 if (!fileref->oldutf8.Buffer) 6630 oldcrc32 = crc32; 6631 else 6632 oldcrc32 = calc_crc32c(0xfffffffe, (uint8_t*)fileref->oldutf8.Buffer, fileref->oldutf8.Length); 6633 6634 olddi = ExAllocatePoolWithTag(PagedPool, sizeof(DIR_ITEM) - 1 + oldutf8->Length, ALLOC_TAG); 6635 if (!olddi) { 6636 ERR("out of memory\n"); 6637 return STATUS_INSUFFICIENT_RESOURCES; 6638 } 6639 6640 olddi->m = 0; 6641 olddi->n = (uint16_t)oldutf8->Length; 6642 RtlCopyMemory(olddi->name, oldutf8->Buffer, oldutf8->Length); 6643 6644 // delete DIR_ITEM (0x54) 6645 6646 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, 6647 oldcrc32, olddi, sizeof(DIR_ITEM) - 1 + oldutf8->Length, Batch_DeleteDirItem); 6648 if (!NT_SUCCESS(Status)) { 6649 ERR("insert_tree_item_batch returned %08lx\n", Status); 6650 ExFreePool(olddi); 6651 return Status; 6652 } 6653 6654 // add DIR_ITEM (0x54) 6655 6656 disize = (uint16_t)(offsetof(DIR_ITEM, name[0]) + fileref->dc->utf8.Length); 6657 di = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); 6658 if (!di) { 6659 ERR("out of memory\n"); 6660 return STATUS_INSUFFICIENT_RESOURCES; 6661 } 6662 6663 di2 = ExAllocatePoolWithTag(PagedPool, disize, ALLOC_TAG); 6664 if (!di2) { 6665 ERR("out of memory\n"); 6666 ExFreePool(di); 6667 return STATUS_INSUFFICIENT_RESOURCES; 6668 } 6669 6670 if (fileref->dc) 6671 di->key = fileref->dc->key; 6672 else if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { 6673 di->key.obj_id = fileref->fcb->inode; 6674 di->key.obj_type = TYPE_INODE_ITEM; 6675 di->key.offset = 0; 6676 } else { // subvolume 6677 di->key.obj_id = fileref->fcb->subvol->id; 6678 di->key.obj_type = TYPE_ROOT_ITEM; 6679 di->key.offset = 0xffffffffffffffff; 6680 } 6681 6682 di->transid = fileref->fcb->Vcb->superblock.generation; 6683 di->m = 0; 6684 di->n = (uint16_t)fileref->dc->utf8.Length; 6685 di->type = fileref->fcb->type; 6686 RtlCopyMemory(di->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6687 6688 RtlCopyMemory(di2, di, disize); 6689 6690 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_ITEM, crc32, 6691 di, disize, Batch_DirItem); 6692 if (!NT_SUCCESS(Status)) { 6693 ERR("insert_tree_item_batch returned %08lx\n", Status); 6694 ExFreePool(di2); 6695 ExFreePool(di); 6696 return Status; 6697 } 6698 6699 if (fileref->parent->fcb->subvol == fileref->fcb->subvol) { 6700 INODE_REF *ir, *ir2; 6701 6702 // delete INODE_REF (0xc) 6703 6704 ir = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + oldutf8->Length, ALLOC_TAG); 6705 if (!ir) { 6706 ERR("out of memory\n"); 6707 ExFreePool(di2); 6708 return STATUS_INSUFFICIENT_RESOURCES; 6709 } 6710 6711 ir->index = fileref->dc->index; 6712 ir->n = oldutf8->Length; 6713 RtlCopyMemory(ir->name, oldutf8->Buffer, ir->n); 6714 6715 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode, 6716 ir, sizeof(INODE_REF) - 1 + ir->n, Batch_DeleteInodeRef); 6717 if (!NT_SUCCESS(Status)) { 6718 ERR("insert_tree_item_batch returned %08lx\n", Status); 6719 ExFreePool(ir); 6720 ExFreePool(di2); 6721 return Status; 6722 } 6723 6724 // add INODE_REF (0xc) 6725 6726 ir2 = ExAllocatePoolWithTag(PagedPool, sizeof(INODE_REF) - 1 + fileref->dc->utf8.Length, ALLOC_TAG); 6727 if (!ir2) { 6728 ERR("out of memory\n"); 6729 ExFreePool(di2); 6730 return STATUS_INSUFFICIENT_RESOURCES; 6731 } 6732 6733 ir2->index = fileref->dc->index; 6734 ir2->n = fileref->dc->utf8.Length; 6735 RtlCopyMemory(ir2->name, fileref->dc->utf8.Buffer, ir2->n); 6736 6737 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->fcb->subvol, fileref->fcb->inode, TYPE_INODE_REF, fileref->parent->fcb->inode, 6738 ir2, sizeof(INODE_REF) - 1 + ir2->n, Batch_InodeRef); 6739 if (!NT_SUCCESS(Status)) { 6740 ERR("insert_tree_item_batch returned %08lx\n", Status); 6741 ExFreePool(ir2); 6742 ExFreePool(di2); 6743 return Status; 6744 } 6745 } else if (fileref->fcb != fileref->fcb->Vcb->dummy_fcb) { // subvolume 6746 ULONG rrlen; 6747 ROOT_REF* rr; 6748 6749 Status = delete_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, fileref->parent->fcb->inode, oldutf8, Irp); 6750 if (!NT_SUCCESS(Status)) { 6751 ERR("delete_root_ref returned %08lx\n", Status); 6752 ExFreePool(di2); 6753 return Status; 6754 } 6755 6756 rrlen = sizeof(ROOT_REF) - 1 + fileref->dc->utf8.Length; 6757 6758 rr = ExAllocatePoolWithTag(PagedPool, rrlen, ALLOC_TAG); 6759 if (!rr) { 6760 ERR("out of memory\n"); 6761 ExFreePool(di2); 6762 return STATUS_INSUFFICIENT_RESOURCES; 6763 } 6764 6765 rr->dir = fileref->parent->fcb->inode; 6766 rr->index = fileref->dc->index; 6767 rr->n = fileref->dc->utf8.Length; 6768 RtlCopyMemory(rr->name, fileref->dc->utf8.Buffer, fileref->dc->utf8.Length); 6769 6770 Status = add_root_ref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, rr, Irp); 6771 if (!NT_SUCCESS(Status)) { 6772 ERR("add_root_ref returned %08lx\n", Status); 6773 ExFreePool(di2); 6774 return Status; 6775 } 6776 6777 Status = update_root_backref(fileref->fcb->Vcb, fileref->fcb->subvol->id, fileref->parent->fcb->subvol->id, Irp); 6778 if (!NT_SUCCESS(Status)) { 6779 ERR("update_root_backref returned %08lx\n", Status); 6780 ExFreePool(di2); 6781 return Status; 6782 } 6783 } 6784 6785 // delete DIR_INDEX (0x60) 6786 6787 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, 6788 fileref->dc->index, NULL, 0, Batch_Delete); 6789 if (!NT_SUCCESS(Status)) { 6790 ERR("insert_tree_item_batch returned %08lx\n", Status); 6791 ExFreePool(di2); 6792 return Status; 6793 } 6794 6795 // add DIR_INDEX (0x60) 6796 6797 Status = insert_tree_item_batch(batchlist, fileref->fcb->Vcb, fileref->parent->fcb->subvol, fileref->parent->fcb->inode, TYPE_DIR_INDEX, 6798 fileref->dc->index, di2, disize, Batch_Insert); 6799 if (!NT_SUCCESS(Status)) { 6800 ERR("insert_tree_item_batch returned %08lx\n", Status); 6801 ExFreePool(di2); 6802 return Status; 6803 } 6804 6805 if (fileref->oldutf8.Buffer) { 6806 ExFreePool(fileref->oldutf8.Buffer); 6807 fileref->oldutf8.Buffer = NULL; 6808 } 6809 } 6810 6811 fileref->dirty = false; 6812 6813 return STATUS_SUCCESS; 6814 } 6815 6816 static void flush_disk_caches(device_extension* Vcb) { 6817 LIST_ENTRY* le; 6818 ioctl_context context; 6819 ULONG num; 6820 #ifdef __REACTOS__ 6821 unsigned int i; 6822 #endif 6823 6824 context.left = 0; 6825 6826 le = Vcb->devices.Flink; 6827 6828 while (le != &Vcb->devices) { 6829 device* dev = CONTAINING_RECORD(le, device, list_entry); 6830 6831 if (dev->devobj && !dev->readonly && dev->can_flush) 6832 context.left++; 6833 6834 le = le->Flink; 6835 } 6836 6837 if (context.left == 0) 6838 return; 6839 6840 num = 0; 6841 6842 KeInitializeEvent(&context.Event, NotificationEvent, false); 6843 6844 context.stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(ioctl_context_stripe) * context.left, ALLOC_TAG); 6845 if (!context.stripes) { 6846 ERR("out of memory\n"); 6847 return; 6848 } 6849 6850 RtlZeroMemory(context.stripes, sizeof(ioctl_context_stripe) * context.left); 6851 6852 le = Vcb->devices.Flink; 6853 6854 while (le != &Vcb->devices) { 6855 device* dev = CONTAINING_RECORD(le, device, list_entry); 6856 6857 if (dev->devobj && !dev->readonly && dev->can_flush) { 6858 PIO_STACK_LOCATION IrpSp; 6859 ioctl_context_stripe* stripe = &context.stripes[num]; 6860 6861 RtlZeroMemory(&stripe->apte, sizeof(ATA_PASS_THROUGH_EX)); 6862 6863 stripe->apte.Length = sizeof(ATA_PASS_THROUGH_EX); 6864 stripe->apte.TimeOutValue = 5; 6865 stripe->apte.CurrentTaskFile[6] = IDE_COMMAND_FLUSH_CACHE; 6866 6867 stripe->Irp = IoAllocateIrp(dev->devobj->StackSize, false); 6868 6869 if (!stripe->Irp) { 6870 ERR("IoAllocateIrp failed\n"); 6871 goto nextdev; 6872 } 6873 6874 IrpSp = IoGetNextIrpStackLocation(stripe->Irp); 6875 IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL; 6876 IrpSp->FileObject = dev->fileobj; 6877 6878 IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_ATA_PASS_THROUGH; 6879 IrpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(ATA_PASS_THROUGH_EX); 6880 IrpSp->Parameters.DeviceIoControl.OutputBufferLength = sizeof(ATA_PASS_THROUGH_EX); 6881 6882 stripe->Irp->AssociatedIrp.SystemBuffer = &stripe->apte; 6883 stripe->Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION; 6884 stripe->Irp->UserBuffer = &stripe->apte; 6885 stripe->Irp->UserIosb = &stripe->iosb; 6886 6887 IoSetCompletionRoutine(stripe->Irp, ioctl_completion, &context, true, true, true); 6888 6889 IoCallDriver(dev->devobj, stripe->Irp); 6890 6891 nextdev: 6892 num++; 6893 } 6894 6895 le = le->Flink; 6896 } 6897 6898 KeWaitForSingleObject(&context.Event, Executive, KernelMode, false, NULL); 6899 6900 #ifndef __REACTOS__ 6901 for (unsigned int i = 0; i < num; i++) { 6902 #else 6903 for (i = 0; i < num; i++) { 6904 #endif 6905 if (context.stripes[i].Irp) 6906 IoFreeIrp(context.stripes[i].Irp); 6907 } 6908 6909 ExFreePool(context.stripes); 6910 } 6911 6912 static NTSTATUS flush_changed_dev_stats(device_extension* Vcb, device* dev, PIRP Irp) { 6913 NTSTATUS Status; 6914 KEY searchkey; 6915 traverse_ptr tp; 6916 uint16_t statslen; 6917 uint64_t* stats; 6918 6919 searchkey.obj_id = 0; 6920 searchkey.obj_type = TYPE_DEV_STATS; 6921 searchkey.offset = dev->devitem.dev_id; 6922 6923 Status = find_item(Vcb, Vcb->dev_root, &tp, &searchkey, false, Irp); 6924 if (!NT_SUCCESS(Status)) { 6925 ERR("find_item returned %08lx\n", Status); 6926 return Status; 6927 } 6928 6929 if (!keycmp(tp.item->key, searchkey)) { 6930 Status = delete_tree_item(Vcb, &tp); 6931 if (!NT_SUCCESS(Status)) { 6932 ERR("delete_tree_item returned %08lx\n", Status); 6933 return Status; 6934 } 6935 } 6936 6937 statslen = sizeof(uint64_t) * 5; 6938 stats = ExAllocatePoolWithTag(PagedPool, statslen, ALLOC_TAG); 6939 if (!stats) { 6940 ERR("out of memory\n"); 6941 return STATUS_INSUFFICIENT_RESOURCES; 6942 } 6943 6944 RtlCopyMemory(stats, dev->stats, statslen); 6945 6946 Status = insert_tree_item(Vcb, Vcb->dev_root, 0, TYPE_DEV_STATS, dev->devitem.dev_id, stats, statslen, NULL, Irp); 6947 if (!NT_SUCCESS(Status)) { 6948 ERR("insert_tree_item returned %08lx\n", Status); 6949 ExFreePool(stats); 6950 return Status; 6951 } 6952 6953 return STATUS_SUCCESS; 6954 } 6955 6956 static NTSTATUS flush_subvol(device_extension* Vcb, root* r, PIRP Irp) { 6957 NTSTATUS Status; 6958 6959 if (r != Vcb->root_root && r != Vcb->chunk_root) { 6960 KEY searchkey; 6961 traverse_ptr tp; 6962 ROOT_ITEM* ri; 6963 6964 searchkey.obj_id = r->id; 6965 searchkey.obj_type = TYPE_ROOT_ITEM; 6966 searchkey.offset = 0xffffffffffffffff; 6967 6968 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 6969 if (!NT_SUCCESS(Status)) { 6970 ERR("error - find_item returned %08lx\n", Status); 6971 return Status; 6972 } 6973 6974 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 6975 ERR("could not find ROOT_ITEM for tree %I64x\n", searchkey.obj_id); 6976 return STATUS_INTERNAL_ERROR; 6977 } 6978 6979 ri = ExAllocatePoolWithTag(PagedPool, sizeof(ROOT_ITEM), ALLOC_TAG); 6980 if (!ri) { 6981 ERR("out of memory\n"); 6982 return STATUS_INSUFFICIENT_RESOURCES; 6983 } 6984 6985 RtlCopyMemory(ri, &r->root_item, sizeof(ROOT_ITEM)); 6986 6987 Status = delete_tree_item(Vcb, &tp); 6988 if (!NT_SUCCESS(Status)) { 6989 ERR("delete_tree_item returned %08lx\n", Status); 6990 return Status; 6991 } 6992 6993 Status = insert_tree_item(Vcb, Vcb->root_root, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, ri, sizeof(ROOT_ITEM), NULL, Irp); 6994 if (!NT_SUCCESS(Status)) { 6995 ERR("insert_tree_item returned %08lx\n", Status); 6996 return Status; 6997 } 6998 } 6999 7000 if (r->received) { 7001 KEY searchkey; 7002 traverse_ptr tp; 7003 7004 if (!Vcb->uuid_root) { 7005 root* uuid_root; 7006 7007 TRACE("uuid root doesn't exist, creating it\n"); 7008 7009 Status = create_root(Vcb, BTRFS_ROOT_UUID, &uuid_root, false, 0, Irp); 7010 7011 if (!NT_SUCCESS(Status)) { 7012 ERR("create_root returned %08lx\n", Status); 7013 return Status; 7014 } 7015 7016 Vcb->uuid_root = uuid_root; 7017 } 7018 7019 RtlCopyMemory(&searchkey.obj_id, &r->root_item.received_uuid, sizeof(uint64_t)); 7020 searchkey.obj_type = TYPE_SUBVOL_REC_UUID; 7021 RtlCopyMemory(&searchkey.offset, &r->root_item.received_uuid.uuid[sizeof(uint64_t)], sizeof(uint64_t)); 7022 7023 Status = find_item(Vcb, Vcb->uuid_root, &tp, &searchkey, false, Irp); 7024 if (!NT_SUCCESS(Status)) { 7025 ERR("find_item returned %08lx\n", Status); 7026 return Status; 7027 } 7028 7029 if (!keycmp(tp.item->key, searchkey)) { 7030 if (tp.item->size + sizeof(uint64_t) <= Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node)) { 7031 uint64_t* ids; 7032 7033 ids = ExAllocatePoolWithTag(PagedPool, tp.item->size + sizeof(uint64_t), ALLOC_TAG); 7034 if (!ids) { 7035 ERR("out of memory\n"); 7036 return STATUS_INSUFFICIENT_RESOURCES; 7037 } 7038 7039 RtlCopyMemory(ids, tp.item->data, tp.item->size); 7040 RtlCopyMemory((uint8_t*)ids + tp.item->size, &r->id, sizeof(uint64_t)); 7041 7042 Status = delete_tree_item(Vcb, &tp); 7043 if (!NT_SUCCESS(Status)) { 7044 ERR("delete_tree_item returned %08lx\n", Status); 7045 ExFreePool(ids); 7046 return Status; 7047 } 7048 7049 Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ids, tp.item->size + sizeof(uint64_t), NULL, Irp); 7050 if (!NT_SUCCESS(Status)) { 7051 ERR("insert_tree_item returned %08lx\n", Status); 7052 ExFreePool(ids); 7053 return Status; 7054 } 7055 } 7056 } else { 7057 uint64_t* root_num; 7058 7059 root_num = ExAllocatePoolWithTag(PagedPool, sizeof(uint64_t), ALLOC_TAG); 7060 if (!root_num) { 7061 ERR("out of memory\n"); 7062 return STATUS_INSUFFICIENT_RESOURCES; 7063 } 7064 7065 *root_num = r->id; 7066 7067 Status = insert_tree_item(Vcb, Vcb->uuid_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, root_num, sizeof(uint64_t), NULL, Irp); 7068 if (!NT_SUCCESS(Status)) { 7069 ERR("insert_tree_item returned %08lx\n", Status); 7070 ExFreePool(root_num); 7071 return Status; 7072 } 7073 } 7074 7075 r->received = false; 7076 } 7077 7078 r->dirty = false; 7079 7080 return STATUS_SUCCESS; 7081 } 7082 7083 static NTSTATUS test_not_full(device_extension* Vcb) { 7084 uint64_t reserve, could_alloc, free_space; 7085 LIST_ENTRY* le; 7086 7087 // This function ensures we drop into readonly mode if we're about to leave very little 7088 // space for metadata - this is similar to the "global reserve" of the Linux driver. 7089 // Otherwise we might completely fill our space, at which point due to COW we can't 7090 // delete anything in order to fix this. 7091 7092 reserve = Vcb->extent_root->root_item.bytes_used; 7093 reserve += Vcb->root_root->root_item.bytes_used; 7094 if (Vcb->checksum_root) reserve += Vcb->checksum_root->root_item.bytes_used; 7095 7096 reserve = max(reserve, 0x1000000); // 16 M 7097 reserve = min(reserve, 0x20000000); // 512 M 7098 7099 // Find out how much space would be available for new metadata chunks 7100 7101 could_alloc = 0; 7102 7103 if (Vcb->metadata_flags & BLOCK_FLAG_RAID5) { 7104 uint64_t s1 = 0, s2 = 0, s3 = 0; 7105 7106 le = Vcb->devices.Flink; 7107 while (le != &Vcb->devices) { 7108 device* dev = CONTAINING_RECORD(le, device, list_entry); 7109 7110 if (!dev->readonly) { 7111 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7112 7113 if (space >= s1) { 7114 s3 = s2; 7115 s2 = s1; 7116 s1 = space; 7117 } else if (space >= s2) { 7118 s3 = s2; 7119 s2 = space; 7120 } else if (space >= s3) 7121 s3 = space; 7122 } 7123 7124 le = le->Flink; 7125 } 7126 7127 could_alloc = s3 * 2; 7128 } else if (Vcb->metadata_flags & (BLOCK_FLAG_RAID10 | BLOCK_FLAG_RAID6)) { 7129 uint64_t s1 = 0, s2 = 0, s3 = 0, s4 = 0; 7130 7131 le = Vcb->devices.Flink; 7132 while (le != &Vcb->devices) { 7133 device* dev = CONTAINING_RECORD(le, device, list_entry); 7134 7135 if (!dev->readonly) { 7136 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7137 7138 if (space >= s1) { 7139 s4 = s3; 7140 s3 = s2; 7141 s2 = s1; 7142 s1 = space; 7143 } else if (space >= s2) { 7144 s4 = s3; 7145 s3 = s2; 7146 s2 = space; 7147 } else if (space >= s3) { 7148 s4 = s3; 7149 s3 = space; 7150 } else if (space >= s4) 7151 s4 = space; 7152 } 7153 7154 le = le->Flink; 7155 } 7156 7157 could_alloc = s4 * 2; 7158 } else if (Vcb->metadata_flags & (BLOCK_FLAG_RAID0 | BLOCK_FLAG_RAID1)) { 7159 uint64_t s1 = 0, s2 = 0; 7160 7161 le = Vcb->devices.Flink; 7162 while (le != &Vcb->devices) { 7163 device* dev = CONTAINING_RECORD(le, device, list_entry); 7164 7165 if (!dev->readonly) { 7166 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7167 7168 if (space >= s1) { 7169 s2 = s1; 7170 s1 = space; 7171 } else if (space >= s2) 7172 s2 = space; 7173 } 7174 7175 le = le->Flink; 7176 } 7177 7178 if (Vcb->metadata_flags & BLOCK_FLAG_RAID1) 7179 could_alloc = s2; 7180 else // RAID0 7181 could_alloc = s2 * 2; 7182 } else if (Vcb->metadata_flags & BLOCK_FLAG_DUPLICATE) { 7183 le = Vcb->devices.Flink; 7184 while (le != &Vcb->devices) { 7185 device* dev = CONTAINING_RECORD(le, device, list_entry); 7186 7187 if (!dev->readonly) { 7188 uint64_t space = (dev->devitem.num_bytes - dev->devitem.bytes_used) / 2; 7189 7190 could_alloc = max(could_alloc, space); 7191 } 7192 7193 le = le->Flink; 7194 } 7195 } else if (Vcb->metadata_flags & BLOCK_FLAG_RAID1C3) { 7196 uint64_t s1 = 0, s2 = 0, s3 = 0; 7197 7198 le = Vcb->devices.Flink; 7199 while (le != &Vcb->devices) { 7200 device* dev = CONTAINING_RECORD(le, device, list_entry); 7201 7202 if (!dev->readonly) { 7203 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7204 7205 if (space >= s1) { 7206 s3 = s2; 7207 s2 = s1; 7208 s1 = space; 7209 } else if (space >= s2) { 7210 s3 = s2; 7211 s2 = space; 7212 } else if (space >= s3) 7213 s3 = space; 7214 } 7215 7216 le = le->Flink; 7217 } 7218 7219 could_alloc = s3; 7220 } else if (Vcb->metadata_flags & BLOCK_FLAG_RAID1C4) { 7221 uint64_t s1 = 0, s2 = 0, s3 = 0, s4 = 0; 7222 7223 le = Vcb->devices.Flink; 7224 while (le != &Vcb->devices) { 7225 device* dev = CONTAINING_RECORD(le, device, list_entry); 7226 7227 if (!dev->readonly) { 7228 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7229 7230 if (space >= s1) { 7231 s4 = s3; 7232 s3 = s2; 7233 s2 = s1; 7234 s1 = space; 7235 } else if (space >= s2) { 7236 s4 = s3; 7237 s3 = s2; 7238 s2 = space; 7239 } else if (space >= s3) { 7240 s4 = s3; 7241 s3 = space; 7242 } else if (space >= s4) 7243 s4 = space; 7244 } 7245 7246 le = le->Flink; 7247 } 7248 7249 could_alloc = s4; 7250 } else { // SINGLE 7251 le = Vcb->devices.Flink; 7252 while (le != &Vcb->devices) { 7253 device* dev = CONTAINING_RECORD(le, device, list_entry); 7254 7255 if (!dev->readonly) { 7256 uint64_t space = dev->devitem.num_bytes - dev->devitem.bytes_used; 7257 7258 could_alloc = max(could_alloc, space); 7259 } 7260 7261 le = le->Flink; 7262 } 7263 } 7264 7265 if (could_alloc >= reserve) 7266 return STATUS_SUCCESS; 7267 7268 free_space = 0; 7269 7270 le = Vcb->chunks.Flink; 7271 while (le != &Vcb->chunks) { 7272 chunk* c = CONTAINING_RECORD(le, chunk, list_entry); 7273 7274 if (!c->reloc && !c->readonly && c->chunk_item->type & BLOCK_FLAG_METADATA) { 7275 free_space += c->chunk_item->size - c->used; 7276 7277 if (free_space + could_alloc >= reserve) 7278 return STATUS_SUCCESS; 7279 } 7280 7281 le = le->Flink; 7282 } 7283 7284 return STATUS_DISK_FULL; 7285 } 7286 7287 static NTSTATUS check_for_orphans_root(device_extension* Vcb, root* r, PIRP Irp) { 7288 NTSTATUS Status; 7289 KEY searchkey; 7290 traverse_ptr tp; 7291 LIST_ENTRY rollback; 7292 7293 TRACE("(%p, %p)\n", Vcb, r); 7294 7295 InitializeListHead(&rollback); 7296 7297 searchkey.obj_id = BTRFS_ORPHAN_INODE_OBJID; 7298 searchkey.obj_type = TYPE_ORPHAN_INODE; 7299 searchkey.offset = 0; 7300 7301 Status = find_item(Vcb, r, &tp, &searchkey, false, Irp); 7302 if (!NT_SUCCESS(Status)) { 7303 ERR("find_item returned %08lx\n", Status); 7304 return Status; 7305 } 7306 7307 do { 7308 traverse_ptr next_tp; 7309 7310 if (tp.item->key.obj_id > searchkey.obj_id || (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type > searchkey.obj_type)) 7311 break; 7312 7313 if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) { 7314 fcb* fcb; 7315 7316 TRACE("removing orphaned inode %I64x\n", tp.item->key.offset); 7317 7318 Status = open_fcb(Vcb, r, tp.item->key.offset, 0, NULL, false, NULL, &fcb, PagedPool, Irp); 7319 if (!NT_SUCCESS(Status)) 7320 ERR("open_fcb returned %08lx\n", Status); 7321 else { 7322 if (fcb->inode_item.st_nlink == 0) { 7323 if (fcb->type != BTRFS_TYPE_DIRECTORY && fcb->inode_item.st_size > 0) { 7324 Status = excise_extents(Vcb, fcb, 0, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size), Irp, &rollback); 7325 if (!NT_SUCCESS(Status)) { 7326 ERR("excise_extents returned %08lx\n", Status); 7327 goto end; 7328 } 7329 } 7330 7331 fcb->deleted = true; 7332 7333 mark_fcb_dirty(fcb); 7334 } 7335 7336 free_fcb(fcb); 7337 7338 Status = delete_tree_item(Vcb, &tp); 7339 if (!NT_SUCCESS(Status)) { 7340 ERR("delete_tree_item returned %08lx\n", Status); 7341 goto end; 7342 } 7343 } 7344 } 7345 7346 if (find_next_item(Vcb, &tp, &next_tp, false, Irp)) 7347 tp = next_tp; 7348 else 7349 break; 7350 } while (true); 7351 7352 Status = STATUS_SUCCESS; 7353 7354 clear_rollback(&rollback); 7355 7356 end: 7357 do_rollback(Vcb, &rollback); 7358 7359 return Status; 7360 } 7361 7362 static NTSTATUS check_for_orphans(device_extension* Vcb, PIRP Irp) { 7363 NTSTATUS Status; 7364 LIST_ENTRY* le; 7365 7366 if (IsListEmpty(&Vcb->dirty_filerefs)) 7367 return STATUS_SUCCESS; 7368 7369 le = Vcb->dirty_filerefs.Flink; 7370 while (le != &Vcb->dirty_filerefs) { 7371 file_ref* fr = CONTAINING_RECORD(le, file_ref, list_entry_dirty); 7372 7373 if (!fr->fcb->subvol->checked_for_orphans) { 7374 Status = check_for_orphans_root(Vcb, fr->fcb->subvol, Irp); 7375 if (!NT_SUCCESS(Status)) { 7376 ERR("check_for_orphans_root returned %08lx\n", Status); 7377 return Status; 7378 } 7379 7380 fr->fcb->subvol->checked_for_orphans = true; 7381 } 7382 7383 le = le->Flink; 7384 } 7385 7386 return STATUS_SUCCESS; 7387 } 7388 7389 static NTSTATUS do_write2(device_extension* Vcb, PIRP Irp, LIST_ENTRY* rollback) { 7390 NTSTATUS Status; 7391 LIST_ENTRY *le, batchlist; 7392 bool cache_changed = false; 7393 volume_device_extension* vde; 7394 bool no_cache = false; 7395 #ifdef DEBUG_FLUSH_TIMES 7396 uint64_t filerefs = 0, fcbs = 0; 7397 LARGE_INTEGER freq, time1, time2; 7398 #endif 7399 #ifdef DEBUG_WRITE_LOOPS 7400 UINT loops = 0; 7401 #endif 7402 7403 TRACE("(%p)\n", Vcb); 7404 7405 InitializeListHead(&batchlist); 7406 7407 #ifdef DEBUG_FLUSH_TIMES 7408 time1 = KeQueryPerformanceCounter(&freq); 7409 #endif 7410 7411 Status = check_for_orphans(Vcb, Irp); 7412 if (!NT_SUCCESS(Status)) { 7413 ERR("check_for_orphans returned %08lx\n", Status); 7414 return Status; 7415 } 7416 7417 ExAcquireResourceExclusiveLite(&Vcb->dirty_filerefs_lock, true); 7418 7419 while (!IsListEmpty(&Vcb->dirty_filerefs)) { 7420 file_ref* fr = CONTAINING_RECORD(RemoveHeadList(&Vcb->dirty_filerefs), file_ref, list_entry_dirty); 7421 7422 flush_fileref(fr, &batchlist, Irp); 7423 free_fileref(fr); 7424 7425 #ifdef DEBUG_FLUSH_TIMES 7426 filerefs++; 7427 #endif 7428 } 7429 7430 ExReleaseResourceLite(&Vcb->dirty_filerefs_lock); 7431 7432 Status = commit_batch_list(Vcb, &batchlist, Irp); 7433 if (!NT_SUCCESS(Status)) { 7434 ERR("commit_batch_list returned %08lx\n", Status); 7435 return Status; 7436 } 7437 7438 #ifdef DEBUG_FLUSH_TIMES 7439 time2 = KeQueryPerformanceCounter(NULL); 7440 7441 ERR("flushed %I64u filerefs in %I64u (freq = %I64u)\n", filerefs, time2.QuadPart - time1.QuadPart, freq.QuadPart); 7442 7443 time1 = KeQueryPerformanceCounter(&freq); 7444 #endif 7445 7446 // We process deleted streams first, so we don't run over our xattr 7447 // limit unless we absolutely have to. 7448 // We also process deleted normal files, to avoid any problems 7449 // caused by inode collisions. 7450 7451 ExAcquireResourceExclusiveLite(&Vcb->dirty_fcbs_lock, true); 7452 7453 le = Vcb->dirty_fcbs.Flink; 7454 while (le != &Vcb->dirty_fcbs) { 7455 fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_dirty); 7456 LIST_ENTRY* le2 = le->Flink; 7457 7458 if (fcb->deleted) { 7459 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 7460 Status = flush_fcb(fcb, false, &batchlist, Irp); 7461 ExReleaseResourceLite(fcb->Header.Resource); 7462 7463 free_fcb(fcb); 7464 7465 if (!NT_SUCCESS(Status)) { 7466 ERR("flush_fcb returned %08lx\n", Status); 7467 clear_batch_list(Vcb, &batchlist); 7468 ExReleaseResourceLite(&Vcb->dirty_fcbs_lock); 7469 return Status; 7470 } 7471 7472 #ifdef DEBUG_FLUSH_TIMES 7473 fcbs++; 7474 #endif 7475 } 7476 7477 le = le2; 7478 } 7479 7480 Status = commit_batch_list(Vcb, &batchlist, Irp); 7481 if (!NT_SUCCESS(Status)) { 7482 ERR("commit_batch_list returned %08lx\n", Status); 7483 ExReleaseResourceLite(&Vcb->dirty_fcbs_lock); 7484 return Status; 7485 } 7486 7487 le = Vcb->dirty_fcbs.Flink; 7488 while (le != &Vcb->dirty_fcbs) { 7489 fcb* fcb = CONTAINING_RECORD(le, struct _fcb, list_entry_dirty); 7490 LIST_ENTRY* le2 = le->Flink; 7491 7492 if (fcb->subvol != Vcb->root_root) { 7493 ExAcquireResourceExclusiveLite(fcb->Header.Resource, true); 7494 Status = flush_fcb(fcb, false, &batchlist, Irp); 7495 ExReleaseResourceLite(fcb->Header.Resource); 7496 free_fcb(fcb); 7497 7498 if (!NT_SUCCESS(Status)) { 7499 ERR("flush_fcb returned %08lx\n", Status); 7500 ExReleaseResourceLite(&Vcb->dirty_fcbs_lock); 7501 return Status; 7502 } 7503 7504 #ifdef DEBUG_FLUSH_TIMES 7505 fcbs++; 7506 #endif 7507 } 7508 7509 le = le2; 7510 } 7511 7512 ExReleaseResourceLite(&Vcb->dirty_fcbs_lock); 7513 7514 Status = commit_batch_list(Vcb, &batchlist, Irp); 7515 if (!NT_SUCCESS(Status)) { 7516 ERR("commit_batch_list returned %08lx\n", Status); 7517 return Status; 7518 } 7519 7520 #ifdef DEBUG_FLUSH_TIMES 7521 time2 = KeQueryPerformanceCounter(NULL); 7522 7523 ERR("flushed %I64u fcbs in %I64u (freq = %I64u)\n", filerefs, time2.QuadPart - time1.QuadPart, freq.QuadPart); 7524 #endif 7525 7526 // no need to get dirty_subvols_lock here, as we have tree_lock exclusively 7527 while (!IsListEmpty(&Vcb->dirty_subvols)) { 7528 root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->dirty_subvols), root, list_entry_dirty); 7529 7530 Status = flush_subvol(Vcb, r, Irp); 7531 if (!NT_SUCCESS(Status)) { 7532 ERR("flush_subvol returned %08lx\n", Status); 7533 return Status; 7534 } 7535 } 7536 7537 if (!IsListEmpty(&Vcb->drop_roots)) { 7538 Status = drop_roots(Vcb, Irp, rollback); 7539 7540 if (!NT_SUCCESS(Status)) { 7541 ERR("drop_roots returned %08lx\n", Status); 7542 return Status; 7543 } 7544 } 7545 7546 Status = update_chunks(Vcb, &batchlist, Irp, rollback); 7547 7548 if (!NT_SUCCESS(Status)) { 7549 ERR("update_chunks returned %08lx\n", Status); 7550 return Status; 7551 } 7552 7553 Status = commit_batch_list(Vcb, &batchlist, Irp); 7554 7555 // If only changing superblock, e.g. changing label, we still need to rewrite 7556 // the root tree so the generations match, otherwise you won't be able to mount on Linux. 7557 if (!Vcb->root_root->treeholder.tree || !Vcb->root_root->treeholder.tree->write) { 7558 KEY searchkey; 7559 7560 traverse_ptr tp; 7561 7562 searchkey.obj_id = 0; 7563 searchkey.obj_type = 0; 7564 searchkey.offset = 0; 7565 7566 Status = find_item(Vcb, Vcb->root_root, &tp, &searchkey, false, Irp); 7567 if (!NT_SUCCESS(Status)) { 7568 ERR("error - find_item returned %08lx\n", Status); 7569 return Status; 7570 } 7571 7572 Vcb->root_root->treeholder.tree->write = true; 7573 } 7574 7575 // make sure we always update the extent tree 7576 Status = add_root_item_to_cache(Vcb, BTRFS_ROOT_EXTENT, Irp); 7577 if (!NT_SUCCESS(Status)) { 7578 ERR("add_root_item_to_cache returned %08lx\n", Status); 7579 return Status; 7580 } 7581 7582 if (Vcb->stats_changed) { 7583 le = Vcb->devices.Flink; 7584 while (le != &Vcb->devices) { 7585 device* dev = CONTAINING_RECORD(le, device, list_entry); 7586 7587 if (dev->stats_changed) { 7588 Status = flush_changed_dev_stats(Vcb, dev, Irp); 7589 if (!NT_SUCCESS(Status)) { 7590 ERR("flush_changed_dev_stats returned %08lx\n", Status); 7591 return Status; 7592 } 7593 dev->stats_changed = false; 7594 } 7595 7596 le = le->Flink; 7597 } 7598 7599 Vcb->stats_changed = false; 7600 } 7601 7602 do { 7603 Status = add_parents(Vcb, Irp); 7604 if (!NT_SUCCESS(Status)) { 7605 ERR("add_parents returned %08lx\n", Status); 7606 goto end; 7607 } 7608 7609 Status = allocate_tree_extents(Vcb, Irp, rollback); 7610 if (!NT_SUCCESS(Status)) { 7611 ERR("allocate_tree_extents returned %08lx\n", Status); 7612 goto end; 7613 } 7614 7615 Status = do_splits(Vcb, Irp, rollback); 7616 if (!NT_SUCCESS(Status)) { 7617 ERR("do_splits returned %08lx\n", Status); 7618 goto end; 7619 } 7620 7621 Status = update_chunk_usage(Vcb, Irp, rollback); 7622 if (!NT_SUCCESS(Status)) { 7623 ERR("update_chunk_usage returned %08lx\n", Status); 7624 goto end; 7625 } 7626 7627 if (!(Vcb->superblock.compat_ro_flags & BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE)) { 7628 if (!no_cache) { 7629 Status = allocate_cache(Vcb, &cache_changed, Irp, rollback); 7630 if (!NT_SUCCESS(Status)) { 7631 WARN("allocate_cache returned %08lx\n", Status); 7632 no_cache = true; 7633 cache_changed = false; 7634 } 7635 } 7636 } else { 7637 Status = update_chunk_caches_tree(Vcb, Irp); 7638 if (!NT_SUCCESS(Status)) { 7639 ERR("update_chunk_caches_tree returned %08lx\n", Status); 7640 goto end; 7641 } 7642 } 7643 7644 #ifdef DEBUG_WRITE_LOOPS 7645 loops++; 7646 7647 if (cache_changed) 7648 ERR("cache has changed, looping again\n"); 7649 #endif 7650 } while (cache_changed || !trees_consistent(Vcb)); 7651 7652 #ifdef DEBUG_WRITE_LOOPS 7653 ERR("%u loops\n", loops); 7654 #endif 7655 7656 TRACE("trees consistent\n"); 7657 7658 Status = update_root_root(Vcb, no_cache, Irp, rollback); 7659 if (!NT_SUCCESS(Status)) { 7660 ERR("update_root_root returned %08lx\n", Status); 7661 goto end; 7662 } 7663 7664 Status = write_trees(Vcb, Irp); 7665 if (!NT_SUCCESS(Status)) { 7666 ERR("write_trees returned %08lx\n", Status); 7667 goto end; 7668 } 7669 7670 Status = test_not_full(Vcb); 7671 if (!NT_SUCCESS(Status)) { 7672 ERR("test_not_full returned %08lx\n", Status); 7673 goto end; 7674 } 7675 7676 #ifdef DEBUG_PARANOID 7677 le = Vcb->trees.Flink; 7678 while (le != &Vcb->trees) { 7679 tree* t = CONTAINING_RECORD(le, tree, list_entry); 7680 KEY searchkey; 7681 traverse_ptr tp; 7682 7683 searchkey.obj_id = t->header.address; 7684 searchkey.obj_type = TYPE_METADATA_ITEM; 7685 searchkey.offset = 0xffffffffffffffff; 7686 7687 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 7688 if (!NT_SUCCESS(Status)) { 7689 ERR("error - find_item returned %08lx\n", Status); 7690 goto end; 7691 } 7692 7693 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 7694 searchkey.obj_id = t->header.address; 7695 searchkey.obj_type = TYPE_EXTENT_ITEM; 7696 searchkey.offset = 0xffffffffffffffff; 7697 7698 Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, false, Irp); 7699 if (!NT_SUCCESS(Status)) { 7700 ERR("error - find_item returned %08lx\n", Status); 7701 goto end; 7702 } 7703 7704 if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { 7705 ERR("error - could not find entry in extent tree for tree at %I64x\n", t->header.address); 7706 Status = STATUS_INTERNAL_ERROR; 7707 goto end; 7708 } 7709 } 7710 7711 le = le->Flink; 7712 } 7713 #endif 7714 7715 Vcb->superblock.cache_generation = Vcb->superblock.generation; 7716 7717 if (!Vcb->options.no_barrier) 7718 flush_disk_caches(Vcb); 7719 7720 Status = write_superblocks(Vcb, Irp); 7721 if (!NT_SUCCESS(Status)) { 7722 ERR("write_superblocks returned %08lx\n", Status); 7723 goto end; 7724 } 7725 7726 vde = Vcb->vde; 7727 7728 if (vde) { 7729 pdo_device_extension* pdode = vde->pdode; 7730 7731 ExAcquireResourceSharedLite(&pdode->child_lock, true); 7732 7733 le = pdode->children.Flink; 7734 7735 while (le != &pdode->children) { 7736 volume_child* vc = CONTAINING_RECORD(le, volume_child, list_entry); 7737 7738 vc->generation = Vcb->superblock.generation; 7739 le = le->Flink; 7740 } 7741 7742 ExReleaseResourceLite(&pdode->child_lock); 7743 } 7744 7745 clean_space_cache(Vcb); 7746 7747 le = Vcb->chunks.Flink; 7748 while (le != &Vcb->chunks) { 7749 chunk* c = CONTAINING_RECORD(le, chunk, list_entry); 7750 7751 c->changed = false; 7752 c->space_changed = false; 7753 7754 le = le->Flink; 7755 } 7756 7757 Vcb->superblock.generation++; 7758 7759 Status = STATUS_SUCCESS; 7760 7761 le = Vcb->trees.Flink; 7762 while (le != &Vcb->trees) { 7763 tree* t = CONTAINING_RECORD(le, tree, list_entry); 7764 7765 t->write = false; 7766 7767 le = le->Flink; 7768 } 7769 7770 Vcb->need_write = false; 7771 7772 while (!IsListEmpty(&Vcb->drop_roots)) { 7773 root* r = CONTAINING_RECORD(RemoveHeadList(&Vcb->drop_roots), root, list_entry); 7774 7775 if (IsListEmpty(&r->fcbs)) { 7776 ExDeleteResourceLite(&r->nonpaged->load_tree_lock); 7777 ExFreePool(r->nonpaged); 7778 ExFreePool(r); 7779 } else 7780 r->dropped = true; 7781 } 7782 7783 end: 7784 TRACE("do_write returning %08lx\n", Status); 7785 7786 return Status; 7787 } 7788 7789 NTSTATUS do_write(device_extension* Vcb, PIRP Irp) { 7790 LIST_ENTRY rollback; 7791 NTSTATUS Status; 7792 7793 InitializeListHead(&rollback); 7794 7795 Status = do_write2(Vcb, Irp, &rollback); 7796 7797 if (!NT_SUCCESS(Status)) { 7798 ERR("do_write2 returned %08lx, dropping into readonly mode\n", Status); 7799 Vcb->readonly = true; 7800 FsRtlNotifyVolumeEvent(Vcb->root_file, FSRTL_VOLUME_FORCED_CLOSED); 7801 do_rollback(Vcb, &rollback); 7802 } else 7803 clear_rollback(&rollback); 7804 7805 return Status; 7806 } 7807 7808 static void do_flush(device_extension* Vcb) { 7809 NTSTATUS Status; 7810 7811 ExAcquireResourceExclusiveLite(&Vcb->tree_lock, true); 7812 7813 if (Vcb->need_write && !Vcb->readonly) 7814 Status = do_write(Vcb, NULL); 7815 else 7816 Status = STATUS_SUCCESS; 7817 7818 free_trees(Vcb); 7819 7820 if (!NT_SUCCESS(Status)) 7821 ERR("do_write returned %08lx\n", Status); 7822 7823 ExReleaseResourceLite(&Vcb->tree_lock); 7824 } 7825 7826 _Function_class_(KSTART_ROUTINE) 7827 void __stdcall flush_thread(void* context) { 7828 DEVICE_OBJECT* devobj = context; 7829 device_extension* Vcb = devobj->DeviceExtension; 7830 LARGE_INTEGER due_time; 7831 7832 ObReferenceObject(devobj); 7833 7834 KeInitializeTimer(&Vcb->flush_thread_timer); 7835 7836 due_time.QuadPart = (uint64_t)Vcb->options.flush_interval * -10000000; 7837 7838 KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL); 7839 7840 while (true) { 7841 KeWaitForSingleObject(&Vcb->flush_thread_timer, Executive, KernelMode, false, NULL); 7842 7843 if (!(devobj->Vpb->Flags & VPB_MOUNTED) || Vcb->removing) 7844 break; 7845 7846 if (!Vcb->locked) 7847 do_flush(Vcb); 7848 7849 KeSetTimer(&Vcb->flush_thread_timer, due_time, NULL); 7850 } 7851 7852 ObDereferenceObject(devobj); 7853 KeCancelTimer(&Vcb->flush_thread_timer); 7854 7855 KeSetEvent(&Vcb->flush_thread_finished, 0, false); 7856 7857 PsTerminateSystemThread(STATUS_SUCCESS); 7858 } 7859