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 20 typedef struct { 21 UINT64 start; 22 UINT64 end; 23 UINT8* data; 24 PMDL mdl; 25 UINT64 irp_offset; 26 } write_stripe; 27 28 _Function_class_(IO_COMPLETION_ROUTINE) 29 #ifdef __REACTOS__ 30 static NTSTATUS NTAPI write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr); 31 #else 32 static NTSTATUS write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr); 33 #endif 34 35 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback); 36 37 extern tPsUpdateDiskCounters fPsUpdateDiskCounters; 38 extern tCcCopyWriteEx fCcCopyWriteEx; 39 extern tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters; 40 extern BOOL diskacc; 41 42 BOOL find_data_address_in_chunk(device_extension* Vcb, chunk* c, UINT64 length, UINT64* address) { 43 LIST_ENTRY* le; 44 space* s; 45 46 TRACE("(%p, %llx, %llx, %p)\n", Vcb, c->offset, length, address); 47 48 if (length > c->chunk_item->size - c->used) 49 return FALSE; 50 51 if (!c->cache_loaded) { 52 NTSTATUS Status = load_cache_chunk(Vcb, c, NULL); 53 54 if (!NT_SUCCESS(Status)) { 55 ERR("load_cache_chunk returned %08x\n", Status); 56 return FALSE; 57 } 58 } 59 60 if (IsListEmpty(&c->space_size)) 61 return FALSE; 62 63 le = c->space_size.Flink; 64 while (le != &c->space_size) { 65 s = CONTAINING_RECORD(le, space, list_entry_size); 66 67 if (s->size == length) { 68 *address = s->address; 69 return TRUE; 70 } else if (s->size < length) { 71 if (le == c->space_size.Flink) 72 return FALSE; 73 74 s = CONTAINING_RECORD(le->Blink, space, list_entry_size); 75 76 *address = s->address; 77 return TRUE; 78 } 79 80 le = le->Flink; 81 } 82 83 s = CONTAINING_RECORD(c->space_size.Blink, space, list_entry_size); 84 85 if (s->size > length) { 86 *address = s->address; 87 return TRUE; 88 } 89 90 return FALSE; 91 } 92 93 chunk* get_chunk_from_address(device_extension* Vcb, UINT64 address) { 94 LIST_ENTRY* le2; 95 96 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE); 97 98 le2 = Vcb->chunks.Flink; 99 while (le2 != &Vcb->chunks) { 100 chunk* c = CONTAINING_RECORD(le2, chunk, list_entry); 101 102 if (address >= c->offset && address < c->offset + c->chunk_item->size) { 103 ExReleaseResourceLite(&Vcb->chunk_lock); 104 return c; 105 } 106 107 le2 = le2->Flink; 108 } 109 110 ExReleaseResourceLite(&Vcb->chunk_lock); 111 112 return NULL; 113 } 114 115 typedef struct { 116 space* dh; 117 device* device; 118 } stripe; 119 120 static UINT64 find_new_chunk_address(device_extension* Vcb, UINT64 size) { 121 UINT64 lastaddr; 122 LIST_ENTRY* le; 123 124 lastaddr = 0xc00000; 125 126 le = Vcb->chunks.Flink; 127 while (le != &Vcb->chunks) { 128 chunk* c = CONTAINING_RECORD(le, chunk, list_entry); 129 130 if (c->offset >= lastaddr + size) 131 return lastaddr; 132 133 lastaddr = c->offset + c->chunk_item->size; 134 135 le = le->Flink; 136 } 137 138 return lastaddr; 139 } 140 141 static BOOL find_new_dup_stripes(device_extension* Vcb, stripe* stripes, UINT64 max_stripe_size, BOOL full_size) { 142 UINT64 devusage = 0xffffffffffffffff; 143 space *devdh1 = NULL, *devdh2 = NULL; 144 LIST_ENTRY* le; 145 device* dev2 = NULL; 146 147 le = Vcb->devices.Flink; 148 149 while (le != &Vcb->devices) { 150 device* dev = CONTAINING_RECORD(le, device, list_entry); 151 152 if (!dev->readonly && !dev->reloc && dev->devobj) { 153 UINT64 usage = (dev->devitem.bytes_used * 4096) / dev->devitem.num_bytes; 154 155 // favour devices which have been used the least 156 if (usage < devusage) { 157 if (!IsListEmpty(&dev->space)) { 158 LIST_ENTRY* le2; 159 space *dh1 = NULL, *dh2 = NULL; 160 161 le2 = dev->space.Flink; 162 while (le2 != &dev->space) { 163 space* dh = CONTAINING_RECORD(le2, space, list_entry); 164 165 if (dh->size >= max_stripe_size && (!dh1 || !dh2 || dh->size < dh1->size)) { 166 dh2 = dh1; 167 dh1 = dh; 168 } 169 170 le2 = le2->Flink; 171 } 172 173 if (dh1 && (dh2 || dh1->size >= 2 * max_stripe_size)) { 174 dev2 = dev; 175 devusage = usage; 176 devdh1 = dh1; 177 devdh2 = dh2 ? dh2 : dh1; 178 } 179 } 180 } 181 } 182 183 le = le->Flink; 184 } 185 186 if (!devdh1) { 187 UINT64 size = 0; 188 189 // Can't find hole of at least max_stripe_size; look for the largest one we can find 190 191 if (full_size) 192 return FALSE; 193 194 le = Vcb->devices.Flink; 195 while (le != &Vcb->devices) { 196 device* dev = CONTAINING_RECORD(le, device, list_entry); 197 198 if (!dev->readonly && !dev->reloc) { 199 if (!IsListEmpty(&dev->space)) { 200 LIST_ENTRY* le2; 201 space *dh1 = NULL, *dh2 = NULL; 202 203 le2 = dev->space.Flink; 204 while (le2 != &dev->space) { 205 space* dh = CONTAINING_RECORD(le2, space, list_entry); 206 207 if (!dh1 || !dh2 || dh->size < dh1->size) { 208 dh2 = dh1; 209 dh1 = dh; 210 } 211 212 le2 = le2->Flink; 213 } 214 215 if (dh1) { 216 UINT64 devsize; 217 218 if (dh2) 219 devsize = max(dh1->size / 2, min(dh1->size, dh2->size)); 220 else 221 devsize = dh1->size / 2; 222 223 if (devsize > size) { 224 dev2 = dev; 225 devdh1 = dh1; 226 227 if (dh2 && min(dh1->size, dh2->size) > dh1->size / 2) 228 devdh2 = dh2; 229 else 230 devdh2 = dh1; 231 232 size = devsize; 233 } 234 } 235 } 236 } 237 238 le = le->Flink; 239 } 240 241 if (!devdh1) 242 return FALSE; 243 } 244 245 stripes[0].device = stripes[1].device = dev2; 246 stripes[0].dh = devdh1; 247 stripes[1].dh = devdh2; 248 249 return TRUE; 250 } 251 252 static BOOL find_new_stripe(device_extension* Vcb, stripe* stripes, UINT16 i, UINT64 max_stripe_size, BOOL allow_missing, BOOL full_size) { 253 UINT64 k, devusage = 0xffffffffffffffff; 254 space* devdh = NULL; 255 LIST_ENTRY* le; 256 device* dev2 = NULL; 257 258 le = Vcb->devices.Flink; 259 while (le != &Vcb->devices) { 260 device* dev = CONTAINING_RECORD(le, device, list_entry); 261 UINT64 usage; 262 BOOL skip = FALSE; 263 264 if (dev->readonly || dev->reloc || (!dev->devobj && !allow_missing)) { 265 le = le->Flink; 266 continue; 267 } 268 269 // skip this device if it already has a stripe 270 if (i > 0) { 271 for (k = 0; k < i; k++) { 272 if (stripes[k].device == dev) { 273 skip = TRUE; 274 break; 275 } 276 } 277 } 278 279 if (!skip) { 280 usage = (dev->devitem.bytes_used * 4096) / dev->devitem.num_bytes; 281 282 // favour devices which have been used the least 283 if (usage < devusage) { 284 if (!IsListEmpty(&dev->space)) { 285 LIST_ENTRY* le2; 286 287 le2 = dev->space.Flink; 288 while (le2 != &dev->space) { 289 space* dh = CONTAINING_RECORD(le2, space, list_entry); 290 291 if ((dev2 != dev && dh->size >= max_stripe_size) || 292 (dev2 == dev && dh->size >= max_stripe_size && dh->size < devdh->size) 293 ) { 294 devdh = dh; 295 dev2 = dev; 296 devusage = usage; 297 } 298 299 le2 = le2->Flink; 300 } 301 } 302 } 303 } 304 305 le = le->Flink; 306 } 307 308 if (!devdh) { 309 // Can't find hole of at least max_stripe_size; look for the largest one we can find 310 311 if (full_size) 312 return FALSE; 313 314 le = Vcb->devices.Flink; 315 while (le != &Vcb->devices) { 316 device* dev = CONTAINING_RECORD(le, device, list_entry); 317 BOOL skip = FALSE; 318 319 if (dev->readonly || dev->reloc || (!dev->devobj && !allow_missing)) { 320 le = le->Flink; 321 continue; 322 } 323 324 // skip this device if it already has a stripe 325 if (i > 0) { 326 for (k = 0; k < i; k++) { 327 if (stripes[k].device == dev) { 328 skip = TRUE; 329 break; 330 } 331 } 332 } 333 334 if (!skip) { 335 if (!IsListEmpty(&dev->space)) { 336 LIST_ENTRY* le2; 337 338 le2 = dev->space.Flink; 339 while (le2 != &dev->space) { 340 space* dh = CONTAINING_RECORD(le2, space, list_entry); 341 342 if (!devdh || devdh->size < dh->size) { 343 devdh = dh; 344 dev2 = dev; 345 } 346 347 le2 = le2->Flink; 348 } 349 } 350 } 351 352 le = le->Flink; 353 } 354 355 if (!devdh) 356 return FALSE; 357 } 358 359 stripes[i].dh = devdh; 360 stripes[i].device = dev2; 361 362 return TRUE; 363 } 364 365 NTSTATUS alloc_chunk(device_extension* Vcb, UINT64 flags, chunk** pc, BOOL full_size) { 366 NTSTATUS Status; 367 UINT64 max_stripe_size, max_chunk_size, stripe_size, stripe_length, factor; 368 UINT64 total_size = 0, logaddr; 369 UINT16 i, type, num_stripes, sub_stripes, max_stripes, min_stripes, allowed_missing; 370 stripe* stripes = NULL; 371 UINT16 cisize; 372 CHUNK_ITEM_STRIPE* cis; 373 chunk* c = NULL; 374 space* s = NULL; 375 LIST_ENTRY* le; 376 377 le = Vcb->devices.Flink; 378 while (le != &Vcb->devices) { 379 device* dev = CONTAINING_RECORD(le, device, list_entry); 380 total_size += dev->devitem.num_bytes; 381 382 le = le->Flink; 383 } 384 385 TRACE("total_size = %llx\n", total_size); 386 387 // We purposely check for DATA first - mixed blocks have the same size 388 // as DATA ones. 389 if (flags & BLOCK_FLAG_DATA) { 390 max_stripe_size = 0x40000000; // 1 GB 391 max_chunk_size = 10 * max_stripe_size; 392 } else if (flags & BLOCK_FLAG_METADATA) { 393 if (total_size > 0xC80000000) // 50 GB 394 max_stripe_size = 0x40000000; // 1 GB 395 else 396 max_stripe_size = 0x10000000; // 256 MB 397 398 max_chunk_size = max_stripe_size; 399 } else if (flags & BLOCK_FLAG_SYSTEM) { 400 max_stripe_size = 0x2000000; // 32 MB 401 max_chunk_size = 2 * max_stripe_size; 402 } else { 403 ERR("unknown chunk type\n"); 404 return STATUS_INTERNAL_ERROR; 405 } 406 407 if (flags & BLOCK_FLAG_DUPLICATE) { 408 min_stripes = 2; 409 max_stripes = 2; 410 sub_stripes = 0; 411 type = BLOCK_FLAG_DUPLICATE; 412 allowed_missing = 0; 413 } else if (flags & BLOCK_FLAG_RAID0) { 414 min_stripes = 2; 415 max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices); 416 sub_stripes = 0; 417 type = BLOCK_FLAG_RAID0; 418 allowed_missing = 0; 419 } else if (flags & BLOCK_FLAG_RAID1) { 420 min_stripes = 2; 421 max_stripes = 2; 422 sub_stripes = 1; 423 type = BLOCK_FLAG_RAID1; 424 allowed_missing = 1; 425 } else if (flags & BLOCK_FLAG_RAID10) { 426 min_stripes = 4; 427 max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices); 428 sub_stripes = 2; 429 type = BLOCK_FLAG_RAID10; 430 allowed_missing = 1; 431 } else if (flags & BLOCK_FLAG_RAID5) { 432 min_stripes = 3; 433 max_stripes = (UINT16)min(0xffff, Vcb->superblock.num_devices); 434 sub_stripes = 1; 435 type = BLOCK_FLAG_RAID5; 436 allowed_missing = 1; 437 } else if (flags & BLOCK_FLAG_RAID6) { 438 min_stripes = 4; 439 max_stripes = 257; 440 sub_stripes = 1; 441 type = BLOCK_FLAG_RAID6; 442 allowed_missing = 2; 443 } else { // SINGLE 444 min_stripes = 1; 445 max_stripes = 1; 446 sub_stripes = 1; 447 type = 0; 448 allowed_missing = 0; 449 } 450 451 if (max_chunk_size > total_size / 10) { // cap at 10% 452 max_chunk_size = total_size / 10; 453 max_stripe_size = max_chunk_size / min_stripes; 454 } 455 456 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size, max_stripe_size); 457 458 stripes = ExAllocatePoolWithTag(PagedPool, sizeof(stripe) * max_stripes, ALLOC_TAG); 459 if (!stripes) { 460 ERR("out of memory\n"); 461 Status = STATUS_INSUFFICIENT_RESOURCES; 462 goto end; 463 } 464 465 num_stripes = 0; 466 467 if (type == BLOCK_FLAG_DUPLICATE) { 468 if (!find_new_dup_stripes(Vcb, stripes, max_stripe_size, full_size)) { 469 Status = STATUS_DISK_FULL; 470 goto end; 471 } 472 else 473 num_stripes = max_stripes; 474 } else { 475 for (i = 0; i < max_stripes; i++) { 476 if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, FALSE, full_size)) 477 break; 478 else 479 num_stripes++; 480 } 481 } 482 483 if (num_stripes < min_stripes && Vcb->options.allow_degraded && allowed_missing > 0) { 484 UINT16 added_missing = 0; 485 486 for (i = num_stripes; i < max_stripes; i++) { 487 if (!find_new_stripe(Vcb, stripes, i, max_stripe_size, TRUE, full_size)) 488 break; 489 else { 490 added_missing++; 491 if (added_missing >= allowed_missing) 492 break; 493 } 494 } 495 496 num_stripes += added_missing; 497 } 498 499 // for RAID10, round down to an even number of stripes 500 if (type == BLOCK_FLAG_RAID10 && (num_stripes % sub_stripes) != 0) { 501 num_stripes -= num_stripes % sub_stripes; 502 } 503 504 if (num_stripes < min_stripes) { 505 WARN("found %u stripes, needed at least %u\n", num_stripes, min_stripes); 506 Status = STATUS_DISK_FULL; 507 goto end; 508 } 509 510 c = ExAllocatePoolWithTag(NonPagedPool, sizeof(chunk), ALLOC_TAG); 511 if (!c) { 512 ERR("out of memory\n"); 513 Status = STATUS_INSUFFICIENT_RESOURCES; 514 goto end; 515 } 516 517 c->devices = NULL; 518 519 cisize = sizeof(CHUNK_ITEM) + (num_stripes * sizeof(CHUNK_ITEM_STRIPE)); 520 c->chunk_item = ExAllocatePoolWithTag(NonPagedPool, cisize, ALLOC_TAG); 521 if (!c->chunk_item) { 522 ERR("out of memory\n"); 523 Status = STATUS_INSUFFICIENT_RESOURCES; 524 goto end; 525 } 526 527 stripe_length = 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel 528 529 if (type == BLOCK_FLAG_DUPLICATE && stripes[1].dh == stripes[0].dh) 530 stripe_size = min(stripes[0].dh->size / 2, max_stripe_size); 531 else { 532 stripe_size = max_stripe_size; 533 for (i = 0; i < num_stripes; i++) { 534 if (stripes[i].dh->size < stripe_size) 535 stripe_size = stripes[i].dh->size; 536 } 537 } 538 539 if (type == 0 || type == BLOCK_FLAG_DUPLICATE || type == BLOCK_FLAG_RAID1) 540 factor = 1; 541 else if (type == BLOCK_FLAG_RAID0) 542 factor = num_stripes; 543 else if (type == BLOCK_FLAG_RAID10) 544 factor = num_stripes / sub_stripes; 545 else if (type == BLOCK_FLAG_RAID5) 546 factor = num_stripes - 1; 547 else if (type == BLOCK_FLAG_RAID6) 548 factor = num_stripes - 2; 549 550 if (stripe_size * factor > max_chunk_size) 551 stripe_size = max_chunk_size / factor; 552 553 if (stripe_size % stripe_length > 0) 554 stripe_size -= stripe_size % stripe_length; 555 556 if (stripe_size == 0) { 557 ERR("not enough free space found (stripe_size == 0)\n"); 558 Status = STATUS_DISK_FULL; 559 goto end; 560 } 561 562 c->chunk_item->size = stripe_size * factor; 563 c->chunk_item->root_id = Vcb->extent_root->id; 564 c->chunk_item->stripe_length = stripe_length; 565 c->chunk_item->type = flags; 566 c->chunk_item->opt_io_alignment = (UINT32)c->chunk_item->stripe_length; 567 c->chunk_item->opt_io_width = (UINT32)c->chunk_item->stripe_length; 568 c->chunk_item->sector_size = stripes[0].device->devitem.minimal_io_size; 569 c->chunk_item->num_stripes = num_stripes; 570 c->chunk_item->sub_stripes = sub_stripes; 571 572 c->devices = ExAllocatePoolWithTag(NonPagedPool, sizeof(device*) * num_stripes, ALLOC_TAG); 573 if (!c->devices) { 574 ERR("out of memory\n"); 575 Status = STATUS_INSUFFICIENT_RESOURCES; 576 goto end; 577 } 578 579 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 580 for (i = 0; i < num_stripes; i++) { 581 cis[i].dev_id = stripes[i].device->devitem.dev_id; 582 583 if (type == BLOCK_FLAG_DUPLICATE && i == 1 && stripes[i].dh == stripes[0].dh) 584 cis[i].offset = stripes[0].dh->address + stripe_size; 585 else 586 cis[i].offset = stripes[i].dh->address; 587 588 cis[i].dev_uuid = stripes[i].device->devitem.device_uuid; 589 590 c->devices[i] = stripes[i].device; 591 } 592 593 logaddr = find_new_chunk_address(Vcb, c->chunk_item->size); 594 595 Vcb->superblock.chunk_root_generation = Vcb->superblock.generation; 596 597 c->size = cisize; 598 c->offset = logaddr; 599 c->used = c->oldused = 0; 600 c->cache = c->old_cache = NULL; 601 c->readonly = FALSE; 602 c->reloc = FALSE; 603 c->last_alloc_set = FALSE; 604 c->last_stripe = 0; 605 c->cache_loaded = TRUE; 606 c->changed = FALSE; 607 c->space_changed = FALSE; 608 c->balance_num = 0; 609 610 InitializeListHead(&c->space); 611 InitializeListHead(&c->space_size); 612 InitializeListHead(&c->deleting); 613 InitializeListHead(&c->changed_extents); 614 615 InitializeListHead(&c->range_locks); 616 ExInitializeResourceLite(&c->range_locks_lock); 617 KeInitializeEvent(&c->range_locks_event, NotificationEvent, FALSE); 618 619 InitializeListHead(&c->partial_stripes); 620 ExInitializeResourceLite(&c->partial_stripes_lock); 621 622 ExInitializeResourceLite(&c->lock); 623 ExInitializeResourceLite(&c->changed_extents_lock); 624 625 s = ExAllocatePoolWithTag(NonPagedPool, sizeof(space), ALLOC_TAG); 626 if (!s) { 627 ERR("out of memory\n"); 628 Status = STATUS_INSUFFICIENT_RESOURCES; 629 goto end; 630 } 631 632 s->address = c->offset; 633 s->size = c->chunk_item->size; 634 InsertTailList(&c->space, &s->list_entry); 635 InsertTailList(&c->space_size, &s->list_entry_size); 636 637 protect_superblocks(c); 638 639 for (i = 0; i < num_stripes; i++) { 640 stripes[i].device->devitem.bytes_used += stripe_size; 641 642 space_list_subtract2(&stripes[i].device->space, NULL, cis[i].offset, stripe_size, NULL, NULL); 643 } 644 645 Status = STATUS_SUCCESS; 646 647 if (flags & BLOCK_FLAG_RAID5 || flags & BLOCK_FLAG_RAID6) 648 Vcb->superblock.incompat_flags |= BTRFS_INCOMPAT_FLAGS_RAID56; 649 650 end: 651 if (stripes) 652 ExFreePool(stripes); 653 654 if (!NT_SUCCESS(Status)) { 655 if (c) { 656 if (c->devices) 657 ExFreePool(c->devices); 658 659 if (c->chunk_item) 660 ExFreePool(c->chunk_item); 661 662 ExFreePool(c); 663 } 664 665 if (s) ExFreePool(s); 666 } else { 667 BOOL done = FALSE; 668 669 le = Vcb->chunks.Flink; 670 while (le != &Vcb->chunks) { 671 chunk* c2 = CONTAINING_RECORD(le, chunk, list_entry); 672 673 if (c2->offset > c->offset) { 674 InsertHeadList(le->Blink, &c->list_entry); 675 done = TRUE; 676 break; 677 } 678 679 le = le->Flink; 680 } 681 682 if (!done) 683 InsertTailList(&Vcb->chunks, &c->list_entry); 684 685 c->created = TRUE; 686 c->changed = TRUE; 687 c->space_changed = TRUE; 688 c->list_entry_balance.Flink = NULL; 689 690 *pc = c; 691 } 692 693 return Status; 694 } 695 696 static NTSTATUS prepare_raid0_write(_Pre_satisfies_(_Curr_->chunk_item->num_stripes>0) _In_ chunk* c, _In_ UINT64 address, _In_reads_bytes_(length) void* data, 697 _In_ UINT32 length, _In_ write_stripe* stripes, _In_ PIRP Irp, _In_ UINT64 irp_offset, _In_ write_data_context* wtc) { 698 UINT64 startoff, endoff; 699 UINT16 startoffstripe, endoffstripe, stripenum; 700 UINT64 pos, *stripeoff; 701 UINT32 i; 702 BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0); 703 PMDL master_mdl; 704 PFN_NUMBER* pfns; 705 706 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); 707 if (!stripeoff) { 708 ERR("out of memory\n"); 709 return STATUS_INSUFFICIENT_RESOURCES; 710 } 711 712 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &startoff, &startoffstripe); 713 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes, &endoff, &endoffstripe); 714 715 if (file_write) { 716 master_mdl = Irp->MdlAddress; 717 718 pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1); 719 pfns = &pfns[irp_offset >> PAGE_SHIFT]; 720 } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) { 721 wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 722 if (!wtc->scratch) { 723 ERR("out of memory\n"); 724 return STATUS_INSUFFICIENT_RESOURCES; 725 } 726 727 RtlCopyMemory(wtc->scratch, data, length); 728 729 master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL); 730 if (!master_mdl) { 731 ERR("out of memory\n"); 732 return STATUS_INSUFFICIENT_RESOURCES; 733 } 734 735 MmBuildMdlForNonPagedPool(master_mdl); 736 737 wtc->mdl = master_mdl; 738 739 pfns = (PFN_NUMBER*)(master_mdl + 1); 740 } else { 741 NTSTATUS Status = STATUS_SUCCESS; 742 743 master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL); 744 if (!master_mdl) { 745 ERR("out of memory\n"); 746 return STATUS_INSUFFICIENT_RESOURCES; 747 } 748 749 _SEH2_TRY { 750 MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess); 751 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 752 Status = _SEH2_GetExceptionCode(); 753 } _SEH2_END; 754 755 if (!NT_SUCCESS(Status)) { 756 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 757 IoFreeMdl(master_mdl); 758 return Status; 759 } 760 761 wtc->mdl = master_mdl; 762 763 pfns = (PFN_NUMBER*)(master_mdl + 1); 764 } 765 766 for (i = 0; i < c->chunk_item->num_stripes; i++) { 767 if (startoffstripe > i) 768 stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 769 else if (startoffstripe == i) 770 stripes[i].start = startoff; 771 else 772 stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length); 773 774 if (endoffstripe > i) 775 stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 776 else if (endoffstripe == i) 777 stripes[i].end = endoff + 1; 778 else 779 stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length); 780 781 if (stripes[i].start != stripes[i].end) { 782 stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL); 783 if (!stripes[i].mdl) { 784 ERR("IoAllocateMdl failed\n"); 785 ExFreePool(stripeoff); 786 return STATUS_INSUFFICIENT_RESOURCES; 787 } 788 } 789 } 790 791 pos = 0; 792 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes); 793 794 stripenum = startoffstripe; 795 796 while (pos < length) { 797 PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum].mdl + 1); 798 799 if (pos == 0) { 800 UINT32 writelen = (UINT32)min(stripes[stripenum].end - stripes[stripenum].start, 801 c->chunk_item->stripe_length - (stripes[stripenum].start % c->chunk_item->stripe_length)); 802 803 RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 804 805 stripeoff[stripenum] += writelen; 806 pos += writelen; 807 } else if (length - pos < c->chunk_item->stripe_length) { 808 RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)((length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 809 break; 810 } else { 811 RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 812 813 stripeoff[stripenum] += c->chunk_item->stripe_length; 814 pos += c->chunk_item->stripe_length; 815 } 816 817 stripenum = (stripenum + 1) % c->chunk_item->num_stripes; 818 } 819 820 ExFreePool(stripeoff); 821 822 return STATUS_SUCCESS; 823 } 824 825 static NTSTATUS prepare_raid10_write(_Pre_satisfies_(_Curr_->chunk_item->sub_stripes>0&&_Curr_->chunk_item->num_stripes>=_Curr_->chunk_item->sub_stripes) _In_ chunk* c, 826 _In_ UINT64 address, _In_reads_bytes_(length) void* data, _In_ UINT32 length, _In_ write_stripe* stripes, 827 _In_ PIRP Irp, _In_ UINT64 irp_offset, _In_ write_data_context* wtc) { 828 UINT64 startoff, endoff; 829 UINT16 startoffstripe, endoffstripe, stripenum; 830 UINT64 pos, *stripeoff; 831 UINT32 i; 832 BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0); 833 PMDL master_mdl; 834 PFN_NUMBER* pfns; 835 836 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &startoff, &startoffstripe); 837 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, c->chunk_item->num_stripes / c->chunk_item->sub_stripes, &endoff, &endoffstripe); 838 839 stripenum = startoffstripe; 840 startoffstripe *= c->chunk_item->sub_stripes; 841 endoffstripe *= c->chunk_item->sub_stripes; 842 843 if (file_write) { 844 master_mdl = Irp->MdlAddress; 845 846 pfns = (PFN_NUMBER*)(Irp->MdlAddress + 1); 847 pfns = &pfns[irp_offset >> PAGE_SHIFT]; 848 } else if (((ULONG_PTR)data % PAGE_SIZE) != 0) { 849 wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 850 if (!wtc->scratch) { 851 ERR("out of memory\n"); 852 return STATUS_INSUFFICIENT_RESOURCES; 853 } 854 855 RtlCopyMemory(wtc->scratch, data, length); 856 857 master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL); 858 if (!master_mdl) { 859 ERR("out of memory\n"); 860 return STATUS_INSUFFICIENT_RESOURCES; 861 } 862 863 MmBuildMdlForNonPagedPool(master_mdl); 864 865 wtc->mdl = master_mdl; 866 867 pfns = (PFN_NUMBER*)(master_mdl + 1); 868 } else { 869 NTSTATUS Status = STATUS_SUCCESS; 870 871 master_mdl = IoAllocateMdl(data, length, FALSE, FALSE, NULL); 872 if (!master_mdl) { 873 ERR("out of memory\n"); 874 return STATUS_INSUFFICIENT_RESOURCES; 875 } 876 877 _SEH2_TRY { 878 MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess); 879 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 880 Status = _SEH2_GetExceptionCode(); 881 } _SEH2_END; 882 883 if (!NT_SUCCESS(Status)) { 884 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 885 IoFreeMdl(master_mdl); 886 return Status; 887 } 888 889 wtc->mdl = master_mdl; 890 891 pfns = (PFN_NUMBER*)(master_mdl + 1); 892 } 893 894 for (i = 0; i < c->chunk_item->num_stripes; i += c->chunk_item->sub_stripes) { 895 UINT16 j; 896 897 if (startoffstripe > i) 898 stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 899 else if (startoffstripe == i) 900 stripes[i].start = startoff; 901 else 902 stripes[i].start = startoff - (startoff % c->chunk_item->stripe_length); 903 904 if (endoffstripe > i) 905 stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 906 else if (endoffstripe == i) 907 stripes[i].end = endoff + 1; 908 else 909 stripes[i].end = endoff - (endoff % c->chunk_item->stripe_length); 910 911 stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL); 912 if (!stripes[i].mdl) { 913 ERR("IoAllocateMdl failed\n"); 914 return STATUS_INSUFFICIENT_RESOURCES; 915 } 916 917 for (j = 1; j < c->chunk_item->sub_stripes; j++) { 918 stripes[i+j].start = stripes[i].start; 919 stripes[i+j].end = stripes[i].end; 920 stripes[i+j].data = stripes[i].data; 921 stripes[i+j].mdl = stripes[i].mdl; 922 } 923 } 924 925 pos = 0; 926 927 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes, ALLOC_TAG); 928 if (!stripeoff) { 929 ERR("out of memory\n"); 930 return STATUS_INSUFFICIENT_RESOURCES; 931 } 932 933 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes / c->chunk_item->sub_stripes); 934 935 while (pos < length) { 936 PFN_NUMBER* stripe_pfns = (PFN_NUMBER*)(stripes[stripenum * c->chunk_item->sub_stripes].mdl + 1); 937 938 if (pos == 0) { 939 UINT32 writelen = (UINT32)min(stripes[stripenum * c->chunk_item->sub_stripes].end - stripes[stripenum * c->chunk_item->sub_stripes].start, 940 c->chunk_item->stripe_length - (stripes[stripenum * c->chunk_item->sub_stripes].start % c->chunk_item->stripe_length)); 941 942 RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 943 944 stripeoff[stripenum] += writelen; 945 pos += writelen; 946 } else if (length - pos < c->chunk_item->stripe_length) { 947 RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)((length - pos) * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 948 break; 949 } else { 950 RtlCopyMemory(&stripe_pfns[stripeoff[stripenum] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 951 952 stripeoff[stripenum] += c->chunk_item->stripe_length; 953 pos += c->chunk_item->stripe_length; 954 } 955 956 stripenum = (stripenum + 1) % (c->chunk_item->num_stripes / c->chunk_item->sub_stripes); 957 } 958 959 ExFreePool(stripeoff); 960 961 return STATUS_SUCCESS; 962 } 963 964 static NTSTATUS add_partial_stripe(device_extension* Vcb, chunk *c, UINT64 address, UINT32 length, void* data) { 965 NTSTATUS Status; 966 LIST_ENTRY* le; 967 partial_stripe* ps; 968 UINT64 stripe_addr; 969 UINT16 num_data_stripes; 970 ULONG bmplen; 971 972 num_data_stripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2); 973 stripe_addr = address - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length)); 974 975 ExAcquireResourceExclusiveLite(&c->partial_stripes_lock, TRUE); 976 977 le = c->partial_stripes.Flink; 978 while (le != &c->partial_stripes) { 979 ps = CONTAINING_RECORD(le, partial_stripe, list_entry); 980 981 if (ps->address == stripe_addr) { 982 // update existing entry 983 984 RtlCopyMemory(ps->data + address - stripe_addr, data, length); 985 RtlClearBits(&ps->bmp, (ULONG)((address - stripe_addr) / Vcb->superblock.sector_size), length / Vcb->superblock.sector_size); 986 987 // if now filled, flush 988 if (RtlAreBitsClear(&ps->bmp, 0, (ULONG)((num_data_stripes * c->chunk_item->stripe_length) / Vcb->superblock.sector_size))) { 989 Status = flush_partial_stripe(Vcb, c, ps); 990 if (!NT_SUCCESS(Status)) { 991 ERR("flush_partial_stripe returned %08x\n", Status); 992 goto end; 993 } 994 995 RemoveEntryList(&ps->list_entry); 996 997 if (ps->bmparr) 998 ExFreePool(ps->bmparr); 999 1000 ExFreePool(ps); 1001 } 1002 1003 Status = STATUS_SUCCESS; 1004 goto end; 1005 } else if (ps->address > stripe_addr) 1006 break; 1007 1008 le = le->Flink; 1009 } 1010 1011 // add new entry 1012 1013 ps = ExAllocatePoolWithTag(NonPagedPool, offsetof(partial_stripe, data[0]) + (ULONG)(num_data_stripes * c->chunk_item->stripe_length), ALLOC_TAG); 1014 if (!ps) { 1015 ERR("out of memory\n"); 1016 Status = STATUS_INSUFFICIENT_RESOURCES; 1017 goto end; 1018 } 1019 1020 bmplen = (ULONG)sector_align(((num_data_stripes * c->chunk_item->stripe_length) / (8 * Vcb->superblock.sector_size) + 1), sizeof(ULONG)); 1021 1022 ps->address = stripe_addr; 1023 ps->bmparr = ExAllocatePoolWithTag(NonPagedPool, bmplen, ALLOC_TAG); 1024 if (!ps->bmparr) { 1025 ERR("out of memory\n"); 1026 ExFreePool(ps); 1027 Status = STATUS_INSUFFICIENT_RESOURCES; 1028 goto end; 1029 } 1030 1031 RtlInitializeBitMap(&ps->bmp, ps->bmparr, (ULONG)((num_data_stripes * c->chunk_item->stripe_length) / Vcb->superblock.sector_size)); 1032 RtlSetAllBits(&ps->bmp); 1033 1034 RtlCopyMemory(ps->data + address - stripe_addr, data, length); 1035 RtlClearBits(&ps->bmp, (ULONG)((address - stripe_addr) / Vcb->superblock.sector_size), length / Vcb->superblock.sector_size); 1036 1037 InsertHeadList(le->Blink, &ps->list_entry); 1038 1039 Status = STATUS_SUCCESS; 1040 1041 end: 1042 ExReleaseResourceLite(&c->partial_stripes_lock); 1043 1044 return Status; 1045 } 1046 1047 typedef struct { 1048 PMDL mdl; 1049 PFN_NUMBER* pfns; 1050 } log_stripe; 1051 1052 static NTSTATUS prepare_raid5_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp, 1053 UINT64 irp_offset, ULONG priority, write_data_context* wtc) { 1054 UINT64 startoff, endoff, parity_start, parity_end; 1055 UINT16 startoffstripe, endoffstripe, parity, num_data_stripes = c->chunk_item->num_stripes - 1; 1056 UINT64 pos, parity_pos, *stripeoff = NULL; 1057 UINT32 i; 1058 BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0); 1059 PMDL master_mdl; 1060 NTSTATUS Status; 1061 PFN_NUMBER *pfns, *parity_pfns; 1062 log_stripe* log_stripes = NULL; 1063 1064 if ((address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) { 1065 UINT64 delta = (address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length); 1066 1067 delta = min(irp_offset + length, delta); 1068 Status = add_partial_stripe(Vcb, c, address + length - delta, (UINT32)delta, (UINT8*)data + irp_offset + length - delta); 1069 if (!NT_SUCCESS(Status)) { 1070 ERR("add_partial_stripe returned %08x\n", Status); 1071 goto exit; 1072 } 1073 1074 length -= (UINT32)delta; 1075 } 1076 1077 if (length > 0 && (address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) { 1078 UINT64 delta = (num_data_stripes * c->chunk_item->stripe_length) - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length)); 1079 1080 Status = add_partial_stripe(Vcb, c, address, (UINT32)delta, (UINT8*)data + irp_offset); 1081 if (!NT_SUCCESS(Status)) { 1082 ERR("add_partial_stripe returned %08x\n", Status); 1083 goto exit; 1084 } 1085 1086 address += delta; 1087 length -= (UINT32)delta; 1088 irp_offset += delta; 1089 } 1090 1091 if (length == 0) { 1092 Status = STATUS_SUCCESS; 1093 goto exit; 1094 } 1095 1096 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, num_data_stripes, &startoff, &startoffstripe); 1097 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, num_data_stripes, &endoff, &endoffstripe); 1098 1099 pos = 0; 1100 while (pos < length) { 1101 parity = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1102 1103 if (pos == 0) { 1104 UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes; 1105 ULONG skip, writelen; 1106 1107 i = startoffstripe; 1108 while (stripe != parity) { 1109 if (i == startoffstripe) { 1110 writelen = (ULONG)min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length)); 1111 1112 stripes[stripe].start = startoff; 1113 stripes[stripe].end = startoff + writelen; 1114 1115 pos += writelen; 1116 1117 if (pos == length) 1118 break; 1119 } else { 1120 writelen = (ULONG)min(length - pos, c->chunk_item->stripe_length); 1121 1122 stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length); 1123 stripes[stripe].end = stripes[stripe].start + writelen; 1124 1125 pos += writelen; 1126 1127 if (pos == length) 1128 break; 1129 } 1130 1131 i++; 1132 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1133 } 1134 1135 if (pos == length) 1136 break; 1137 1138 for (i = 0; i < startoffstripe; i++) { 1139 stripe = (parity + i + 1) % c->chunk_item->num_stripes; 1140 1141 stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1142 } 1143 1144 stripes[parity].start = stripes[parity].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1145 1146 if (length - pos > c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length) { 1147 skip = (ULONG)(((length - pos) / (c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length)) - 1); 1148 1149 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1150 stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length; 1151 } 1152 1153 pos += skip * num_data_stripes * c->chunk_item->num_stripes * c->chunk_item->stripe_length; 1154 } 1155 } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) { 1156 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1157 stripes[i].end += c->chunk_item->stripe_length; 1158 } 1159 1160 pos += c->chunk_item->stripe_length * num_data_stripes; 1161 } else { 1162 UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes; 1163 1164 i = 0; 1165 while (stripe != parity) { 1166 if (endoffstripe == i) { 1167 stripes[stripe].end = endoff + 1; 1168 break; 1169 } else if (endoffstripe > i) 1170 stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1171 1172 i++; 1173 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1174 } 1175 1176 break; 1177 } 1178 } 1179 1180 parity_start = 0xffffffffffffffff; 1181 parity_end = 0; 1182 1183 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1184 if (stripes[i].start != 0 || stripes[i].end != 0) { 1185 parity_start = min(stripes[i].start, parity_start); 1186 parity_end = max(stripes[i].end, parity_end); 1187 } 1188 } 1189 1190 if (parity_end == parity_start) { 1191 Status = STATUS_SUCCESS; 1192 goto exit; 1193 } 1194 1195 parity = (((address - c->offset) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1196 stripes[parity].start = parity_start; 1197 1198 parity = (((address - c->offset + length - 1) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1199 stripes[parity].end = parity_end; 1200 1201 log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * num_data_stripes, ALLOC_TAG); 1202 if (!log_stripes) { 1203 ERR("out of memory\n"); 1204 Status = STATUS_INSUFFICIENT_RESOURCES; 1205 goto exit; 1206 } 1207 1208 RtlZeroMemory(log_stripes, sizeof(log_stripe) * num_data_stripes); 1209 1210 for (i = 0; i < num_data_stripes; i++) { 1211 log_stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL); 1212 if (!log_stripes[i].mdl) { 1213 ERR("out of memory\n"); 1214 Status = STATUS_INSUFFICIENT_RESOURCES; 1215 goto exit; 1216 } 1217 1218 log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL; 1219 log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1); 1220 } 1221 1222 wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG); 1223 if (!wtc->parity1) { 1224 ERR("out of memory\n"); 1225 Status = STATUS_INSUFFICIENT_RESOURCES; 1226 goto exit; 1227 } 1228 1229 wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL); 1230 if (!wtc->parity1_mdl) { 1231 ERR("out of memory\n"); 1232 Status = STATUS_INSUFFICIENT_RESOURCES; 1233 goto exit; 1234 } 1235 1236 MmBuildMdlForNonPagedPool(wtc->parity1_mdl); 1237 1238 if (file_write) 1239 master_mdl = Irp->MdlAddress; 1240 else if (((ULONG_PTR)data % PAGE_SIZE) != 0) { 1241 wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 1242 if (!wtc->scratch) { 1243 ERR("out of memory\n"); 1244 Status = STATUS_INSUFFICIENT_RESOURCES; 1245 goto exit; 1246 } 1247 1248 RtlCopyMemory(wtc->scratch, (UINT8*)data + irp_offset, length); 1249 1250 master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL); 1251 if (!master_mdl) { 1252 ERR("out of memory\n"); 1253 Status = STATUS_INSUFFICIENT_RESOURCES; 1254 goto exit; 1255 } 1256 1257 MmBuildMdlForNonPagedPool(master_mdl); 1258 1259 wtc->mdl = master_mdl; 1260 } else { 1261 master_mdl = IoAllocateMdl((UINT8*)data + irp_offset, length, FALSE, FALSE, NULL); 1262 if (!master_mdl) { 1263 ERR("out of memory\n"); 1264 Status = STATUS_INSUFFICIENT_RESOURCES; 1265 goto exit; 1266 } 1267 1268 Status = STATUS_SUCCESS; 1269 1270 _SEH2_TRY { 1271 MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess); 1272 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 1273 Status = _SEH2_GetExceptionCode(); 1274 } _SEH2_END; 1275 1276 if (!NT_SUCCESS(Status)) { 1277 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 1278 IoFreeMdl(master_mdl); 1279 return Status; 1280 } 1281 1282 wtc->mdl = master_mdl; 1283 } 1284 1285 pfns = (PFN_NUMBER*)(master_mdl + 1); 1286 parity_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1); 1287 1288 if (file_write) 1289 pfns = &pfns[irp_offset >> PAGE_SHIFT]; 1290 1291 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1292 if (stripes[i].start != stripes[i].end) { 1293 stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL); 1294 if (!stripes[i].mdl) { 1295 ERR("IoAllocateMdl failed\n"); 1296 Status = STATUS_INSUFFICIENT_RESOURCES; 1297 goto exit; 1298 } 1299 } 1300 } 1301 1302 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); 1303 if (!stripeoff) { 1304 ERR("out of memory\n"); 1305 Status = STATUS_INSUFFICIENT_RESOURCES; 1306 goto exit; 1307 } 1308 1309 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes); 1310 1311 pos = 0; 1312 parity_pos = 0; 1313 1314 while (pos < length) { 1315 PFN_NUMBER* stripe_pfns; 1316 1317 parity = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1318 1319 if (pos == 0) { 1320 UINT16 stripe = (parity + startoffstripe + 1) % c->chunk_item->num_stripes; 1321 UINT32 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, 1322 c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length))); 1323 UINT32 maxwritelen = writelen; 1324 1325 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1326 1327 RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1328 1329 RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1330 log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT; 1331 1332 stripeoff[stripe] = writelen; 1333 pos += writelen; 1334 1335 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1336 i = startoffstripe + 1; 1337 1338 while (stripe != parity) { 1339 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1340 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length)); 1341 1342 if (writelen == 0) 1343 break; 1344 1345 if (writelen > maxwritelen) 1346 maxwritelen = writelen; 1347 1348 RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1349 1350 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1351 log_stripes[i].pfns += writelen >> PAGE_SHIFT; 1352 1353 stripeoff[stripe] = writelen; 1354 pos += writelen; 1355 1356 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1357 i++; 1358 } 1359 1360 stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1); 1361 1362 RtlCopyMemory(stripe_pfns, parity_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1363 stripeoff[parity] = maxwritelen; 1364 parity_pos = maxwritelen; 1365 } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) { 1366 UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes; 1367 1368 i = 0; 1369 while (stripe != parity) { 1370 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1371 1372 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1373 1374 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1375 log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT; 1376 1377 stripeoff[stripe] += c->chunk_item->stripe_length; 1378 pos += c->chunk_item->stripe_length; 1379 1380 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1381 i++; 1382 } 1383 1384 stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1); 1385 1386 RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1387 stripeoff[parity] += c->chunk_item->stripe_length; 1388 parity_pos += c->chunk_item->stripe_length; 1389 } else { 1390 UINT16 stripe = (parity + 1) % c->chunk_item->num_stripes; 1391 UINT32 writelen, maxwritelen = 0; 1392 1393 i = 0; 1394 while (pos < length) { 1395 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1396 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length)); 1397 1398 if (writelen == 0) 1399 break; 1400 1401 if (writelen > maxwritelen) 1402 maxwritelen = writelen; 1403 1404 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1405 1406 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1407 log_stripes[i].pfns += writelen >> PAGE_SHIFT; 1408 1409 stripeoff[stripe] += writelen; 1410 pos += writelen; 1411 1412 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1413 i++; 1414 } 1415 1416 stripe_pfns = (PFN_NUMBER*)(stripes[parity].mdl + 1); 1417 1418 RtlCopyMemory(&stripe_pfns[stripeoff[parity] >> PAGE_SHIFT], &parity_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1419 } 1420 } 1421 1422 for (i = 0; i < num_data_stripes; i++) { 1423 UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[i].mdl, priority); 1424 1425 if (i == 0) 1426 RtlCopyMemory(wtc->parity1, ss, (UINT32)(parity_end - parity_start)); 1427 else 1428 do_xor(wtc->parity1, ss, (UINT32)(parity_end - parity_start)); 1429 } 1430 1431 Status = STATUS_SUCCESS; 1432 1433 exit: 1434 if (log_stripes) { 1435 for (i = 0; i < num_data_stripes; i++) { 1436 if (log_stripes[i].mdl) 1437 IoFreeMdl(log_stripes[i].mdl); 1438 } 1439 1440 ExFreePool(log_stripes); 1441 } 1442 1443 if (stripeoff) 1444 ExFreePool(stripeoff); 1445 1446 return Status; 1447 } 1448 1449 static NTSTATUS prepare_raid6_write(device_extension* Vcb, chunk* c, UINT64 address, void* data, UINT32 length, write_stripe* stripes, PIRP Irp, 1450 UINT64 irp_offset, ULONG priority, write_data_context* wtc) { 1451 UINT64 startoff, endoff, parity_start, parity_end; 1452 UINT16 startoffstripe, endoffstripe, parity1, num_data_stripes = c->chunk_item->num_stripes - 2; 1453 UINT64 pos, parity_pos, *stripeoff = NULL; 1454 UINT32 i; 1455 BOOL file_write = Irp && Irp->MdlAddress && (Irp->MdlAddress->ByteOffset == 0); 1456 PMDL master_mdl; 1457 NTSTATUS Status; 1458 PFN_NUMBER *pfns, *parity1_pfns, *parity2_pfns; 1459 log_stripe* log_stripes = NULL; 1460 1461 if ((address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) { 1462 UINT64 delta = (address + length - c->offset) % (num_data_stripes * c->chunk_item->stripe_length); 1463 1464 delta = min(irp_offset + length, delta); 1465 Status = add_partial_stripe(Vcb, c, address + length - delta, (UINT32)delta, (UINT8*)data + irp_offset + length - delta); 1466 if (!NT_SUCCESS(Status)) { 1467 ERR("add_partial_stripe returned %08x\n", Status); 1468 goto exit; 1469 } 1470 1471 length -= (UINT32)delta; 1472 } 1473 1474 if (length > 0 && (address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length) > 0) { 1475 UINT64 delta = (num_data_stripes * c->chunk_item->stripe_length) - ((address - c->offset) % (num_data_stripes * c->chunk_item->stripe_length)); 1476 1477 Status = add_partial_stripe(Vcb, c, address, (UINT32)delta, (UINT8*)data + irp_offset); 1478 if (!NT_SUCCESS(Status)) { 1479 ERR("add_partial_stripe returned %08x\n", Status); 1480 goto exit; 1481 } 1482 1483 address += delta; 1484 length -= (UINT32)delta; 1485 irp_offset += delta; 1486 } 1487 1488 if (length == 0) { 1489 Status = STATUS_SUCCESS; 1490 goto exit; 1491 } 1492 1493 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, num_data_stripes, &startoff, &startoffstripe); 1494 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, num_data_stripes, &endoff, &endoffstripe); 1495 1496 pos = 0; 1497 while (pos < length) { 1498 parity1 = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1499 1500 if (pos == 0) { 1501 UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes; 1502 UINT16 parity2 = (parity1 + 1) % c->chunk_item->num_stripes; 1503 ULONG skip, writelen; 1504 1505 i = startoffstripe; 1506 while (stripe != parity1) { 1507 if (i == startoffstripe) { 1508 writelen = (ULONG)min(length, c->chunk_item->stripe_length - (startoff % c->chunk_item->stripe_length)); 1509 1510 stripes[stripe].start = startoff; 1511 stripes[stripe].end = startoff + writelen; 1512 1513 pos += writelen; 1514 1515 if (pos == length) 1516 break; 1517 } else { 1518 writelen = (ULONG)min(length - pos, c->chunk_item->stripe_length); 1519 1520 stripes[stripe].start = startoff - (startoff % c->chunk_item->stripe_length); 1521 stripes[stripe].end = stripes[stripe].start + writelen; 1522 1523 pos += writelen; 1524 1525 if (pos == length) 1526 break; 1527 } 1528 1529 i++; 1530 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1531 } 1532 1533 if (pos == length) 1534 break; 1535 1536 for (i = 0; i < startoffstripe; i++) { 1537 stripe = (parity1 + i + 2) % c->chunk_item->num_stripes; 1538 1539 stripes[stripe].start = stripes[stripe].end = startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1540 } 1541 1542 stripes[parity1].start = stripes[parity1].end = stripes[parity2].start = stripes[parity2].end = 1543 startoff - (startoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1544 1545 if (length - pos > c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length) { 1546 skip = (ULONG)(((length - pos) / (c->chunk_item->num_stripes * num_data_stripes * c->chunk_item->stripe_length)) - 1); 1547 1548 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1549 stripes[i].end += skip * c->chunk_item->num_stripes * c->chunk_item->stripe_length; 1550 } 1551 1552 pos += skip * num_data_stripes * c->chunk_item->num_stripes * c->chunk_item->stripe_length; 1553 } 1554 } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) { 1555 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1556 stripes[i].end += c->chunk_item->stripe_length; 1557 } 1558 1559 pos += c->chunk_item->stripe_length * num_data_stripes; 1560 } else { 1561 UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes; 1562 1563 i = 0; 1564 while (stripe != parity1) { 1565 if (endoffstripe == i) { 1566 stripes[stripe].end = endoff + 1; 1567 break; 1568 } else if (endoffstripe > i) 1569 stripes[stripe].end = endoff - (endoff % c->chunk_item->stripe_length) + c->chunk_item->stripe_length; 1570 1571 i++; 1572 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1573 } 1574 1575 break; 1576 } 1577 } 1578 1579 parity_start = 0xffffffffffffffff; 1580 parity_end = 0; 1581 1582 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1583 if (stripes[i].start != 0 || stripes[i].end != 0) { 1584 parity_start = min(stripes[i].start, parity_start); 1585 parity_end = max(stripes[i].end, parity_end); 1586 } 1587 } 1588 1589 if (parity_end == parity_start) { 1590 Status = STATUS_SUCCESS; 1591 goto exit; 1592 } 1593 1594 parity1 = (((address - c->offset) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1595 stripes[parity1].start = stripes[(parity1 + 1) % c->chunk_item->num_stripes].start = parity_start; 1596 1597 parity1 = (((address - c->offset + length - 1) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1598 stripes[parity1].end = stripes[(parity1 + 1) % c->chunk_item->num_stripes].end = parity_end; 1599 1600 log_stripes = ExAllocatePoolWithTag(NonPagedPool, sizeof(log_stripe) * num_data_stripes, ALLOC_TAG); 1601 if (!log_stripes) { 1602 ERR("out of memory\n"); 1603 Status = STATUS_INSUFFICIENT_RESOURCES; 1604 goto exit; 1605 } 1606 1607 RtlZeroMemory(log_stripes, sizeof(log_stripe) * num_data_stripes); 1608 1609 for (i = 0; i < num_data_stripes; i++) { 1610 log_stripes[i].mdl = IoAllocateMdl(NULL, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL); 1611 if (!log_stripes[i].mdl) { 1612 ERR("out of memory\n"); 1613 Status = STATUS_INSUFFICIENT_RESOURCES; 1614 goto exit; 1615 } 1616 1617 log_stripes[i].mdl->MdlFlags |= MDL_PARTIAL; 1618 log_stripes[i].pfns = (PFN_NUMBER*)(log_stripes[i].mdl + 1); 1619 } 1620 1621 wtc->parity1 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG); 1622 if (!wtc->parity1) { 1623 ERR("out of memory\n"); 1624 Status = STATUS_INSUFFICIENT_RESOURCES; 1625 goto exit; 1626 } 1627 1628 wtc->parity2 = ExAllocatePoolWithTag(NonPagedPool, (ULONG)(parity_end - parity_start), ALLOC_TAG); 1629 if (!wtc->parity2) { 1630 ERR("out of memory\n"); 1631 Status = STATUS_INSUFFICIENT_RESOURCES; 1632 goto exit; 1633 } 1634 1635 wtc->parity1_mdl = IoAllocateMdl(wtc->parity1, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL); 1636 if (!wtc->parity1_mdl) { 1637 ERR("out of memory\n"); 1638 Status = STATUS_INSUFFICIENT_RESOURCES; 1639 goto exit; 1640 } 1641 1642 MmBuildMdlForNonPagedPool(wtc->parity1_mdl); 1643 1644 wtc->parity2_mdl = IoAllocateMdl(wtc->parity2, (ULONG)(parity_end - parity_start), FALSE, FALSE, NULL); 1645 if (!wtc->parity2_mdl) { 1646 ERR("out of memory\n"); 1647 Status = STATUS_INSUFFICIENT_RESOURCES; 1648 goto exit; 1649 } 1650 1651 MmBuildMdlForNonPagedPool(wtc->parity2_mdl); 1652 1653 if (file_write) 1654 master_mdl = Irp->MdlAddress; 1655 else if (((ULONG_PTR)data % PAGE_SIZE) != 0) { 1656 wtc->scratch = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG); 1657 if (!wtc->scratch) { 1658 ERR("out of memory\n"); 1659 Status = STATUS_INSUFFICIENT_RESOURCES; 1660 goto exit; 1661 } 1662 1663 RtlCopyMemory(wtc->scratch, (UINT8*)data + irp_offset, length); 1664 1665 master_mdl = IoAllocateMdl(wtc->scratch, length, FALSE, FALSE, NULL); 1666 if (!master_mdl) { 1667 ERR("out of memory\n"); 1668 Status = STATUS_INSUFFICIENT_RESOURCES; 1669 goto exit; 1670 } 1671 1672 MmBuildMdlForNonPagedPool(master_mdl); 1673 1674 wtc->mdl = master_mdl; 1675 } else { 1676 master_mdl = IoAllocateMdl((UINT8*)data + irp_offset, length, FALSE, FALSE, NULL); 1677 if (!master_mdl) { 1678 ERR("out of memory\n"); 1679 Status = STATUS_INSUFFICIENT_RESOURCES; 1680 goto exit; 1681 } 1682 1683 Status = STATUS_SUCCESS; 1684 1685 _SEH2_TRY { 1686 MmProbeAndLockPages(master_mdl, KernelMode, IoReadAccess); 1687 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 1688 Status = _SEH2_GetExceptionCode(); 1689 } _SEH2_END; 1690 1691 if (!NT_SUCCESS(Status)) { 1692 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 1693 IoFreeMdl(master_mdl); 1694 goto exit; 1695 } 1696 1697 wtc->mdl = master_mdl; 1698 } 1699 1700 pfns = (PFN_NUMBER*)(master_mdl + 1); 1701 parity1_pfns = (PFN_NUMBER*)(wtc->parity1_mdl + 1); 1702 parity2_pfns = (PFN_NUMBER*)(wtc->parity2_mdl + 1); 1703 1704 if (file_write) 1705 pfns = &pfns[irp_offset >> PAGE_SHIFT]; 1706 1707 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1708 if (stripes[i].start != stripes[i].end) { 1709 stripes[i].mdl = IoAllocateMdl((UINT8*)MmGetMdlVirtualAddress(master_mdl) + irp_offset, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL); 1710 if (!stripes[i].mdl) { 1711 ERR("IoAllocateMdl failed\n"); 1712 Status = STATUS_INSUFFICIENT_RESOURCES; 1713 goto exit; 1714 } 1715 } 1716 } 1717 1718 stripeoff = ExAllocatePoolWithTag(PagedPool, sizeof(UINT64) * c->chunk_item->num_stripes, ALLOC_TAG); 1719 if (!stripeoff) { 1720 ERR("out of memory\n"); 1721 Status = STATUS_INSUFFICIENT_RESOURCES; 1722 goto exit; 1723 } 1724 1725 RtlZeroMemory(stripeoff, sizeof(UINT64) * c->chunk_item->num_stripes); 1726 1727 pos = 0; 1728 parity_pos = 0; 1729 1730 while (pos < length) { 1731 PFN_NUMBER* stripe_pfns; 1732 1733 parity1 = (((address - c->offset + pos) / (num_data_stripes * c->chunk_item->stripe_length)) + num_data_stripes) % c->chunk_item->num_stripes; 1734 1735 if (pos == 0) { 1736 UINT16 stripe = (parity1 + startoffstripe + 2) % c->chunk_item->num_stripes, parity2; 1737 UINT32 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, 1738 c->chunk_item->stripe_length - (stripes[stripe].start % c->chunk_item->stripe_length))); 1739 UINT32 maxwritelen = writelen; 1740 1741 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1742 1743 RtlCopyMemory(stripe_pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1744 1745 RtlCopyMemory(log_stripes[startoffstripe].pfns, pfns, writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1746 log_stripes[startoffstripe].pfns += writelen >> PAGE_SHIFT; 1747 1748 stripeoff[stripe] = writelen; 1749 pos += writelen; 1750 1751 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1752 i = startoffstripe + 1; 1753 1754 while (stripe != parity1) { 1755 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1756 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length)); 1757 1758 if (writelen == 0) 1759 break; 1760 1761 if (writelen > maxwritelen) 1762 maxwritelen = writelen; 1763 1764 RtlCopyMemory(stripe_pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1765 1766 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1767 log_stripes[i].pfns += writelen >> PAGE_SHIFT; 1768 1769 stripeoff[stripe] = writelen; 1770 pos += writelen; 1771 1772 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1773 i++; 1774 } 1775 1776 stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1); 1777 RtlCopyMemory(stripe_pfns, parity1_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1778 stripeoff[parity1] = maxwritelen; 1779 1780 parity2 = (parity1 + 1) % c->chunk_item->num_stripes; 1781 1782 stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1); 1783 RtlCopyMemory(stripe_pfns, parity2_pfns, maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1784 stripeoff[parity2] = maxwritelen; 1785 1786 parity_pos = maxwritelen; 1787 } else if (length - pos >= c->chunk_item->stripe_length * num_data_stripes) { 1788 UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2; 1789 1790 i = 0; 1791 while (stripe != parity1) { 1792 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1793 1794 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1795 1796 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1797 log_stripes[i].pfns += c->chunk_item->stripe_length >> PAGE_SHIFT; 1798 1799 stripeoff[stripe] += c->chunk_item->stripe_length; 1800 pos += c->chunk_item->stripe_length; 1801 1802 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1803 i++; 1804 } 1805 1806 stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1); 1807 RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1808 stripeoff[parity1] += c->chunk_item->stripe_length; 1809 1810 parity2 = (parity1 + 1) % c->chunk_item->num_stripes; 1811 1812 stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1); 1813 RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], (ULONG)(c->chunk_item->stripe_length * sizeof(PFN_NUMBER) >> PAGE_SHIFT)); 1814 stripeoff[parity2] += c->chunk_item->stripe_length; 1815 1816 parity_pos += c->chunk_item->stripe_length; 1817 } else { 1818 UINT16 stripe = (parity1 + 2) % c->chunk_item->num_stripes, parity2; 1819 UINT32 writelen, maxwritelen = 0; 1820 1821 i = 0; 1822 while (pos < length) { 1823 stripe_pfns = (PFN_NUMBER*)(stripes[stripe].mdl + 1); 1824 writelen = (UINT32)min(length - pos, min(stripes[stripe].end - stripes[stripe].start, c->chunk_item->stripe_length)); 1825 1826 if (writelen == 0) 1827 break; 1828 1829 if (writelen > maxwritelen) 1830 maxwritelen = writelen; 1831 1832 RtlCopyMemory(&stripe_pfns[stripeoff[stripe] >> PAGE_SHIFT], &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1833 1834 RtlCopyMemory(log_stripes[i].pfns, &pfns[pos >> PAGE_SHIFT], writelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1835 log_stripes[i].pfns += writelen >> PAGE_SHIFT; 1836 1837 stripeoff[stripe] += writelen; 1838 pos += writelen; 1839 1840 stripe = (stripe + 1) % c->chunk_item->num_stripes; 1841 i++; 1842 } 1843 1844 stripe_pfns = (PFN_NUMBER*)(stripes[parity1].mdl + 1); 1845 RtlCopyMemory(&stripe_pfns[stripeoff[parity1] >> PAGE_SHIFT], &parity1_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1846 1847 parity2 = (parity1 + 1) % c->chunk_item->num_stripes; 1848 1849 stripe_pfns = (PFN_NUMBER*)(stripes[parity2].mdl + 1); 1850 RtlCopyMemory(&stripe_pfns[stripeoff[parity2] >> PAGE_SHIFT], &parity2_pfns[parity_pos >> PAGE_SHIFT], maxwritelen * sizeof(PFN_NUMBER) >> PAGE_SHIFT); 1851 } 1852 } 1853 1854 for (i = 0; i < num_data_stripes; i++) { 1855 UINT8* ss = MmGetSystemAddressForMdlSafe(log_stripes[c->chunk_item->num_stripes - 3 - i].mdl, priority); 1856 1857 if (i == 0) { 1858 RtlCopyMemory(wtc->parity1, ss, (ULONG)(parity_end - parity_start)); 1859 RtlCopyMemory(wtc->parity2, ss, (ULONG)(parity_end - parity_start)); 1860 } else { 1861 do_xor(wtc->parity1, ss, (UINT32)(parity_end - parity_start)); 1862 1863 galois_double(wtc->parity2, (UINT32)(parity_end - parity_start)); 1864 do_xor(wtc->parity2, ss, (UINT32)(parity_end - parity_start)); 1865 } 1866 } 1867 1868 Status = STATUS_SUCCESS; 1869 1870 exit: 1871 if (log_stripes) { 1872 for (i = 0; i < num_data_stripes; i++) { 1873 if (log_stripes[i].mdl) 1874 IoFreeMdl(log_stripes[i].mdl); 1875 } 1876 1877 ExFreePool(log_stripes); 1878 } 1879 1880 if (stripeoff) 1881 ExFreePool(stripeoff); 1882 1883 return Status; 1884 } 1885 1886 NTSTATUS write_data(_In_ device_extension* Vcb, _In_ UINT64 address, _In_reads_bytes_(length) void* data, _In_ UINT32 length, _In_ write_data_context* wtc, 1887 _In_opt_ PIRP Irp, _In_opt_ chunk* c, _In_ BOOL file_write, _In_ UINT64 irp_offset, _In_ ULONG priority) { 1888 NTSTATUS Status; 1889 UINT32 i; 1890 CHUNK_ITEM_STRIPE* cis; 1891 write_stripe* stripes = NULL; 1892 UINT64 total_writing = 0; 1893 ULONG allowed_missing, missing; 1894 1895 TRACE("(%p, %llx, %p, %x)\n", Vcb, address, data, length); 1896 1897 if (!c) { 1898 c = get_chunk_from_address(Vcb, address); 1899 if (!c) { 1900 ERR("could not get chunk for address %llx\n", address); 1901 return STATUS_INTERNAL_ERROR; 1902 } 1903 } 1904 1905 stripes = ExAllocatePoolWithTag(PagedPool, sizeof(write_stripe) * c->chunk_item->num_stripes, ALLOC_TAG); 1906 if (!stripes) { 1907 ERR("out of memory\n"); 1908 return STATUS_INSUFFICIENT_RESOURCES; 1909 } 1910 1911 RtlZeroMemory(stripes, sizeof(write_stripe) * c->chunk_item->num_stripes); 1912 1913 cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1]; 1914 1915 if (c->chunk_item->type & BLOCK_FLAG_RAID0) { 1916 Status = prepare_raid0_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc); 1917 if (!NT_SUCCESS(Status)) { 1918 ERR("prepare_raid0_write returned %08x\n", Status); 1919 goto prepare_failed; 1920 } 1921 1922 allowed_missing = 0; 1923 } else if (c->chunk_item->type & BLOCK_FLAG_RAID10) { 1924 Status = prepare_raid10_write(c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, wtc); 1925 if (!NT_SUCCESS(Status)) { 1926 ERR("prepare_raid10_write returned %08x\n", Status); 1927 goto prepare_failed; 1928 } 1929 1930 allowed_missing = 1; 1931 } else if (c->chunk_item->type & BLOCK_FLAG_RAID5) { 1932 Status = prepare_raid5_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, priority, wtc); 1933 if (!NT_SUCCESS(Status)) { 1934 ERR("prepare_raid5_write returned %08x\n", Status); 1935 goto prepare_failed; 1936 } 1937 1938 allowed_missing = 1; 1939 } else if (c->chunk_item->type & BLOCK_FLAG_RAID6) { 1940 Status = prepare_raid6_write(Vcb, c, address, data, length, stripes, file_write ? Irp : NULL, irp_offset, priority, wtc); 1941 if (!NT_SUCCESS(Status)) { 1942 ERR("prepare_raid6_write returned %08x\n", Status); 1943 goto prepare_failed; 1944 } 1945 1946 allowed_missing = 2; 1947 } else { // write same data to every location - SINGLE, DUP, RAID1 1948 for (i = 0; i < c->chunk_item->num_stripes; i++) { 1949 stripes[i].start = address - c->offset; 1950 stripes[i].end = stripes[i].start + length; 1951 stripes[i].data = data; 1952 stripes[i].irp_offset = irp_offset; 1953 1954 if (c->devices[i]->devobj) { 1955 if (file_write) { 1956 UINT8* va; 1957 ULONG writelen = (ULONG)(stripes[i].end - stripes[i].start); 1958 1959 va = (UINT8*)MmGetMdlVirtualAddress(Irp->MdlAddress) + stripes[i].irp_offset; 1960 1961 stripes[i].mdl = IoAllocateMdl(va, writelen, FALSE, FALSE, NULL); 1962 if (!stripes[i].mdl) { 1963 ERR("IoAllocateMdl failed\n"); 1964 Status = STATUS_INSUFFICIENT_RESOURCES; 1965 goto prepare_failed; 1966 } 1967 1968 IoBuildPartialMdl(Irp->MdlAddress, stripes[i].mdl, va, writelen); 1969 } else { 1970 stripes[i].mdl = IoAllocateMdl(stripes[i].data, (ULONG)(stripes[i].end - stripes[i].start), FALSE, FALSE, NULL); 1971 if (!stripes[i].mdl) { 1972 ERR("IoAllocateMdl failed\n"); 1973 Status = STATUS_INSUFFICIENT_RESOURCES; 1974 goto prepare_failed; 1975 } 1976 1977 Status = STATUS_SUCCESS; 1978 1979 _SEH2_TRY { 1980 MmProbeAndLockPages(stripes[i].mdl, KernelMode, IoReadAccess); 1981 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 1982 Status = _SEH2_GetExceptionCode(); 1983 } _SEH2_END; 1984 1985 if (!NT_SUCCESS(Status)) { 1986 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 1987 IoFreeMdl(stripes[i].mdl); 1988 stripes[i].mdl = NULL; 1989 goto prepare_failed; 1990 } 1991 } 1992 } 1993 } 1994 1995 allowed_missing = c->chunk_item->num_stripes - 1; 1996 } 1997 1998 missing = 0; 1999 for (i = 0; i < c->chunk_item->num_stripes; i++) { 2000 if (!c->devices[i]->devobj) 2001 missing++; 2002 } 2003 2004 if (missing > allowed_missing) { 2005 ERR("cannot write as %u missing devices (maximum %u)\n", missing, allowed_missing); 2006 Status = STATUS_DEVICE_NOT_READY; 2007 goto prepare_failed; 2008 } 2009 2010 for (i = 0; i < c->chunk_item->num_stripes; i++) { 2011 write_data_stripe* stripe; 2012 PIO_STACK_LOCATION IrpSp; 2013 2014 stripe = ExAllocatePoolWithTag(NonPagedPool, sizeof(write_data_stripe), ALLOC_TAG); 2015 if (!stripe) { 2016 ERR("out of memory\n"); 2017 Status = STATUS_INSUFFICIENT_RESOURCES; 2018 goto end; 2019 } 2020 2021 if (stripes[i].start == stripes[i].end || !c->devices[i]->devobj) { 2022 stripe->status = WriteDataStatus_Ignore; 2023 stripe->Irp = NULL; 2024 stripe->buf = stripes[i].data; 2025 stripe->mdl = NULL; 2026 } else { 2027 stripe->context = (struct _write_data_context*)wtc; 2028 stripe->buf = stripes[i].data; 2029 stripe->device = c->devices[i]; 2030 RtlZeroMemory(&stripe->iosb, sizeof(IO_STATUS_BLOCK)); 2031 stripe->status = WriteDataStatus_Pending; 2032 stripe->mdl = stripes[i].mdl; 2033 2034 if (!Irp) { 2035 stripe->Irp = IoAllocateIrp(stripe->device->devobj->StackSize, FALSE); 2036 2037 if (!stripe->Irp) { 2038 ERR("IoAllocateIrp failed\n"); 2039 ExFreePool(stripe); 2040 Status = STATUS_INSUFFICIENT_RESOURCES; 2041 goto end; 2042 } 2043 } else { 2044 stripe->Irp = IoMakeAssociatedIrp(Irp, stripe->device->devobj->StackSize); 2045 2046 if (!stripe->Irp) { 2047 ERR("IoMakeAssociatedIrp failed\n"); 2048 ExFreePool(stripe); 2049 Status = STATUS_INSUFFICIENT_RESOURCES; 2050 goto end; 2051 } 2052 } 2053 2054 IrpSp = IoGetNextIrpStackLocation(stripe->Irp); 2055 IrpSp->MajorFunction = IRP_MJ_WRITE; 2056 2057 if (stripe->device->devobj->Flags & DO_BUFFERED_IO) { 2058 stripe->Irp->AssociatedIrp.SystemBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, priority); 2059 2060 stripe->Irp->Flags = IRP_BUFFERED_IO; 2061 } else if (stripe->device->devobj->Flags & DO_DIRECT_IO) 2062 stripe->Irp->MdlAddress = stripe->mdl; 2063 else 2064 stripe->Irp->UserBuffer = MmGetSystemAddressForMdlSafe(stripes[i].mdl, priority); 2065 2066 #ifdef DEBUG_PARANOID 2067 if (stripes[i].end < stripes[i].start) { 2068 ERR("trying to write stripe with negative length (%llx < %llx)\n", stripes[i].end, stripes[i].start); 2069 int3; 2070 } 2071 #endif 2072 2073 IrpSp->Parameters.Write.Length = (ULONG)(stripes[i].end - stripes[i].start); 2074 IrpSp->Parameters.Write.ByteOffset.QuadPart = stripes[i].start + cis[i].offset; 2075 2076 total_writing += IrpSp->Parameters.Write.Length; 2077 2078 stripe->Irp->UserIosb = &stripe->iosb; 2079 wtc->stripes_left++; 2080 2081 IoSetCompletionRoutine(stripe->Irp, write_data_completion, stripe, TRUE, TRUE, TRUE); 2082 } 2083 2084 InsertTailList(&wtc->stripes, &stripe->list_entry); 2085 } 2086 2087 if (diskacc) 2088 fFsRtlUpdateDiskCounters(0, total_writing); 2089 2090 Status = STATUS_SUCCESS; 2091 2092 end: 2093 2094 if (stripes) ExFreePool(stripes); 2095 2096 if (!NT_SUCCESS(Status)) 2097 free_write_data_stripes(wtc); 2098 2099 return Status; 2100 2101 prepare_failed: 2102 for (i = 0; i < c->chunk_item->num_stripes; i++) { 2103 if (stripes[i].mdl && (i == 0 || stripes[i].mdl != stripes[i-1].mdl)) { 2104 if (stripes[i].mdl->MdlFlags & MDL_PAGES_LOCKED) 2105 MmUnlockPages(stripes[i].mdl); 2106 2107 IoFreeMdl(stripes[i].mdl); 2108 } 2109 } 2110 2111 if (wtc->parity1_mdl) { 2112 if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED) 2113 MmUnlockPages(wtc->parity1_mdl); 2114 2115 IoFreeMdl(wtc->parity1_mdl); 2116 wtc->parity1_mdl = NULL; 2117 } 2118 2119 if (wtc->parity2_mdl) { 2120 if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED) 2121 MmUnlockPages(wtc->parity2_mdl); 2122 2123 IoFreeMdl(wtc->parity2_mdl); 2124 wtc->parity2_mdl = NULL; 2125 } 2126 2127 if (wtc->mdl) { 2128 if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED) 2129 MmUnlockPages(wtc->mdl); 2130 2131 IoFreeMdl(wtc->mdl); 2132 wtc->mdl = NULL; 2133 } 2134 2135 if (wtc->parity1) { 2136 ExFreePool(wtc->parity1); 2137 wtc->parity1 = NULL; 2138 } 2139 2140 if (wtc->parity2) { 2141 ExFreePool(wtc->parity2); 2142 wtc->parity2 = NULL; 2143 } 2144 2145 if (wtc->scratch) { 2146 ExFreePool(wtc->scratch); 2147 wtc->scratch = NULL; 2148 } 2149 2150 ExFreePool(stripes); 2151 return Status; 2152 } 2153 2154 void get_raid56_lock_range(chunk* c, UINT64 address, UINT64 length, UINT64* lockaddr, UINT64* locklen) { 2155 UINT64 startoff, endoff; 2156 UINT16 startoffstripe, endoffstripe, datastripes; 2157 2158 datastripes = c->chunk_item->num_stripes - (c->chunk_item->type & BLOCK_FLAG_RAID5 ? 1 : 2); 2159 2160 get_raid0_offset(address - c->offset, c->chunk_item->stripe_length, datastripes, &startoff, &startoffstripe); 2161 get_raid0_offset(address + length - c->offset - 1, c->chunk_item->stripe_length, datastripes, &endoff, &endoffstripe); 2162 2163 startoff -= startoff % c->chunk_item->stripe_length; 2164 endoff = sector_align(endoff, c->chunk_item->stripe_length); 2165 2166 *lockaddr = c->offset + (startoff * datastripes); 2167 *locklen = (endoff - startoff) * datastripes; 2168 } 2169 2170 NTSTATUS write_data_complete(device_extension* Vcb, UINT64 address, void* data, UINT32 length, PIRP Irp, chunk* c, BOOL file_write, UINT64 irp_offset, ULONG priority) { 2171 write_data_context wtc; 2172 NTSTATUS Status; 2173 UINT64 lockaddr, locklen; 2174 2175 KeInitializeEvent(&wtc.Event, NotificationEvent, FALSE); 2176 InitializeListHead(&wtc.stripes); 2177 wtc.stripes_left = 0; 2178 wtc.parity1 = wtc.parity2 = wtc.scratch = NULL; 2179 wtc.mdl = wtc.parity1_mdl = wtc.parity2_mdl = NULL; 2180 2181 if (!c) { 2182 c = get_chunk_from_address(Vcb, address); 2183 if (!c) { 2184 ERR("could not get chunk for address %llx\n", address); 2185 return STATUS_INTERNAL_ERROR; 2186 } 2187 } 2188 2189 if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) { 2190 get_raid56_lock_range(c, address, length, &lockaddr, &locklen); 2191 chunk_lock_range(Vcb, c, lockaddr, locklen); 2192 } 2193 2194 _SEH2_TRY { 2195 Status = write_data(Vcb, address, data, length, &wtc, Irp, c, file_write, irp_offset, priority); 2196 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 2197 Status = _SEH2_GetExceptionCode(); 2198 } _SEH2_END; 2199 2200 if (!NT_SUCCESS(Status)) { 2201 ERR("write_data returned %08x\n", Status); 2202 2203 if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) 2204 chunk_unlock_range(Vcb, c, lockaddr, locklen); 2205 2206 free_write_data_stripes(&wtc); 2207 return Status; 2208 } 2209 2210 if (wtc.stripes.Flink != &wtc.stripes) { 2211 // launch writes and wait 2212 LIST_ENTRY* le = wtc.stripes.Flink; 2213 BOOL no_wait = TRUE; 2214 2215 while (le != &wtc.stripes) { 2216 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 2217 2218 if (stripe->status != WriteDataStatus_Ignore) { 2219 IoCallDriver(stripe->device->devobj, stripe->Irp); 2220 no_wait = FALSE; 2221 } 2222 2223 le = le->Flink; 2224 } 2225 2226 if (!no_wait) 2227 KeWaitForSingleObject(&wtc.Event, Executive, KernelMode, FALSE, NULL); 2228 2229 le = wtc.stripes.Flink; 2230 while (le != &wtc.stripes) { 2231 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 2232 2233 if (stripe->status != WriteDataStatus_Ignore && !NT_SUCCESS(stripe->iosb.Status)) { 2234 Status = stripe->iosb.Status; 2235 2236 log_device_error(Vcb, stripe->device, BTRFS_DEV_STAT_WRITE_ERRORS); 2237 break; 2238 } 2239 2240 le = le->Flink; 2241 } 2242 2243 free_write_data_stripes(&wtc); 2244 } 2245 2246 if (c->chunk_item->type & BLOCK_FLAG_RAID5 || c->chunk_item->type & BLOCK_FLAG_RAID6) 2247 chunk_unlock_range(Vcb, c, lockaddr, locklen); 2248 2249 return Status; 2250 } 2251 2252 _Function_class_(IO_COMPLETION_ROUTINE) 2253 #ifdef __REACTOS__ 2254 static NTSTATUS NTAPI write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 2255 #else 2256 static NTSTATUS write_data_completion(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID conptr) { 2257 #endif 2258 write_data_stripe* stripe = conptr; 2259 write_data_context* context = (write_data_context*)stripe->context; 2260 LIST_ENTRY* le; 2261 2262 UNUSED(DeviceObject); 2263 2264 // FIXME - we need a lock here 2265 2266 if (stripe->status == WriteDataStatus_Cancelling) { 2267 stripe->status = WriteDataStatus_Cancelled; 2268 goto end; 2269 } 2270 2271 stripe->iosb = Irp->IoStatus; 2272 2273 if (NT_SUCCESS(Irp->IoStatus.Status)) { 2274 stripe->status = WriteDataStatus_Success; 2275 } else { 2276 le = context->stripes.Flink; 2277 2278 stripe->status = WriteDataStatus_Error; 2279 2280 while (le != &context->stripes) { 2281 write_data_stripe* s2 = CONTAINING_RECORD(le, write_data_stripe, list_entry); 2282 2283 if (s2->status == WriteDataStatus_Pending) { 2284 s2->status = WriteDataStatus_Cancelling; 2285 IoCancelIrp(s2->Irp); 2286 } 2287 2288 le = le->Flink; 2289 } 2290 } 2291 2292 end: 2293 if (InterlockedDecrement(&context->stripes_left) == 0) 2294 KeSetEvent(&context->Event, 0, FALSE); 2295 2296 return STATUS_MORE_PROCESSING_REQUIRED; 2297 } 2298 2299 void free_write_data_stripes(write_data_context* wtc) { 2300 LIST_ENTRY* le; 2301 PMDL last_mdl = NULL; 2302 2303 if (wtc->parity1_mdl) { 2304 if (wtc->parity1_mdl->MdlFlags & MDL_PAGES_LOCKED) 2305 MmUnlockPages(wtc->parity1_mdl); 2306 2307 IoFreeMdl(wtc->parity1_mdl); 2308 } 2309 2310 if (wtc->parity2_mdl) { 2311 if (wtc->parity2_mdl->MdlFlags & MDL_PAGES_LOCKED) 2312 MmUnlockPages(wtc->parity2_mdl); 2313 2314 IoFreeMdl(wtc->parity2_mdl); 2315 } 2316 2317 if (wtc->mdl) { 2318 if (wtc->mdl->MdlFlags & MDL_PAGES_LOCKED) 2319 MmUnlockPages(wtc->mdl); 2320 2321 IoFreeMdl(wtc->mdl); 2322 } 2323 2324 if (wtc->parity1) 2325 ExFreePool(wtc->parity1); 2326 2327 if (wtc->parity2) 2328 ExFreePool(wtc->parity2); 2329 2330 if (wtc->scratch) 2331 ExFreePool(wtc->scratch); 2332 2333 le = wtc->stripes.Flink; 2334 while (le != &wtc->stripes) { 2335 write_data_stripe* stripe = CONTAINING_RECORD(le, write_data_stripe, list_entry); 2336 2337 if (stripe->mdl && stripe->mdl != last_mdl) { 2338 if (stripe->mdl->MdlFlags & MDL_PAGES_LOCKED) 2339 MmUnlockPages(stripe->mdl); 2340 2341 IoFreeMdl(stripe->mdl); 2342 } 2343 2344 last_mdl = stripe->mdl; 2345 2346 if (stripe->Irp) 2347 IoFreeIrp(stripe->Irp); 2348 2349 le = le->Flink; 2350 } 2351 2352 while (!IsListEmpty(&wtc->stripes)) { 2353 write_data_stripe* stripe = CONTAINING_RECORD(RemoveHeadList(&wtc->stripes), write_data_stripe, list_entry); 2354 2355 ExFreePool(stripe); 2356 } 2357 } 2358 2359 void add_extent(_In_ fcb* fcb, _In_ LIST_ENTRY* prevextle, _In_ __drv_aliasesMem extent* newext) { 2360 LIST_ENTRY* le = prevextle->Flink; 2361 2362 while (le != &fcb->extents) { 2363 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 2364 2365 if (ext->offset >= newext->offset) { 2366 InsertHeadList(ext->list_entry.Blink, &newext->list_entry); 2367 return; 2368 } 2369 2370 le = le->Flink; 2371 } 2372 2373 InsertTailList(&fcb->extents, &newext->list_entry); 2374 } 2375 2376 NTSTATUS excise_extents(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 end_data, PIRP Irp, LIST_ENTRY* rollback) { 2377 NTSTATUS Status; 2378 LIST_ENTRY* le; 2379 2380 le = fcb->extents.Flink; 2381 2382 while (le != &fcb->extents) { 2383 LIST_ENTRY* le2 = le->Flink; 2384 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 2385 EXTENT_DATA* ed = &ext->extent_data; 2386 EXTENT_DATA2* ed2 = NULL; 2387 UINT64 len; 2388 2389 if (!ext->ignore) { 2390 if (ed->type != EXTENT_TYPE_INLINE) 2391 ed2 = (EXTENT_DATA2*)ed->data; 2392 2393 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; 2394 2395 if (ext->offset < end_data && ext->offset + len > start_data) { 2396 if (ed->type == EXTENT_TYPE_INLINE) { 2397 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all 2398 remove_fcb_extent(fcb, ext, rollback); 2399 2400 fcb->inode_item.st_blocks -= len; 2401 fcb->inode_item_changed = TRUE; 2402 } else { 2403 ERR("trying to split inline extent\n"); 2404 #ifdef DEBUG_PARANOID 2405 int3; 2406 #endif 2407 return STATUS_INTERNAL_ERROR; 2408 } 2409 } else if (ed->type != EXTENT_TYPE_INLINE) { 2410 if (start_data <= ext->offset && end_data >= ext->offset + len) { // remove all 2411 if (ed2->size != 0) { 2412 chunk* c; 2413 2414 fcb->inode_item.st_blocks -= len; 2415 fcb->inode_item_changed = TRUE; 2416 2417 c = get_chunk_from_address(Vcb, ed2->address); 2418 2419 if (!c) { 2420 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 2421 } else { 2422 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, -1, 2423 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 2424 if (!NT_SUCCESS(Status)) { 2425 ERR("update_changed_extent_ref returned %08x\n", Status); 2426 goto end; 2427 } 2428 } 2429 } 2430 2431 remove_fcb_extent(fcb, ext, rollback); 2432 } else if (start_data <= ext->offset && end_data < ext->offset + len) { // remove beginning 2433 EXTENT_DATA2* ned2; 2434 extent* newext; 2435 2436 if (ed2->size != 0) { 2437 fcb->inode_item.st_blocks -= end_data - ext->offset; 2438 fcb->inode_item_changed = TRUE; 2439 } 2440 2441 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); 2442 if (!newext) { 2443 ERR("out of memory\n"); 2444 Status = STATUS_INSUFFICIENT_RESOURCES; 2445 goto end; 2446 } 2447 2448 ned2 = (EXTENT_DATA2*)newext->extent_data.data; 2449 2450 newext->extent_data.generation = Vcb->superblock.generation; 2451 newext->extent_data.decoded_size = ed->decoded_size; 2452 newext->extent_data.compression = ed->compression; 2453 newext->extent_data.encryption = ed->encryption; 2454 newext->extent_data.encoding = ed->encoding; 2455 newext->extent_data.type = ed->type; 2456 ned2->address = ed2->address; 2457 ned2->size = ed2->size; 2458 ned2->offset = ed2->offset + (end_data - ext->offset); 2459 ned2->num_bytes = ed2->num_bytes - (end_data - ext->offset); 2460 2461 newext->offset = end_data; 2462 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 2463 newext->unique = ext->unique; 2464 newext->ignore = FALSE; 2465 newext->inserted = TRUE; 2466 2467 if (ext->csum) { 2468 if (ed->compression == BTRFS_COMPRESSION_NONE) { 2469 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2470 if (!newext->csum) { 2471 ERR("out of memory\n"); 2472 Status = STATUS_INSUFFICIENT_RESOURCES; 2473 ExFreePool(newext); 2474 goto end; 2475 } 2476 2477 RtlCopyMemory(newext->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size], 2478 (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size)); 2479 } else { 2480 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2481 if (!newext->csum) { 2482 ERR("out of memory\n"); 2483 Status = STATUS_INSUFFICIENT_RESOURCES; 2484 ExFreePool(newext); 2485 goto end; 2486 } 2487 2488 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size)); 2489 } 2490 } else 2491 newext->csum = NULL; 2492 2493 add_extent(fcb, &ext->list_entry, newext); 2494 2495 remove_fcb_extent(fcb, ext, rollback); 2496 } else if (start_data > ext->offset && end_data >= ext->offset + len) { // remove end 2497 EXTENT_DATA2* ned2; 2498 extent* newext; 2499 2500 if (ed2->size != 0) { 2501 fcb->inode_item.st_blocks -= ext->offset + len - start_data; 2502 fcb->inode_item_changed = TRUE; 2503 } 2504 2505 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); 2506 if (!newext) { 2507 ERR("out of memory\n"); 2508 Status = STATUS_INSUFFICIENT_RESOURCES; 2509 goto end; 2510 } 2511 2512 ned2 = (EXTENT_DATA2*)newext->extent_data.data; 2513 2514 newext->extent_data.generation = Vcb->superblock.generation; 2515 newext->extent_data.decoded_size = ed->decoded_size; 2516 newext->extent_data.compression = ed->compression; 2517 newext->extent_data.encryption = ed->encryption; 2518 newext->extent_data.encoding = ed->encoding; 2519 newext->extent_data.type = ed->type; 2520 ned2->address = ed2->address; 2521 ned2->size = ed2->size; 2522 ned2->offset = ed2->offset; 2523 ned2->num_bytes = start_data - ext->offset; 2524 2525 newext->offset = ext->offset; 2526 newext->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 2527 newext->unique = ext->unique; 2528 newext->ignore = FALSE; 2529 newext->inserted = TRUE; 2530 2531 if (ext->csum) { 2532 if (ed->compression == BTRFS_COMPRESSION_NONE) { 2533 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2534 if (!newext->csum) { 2535 ERR("out of memory\n"); 2536 Status = STATUS_INSUFFICIENT_RESOURCES; 2537 ExFreePool(newext); 2538 goto end; 2539 } 2540 2541 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ned2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size)); 2542 } else { 2543 newext->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2544 if (!newext->csum) { 2545 ERR("out of memory\n"); 2546 Status = STATUS_INSUFFICIENT_RESOURCES; 2547 ExFreePool(newext); 2548 goto end; 2549 } 2550 2551 RtlCopyMemory(newext->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size)); 2552 } 2553 } else 2554 newext->csum = NULL; 2555 2556 InsertHeadList(&ext->list_entry, &newext->list_entry); 2557 2558 remove_fcb_extent(fcb, ext, rollback); 2559 } else if (start_data > ext->offset && end_data < ext->offset + len) { // remove middle 2560 EXTENT_DATA2 *neda2, *nedb2; 2561 extent *newext1, *newext2; 2562 2563 if (ed2->size != 0) { 2564 chunk* c; 2565 2566 fcb->inode_item.st_blocks -= end_data - start_data; 2567 fcb->inode_item_changed = TRUE; 2568 2569 c = get_chunk_from_address(Vcb, ed2->address); 2570 2571 if (!c) { 2572 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 2573 } else { 2574 Status = update_changed_extent_ref(Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, 2575 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 2576 if (!NT_SUCCESS(Status)) { 2577 ERR("update_changed_extent_ref returned %08x\n", Status); 2578 goto end; 2579 } 2580 } 2581 } 2582 2583 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); 2584 if (!newext1) { 2585 ERR("out of memory\n"); 2586 Status = STATUS_INSUFFICIENT_RESOURCES; 2587 goto end; 2588 } 2589 2590 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2), ALLOC_TAG); 2591 if (!newext2) { 2592 ERR("out of memory\n"); 2593 Status = STATUS_INSUFFICIENT_RESOURCES; 2594 ExFreePool(newext1); 2595 goto end; 2596 } 2597 2598 neda2 = (EXTENT_DATA2*)newext1->extent_data.data; 2599 2600 newext1->extent_data.generation = Vcb->superblock.generation; 2601 newext1->extent_data.decoded_size = ed->decoded_size; 2602 newext1->extent_data.compression = ed->compression; 2603 newext1->extent_data.encryption = ed->encryption; 2604 newext1->extent_data.encoding = ed->encoding; 2605 newext1->extent_data.type = ed->type; 2606 neda2->address = ed2->address; 2607 neda2->size = ed2->size; 2608 neda2->offset = ed2->offset; 2609 neda2->num_bytes = start_data - ext->offset; 2610 2611 nedb2 = (EXTENT_DATA2*)newext2->extent_data.data; 2612 2613 newext2->extent_data.generation = Vcb->superblock.generation; 2614 newext2->extent_data.decoded_size = ed->decoded_size; 2615 newext2->extent_data.compression = ed->compression; 2616 newext2->extent_data.encryption = ed->encryption; 2617 newext2->extent_data.encoding = ed->encoding; 2618 newext2->extent_data.type = ed->type; 2619 nedb2->address = ed2->address; 2620 nedb2->size = ed2->size; 2621 nedb2->offset = ed2->offset + (end_data - ext->offset); 2622 nedb2->num_bytes = ext->offset + len - end_data; 2623 2624 newext1->offset = ext->offset; 2625 newext1->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 2626 newext1->unique = ext->unique; 2627 newext1->ignore = FALSE; 2628 newext1->inserted = TRUE; 2629 2630 newext2->offset = end_data; 2631 newext2->datalen = sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2); 2632 newext2->unique = ext->unique; 2633 newext2->ignore = FALSE; 2634 newext2->inserted = TRUE; 2635 2636 if (ext->csum) { 2637 if (ed->compression == BTRFS_COMPRESSION_NONE) { 2638 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2639 if (!newext1->csum) { 2640 ERR("out of memory\n"); 2641 Status = STATUS_INSUFFICIENT_RESOURCES; 2642 ExFreePool(newext1); 2643 ExFreePool(newext2); 2644 goto end; 2645 } 2646 2647 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2648 if (!newext2->csum) { 2649 ERR("out of memory\n"); 2650 Status = STATUS_INSUFFICIENT_RESOURCES; 2651 ExFreePool(newext1->csum); 2652 ExFreePool(newext1); 2653 ExFreePool(newext2); 2654 goto end; 2655 } 2656 2657 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(neda2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size)); 2658 RtlCopyMemory(newext2->csum, &ext->csum[(end_data - ext->offset) / Vcb->superblock.sector_size], 2659 (ULONG)(nedb2->num_bytes * sizeof(UINT32) / Vcb->superblock.sector_size)); 2660 } else { 2661 newext1->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2662 if (!newext1->csum) { 2663 ERR("out of memory\n"); 2664 Status = STATUS_INSUFFICIENT_RESOURCES; 2665 ExFreePool(newext1); 2666 ExFreePool(newext2); 2667 goto end; 2668 } 2669 2670 newext2->csum = ExAllocatePoolWithTag(PagedPool, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size), ALLOC_TAG); 2671 if (!newext2->csum) { 2672 ERR("out of memory\n"); 2673 Status = STATUS_INSUFFICIENT_RESOURCES; 2674 ExFreePool(newext1->csum); 2675 ExFreePool(newext1); 2676 ExFreePool(newext2); 2677 goto end; 2678 } 2679 2680 RtlCopyMemory(newext1->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size)); 2681 RtlCopyMemory(newext2->csum, ext->csum, (ULONG)(ed2->size * sizeof(UINT32) / Vcb->superblock.sector_size)); 2682 } 2683 } else { 2684 newext1->csum = NULL; 2685 newext2->csum = NULL; 2686 } 2687 2688 InsertHeadList(&ext->list_entry, &newext1->list_entry); 2689 add_extent(fcb, &newext1->list_entry, newext2); 2690 2691 remove_fcb_extent(fcb, ext, rollback); 2692 } 2693 } 2694 } 2695 } 2696 2697 le = le2; 2698 } 2699 2700 Status = STATUS_SUCCESS; 2701 2702 end: 2703 fcb->extents_changed = TRUE; 2704 mark_fcb_dirty(fcb); 2705 2706 return Status; 2707 } 2708 2709 void add_insert_extent_rollback(LIST_ENTRY* rollback, fcb* fcb, extent* ext) { 2710 rollback_extent* re; 2711 2712 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG); 2713 if (!re) { 2714 ERR("out of memory\n"); 2715 return; 2716 } 2717 2718 re->fcb = fcb; 2719 re->ext = ext; 2720 2721 add_rollback(rollback, ROLLBACK_INSERT_EXTENT, re); 2722 } 2723 2724 #ifdef _MSC_VER 2725 #pragma warning(push) 2726 #pragma warning(suppress: 28194) 2727 #endif 2728 NTSTATUS add_extent_to_fcb(_In_ fcb* fcb, _In_ UINT64 offset, _In_reads_bytes_(edsize) EXTENT_DATA* ed, _In_ UINT16 edsize, 2729 _In_ BOOL unique, _In_opt_ _When_(return >= 0, __drv_aliasesMem) UINT32* csum, _In_ LIST_ENTRY* rollback) { 2730 extent* ext; 2731 LIST_ENTRY* le; 2732 2733 ext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + edsize, ALLOC_TAG); 2734 if (!ext) { 2735 ERR("out of memory\n"); 2736 return STATUS_INSUFFICIENT_RESOURCES; 2737 } 2738 2739 ext->offset = offset; 2740 ext->datalen = edsize; 2741 ext->unique = unique; 2742 ext->ignore = FALSE; 2743 ext->inserted = TRUE; 2744 ext->csum = csum; 2745 2746 RtlCopyMemory(&ext->extent_data, ed, edsize); 2747 2748 le = fcb->extents.Flink; 2749 while (le != &fcb->extents) { 2750 extent* oldext = CONTAINING_RECORD(le, extent, list_entry); 2751 2752 if (oldext->offset >= offset) { 2753 InsertHeadList(le->Blink, &ext->list_entry); 2754 goto end; 2755 } 2756 2757 le = le->Flink; 2758 } 2759 2760 InsertTailList(&fcb->extents, &ext->list_entry); 2761 2762 end: 2763 add_insert_extent_rollback(rollback, fcb, ext); 2764 2765 return STATUS_SUCCESS; 2766 } 2767 #ifdef _MSC_VER 2768 #pragma warning(pop) 2769 #endif 2770 2771 static void remove_fcb_extent(fcb* fcb, extent* ext, LIST_ENTRY* rollback) { 2772 if (!ext->ignore) { 2773 rollback_extent* re; 2774 2775 ext->ignore = TRUE; 2776 2777 re = ExAllocatePoolWithTag(NonPagedPool, sizeof(rollback_extent), ALLOC_TAG); 2778 if (!re) { 2779 ERR("out of memory\n"); 2780 return; 2781 } 2782 2783 re->fcb = fcb; 2784 re->ext = ext; 2785 2786 add_rollback(rollback, ROLLBACK_DELETE_EXTENT, re); 2787 } 2788 } 2789 2790 NTSTATUS calc_csum(_In_ device_extension* Vcb, _In_reads_bytes_(sectors*Vcb->superblock.sector_size) UINT8* data, 2791 _In_ UINT32 sectors, _Out_writes_bytes_(sectors*sizeof(UINT32)) UINT32* csum) { 2792 NTSTATUS Status; 2793 calc_job* cj; 2794 2795 // From experimenting, it seems that 40 sectors is roughly the crossover 2796 // point where offloading the crc32 calculation becomes worth it. 2797 2798 if (sectors < 40 || KeQueryActiveProcessorCount(NULL) < 2) { 2799 ULONG j; 2800 2801 for (j = 0; j < sectors; j++) { 2802 csum[j] = ~calc_crc32c(0xffffffff, data + (j * Vcb->superblock.sector_size), Vcb->superblock.sector_size); 2803 } 2804 2805 return STATUS_SUCCESS; 2806 } 2807 2808 Status = add_calc_job(Vcb, data, sectors, csum, &cj); 2809 if (!NT_SUCCESS(Status)) { 2810 ERR("add_calc_job returned %08x\n", Status); 2811 return Status; 2812 } 2813 2814 KeWaitForSingleObject(&cj->event, Executive, KernelMode, FALSE, NULL); 2815 free_calc_job(cj); 2816 2817 return STATUS_SUCCESS; 2818 } 2819 2820 _Requires_lock_held_(c->lock) 2821 _When_(return != 0, _Releases_lock_(c->lock)) 2822 BOOL insert_extent_chunk(_In_ device_extension* Vcb, _In_ fcb* fcb, _In_ chunk* c, _In_ UINT64 start_data, _In_ UINT64 length, _In_ BOOL prealloc, _In_opt_ void* data, 2823 _In_opt_ PIRP Irp, _In_ LIST_ENTRY* rollback, _In_ UINT8 compression, _In_ UINT64 decoded_size, _In_ BOOL file_write, _In_ UINT64 irp_offset) { 2824 UINT64 address; 2825 NTSTATUS Status; 2826 EXTENT_DATA* ed; 2827 EXTENT_DATA2* ed2; 2828 UINT16 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)); 2829 UINT32* csum = NULL; 2830 2831 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p)\n", Vcb, fcb->subvol->id, fcb->inode, c->offset, start_data, length, prealloc, data, rollback); 2832 2833 if (!find_data_address_in_chunk(Vcb, c, length, &address)) 2834 return FALSE; 2835 2836 // add extent data to inode 2837 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG); 2838 if (!ed) { 2839 ERR("out of memory\n"); 2840 return FALSE; 2841 } 2842 2843 ed->generation = Vcb->superblock.generation; 2844 ed->decoded_size = decoded_size; 2845 ed->compression = compression; 2846 ed->encryption = BTRFS_ENCRYPTION_NONE; 2847 ed->encoding = BTRFS_ENCODING_NONE; 2848 ed->type = prealloc ? EXTENT_TYPE_PREALLOC : EXTENT_TYPE_REGULAR; 2849 2850 ed2 = (EXTENT_DATA2*)ed->data; 2851 ed2->address = address; 2852 ed2->size = length; 2853 ed2->offset = 0; 2854 ed2->num_bytes = decoded_size; 2855 2856 if (!prealloc && data && !(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 2857 ULONG sl = (ULONG)(length / Vcb->superblock.sector_size); 2858 2859 csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG); 2860 if (!csum) { 2861 ERR("out of memory\n"); 2862 ExFreePool(ed); 2863 return FALSE; 2864 } 2865 2866 Status = calc_csum(Vcb, data, sl, csum); 2867 if (!NT_SUCCESS(Status)) { 2868 ERR("calc_csum returned %08x\n", Status); 2869 ExFreePool(csum); 2870 ExFreePool(ed); 2871 return FALSE; 2872 } 2873 } 2874 2875 Status = add_extent_to_fcb(fcb, start_data, ed, edsize, TRUE, csum, rollback); 2876 if (!NT_SUCCESS(Status)) { 2877 ERR("add_extent_to_fcb returned %08x\n", Status); 2878 if (csum) ExFreePool(csum); 2879 ExFreePool(ed); 2880 return FALSE; 2881 } 2882 2883 ExFreePool(ed); 2884 2885 c->used += length; 2886 space_list_subtract(c, FALSE, address, length, rollback); 2887 2888 fcb->inode_item.st_blocks += decoded_size; 2889 2890 fcb->extents_changed = TRUE; 2891 fcb->inode_item_changed = TRUE; 2892 mark_fcb_dirty(fcb); 2893 2894 ExAcquireResourceExclusiveLite(&c->changed_extents_lock, TRUE); 2895 2896 add_changed_extent_ref(c, address, length, fcb->subvol->id, fcb->inode, start_data, 1, fcb->inode_item.flags & BTRFS_INODE_NODATASUM); 2897 2898 ExReleaseResourceLite(&c->changed_extents_lock); 2899 2900 release_chunk_lock(c, Vcb); 2901 2902 if (data) { 2903 Status = write_data_complete(Vcb, address, data, (UINT32)length, Irp, NULL, file_write, irp_offset, 2904 fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority); 2905 if (!NT_SUCCESS(Status)) 2906 ERR("write_data_complete returned %08x\n", Status); 2907 } 2908 2909 return TRUE; 2910 } 2911 2912 static BOOL try_extend_data(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, 2913 PIRP Irp, UINT64* written, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) { 2914 BOOL success = FALSE; 2915 EXTENT_DATA* ed; 2916 EXTENT_DATA2* ed2; 2917 chunk* c; 2918 LIST_ENTRY* le; 2919 extent* ext = NULL; 2920 2921 le = fcb->extents.Flink; 2922 2923 while (le != &fcb->extents) { 2924 extent* nextext = CONTAINING_RECORD(le, extent, list_entry); 2925 2926 if (!nextext->ignore) { 2927 if (nextext->offset == start_data) { 2928 ext = nextext; 2929 break; 2930 } else if (nextext->offset > start_data) 2931 break; 2932 2933 ext = nextext; 2934 } 2935 2936 le = le->Flink; 2937 } 2938 2939 if (!ext) 2940 return FALSE; 2941 2942 ed = &ext->extent_data; 2943 2944 if (ed->type != EXTENT_TYPE_REGULAR && ed->type != EXTENT_TYPE_PREALLOC) { 2945 TRACE("not extending extent which is not regular or prealloc\n"); 2946 return FALSE; 2947 } 2948 2949 ed2 = (EXTENT_DATA2*)ed->data; 2950 2951 if (ext->offset + ed2->num_bytes != start_data) { 2952 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext->offset, ed2->num_bytes, start_data); 2953 return FALSE; 2954 } 2955 2956 c = get_chunk_from_address(Vcb, ed2->address); 2957 2958 if (c->reloc || c->readonly || c->chunk_item->type != Vcb->data_flags) 2959 return FALSE; 2960 2961 acquire_chunk_lock(c, Vcb); 2962 2963 if (length > c->chunk_item->size - c->used) { 2964 release_chunk_lock(c, Vcb); 2965 return FALSE; 2966 } 2967 2968 if (!c->cache_loaded) { 2969 NTSTATUS Status = load_cache_chunk(Vcb, c, NULL); 2970 2971 if (!NT_SUCCESS(Status)) { 2972 ERR("load_cache_chunk returned %08x\n", Status); 2973 release_chunk_lock(c, Vcb); 2974 return FALSE; 2975 } 2976 } 2977 2978 le = c->space.Flink; 2979 while (le != &c->space) { 2980 space* s = CONTAINING_RECORD(le, space, list_entry); 2981 2982 if (s->address == ed2->address + ed2->size) { 2983 UINT64 newlen = min(min(s->size, length), MAX_EXTENT_SIZE); 2984 2985 success = insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset); 2986 2987 if (success) 2988 *written += newlen; 2989 else 2990 release_chunk_lock(c, Vcb); 2991 2992 return success; 2993 } else if (s->address > ed2->address + ed2->size) 2994 break; 2995 2996 le = le->Flink; 2997 } 2998 2999 release_chunk_lock(c, Vcb); 3000 3001 return FALSE; 3002 } 3003 3004 static NTSTATUS insert_chunk_fragmented(fcb* fcb, UINT64 start, UINT64 length, UINT8* data, BOOL prealloc, LIST_ENTRY* rollback) { 3005 LIST_ENTRY* le; 3006 UINT64 flags = fcb->Vcb->data_flags; 3007 BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE; 3008 NTSTATUS Status; 3009 chunk* c; 3010 3011 ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); 3012 3013 // first create as many chunks as we can 3014 do { 3015 Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE); 3016 } while (NT_SUCCESS(Status)); 3017 3018 if (Status != STATUS_DISK_FULL) { 3019 ERR("alloc_chunk returned %08x\n", Status); 3020 ExReleaseResourceLite(&fcb->Vcb->chunk_lock); 3021 return Status; 3022 } 3023 3024 le = fcb->Vcb->chunks.Flink; 3025 while (le != &fcb->Vcb->chunks) { 3026 c = CONTAINING_RECORD(le, chunk, list_entry); 3027 3028 if (!c->readonly && !c->reloc) { 3029 acquire_chunk_lock(c, fcb->Vcb); 3030 3031 if (c->chunk_item->type == flags) { 3032 while (!IsListEmpty(&c->space_size) && length > 0) { 3033 space* s = CONTAINING_RECORD(c->space_size.Flink, space, list_entry_size); 3034 UINT64 extlen = min(length, s->size); 3035 3036 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, prealloc && !page_file, data, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) { 3037 start += extlen; 3038 length -= extlen; 3039 if (data) data += extlen; 3040 3041 acquire_chunk_lock(c, fcb->Vcb); 3042 } 3043 } 3044 } 3045 3046 release_chunk_lock(c, fcb->Vcb); 3047 3048 if (length == 0) 3049 break; 3050 } 3051 3052 le = le->Flink; 3053 } 3054 3055 ExReleaseResourceLite(&fcb->Vcb->chunk_lock); 3056 3057 return length == 0 ? STATUS_SUCCESS : STATUS_DISK_FULL; 3058 } 3059 3060 static NTSTATUS insert_prealloc_extent(fcb* fcb, UINT64 start, UINT64 length, LIST_ENTRY* rollback) { 3061 LIST_ENTRY* le; 3062 chunk* c; 3063 UINT64 flags; 3064 NTSTATUS Status; 3065 BOOL page_file = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE; 3066 3067 flags = fcb->Vcb->data_flags; 3068 3069 do { 3070 UINT64 extlen = min(MAX_EXTENT_SIZE, length); 3071 3072 ExAcquireResourceSharedLite(&fcb->Vcb->chunk_lock, TRUE); 3073 3074 le = fcb->Vcb->chunks.Flink; 3075 while (le != &fcb->Vcb->chunks) { 3076 c = CONTAINING_RECORD(le, chunk, list_entry); 3077 3078 if (!c->readonly && !c->reloc) { 3079 acquire_chunk_lock(c, fcb->Vcb); 3080 3081 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) { 3082 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) { 3083 ExReleaseResourceLite(&fcb->Vcb->chunk_lock); 3084 goto cont; 3085 } 3086 } 3087 3088 release_chunk_lock(c, fcb->Vcb); 3089 } 3090 3091 le = le->Flink; 3092 } 3093 3094 ExReleaseResourceLite(&fcb->Vcb->chunk_lock); 3095 3096 ExAcquireResourceExclusiveLite(&fcb->Vcb->chunk_lock, TRUE); 3097 3098 Status = alloc_chunk(fcb->Vcb, flags, &c, FALSE); 3099 3100 ExReleaseResourceLite(&fcb->Vcb->chunk_lock); 3101 3102 if (!NT_SUCCESS(Status)) { 3103 ERR("alloc_chunk returned %08x\n", Status); 3104 goto end; 3105 } 3106 3107 acquire_chunk_lock(c, fcb->Vcb); 3108 3109 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= extlen) { 3110 if (insert_extent_chunk(fcb->Vcb, fcb, c, start, extlen, !page_file, NULL, NULL, rollback, BTRFS_COMPRESSION_NONE, extlen, FALSE, 0)) 3111 goto cont; 3112 } 3113 3114 release_chunk_lock(c, fcb->Vcb); 3115 3116 Status = insert_chunk_fragmented(fcb, start, length, NULL, TRUE, rollback); 3117 if (!NT_SUCCESS(Status)) 3118 ERR("insert_chunk_fragmented returned %08x\n", Status); 3119 3120 goto end; 3121 3122 cont: 3123 length -= extlen; 3124 start += extlen; 3125 } while (length > 0); 3126 3127 Status = STATUS_SUCCESS; 3128 3129 end: 3130 return Status; 3131 } 3132 3133 static NTSTATUS insert_extent(device_extension* Vcb, fcb* fcb, UINT64 start_data, UINT64 length, void* data, 3134 PIRP Irp, BOOL file_write, UINT64 irp_offset, LIST_ENTRY* rollback) { 3135 NTSTATUS Status; 3136 LIST_ENTRY* le; 3137 chunk* c; 3138 UINT64 flags, orig_length = length, written = 0; 3139 3140 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb, fcb->subvol->id, fcb->inode, start_data, length, data); 3141 3142 if (start_data > 0) { 3143 try_extend_data(Vcb, fcb, start_data, length, data, Irp, &written, file_write, irp_offset, rollback); 3144 3145 if (written == length) 3146 return STATUS_SUCCESS; 3147 else if (written > 0) { 3148 start_data += written; 3149 irp_offset += written; 3150 length -= written; 3151 data = &((UINT8*)data)[written]; 3152 } 3153 } 3154 3155 flags = Vcb->data_flags; 3156 3157 while (written < orig_length) { 3158 UINT64 newlen = min(length, MAX_EXTENT_SIZE); 3159 BOOL done = FALSE; 3160 3161 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB. 3162 // First, see if we can write the extent part to an existing chunk. 3163 3164 ExAcquireResourceSharedLite(&Vcb->chunk_lock, TRUE); 3165 3166 le = Vcb->chunks.Flink; 3167 while (le != &Vcb->chunks) { 3168 c = CONTAINING_RECORD(le, chunk, list_entry); 3169 3170 if (!c->readonly && !c->reloc) { 3171 acquire_chunk_lock(c, Vcb); 3172 3173 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen && 3174 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) { 3175 written += newlen; 3176 3177 if (written == orig_length) { 3178 ExReleaseResourceLite(&Vcb->chunk_lock); 3179 return STATUS_SUCCESS; 3180 } else { 3181 done = TRUE; 3182 start_data += newlen; 3183 irp_offset += newlen; 3184 length -= newlen; 3185 data = &((UINT8*)data)[newlen]; 3186 break; 3187 } 3188 } else 3189 release_chunk_lock(c, Vcb); 3190 } 3191 3192 le = le->Flink; 3193 } 3194 3195 ExReleaseResourceLite(&Vcb->chunk_lock); 3196 3197 if (done) continue; 3198 3199 // Otherwise, see if we can put it in a new chunk. 3200 3201 ExAcquireResourceExclusiveLite(&Vcb->chunk_lock, TRUE); 3202 3203 Status = alloc_chunk(Vcb, flags, &c, FALSE); 3204 3205 ExReleaseResourceLite(&Vcb->chunk_lock); 3206 3207 if (!NT_SUCCESS(Status)) { 3208 ERR("alloc_chunk returned %08x\n", Status); 3209 return Status; 3210 } 3211 3212 if (c) { 3213 acquire_chunk_lock(c, Vcb); 3214 3215 if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= newlen && 3216 insert_extent_chunk(Vcb, fcb, c, start_data, newlen, FALSE, data, Irp, rollback, BTRFS_COMPRESSION_NONE, newlen, file_write, irp_offset)) { 3217 written += newlen; 3218 3219 if (written == orig_length) 3220 return STATUS_SUCCESS; 3221 else { 3222 done = TRUE; 3223 start_data += newlen; 3224 irp_offset += newlen; 3225 length -= newlen; 3226 data = &((UINT8*)data)[newlen]; 3227 } 3228 } else 3229 release_chunk_lock(c, Vcb); 3230 } 3231 3232 if (!done) { 3233 Status = insert_chunk_fragmented(fcb, start_data, length, data, FALSE, rollback); 3234 if (!NT_SUCCESS(Status)) 3235 ERR("insert_chunk_fragmented returned %08x\n", Status); 3236 3237 return Status; 3238 } 3239 } 3240 3241 return STATUS_DISK_FULL; 3242 } 3243 3244 NTSTATUS truncate_file(fcb* fcb, UINT64 end, PIRP Irp, LIST_ENTRY* rollback) { 3245 NTSTATUS Status; 3246 3247 // FIXME - convert into inline extent if short enough 3248 3249 if (end > 0 && fcb_is_inline(fcb)) { 3250 UINT8* buf; 3251 BOOL make_inline = end <= fcb->Vcb->options.max_inline; 3252 3253 buf = ExAllocatePoolWithTag(PagedPool, (ULONG)(make_inline ? (offsetof(EXTENT_DATA, data[0]) + end) : sector_align(end, fcb->Vcb->superblock.sector_size)), ALLOC_TAG); 3254 if (!buf) { 3255 ERR("out of memory\n"); 3256 return STATUS_INSUFFICIENT_RESOURCES; 3257 } 3258 3259 Status = read_file(fcb, make_inline ? (buf + offsetof(EXTENT_DATA, data[0])) : buf, 0, end, NULL, Irp); 3260 if (!NT_SUCCESS(Status)) { 3261 ERR("read_file returned %08x\n", Status); 3262 ExFreePool(buf); 3263 return Status; 3264 } 3265 3266 Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback); 3267 if (!NT_SUCCESS(Status)) { 3268 ERR("excise_extents returned %08x\n", Status); 3269 ExFreePool(buf); 3270 return Status; 3271 } 3272 3273 if (!make_inline) { 3274 RtlZeroMemory(buf + end, (ULONG)(sector_align(end, fcb->Vcb->superblock.sector_size) - end)); 3275 3276 Status = do_write_file(fcb, 0, sector_align(end, fcb->Vcb->superblock.sector_size), buf, Irp, FALSE, 0, rollback); 3277 if (!NT_SUCCESS(Status)) { 3278 ERR("do_write_file returned %08x\n", Status); 3279 ExFreePool(buf); 3280 return Status; 3281 } 3282 } else { 3283 EXTENT_DATA* ed = (EXTENT_DATA*)buf; 3284 3285 ed->generation = fcb->Vcb->superblock.generation; 3286 ed->decoded_size = end; 3287 ed->compression = BTRFS_COMPRESSION_NONE; 3288 ed->encryption = BTRFS_ENCRYPTION_NONE; 3289 ed->encoding = BTRFS_ENCODING_NONE; 3290 ed->type = EXTENT_TYPE_INLINE; 3291 3292 Status = add_extent_to_fcb(fcb, 0, ed, (UINT16)(offsetof(EXTENT_DATA, data[0]) + end), FALSE, NULL, rollback); 3293 if (!NT_SUCCESS(Status)) { 3294 ERR("add_extent_to_fcb returned %08x\n", Status); 3295 ExFreePool(buf); 3296 return Status; 3297 } 3298 3299 fcb->inode_item.st_blocks += end; 3300 } 3301 3302 ExFreePool(buf); 3303 return STATUS_SUCCESS; 3304 } 3305 3306 Status = excise_extents(fcb->Vcb, fcb, sector_align(end, fcb->Vcb->superblock.sector_size), 3307 sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size), Irp, rollback); 3308 if (!NT_SUCCESS(Status)) { 3309 ERR("excise_extents returned %08x\n", Status); 3310 return Status; 3311 } 3312 3313 fcb->inode_item.st_size = end; 3314 fcb->inode_item_changed = TRUE; 3315 TRACE("setting st_size to %llx\n", end); 3316 3317 fcb->Header.AllocationSize.QuadPart = sector_align(fcb->inode_item.st_size, fcb->Vcb->superblock.sector_size); 3318 fcb->Header.FileSize.QuadPart = fcb->inode_item.st_size; 3319 fcb->Header.ValidDataLength.QuadPart = fcb->inode_item.st_size; 3320 // FIXME - inform cache manager of this 3321 3322 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart); 3323 3324 return STATUS_SUCCESS; 3325 } 3326 3327 NTSTATUS extend_file(fcb* fcb, file_ref* fileref, UINT64 end, BOOL prealloc, PIRP Irp, LIST_ENTRY* rollback) { 3328 UINT64 oldalloc, newalloc; 3329 BOOL cur_inline; 3330 NTSTATUS Status; 3331 3332 TRACE("(%p, %p, %x, %u)\n", fcb, fileref, end, prealloc); 3333 3334 if (fcb->ads) { 3335 if (end > 0xffff) 3336 return STATUS_DISK_FULL; 3337 3338 return stream_set_end_of_file_information(fcb->Vcb, (UINT16)end, fcb, fileref, FALSE); 3339 } else { 3340 extent* ext = NULL; 3341 LIST_ENTRY* le; 3342 3343 le = fcb->extents.Blink; 3344 while (le != &fcb->extents) { 3345 extent* ext2 = CONTAINING_RECORD(le, extent, list_entry); 3346 3347 if (!ext2->ignore) { 3348 ext = ext2; 3349 break; 3350 } 3351 3352 le = le->Blink; 3353 } 3354 3355 oldalloc = 0; 3356 if (ext) { 3357 EXTENT_DATA* ed = &ext->extent_data; 3358 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 3359 3360 oldalloc = ext->offset + (ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes); 3361 cur_inline = ed->type == EXTENT_TYPE_INLINE; 3362 3363 if (cur_inline && end > fcb->Vcb->options.max_inline) { 3364 UINT64 origlength, length; 3365 UINT8* data; 3366 3367 TRACE("giving inline file proper extents\n"); 3368 3369 origlength = ed->decoded_size; 3370 3371 cur_inline = FALSE; 3372 3373 length = sector_align(origlength, fcb->Vcb->superblock.sector_size); 3374 3375 data = ExAllocatePoolWithTag(PagedPool, (ULONG)length, ALLOC_TAG); 3376 if (!data) { 3377 ERR("could not allocate %llx bytes for data\n", length); 3378 return STATUS_INSUFFICIENT_RESOURCES; 3379 } 3380 3381 Status = read_file(fcb, data, 0, origlength, NULL, Irp); 3382 if (!NT_SUCCESS(Status)) { 3383 ERR("read_file returned %08x\n", Status); 3384 ExFreePool(data); 3385 return Status; 3386 } 3387 3388 RtlZeroMemory(data + origlength, (ULONG)(length - origlength)); 3389 3390 Status = excise_extents(fcb->Vcb, fcb, 0, fcb->inode_item.st_size, Irp, rollback); 3391 if (!NT_SUCCESS(Status)) { 3392 ERR("excise_extents returned %08x\n", Status); 3393 ExFreePool(data); 3394 return Status; 3395 } 3396 3397 Status = do_write_file(fcb, 0, length, data, Irp, FALSE, 0, rollback); 3398 if (!NT_SUCCESS(Status)) { 3399 ERR("do_write_file returned %08x\n", Status); 3400 ExFreePool(data); 3401 return Status; 3402 } 3403 3404 oldalloc = ext->offset + length; 3405 3406 ExFreePool(data); 3407 } 3408 3409 if (cur_inline) { 3410 UINT16 edsize; 3411 3412 if (end > oldalloc) { 3413 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end - ext->offset); 3414 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG); 3415 3416 if (!ed) { 3417 ERR("out of memory\n"); 3418 return STATUS_INSUFFICIENT_RESOURCES; 3419 } 3420 3421 ed->generation = fcb->Vcb->superblock.generation; 3422 ed->decoded_size = end - ext->offset; 3423 ed->compression = BTRFS_COMPRESSION_NONE; 3424 ed->encryption = BTRFS_ENCRYPTION_NONE; 3425 ed->encoding = BTRFS_ENCODING_NONE; 3426 ed->type = EXTENT_TYPE_INLINE; 3427 3428 Status = read_file(fcb, ed->data, ext->offset, oldalloc, NULL, Irp); 3429 if (!NT_SUCCESS(Status)) { 3430 ERR("read_file returned %08x\n", Status); 3431 ExFreePool(ed); 3432 return Status; 3433 } 3434 3435 RtlZeroMemory(ed->data + oldalloc - ext->offset, (ULONG)(end - oldalloc)); 3436 3437 remove_fcb_extent(fcb, ext, rollback); 3438 3439 Status = add_extent_to_fcb(fcb, ext->offset, ed, edsize, ext->unique, NULL, rollback); 3440 if (!NT_SUCCESS(Status)) { 3441 ERR("add_extent_to_fcb returned %08x\n", Status); 3442 ExFreePool(ed); 3443 return Status; 3444 } 3445 3446 ExFreePool(ed); 3447 3448 fcb->extents_changed = TRUE; 3449 mark_fcb_dirty(fcb); 3450 } 3451 3452 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc, end); 3453 3454 fcb->inode_item.st_size = end; 3455 TRACE("setting st_size to %llx\n", end); 3456 3457 fcb->inode_item.st_blocks = end; 3458 3459 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end; 3460 } else { 3461 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size); 3462 3463 if (newalloc > oldalloc) { 3464 if (prealloc) { 3465 // FIXME - try and extend previous extent first 3466 3467 Status = insert_prealloc_extent(fcb, oldalloc, newalloc - oldalloc, rollback); 3468 3469 if (!NT_SUCCESS(Status)) { 3470 ERR("insert_prealloc_extent returned %08x\n", Status); 3471 return Status; 3472 } 3473 } 3474 3475 fcb->extents_changed = TRUE; 3476 } 3477 3478 fcb->inode_item.st_size = end; 3479 fcb->inode_item_changed = TRUE; 3480 mark_fcb_dirty(fcb); 3481 3482 TRACE("setting st_size to %llx\n", end); 3483 3484 TRACE("newalloc = %llx\n", newalloc); 3485 3486 fcb->Header.AllocationSize.QuadPart = newalloc; 3487 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end; 3488 } 3489 } else { 3490 if (end > fcb->Vcb->options.max_inline) { 3491 newalloc = sector_align(end, fcb->Vcb->superblock.sector_size); 3492 3493 if (prealloc) { 3494 Status = insert_prealloc_extent(fcb, 0, newalloc, rollback); 3495 3496 if (!NT_SUCCESS(Status)) { 3497 ERR("insert_prealloc_extent returned %08x\n", Status); 3498 return Status; 3499 } 3500 } 3501 3502 fcb->extents_changed = TRUE; 3503 fcb->inode_item_changed = TRUE; 3504 mark_fcb_dirty(fcb); 3505 3506 fcb->inode_item.st_size = end; 3507 TRACE("setting st_size to %llx\n", end); 3508 3509 TRACE("newalloc = %llx\n", newalloc); 3510 3511 fcb->Header.AllocationSize.QuadPart = newalloc; 3512 fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end; 3513 } else { 3514 EXTENT_DATA* ed; 3515 UINT16 edsize; 3516 3517 edsize = (UINT16)(offsetof(EXTENT_DATA, data[0]) + end); 3518 ed = ExAllocatePoolWithTag(PagedPool, edsize, ALLOC_TAG); 3519 3520 if (!ed) { 3521 ERR("out of memory\n"); 3522 return STATUS_INSUFFICIENT_RESOURCES; 3523 } 3524 3525 ed->generation = fcb->Vcb->superblock.generation; 3526 ed->decoded_size = end; 3527 ed->compression = BTRFS_COMPRESSION_NONE; 3528 ed->encryption = BTRFS_ENCRYPTION_NONE; 3529 ed->encoding = BTRFS_ENCODING_NONE; 3530 ed->type = EXTENT_TYPE_INLINE; 3531 3532 RtlZeroMemory(ed->data, (ULONG)end); 3533 3534 Status = add_extent_to_fcb(fcb, 0, ed, edsize, FALSE, NULL, rollback); 3535 if (!NT_SUCCESS(Status)) { 3536 ERR("add_extent_to_fcb returned %08x\n", Status); 3537 ExFreePool(ed); 3538 return Status; 3539 } 3540 3541 ExFreePool(ed); 3542 3543 fcb->extents_changed = TRUE; 3544 fcb->inode_item_changed = TRUE; 3545 mark_fcb_dirty(fcb); 3546 3547 fcb->inode_item.st_size = end; 3548 TRACE("setting st_size to %llx\n", end); 3549 3550 fcb->inode_item.st_blocks = end; 3551 3552 fcb->Header.AllocationSize.QuadPart = fcb->Header.FileSize.QuadPart = fcb->Header.ValidDataLength.QuadPart = end; 3553 } 3554 } 3555 } 3556 3557 return STATUS_SUCCESS; 3558 } 3559 3560 static NTSTATUS do_write_file_prealloc(fcb* fcb, extent* ext, UINT64 start_data, UINT64 end_data, void* data, UINT64* written, 3561 PIRP Irp, BOOL file_write, UINT64 irp_offset, ULONG priority, LIST_ENTRY* rollback) { 3562 EXTENT_DATA* ed = &ext->extent_data; 3563 EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 3564 NTSTATUS Status; 3565 chunk* c = NULL; 3566 3567 if (start_data <= ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace all 3568 extent* newext; 3569 3570 newext = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3571 if (!newext) { 3572 ERR("out of memory\n"); 3573 return STATUS_INSUFFICIENT_RESOURCES; 3574 } 3575 3576 RtlCopyMemory(&newext->extent_data, &ext->extent_data, ext->datalen); 3577 3578 newext->extent_data.type = EXTENT_TYPE_REGULAR; 3579 3580 Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)ed2->num_bytes, Irp, 3581 NULL, file_write, irp_offset + ext->offset - start_data, priority); 3582 if (!NT_SUCCESS(Status)) { 3583 ERR("write_data_complete returned %08x\n", Status); 3584 return Status; 3585 } 3586 3587 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 3588 ULONG sl = (ULONG)(ed2->num_bytes / fcb->Vcb->superblock.sector_size); 3589 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG); 3590 3591 if (!csum) { 3592 ERR("out of memory\n"); 3593 ExFreePool(newext); 3594 return STATUS_INSUFFICIENT_RESOURCES; 3595 } 3596 3597 Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum); 3598 if (!NT_SUCCESS(Status)) { 3599 ERR("calc_csum returned %08x\n", Status); 3600 ExFreePool(csum); 3601 ExFreePool(newext); 3602 return Status; 3603 } 3604 3605 newext->csum = csum; 3606 } else 3607 newext->csum = NULL; 3608 3609 *written = ed2->num_bytes; 3610 3611 newext->offset = ext->offset; 3612 newext->datalen = ext->datalen; 3613 newext->unique = ext->unique; 3614 newext->ignore = FALSE; 3615 newext->inserted = TRUE; 3616 InsertHeadList(&ext->list_entry, &newext->list_entry); 3617 3618 add_insert_extent_rollback(rollback, fcb, newext); 3619 3620 remove_fcb_extent(fcb, ext, rollback); 3621 3622 c = get_chunk_from_address(fcb->Vcb, ed2->address); 3623 } else if (start_data <= ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace beginning 3624 EXTENT_DATA2* ned2; 3625 extent *newext1, *newext2; 3626 3627 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3628 if (!newext1) { 3629 ERR("out of memory\n"); 3630 return STATUS_INSUFFICIENT_RESOURCES; 3631 } 3632 3633 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3634 if (!newext2) { 3635 ERR("out of memory\n"); 3636 ExFreePool(newext1); 3637 return STATUS_INSUFFICIENT_RESOURCES; 3638 } 3639 3640 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen); 3641 newext1->extent_data.type = EXTENT_TYPE_REGULAR; 3642 ned2 = (EXTENT_DATA2*)newext1->extent_data.data; 3643 ned2->num_bytes = end_data - ext->offset; 3644 3645 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen); 3646 ned2 = (EXTENT_DATA2*)newext2->extent_data.data; 3647 ned2->offset += end_data - ext->offset; 3648 ned2->num_bytes -= end_data - ext->offset; 3649 3650 Status = write_data_complete(fcb->Vcb, ed2->address + ed2->offset, (UINT8*)data + ext->offset - start_data, (UINT32)(end_data - ext->offset), 3651 Irp, NULL, file_write, irp_offset + ext->offset - start_data, priority); 3652 if (!NT_SUCCESS(Status)) { 3653 ERR("write_data_complete returned %08x\n", Status); 3654 ExFreePool(newext1); 3655 ExFreePool(newext2); 3656 return Status; 3657 } 3658 3659 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 3660 ULONG sl = (ULONG)((end_data - ext->offset) / fcb->Vcb->superblock.sector_size); 3661 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG); 3662 3663 if (!csum) { 3664 ERR("out of memory\n"); 3665 ExFreePool(newext1); 3666 ExFreePool(newext2); 3667 return STATUS_INSUFFICIENT_RESOURCES; 3668 } 3669 3670 Status = calc_csum(fcb->Vcb, (UINT8*)data + ext->offset - start_data, sl, csum); 3671 if (!NT_SUCCESS(Status)) { 3672 ERR("calc_csum returned %08x\n", Status); 3673 ExFreePool(newext1); 3674 ExFreePool(newext2); 3675 ExFreePool(csum); 3676 return Status; 3677 } 3678 3679 newext1->csum = csum; 3680 } else 3681 newext1->csum = NULL; 3682 3683 *written = end_data - ext->offset; 3684 3685 newext1->offset = ext->offset; 3686 newext1->datalen = ext->datalen; 3687 newext1->unique = ext->unique; 3688 newext1->ignore = FALSE; 3689 newext1->inserted = TRUE; 3690 InsertHeadList(&ext->list_entry, &newext1->list_entry); 3691 3692 add_insert_extent_rollback(rollback, fcb, newext1); 3693 3694 newext2->offset = end_data; 3695 newext2->datalen = ext->datalen; 3696 newext2->unique = ext->unique; 3697 newext2->ignore = FALSE; 3698 newext2->inserted = TRUE; 3699 newext2->csum = NULL; 3700 add_extent(fcb, &newext1->list_entry, newext2); 3701 3702 add_insert_extent_rollback(rollback, fcb, newext2); 3703 3704 c = get_chunk_from_address(fcb->Vcb, ed2->address); 3705 3706 if (!c) 3707 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 3708 else { 3709 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, 3710 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 3711 3712 if (!NT_SUCCESS(Status)) { 3713 ERR("update_changed_extent_ref returned %08x\n", Status); 3714 return Status; 3715 } 3716 } 3717 3718 remove_fcb_extent(fcb, ext, rollback); 3719 } else if (start_data > ext->offset && end_data >= ext->offset + ed2->num_bytes) { // replace end 3720 EXTENT_DATA2* ned2; 3721 extent *newext1, *newext2; 3722 3723 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3724 if (!newext1) { 3725 ERR("out of memory\n"); 3726 return STATUS_INSUFFICIENT_RESOURCES; 3727 } 3728 3729 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3730 if (!newext2) { 3731 ERR("out of memory\n"); 3732 ExFreePool(newext1); 3733 return STATUS_INSUFFICIENT_RESOURCES; 3734 } 3735 3736 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen); 3737 3738 ned2 = (EXTENT_DATA2*)newext1->extent_data.data; 3739 ned2->num_bytes = start_data - ext->offset; 3740 3741 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen); 3742 3743 newext2->extent_data.type = EXTENT_TYPE_REGULAR; 3744 ned2 = (EXTENT_DATA2*)newext2->extent_data.data; 3745 ned2->offset += start_data - ext->offset; 3746 ned2->num_bytes = ext->offset + ed2->num_bytes - start_data; 3747 3748 Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)ned2->num_bytes, Irp, NULL, file_write, irp_offset, priority); 3749 if (!NT_SUCCESS(Status)) { 3750 ERR("write_data_complete returned %08x\n", Status); 3751 ExFreePool(newext1); 3752 ExFreePool(newext2); 3753 return Status; 3754 } 3755 3756 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 3757 ULONG sl = (ULONG)(ned2->num_bytes / fcb->Vcb->superblock.sector_size); 3758 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG); 3759 3760 if (!csum) { 3761 ERR("out of memory\n"); 3762 ExFreePool(newext1); 3763 ExFreePool(newext2); 3764 return STATUS_INSUFFICIENT_RESOURCES; 3765 } 3766 3767 Status = calc_csum(fcb->Vcb, data, sl, csum); 3768 if (!NT_SUCCESS(Status)) { 3769 ERR("calc_csum returned %08x\n", Status); 3770 ExFreePool(newext1); 3771 ExFreePool(newext2); 3772 ExFreePool(csum); 3773 return Status; 3774 } 3775 3776 newext2->csum = csum; 3777 } else 3778 newext2->csum = NULL; 3779 3780 *written = ned2->num_bytes; 3781 3782 newext1->offset = ext->offset; 3783 newext1->datalen = ext->datalen; 3784 newext1->unique = ext->unique; 3785 newext1->ignore = FALSE; 3786 newext1->inserted = TRUE; 3787 newext1->csum = NULL; 3788 InsertHeadList(&ext->list_entry, &newext1->list_entry); 3789 3790 add_insert_extent_rollback(rollback, fcb, newext1); 3791 3792 newext2->offset = start_data; 3793 newext2->datalen = ext->datalen; 3794 newext2->unique = ext->unique; 3795 newext2->ignore = FALSE; 3796 newext2->inserted = TRUE; 3797 add_extent(fcb, &newext1->list_entry, newext2); 3798 3799 add_insert_extent_rollback(rollback, fcb, newext2); 3800 3801 c = get_chunk_from_address(fcb->Vcb, ed2->address); 3802 3803 if (!c) 3804 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 3805 else { 3806 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 1, 3807 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 3808 3809 if (!NT_SUCCESS(Status)) { 3810 ERR("update_changed_extent_ref returned %08x\n", Status); 3811 return Status; 3812 } 3813 } 3814 3815 remove_fcb_extent(fcb, ext, rollback); 3816 } else if (start_data > ext->offset && end_data < ext->offset + ed2->num_bytes) { // replace middle 3817 EXTENT_DATA2* ned2; 3818 extent *newext1, *newext2, *newext3; 3819 3820 newext1 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3821 if (!newext1) { 3822 ERR("out of memory\n"); 3823 return STATUS_INSUFFICIENT_RESOURCES; 3824 } 3825 3826 newext2 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3827 if (!newext2) { 3828 ERR("out of memory\n"); 3829 ExFreePool(newext1); 3830 return STATUS_INSUFFICIENT_RESOURCES; 3831 } 3832 3833 newext3 = ExAllocatePoolWithTag(PagedPool, offsetof(extent, extent_data) + ext->datalen, ALLOC_TAG); 3834 if (!newext3) { 3835 ERR("out of memory\n"); 3836 ExFreePool(newext1); 3837 ExFreePool(newext2); 3838 return STATUS_INSUFFICIENT_RESOURCES; 3839 } 3840 3841 RtlCopyMemory(&newext1->extent_data, &ext->extent_data, ext->datalen); 3842 RtlCopyMemory(&newext2->extent_data, &ext->extent_data, ext->datalen); 3843 RtlCopyMemory(&newext3->extent_data, &ext->extent_data, ext->datalen); 3844 3845 ned2 = (EXTENT_DATA2*)newext1->extent_data.data; 3846 ned2->num_bytes = start_data - ext->offset; 3847 3848 newext2->extent_data.type = EXTENT_TYPE_REGULAR; 3849 ned2 = (EXTENT_DATA2*)newext2->extent_data.data; 3850 ned2->offset += start_data - ext->offset; 3851 ned2->num_bytes = end_data - start_data; 3852 3853 ned2 = (EXTENT_DATA2*)newext3->extent_data.data; 3854 ned2->offset += end_data - ext->offset; 3855 ned2->num_bytes -= end_data - ext->offset; 3856 3857 ned2 = (EXTENT_DATA2*)newext2->extent_data.data; 3858 Status = write_data_complete(fcb->Vcb, ed2->address + ned2->offset, data, (UINT32)(end_data - start_data), Irp, NULL, file_write, irp_offset, priority); 3859 if (!NT_SUCCESS(Status)) { 3860 ERR("write_data_complete returned %08x\n", Status); 3861 ExFreePool(newext1); 3862 ExFreePool(newext2); 3863 ExFreePool(newext3); 3864 return Status; 3865 } 3866 3867 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 3868 ULONG sl = (ULONG)((end_data - start_data) / fcb->Vcb->superblock.sector_size); 3869 UINT32* csum = ExAllocatePoolWithTag(PagedPool, sl * sizeof(UINT32), ALLOC_TAG); 3870 3871 if (!csum) { 3872 ERR("out of memory\n"); 3873 ExFreePool(newext1); 3874 ExFreePool(newext2); 3875 ExFreePool(newext3); 3876 return STATUS_INSUFFICIENT_RESOURCES; 3877 } 3878 3879 Status = calc_csum(fcb->Vcb, data, sl, csum); 3880 if (!NT_SUCCESS(Status)) { 3881 ERR("calc_csum returned %08x\n", Status); 3882 ExFreePool(newext1); 3883 ExFreePool(newext2); 3884 ExFreePool(newext3); 3885 ExFreePool(csum); 3886 return Status; 3887 } 3888 3889 newext2->csum = csum; 3890 } else 3891 newext2->csum = NULL; 3892 3893 *written = end_data - start_data; 3894 3895 newext1->offset = ext->offset; 3896 newext1->datalen = ext->datalen; 3897 newext1->unique = ext->unique; 3898 newext1->ignore = FALSE; 3899 newext1->inserted = TRUE; 3900 newext1->csum = NULL; 3901 InsertHeadList(&ext->list_entry, &newext1->list_entry); 3902 3903 add_insert_extent_rollback(rollback, fcb, newext1); 3904 3905 newext2->offset = start_data; 3906 newext2->datalen = ext->datalen; 3907 newext2->unique = ext->unique; 3908 newext2->ignore = FALSE; 3909 newext2->inserted = TRUE; 3910 add_extent(fcb, &newext1->list_entry, newext2); 3911 3912 add_insert_extent_rollback(rollback, fcb, newext2); 3913 3914 newext3->offset = end_data; 3915 newext3->datalen = ext->datalen; 3916 newext3->unique = ext->unique; 3917 newext3->ignore = FALSE; 3918 newext3->inserted = TRUE; 3919 newext3->csum = NULL; 3920 add_extent(fcb, &newext2->list_entry, newext3); 3921 3922 add_insert_extent_rollback(rollback, fcb, newext3); 3923 3924 c = get_chunk_from_address(fcb->Vcb, ed2->address); 3925 3926 if (!c) 3927 ERR("get_chunk_from_address(%llx) failed\n", ed2->address); 3928 else { 3929 Status = update_changed_extent_ref(fcb->Vcb, c, ed2->address, ed2->size, fcb->subvol->id, fcb->inode, ext->offset - ed2->offset, 2, 3930 fcb->inode_item.flags & BTRFS_INODE_NODATASUM, FALSE, Irp); 3931 3932 if (!NT_SUCCESS(Status)) { 3933 ERR("update_changed_extent_ref returned %08x\n", Status); 3934 return Status; 3935 } 3936 } 3937 3938 remove_fcb_extent(fcb, ext, rollback); 3939 } 3940 3941 if (c) 3942 c->changed = TRUE; 3943 3944 return STATUS_SUCCESS; 3945 } 3946 3947 NTSTATUS do_write_file(fcb* fcb, UINT64 start, UINT64 end_data, void* data, PIRP Irp, BOOL file_write, UINT32 irp_offset, LIST_ENTRY* rollback) { 3948 NTSTATUS Status; 3949 LIST_ENTRY *le, *le2; 3950 UINT64 written = 0, length = end_data - start; 3951 UINT64 last_cow_start; 3952 ULONG priority = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority; 3953 #ifdef DEBUG_PARANOID 3954 UINT64 last_off; 3955 #endif 3956 3957 last_cow_start = 0; 3958 3959 le = fcb->extents.Flink; 3960 while (le != &fcb->extents) { 3961 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 3962 3963 le2 = le->Flink; 3964 3965 if (!ext->ignore) { 3966 EXTENT_DATA* ed = &ext->extent_data; 3967 EXTENT_DATA2* ed2 = ed->type == EXTENT_TYPE_INLINE ? NULL : (EXTENT_DATA2*)ed->data; 3968 UINT64 len; 3969 3970 len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; 3971 3972 if (ext->offset + len <= start) 3973 goto nextitem; 3974 3975 if (ext->offset > start + written + length) 3976 break; 3977 3978 if ((fcb->inode_item.flags & BTRFS_INODE_NODATACOW || ed->type == EXTENT_TYPE_PREALLOC) && ext->unique && ed->compression == BTRFS_COMPRESSION_NONE) { 3979 if (max(last_cow_start, start + written) < ext->offset) { 3980 UINT64 start_write = max(last_cow_start, start + written); 3981 3982 Status = excise_extents(fcb->Vcb, fcb, start_write, ext->offset, Irp, rollback); 3983 if (!NT_SUCCESS(Status)) { 3984 ERR("excise_extents returned %08x\n", Status); 3985 return Status; 3986 } 3987 3988 Status = insert_extent(fcb->Vcb, fcb, start_write, ext->offset - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback); 3989 if (!NT_SUCCESS(Status)) { 3990 ERR("insert_extent returned %08x\n", Status); 3991 return Status; 3992 } 3993 3994 written += ext->offset - start_write; 3995 length -= ext->offset - start_write; 3996 3997 if (length == 0) 3998 break; 3999 } 4000 4001 if (ed->type == EXTENT_TYPE_REGULAR) { 4002 UINT64 writeaddr = ed2->address + ed2->offset + start + written - ext->offset; 4003 UINT64 write_len = min(len, length); 4004 chunk* c; 4005 4006 TRACE("doing non-COW write to %llx\n", writeaddr); 4007 4008 Status = write_data_complete(fcb->Vcb, writeaddr, (UINT8*)data + written, (UINT32)write_len, Irp, NULL, file_write, irp_offset + written, priority); 4009 if (!NT_SUCCESS(Status)) { 4010 ERR("write_data_complete returned %08x\n", Status); 4011 return Status; 4012 } 4013 4014 c = get_chunk_from_address(fcb->Vcb, writeaddr); 4015 if (c) 4016 c->changed = TRUE; 4017 4018 // This shouldn't ever get called - nocow files should always also be nosum. 4019 if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) { 4020 calc_csum(fcb->Vcb, (UINT8*)data + written, (UINT32)(write_len / fcb->Vcb->superblock.sector_size), 4021 &ext->csum[(start + written - ext->offset) / fcb->Vcb->superblock.sector_size]); 4022 4023 ext->inserted = TRUE; 4024 } 4025 4026 written += write_len; 4027 length -= write_len; 4028 4029 if (length == 0) 4030 break; 4031 } else if (ed->type == EXTENT_TYPE_PREALLOC) { 4032 UINT64 write_len; 4033 4034 Status = do_write_file_prealloc(fcb, ext, start + written, end_data, (UINT8*)data + written, &write_len, 4035 Irp, file_write, irp_offset + written, priority, rollback); 4036 if (!NT_SUCCESS(Status)) { 4037 ERR("do_write_file_prealloc returned %08x\n", Status); 4038 return Status; 4039 } 4040 4041 written += write_len; 4042 length -= write_len; 4043 4044 if (length == 0) 4045 break; 4046 } 4047 4048 last_cow_start = ext->offset + len; 4049 } 4050 } 4051 4052 nextitem: 4053 le = le2; 4054 } 4055 4056 if (length > 0) { 4057 UINT64 start_write = max(last_cow_start, start + written); 4058 4059 Status = excise_extents(fcb->Vcb, fcb, start_write, end_data, Irp, rollback); 4060 if (!NT_SUCCESS(Status)) { 4061 ERR("excise_extents returned %08x\n", Status); 4062 return Status; 4063 } 4064 4065 Status = insert_extent(fcb->Vcb, fcb, start_write, end_data - start_write, (UINT8*)data + written, Irp, file_write, irp_offset + written, rollback); 4066 if (!NT_SUCCESS(Status)) { 4067 ERR("insert_extent returned %08x\n", Status); 4068 return Status; 4069 } 4070 } 4071 4072 #ifdef DEBUG_PARANOID 4073 last_off = 0xffffffffffffffff; 4074 4075 le = fcb->extents.Flink; 4076 while (le != &fcb->extents) { 4077 extent* ext = CONTAINING_RECORD(le, extent, list_entry); 4078 4079 if (!ext->ignore) { 4080 if (ext->offset == last_off) { 4081 ERR("offset %llx duplicated\n", ext->offset); 4082 int3; 4083 } else if (ext->offset < last_off && last_off != 0xffffffffffffffff) { 4084 ERR("offsets out of order\n"); 4085 int3; 4086 } 4087 4088 last_off = ext->offset; 4089 } 4090 4091 le = le->Flink; 4092 } 4093 #endif 4094 4095 fcb->extents_changed = TRUE; 4096 mark_fcb_dirty(fcb); 4097 4098 return STATUS_SUCCESS; 4099 } 4100 4101 NTSTATUS write_compressed(fcb* fcb, UINT64 start_data, UINT64 end_data, void* data, PIRP Irp, LIST_ENTRY* rollback) { 4102 NTSTATUS Status; 4103 UINT64 i; 4104 4105 for (i = 0; i < sector_align(end_data - start_data, COMPRESSED_EXTENT_SIZE) / COMPRESSED_EXTENT_SIZE; i++) { 4106 UINT64 s2, e2; 4107 BOOL compressed; 4108 4109 s2 = start_data + (i * COMPRESSED_EXTENT_SIZE); 4110 e2 = min(s2 + COMPRESSED_EXTENT_SIZE, end_data); 4111 4112 Status = write_compressed_bit(fcb, s2, e2, (UINT8*)data + (i * COMPRESSED_EXTENT_SIZE), &compressed, Irp, rollback); 4113 4114 if (!NT_SUCCESS(Status)) { 4115 ERR("write_compressed_bit returned %08x\n", Status); 4116 return Status; 4117 } 4118 4119 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't 4120 // bother with the rest of it. 4121 if (s2 == 0 && e2 == COMPRESSED_EXTENT_SIZE && !compressed && !fcb->Vcb->options.compress_force) { 4122 fcb->inode_item.flags |= BTRFS_INODE_NOCOMPRESS; 4123 fcb->inode_item_changed = TRUE; 4124 mark_fcb_dirty(fcb); 4125 4126 // write subsequent data non-compressed 4127 if (e2 < end_data) { 4128 Status = do_write_file(fcb, e2, end_data, (UINT8*)data + e2, Irp, FALSE, 0, rollback); 4129 4130 if (!NT_SUCCESS(Status)) { 4131 ERR("do_write_file returned %08x\n", Status); 4132 return Status; 4133 } 4134 } 4135 4136 return STATUS_SUCCESS; 4137 } 4138 } 4139 4140 return STATUS_SUCCESS; 4141 } 4142 4143 NTSTATUS write_file2(device_extension* Vcb, PIRP Irp, LARGE_INTEGER offset, void* buf, ULONG* length, BOOLEAN paging_io, BOOLEAN no_cache, 4144 BOOLEAN wait, BOOLEAN deferred_write, BOOLEAN write_irp, LIST_ENTRY* rollback) { 4145 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4146 PFILE_OBJECT FileObject = IrpSp->FileObject; 4147 EXTENT_DATA* ed2; 4148 UINT64 off64, newlength, start_data, end_data; 4149 UINT32 bufhead; 4150 BOOL make_inline; 4151 UINT8* data; 4152 INODE_ITEM* origii; 4153 BOOL changed_length = FALSE; 4154 NTSTATUS Status; 4155 LARGE_INTEGER time; 4156 BTRFS_TIME now; 4157 fcb* fcb; 4158 ccb* ccb; 4159 file_ref* fileref; 4160 BOOL paging_lock = FALSE, fcb_lock = FALSE, tree_lock = FALSE, pagefile; 4161 ULONG filter = 0; 4162 4163 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb, FileObject, offset.QuadPart, buf, *length, paging_io, no_cache); 4164 4165 if (*length == 0) { 4166 WARN("returning success for zero-length write\n"); 4167 return STATUS_SUCCESS; 4168 } 4169 4170 if (!FileObject) { 4171 ERR("error - FileObject was NULL\n"); 4172 return STATUS_ACCESS_DENIED; 4173 } 4174 4175 fcb = FileObject->FsContext; 4176 ccb = FileObject->FsContext2; 4177 fileref = ccb ? ccb->fileref : NULL; 4178 4179 if (!fcb->ads && fcb->type != BTRFS_TYPE_FILE && fcb->type != BTRFS_TYPE_SYMLINK) { 4180 WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb->inode, fcb->type, &fcb->type, fcb); 4181 return STATUS_INVALID_DEVICE_REQUEST; 4182 } 4183 4184 if (offset.LowPart == FILE_WRITE_TO_END_OF_FILE && offset.HighPart == -1) 4185 offset = fcb->Header.FileSize; 4186 4187 off64 = offset.QuadPart; 4188 4189 TRACE("fcb->Header.Flags = %x\n", fcb->Header.Flags); 4190 4191 if (!no_cache && !CcCanIWrite(FileObject, *length, wait, deferred_write)) 4192 return STATUS_PENDING; 4193 4194 if (!wait && no_cache) 4195 return STATUS_PENDING; 4196 4197 if (no_cache && !paging_io && FileObject->SectionObjectPointer->DataSectionObject) { 4198 IO_STATUS_BLOCK iosb; 4199 4200 ExAcquireResourceExclusiveLite(fcb->Header.PagingIoResource, TRUE); 4201 4202 CcFlushCache(FileObject->SectionObjectPointer, &offset, *length, &iosb); 4203 4204 if (!NT_SUCCESS(iosb.Status)) { 4205 ExReleaseResourceLite(fcb->Header.PagingIoResource); 4206 ERR("CcFlushCache returned %08x\n", iosb.Status); 4207 return iosb.Status; 4208 } 4209 4210 paging_lock = TRUE; 4211 4212 CcPurgeCacheSection(FileObject->SectionObjectPointer, &offset, *length, FALSE); 4213 } 4214 4215 if (paging_io) { 4216 if (!ExAcquireResourceSharedLite(fcb->Header.PagingIoResource, wait)) { 4217 Status = STATUS_PENDING; 4218 goto end; 4219 } else 4220 paging_lock = TRUE; 4221 } 4222 4223 pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && paging_io; 4224 4225 if (!pagefile && !ExIsResourceAcquiredExclusiveLite(&Vcb->tree_lock)) { 4226 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { 4227 Status = STATUS_PENDING; 4228 goto end; 4229 } else 4230 tree_lock = TRUE; 4231 } 4232 4233 if (no_cache) { 4234 if (pagefile) { 4235 if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { 4236 Status = STATUS_PENDING; 4237 goto end; 4238 } else 4239 fcb_lock = TRUE; 4240 } else if (!ExIsResourceAcquiredExclusiveLite(fcb->Header.Resource)) { 4241 if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, wait)) { 4242 Status = STATUS_PENDING; 4243 goto end; 4244 } else 4245 fcb_lock = TRUE; 4246 } 4247 } 4248 4249 newlength = fcb->ads ? fcb->adsdata.Length : fcb->inode_item.st_size; 4250 4251 if (fcb->deleted) 4252 newlength = 0; 4253 4254 TRACE("newlength = %llx\n", newlength); 4255 4256 if (off64 + *length > newlength) { 4257 if (paging_io) { 4258 if (off64 >= newlength) { 4259 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength, off64, *length); 4260 TRACE("filename %S\n", file_desc(FileObject)); 4261 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n", 4262 fcb->Header.AllocationSize.QuadPart, fcb->Header.FileSize.QuadPart, fcb->Header.ValidDataLength.QuadPart); 4263 Status = STATUS_SUCCESS; 4264 goto end; 4265 } 4266 4267 *length = (ULONG)(newlength - off64); 4268 } else { 4269 newlength = off64 + *length; 4270 changed_length = TRUE; 4271 4272 TRACE("extending length to %llx\n", newlength); 4273 } 4274 } 4275 4276 if (fcb->ads) 4277 make_inline = FALSE; 4278 else if (fcb->type == BTRFS_TYPE_SYMLINK) 4279 make_inline = newlength <= (Vcb->superblock.node_size - sizeof(tree_header) - sizeof(leaf_node) - offsetof(EXTENT_DATA, data[0])); 4280 else 4281 make_inline = newlength <= fcb->Vcb->options.max_inline; 4282 4283 if (changed_length) { 4284 if (newlength > (UINT64)fcb->Header.AllocationSize.QuadPart) { 4285 if (!tree_lock) { 4286 // We need to acquire the tree lock if we don't have it already - 4287 // we can't give an inline file proper extents at the same time as we're 4288 // doing a flush. 4289 if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) { 4290 Status = STATUS_PENDING; 4291 goto end; 4292 } else 4293 tree_lock = TRUE; 4294 } 4295 4296 Status = extend_file(fcb, fileref, newlength, FALSE, Irp, rollback); 4297 if (!NT_SUCCESS(Status)) { 4298 ERR("extend_file returned %08x\n", Status); 4299 goto end; 4300 } 4301 } else if (!fcb->ads) 4302 fcb->inode_item.st_size = newlength; 4303 4304 fcb->Header.FileSize.QuadPart = newlength; 4305 fcb->Header.ValidDataLength.QuadPart = newlength; 4306 4307 TRACE("AllocationSize = %llx\n", fcb->Header.AllocationSize.QuadPart); 4308 TRACE("FileSize = %llx\n", fcb->Header.FileSize.QuadPart); 4309 TRACE("ValidDataLength = %llx\n", fcb->Header.ValidDataLength.QuadPart); 4310 } 4311 4312 if (!no_cache) { 4313 Status = STATUS_SUCCESS; 4314 4315 _SEH2_TRY { 4316 if (!FileObject->PrivateCacheMap || changed_length) { 4317 CC_FILE_SIZES ccfs; 4318 4319 ccfs.AllocationSize = fcb->Header.AllocationSize; 4320 ccfs.FileSize = fcb->Header.FileSize; 4321 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 4322 4323 if (!FileObject->PrivateCacheMap) 4324 init_file_cache(FileObject, &ccfs); 4325 4326 CcSetFileSizes(FileObject, &ccfs); 4327 } 4328 4329 if (IrpSp->MinorFunction & IRP_MN_MDL) { 4330 CcPrepareMdlWrite(FileObject, &offset, *length, &Irp->MdlAddress, &Irp->IoStatus); 4331 4332 Status = Irp->IoStatus.Status; 4333 goto end; 4334 } else { 4335 if (fCcCopyWriteEx) { 4336 TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject, off64, *length, wait, buf, Irp->Tail.Overlay.Thread); 4337 if (!fCcCopyWriteEx(FileObject, &offset, *length, wait, buf, Irp->Tail.Overlay.Thread)) { 4338 Status = STATUS_PENDING; 4339 goto end; 4340 } 4341 TRACE("CcCopyWriteEx finished\n"); 4342 } else { 4343 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject, off64, *length, wait, buf); 4344 if (!CcCopyWrite(FileObject, &offset, *length, wait, buf)) { 4345 Status = STATUS_PENDING; 4346 goto end; 4347 } 4348 TRACE("CcCopyWrite finished\n"); 4349 } 4350 } 4351 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4352 Status = _SEH2_GetExceptionCode(); 4353 } _SEH2_END; 4354 4355 if (changed_length) { 4356 send_notification_fcb(fcb->ads ? fileref->parent : fileref, fcb->ads ? FILE_NOTIFY_CHANGE_STREAM_SIZE : FILE_NOTIFY_CHANGE_SIZE, 4357 fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, fcb->ads && fileref->dc ? &fileref->dc->name : NULL); 4358 } 4359 4360 goto end; 4361 } 4362 4363 if (fcb->ads) { 4364 if (changed_length) { 4365 char* data2; 4366 4367 if (newlength > fcb->adsmaxlen) { 4368 ERR("error - xattr too long (%llu > %u)\n", newlength, fcb->adsmaxlen); 4369 Status = STATUS_DISK_FULL; 4370 goto end; 4371 } 4372 4373 data2 = ExAllocatePoolWithTag(PagedPool, (ULONG)newlength, ALLOC_TAG); 4374 if (!data2) { 4375 ERR("out of memory\n"); 4376 Status = STATUS_INSUFFICIENT_RESOURCES; 4377 goto end; 4378 } 4379 4380 if (fcb->adsdata.Buffer) { 4381 RtlCopyMemory(data2, fcb->adsdata.Buffer, fcb->adsdata.Length); 4382 ExFreePool(fcb->adsdata.Buffer); 4383 } 4384 4385 if (newlength > fcb->adsdata.Length) 4386 RtlZeroMemory(&data2[fcb->adsdata.Length], (ULONG)(newlength - fcb->adsdata.Length)); 4387 4388 4389 fcb->adsdata.Buffer = data2; 4390 fcb->adsdata.Length = fcb->adsdata.MaximumLength = (USHORT)newlength; 4391 4392 fcb->Header.AllocationSize.QuadPart = newlength; 4393 fcb->Header.FileSize.QuadPart = newlength; 4394 fcb->Header.ValidDataLength.QuadPart = newlength; 4395 } 4396 4397 if (*length > 0) 4398 RtlCopyMemory(&fcb->adsdata.Buffer[off64], buf, *length); 4399 4400 fcb->Header.ValidDataLength.QuadPart = newlength; 4401 4402 mark_fcb_dirty(fcb); 4403 4404 if (fileref) 4405 mark_fileref_dirty(fileref); 4406 } else { 4407 BOOL compress = write_fcb_compressed(fcb), no_buf = FALSE; 4408 4409 if (make_inline) { 4410 start_data = 0; 4411 end_data = sector_align(newlength, fcb->Vcb->superblock.sector_size); 4412 bufhead = sizeof(EXTENT_DATA) - 1; 4413 } else if (compress) { 4414 start_data = off64 & ~(UINT64)(COMPRESSED_EXTENT_SIZE - 1); 4415 end_data = min(sector_align(off64 + *length, COMPRESSED_EXTENT_SIZE), 4416 sector_align(newlength, fcb->Vcb->superblock.sector_size)); 4417 bufhead = 0; 4418 } else { 4419 start_data = off64 & ~(UINT64)(fcb->Vcb->superblock.sector_size - 1); 4420 end_data = sector_align(off64 + *length, fcb->Vcb->superblock.sector_size); 4421 bufhead = 0; 4422 } 4423 4424 if (fcb_is_inline(fcb)) 4425 end_data = max(end_data, sector_align(fcb->inode_item.st_size, Vcb->superblock.sector_size)); 4426 4427 fcb->Header.ValidDataLength.QuadPart = newlength; 4428 TRACE("fcb %p FileSize = %llx\n", fcb, fcb->Header.FileSize.QuadPart); 4429 4430 if (!make_inline && !compress && off64 == start_data && off64 + *length == end_data) { 4431 data = buf; 4432 no_buf = TRUE; 4433 } else { 4434 data = ExAllocatePoolWithTag(PagedPool, (ULONG)(end_data - start_data + bufhead), ALLOC_TAG); 4435 if (!data) { 4436 ERR("out of memory\n"); 4437 Status = STATUS_INSUFFICIENT_RESOURCES; 4438 goto end; 4439 } 4440 4441 RtlZeroMemory(data + bufhead, (ULONG)(end_data - start_data)); 4442 4443 TRACE("start_data = %llx\n", start_data); 4444 TRACE("end_data = %llx\n", end_data); 4445 4446 if (off64 > start_data || off64 + *length < end_data) { 4447 if (changed_length) { 4448 if (fcb->inode_item.st_size > start_data) 4449 Status = read_file(fcb, data + bufhead, start_data, fcb->inode_item.st_size - start_data, NULL, Irp); 4450 else 4451 Status = STATUS_SUCCESS; 4452 } else 4453 Status = read_file(fcb, data + bufhead, start_data, end_data - start_data, NULL, Irp); 4454 4455 if (!NT_SUCCESS(Status)) { 4456 ERR("read_file returned %08x\n", Status); 4457 ExFreePool(data); 4458 goto end; 4459 } 4460 } 4461 4462 RtlCopyMemory(data + bufhead + off64 - start_data, buf, *length); 4463 } 4464 4465 if (make_inline) { 4466 Status = excise_extents(fcb->Vcb, fcb, start_data, end_data, Irp, rollback); 4467 if (!NT_SUCCESS(Status)) { 4468 ERR("error - excise_extents returned %08x\n", Status); 4469 ExFreePool(data); 4470 goto end; 4471 } 4472 4473 ed2 = (EXTENT_DATA*)data; 4474 ed2->generation = fcb->Vcb->superblock.generation; 4475 ed2->decoded_size = newlength; 4476 ed2->compression = BTRFS_COMPRESSION_NONE; 4477 ed2->encryption = BTRFS_ENCRYPTION_NONE; 4478 ed2->encoding = BTRFS_ENCODING_NONE; 4479 ed2->type = EXTENT_TYPE_INLINE; 4480 4481 Status = add_extent_to_fcb(fcb, 0, ed2, (UINT16)(offsetof(EXTENT_DATA, data[0]) + newlength), FALSE, NULL, rollback); 4482 if (!NT_SUCCESS(Status)) { 4483 ERR("add_extent_to_fcb returned %08x\n", Status); 4484 ExFreePool(data); 4485 goto end; 4486 } 4487 4488 fcb->inode_item.st_blocks += newlength; 4489 } else if (compress) { 4490 Status = write_compressed(fcb, start_data, end_data, data, Irp, rollback); 4491 4492 if (!NT_SUCCESS(Status)) { 4493 ERR("write_compressed returned %08x\n", Status); 4494 ExFreePool(data); 4495 goto end; 4496 } 4497 4498 ExFreePool(data); 4499 } else { 4500 if (write_irp && Irp->MdlAddress && no_buf) { 4501 BOOL locked = Irp->MdlAddress->MdlFlags & MDL_PAGES_LOCKED; 4502 4503 if (!locked) { 4504 Status = STATUS_SUCCESS; 4505 4506 _SEH2_TRY { 4507 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess); 4508 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4509 Status = _SEH2_GetExceptionCode(); 4510 } _SEH2_END; 4511 4512 if (!NT_SUCCESS(Status)) { 4513 ERR("MmProbeAndLockPages threw exception %08x\n", Status); 4514 goto end; 4515 } 4516 } 4517 4518 _SEH2_TRY { 4519 Status = do_write_file(fcb, start_data, end_data, data, Irp, TRUE, 0, rollback); 4520 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4521 Status = _SEH2_GetExceptionCode(); 4522 } _SEH2_END; 4523 4524 if (!locked) 4525 MmUnlockPages(Irp->MdlAddress); 4526 } else { 4527 _SEH2_TRY { 4528 Status = do_write_file(fcb, start_data, end_data, data, Irp, FALSE, 0, rollback); 4529 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4530 Status = _SEH2_GetExceptionCode(); 4531 } _SEH2_END; 4532 } 4533 4534 if (!NT_SUCCESS(Status)) { 4535 ERR("do_write_file returned %08x\n", Status); 4536 if (!no_buf) ExFreePool(data); 4537 goto end; 4538 } 4539 4540 if (!no_buf) 4541 ExFreePool(data); 4542 } 4543 } 4544 4545 KeQuerySystemTime(&time); 4546 win_time_to_unix(time, &now); 4547 4548 if (!pagefile) { 4549 if (fcb->ads) { 4550 if (fileref && fileref->parent) 4551 origii = &fileref->parent->fcb->inode_item; 4552 else { 4553 ERR("no parent fcb found for stream\n"); 4554 Status = STATUS_INTERNAL_ERROR; 4555 goto end; 4556 } 4557 } else 4558 origii = &fcb->inode_item; 4559 4560 origii->transid = Vcb->superblock.generation; 4561 origii->sequence++; 4562 4563 if (!ccb->user_set_change_time) 4564 origii->st_ctime = now; 4565 4566 if (!fcb->ads) { 4567 if (changed_length) { 4568 TRACE("setting st_size to %llx\n", newlength); 4569 origii->st_size = newlength; 4570 filter |= FILE_NOTIFY_CHANGE_SIZE; 4571 } 4572 4573 fcb->inode_item_changed = TRUE; 4574 } else { 4575 fileref->parent->fcb->inode_item_changed = TRUE; 4576 4577 if (changed_length) 4578 filter |= FILE_NOTIFY_CHANGE_STREAM_SIZE; 4579 4580 filter |= FILE_NOTIFY_CHANGE_STREAM_WRITE; 4581 } 4582 4583 if (!ccb->user_set_write_time) { 4584 origii->st_mtime = now; 4585 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE; 4586 } 4587 4588 mark_fcb_dirty(fcb->ads ? fileref->parent->fcb : fcb); 4589 } 4590 4591 if (changed_length) { 4592 CC_FILE_SIZES ccfs; 4593 4594 ccfs.AllocationSize = fcb->Header.AllocationSize; 4595 ccfs.FileSize = fcb->Header.FileSize; 4596 ccfs.ValidDataLength = fcb->Header.ValidDataLength; 4597 4598 _SEH2_TRY { 4599 CcSetFileSizes(FileObject, &ccfs); 4600 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4601 Status = _SEH2_GetExceptionCode(); 4602 goto end; 4603 } _SEH2_END; 4604 } 4605 4606 fcb->subvol->root_item.ctransid = Vcb->superblock.generation; 4607 fcb->subvol->root_item.ctime = now; 4608 4609 Status = STATUS_SUCCESS; 4610 4611 if (filter != 0) 4612 send_notification_fcb(fcb->ads ? fileref->parent : fileref, filter, fcb->ads ? FILE_ACTION_MODIFIED_STREAM : FILE_ACTION_MODIFIED, 4613 fcb->ads && fileref->dc ? &fileref->dc->name : NULL); 4614 4615 end: 4616 if (NT_SUCCESS(Status) && FileObject->Flags & FO_SYNCHRONOUS_IO && !paging_io) { 4617 TRACE("CurrentByteOffset was: %llx\n", FileObject->CurrentByteOffset.QuadPart); 4618 FileObject->CurrentByteOffset.QuadPart = offset.QuadPart + (NT_SUCCESS(Status) ? *length : 0); 4619 TRACE("CurrentByteOffset now: %llx\n", FileObject->CurrentByteOffset.QuadPart); 4620 } 4621 4622 if (fcb_lock) 4623 ExReleaseResourceLite(fcb->Header.Resource); 4624 4625 if (tree_lock) 4626 ExReleaseResourceLite(&Vcb->tree_lock); 4627 4628 if (paging_lock) 4629 ExReleaseResourceLite(fcb->Header.PagingIoResource); 4630 4631 return Status; 4632 } 4633 4634 NTSTATUS write_file(device_extension* Vcb, PIRP Irp, BOOLEAN wait, BOOLEAN deferred_write) { 4635 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4636 void* buf; 4637 NTSTATUS Status; 4638 LARGE_INTEGER offset = IrpSp->Parameters.Write.ByteOffset; 4639 PFILE_OBJECT FileObject = IrpSp->FileObject; 4640 fcb* fcb = FileObject ? FileObject->FsContext : NULL; 4641 LIST_ENTRY rollback; 4642 4643 InitializeListHead(&rollback); 4644 4645 TRACE("write\n"); 4646 4647 Irp->IoStatus.Information = 0; 4648 4649 TRACE("offset = %llx\n", offset.QuadPart); 4650 TRACE("length = %x\n", IrpSp->Parameters.Write.Length); 4651 4652 if (!Irp->AssociatedIrp.SystemBuffer) { 4653 buf = map_user_buffer(Irp, fcb && fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE ? HighPagePriority : NormalPagePriority); 4654 4655 if (Irp->MdlAddress && !buf) { 4656 ERR("MmGetSystemAddressForMdlSafe returned NULL\n"); 4657 Status = STATUS_INSUFFICIENT_RESOURCES; 4658 goto exit; 4659 } 4660 } else 4661 buf = Irp->AssociatedIrp.SystemBuffer; 4662 4663 TRACE("buf = %p\n", buf); 4664 4665 if (fcb && !(Irp->Flags & IRP_PAGING_IO) && !FsRtlCheckLockForWriteAccess(&fcb->lock, Irp)) { 4666 WARN("tried to write to locked region\n"); 4667 Status = STATUS_FILE_LOCK_CONFLICT; 4668 goto exit; 4669 } 4670 4671 Status = write_file2(Vcb, Irp, offset, buf, &IrpSp->Parameters.Write.Length, Irp->Flags & IRP_PAGING_IO, Irp->Flags & IRP_NOCACHE, 4672 wait, deferred_write, TRUE, &rollback); 4673 4674 if (Status == STATUS_PENDING) 4675 goto exit; 4676 else if (!NT_SUCCESS(Status)) { 4677 ERR("write_file2 returned %08x\n", Status); 4678 goto exit; 4679 } 4680 4681 if (NT_SUCCESS(Status)) { 4682 Irp->IoStatus.Information = IrpSp->Parameters.Write.Length; 4683 4684 if (diskacc && Status != STATUS_PENDING && Irp->Flags & IRP_NOCACHE) { 4685 PETHREAD thread = NULL; 4686 4687 if (Irp->Tail.Overlay.Thread && !IoIsSystemThread(Irp->Tail.Overlay.Thread)) 4688 thread = Irp->Tail.Overlay.Thread; 4689 else if (!IoIsSystemThread(PsGetCurrentThread())) 4690 thread = PsGetCurrentThread(); 4691 else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp) 4692 thread = PsGetCurrentThread(); 4693 4694 if (thread) 4695 fPsUpdateDiskCounters(PsGetThreadProcess(thread), 0, IrpSp->Parameters.Write.Length, 0, 1, 0); 4696 } 4697 } 4698 4699 exit: 4700 if (NT_SUCCESS(Status)) 4701 clear_rollback(&rollback); 4702 else 4703 do_rollback(Vcb, &rollback); 4704 4705 return Status; 4706 } 4707 4708 _Dispatch_type_(IRP_MJ_WRITE) 4709 _Function_class_(DRIVER_DISPATCH) 4710 NTSTATUS NTAPI drv_write(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { 4711 NTSTATUS Status; 4712 BOOL top_level; 4713 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); 4714 device_extension* Vcb = DeviceObject->DeviceExtension; 4715 PFILE_OBJECT FileObject = IrpSp->FileObject; 4716 fcb* fcb = FileObject ? FileObject->FsContext : NULL; 4717 ccb* ccb = FileObject ? FileObject->FsContext2 : NULL; 4718 BOOLEAN wait = FileObject ? IoIsOperationSynchronous(Irp) : TRUE; 4719 4720 FsRtlEnterFileSystem(); 4721 4722 top_level = is_top_level(Irp); 4723 4724 if (Vcb && Vcb->type == VCB_TYPE_VOLUME) { 4725 Status = vol_write(DeviceObject, Irp); 4726 goto exit; 4727 } else if (!Vcb || Vcb->type != VCB_TYPE_FS) { 4728 Status = STATUS_INVALID_PARAMETER; 4729 goto end; 4730 } 4731 4732 if (!fcb) { 4733 ERR("fcb was NULL\n"); 4734 Status = STATUS_INVALID_PARAMETER; 4735 goto end; 4736 } 4737 4738 if (!ccb) { 4739 ERR("ccb was NULL\n"); 4740 Status = STATUS_INVALID_PARAMETER; 4741 goto end; 4742 } 4743 4744 if (Irp->RequestorMode == UserMode && !(ccb->access & (FILE_WRITE_DATA | FILE_APPEND_DATA))) { 4745 WARN("insufficient permissions\n"); 4746 Status = STATUS_ACCESS_DENIED; 4747 goto end; 4748 } 4749 4750 if (fcb == Vcb->volume_fcb) { 4751 if (!Vcb->locked || Vcb->locked_fileobj != FileObject) { 4752 ERR("trying to write to volume when not locked, or locked with another FileObject\n"); 4753 Status = STATUS_ACCESS_DENIED; 4754 goto end; 4755 } 4756 4757 TRACE("writing directly to volume\n"); 4758 4759 IoSkipCurrentIrpStackLocation(Irp); 4760 4761 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp); 4762 goto exit; 4763 } 4764 4765 if (is_subvol_readonly(fcb->subvol, Irp)) { 4766 Status = STATUS_ACCESS_DENIED; 4767 goto end; 4768 } 4769 4770 if (Vcb->readonly) { 4771 Status = STATUS_MEDIA_WRITE_PROTECTED; 4772 goto end; 4773 } 4774 4775 _SEH2_TRY { 4776 if (IrpSp->MinorFunction & IRP_MN_COMPLETE) { 4777 CcMdlWriteComplete(IrpSp->FileObject, &IrpSp->Parameters.Write.ByteOffset, Irp->MdlAddress); 4778 4779 Irp->MdlAddress = NULL; 4780 Status = STATUS_SUCCESS; 4781 } else { 4782 // Don't offload jobs when doing paging IO - otherwise this can lead to 4783 // deadlocks in CcCopyWrite. 4784 if (Irp->Flags & IRP_PAGING_IO) 4785 wait = TRUE; 4786 4787 Status = write_file(Vcb, Irp, wait, FALSE); 4788 } 4789 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { 4790 Status = _SEH2_GetExceptionCode(); 4791 } _SEH2_END; 4792 4793 end: 4794 Irp->IoStatus.Status = Status; 4795 4796 TRACE("wrote %u bytes\n", Irp->IoStatus.Information); 4797 4798 if (Status != STATUS_PENDING) 4799 IoCompleteRequest(Irp, IO_NO_INCREMENT); 4800 else { 4801 IoMarkIrpPending(Irp); 4802 4803 if (!add_thread_job(Vcb, Irp)) 4804 do_write_job(Vcb, Irp); 4805 } 4806 4807 exit: 4808 if (top_level) 4809 IoSetTopLevelIrp(NULL); 4810 4811 TRACE("returning %08x\n", Status); 4812 4813 FsRtlExitFileSystem(); 4814 4815 return Status; 4816 } 4817