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