1 /* Copyright (c) Mark Harmstone 2016-17
3 * This file is part of WinBtrfs.
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.
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.
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/>. */
18 #include "btrfs_drv.h"
28 _Function_class_(IO_COMPLETION_ROUTINE
)
30 static NTSTATUS NTAPI
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
);
32 static NTSTATUS
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
);
35 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
);
37 extern tPsUpdateDiskCounters fPsUpdateDiskCounters
;
38 extern tCcCopyWriteEx fCcCopyWriteEx
;
39 extern tFsRtlUpdateDiskCounters fFsRtlUpdateDiskCounters
;
42 BOOL
find_data_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64 length
, UINT64
* address
) {
46 TRACE("(%p, %llx, %llx, %p)\n", Vcb
, c
->offset
, length
, address
);
48 if (length
> c
->chunk_item
->size
- c
->used
)
51 if (!c
->cache_loaded
) {
52 NTSTATUS Status
= load_cache_chunk(Vcb
, c
, NULL
);
54 if (!NT_SUCCESS(Status
)) {
55 ERR("load_cache_chunk returned %08x\n", Status
);
60 if (IsListEmpty(&c
->space_size
))
63 le
= c
->space_size
.Flink
;
64 while (le
!= &c
->space_size
) {
65 s
= CONTAINING_RECORD(le
, space
, list_entry_size
);
67 if (s
->size
== length
) {
68 *address
= s
->address
;
70 } else if (s
->size
< length
) {
71 if (le
== c
->space_size
.Flink
)
74 s
= CONTAINING_RECORD(le
->Blink
, space
, list_entry_size
);
76 *address
= s
->address
;
83 s
= CONTAINING_RECORD(c
->space_size
.Blink
, space
, list_entry_size
);
85 if (s
->size
> length
) {
86 *address
= s
->address
;
93 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
96 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
98 le2
= Vcb
->chunks
.Flink
;
99 while (le2
!= &Vcb
->chunks
) {
100 chunk
* c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
102 if (address
>= c
->offset
&& address
< c
->offset
+ c
->chunk_item
->size
) {
103 ExReleaseResourceLite(&Vcb
->chunk_lock
);
110 ExReleaseResourceLite(&Vcb
->chunk_lock
);
120 static UINT64
find_new_chunk_address(device_extension
* Vcb
, UINT64 size
) {
126 le
= Vcb
->chunks
.Flink
;
127 while (le
!= &Vcb
->chunks
) {
128 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
130 if (c
->offset
>= lastaddr
+ size
)
133 lastaddr
= c
->offset
+ c
->chunk_item
->size
;
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
;
147 le
= Vcb
->devices
.Flink
;
149 while (le
!= &Vcb
->devices
) {
150 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
152 if (!dev
->readonly
&& !dev
->reloc
&& dev
->devobj
) {
153 UINT64 usage
= (dev
->devitem
.bytes_used
* 4096) / dev
->devitem
.num_bytes
;
155 // favour devices which have been used the least
156 if (usage
< devusage
) {
157 if (!IsListEmpty(&dev
->space
)) {
159 space
*dh1
= NULL
, *dh2
= NULL
;
161 le2
= dev
->space
.Flink
;
162 while (le2
!= &dev
->space
) {
163 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
165 if (dh
->size
>= max_stripe_size
&& (!dh1
|| !dh2
|| dh
->size
< dh1
->size
)) {
173 if (dh1
&& (dh2
|| dh1
->size
>= 2 * max_stripe_size
)) {
177 devdh2
= dh2
? dh2
: dh1
;
189 // Can't find hole of at least max_stripe_size; look for the largest one we can find
194 le
= Vcb
->devices
.Flink
;
195 while (le
!= &Vcb
->devices
) {
196 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
198 if (!dev
->readonly
&& !dev
->reloc
) {
199 if (!IsListEmpty(&dev
->space
)) {
201 space
*dh1
= NULL
, *dh2
= NULL
;
203 le2
= dev
->space
.Flink
;
204 while (le2
!= &dev
->space
) {
205 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
207 if (!dh1
|| !dh2
|| dh
->size
< dh1
->size
) {
219 devsize
= max(dh1
->size
/ 2, min(dh1
->size
, dh2
->size
));
221 devsize
= dh1
->size
/ 2;
223 if (devsize
> size
) {
227 if (dh2
&& min(dh1
->size
, dh2
->size
) > dh1
->size
/ 2)
245 stripes
[0].device
= stripes
[1].device
= dev2
;
246 stripes
[0].dh
= devdh1
;
247 stripes
[1].dh
= devdh2
;
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;
258 le
= Vcb
->devices
.Flink
;
259 while (le
!= &Vcb
->devices
) {
260 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
264 if (dev
->readonly
|| dev
->reloc
|| (!dev
->devobj
&& !allow_missing
)) {
269 // skip this device if it already has a stripe
271 for (k
= 0; k
< i
; k
++) {
272 if (stripes
[k
].device
== dev
) {
280 usage
= (dev
->devitem
.bytes_used
* 4096) / dev
->devitem
.num_bytes
;
282 // favour devices which have been used the least
283 if (usage
< devusage
) {
284 if (!IsListEmpty(&dev
->space
)) {
287 le2
= dev
->space
.Flink
;
288 while (le2
!= &dev
->space
) {
289 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
291 if ((dev2
!= dev
&& dh
->size
>= max_stripe_size
) ||
292 (dev2
== dev
&& dh
->size
>= max_stripe_size
&& dh
->size
< devdh
->size
)
309 // Can't find hole of at least max_stripe_size; look for the largest one we can find
314 le
= Vcb
->devices
.Flink
;
315 while (le
!= &Vcb
->devices
) {
316 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
319 if (dev
->readonly
|| dev
->reloc
|| (!dev
->devobj
&& !allow_missing
)) {
324 // skip this device if it already has a stripe
326 for (k
= 0; k
< i
; k
++) {
327 if (stripes
[k
].device
== dev
) {
335 if (!IsListEmpty(&dev
->space
)) {
338 le2
= dev
->space
.Flink
;
339 while (le2
!= &dev
->space
) {
340 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
342 if (!devdh
|| devdh
->size
< dh
->size
) {
359 stripes
[i
].dh
= devdh
;
360 stripes
[i
].device
= dev2
;
365 NTSTATUS
alloc_chunk(device_extension
* Vcb
, UINT64 flags
, chunk
** pc
, BOOL full_size
) {
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
;
372 CHUNK_ITEM_STRIPE
* cis
;
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
;
385 TRACE("total_size = %llx\n", total_size
);
387 // We purposely check for DATA first - mixed blocks have the same size
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
396 max_stripe_size
= 0x10000000; // 256 MB
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
;
403 ERR("unknown chunk type\n");
404 return STATUS_INTERNAL_ERROR
;
407 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
409 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
411 if (flags
& BLOCK_FLAG_DUPLICATE
) {
415 type
= BLOCK_FLAG_DUPLICATE
;
417 } else if (flags
& BLOCK_FLAG_RAID0
) {
419 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
421 type
= BLOCK_FLAG_RAID0
;
423 } else if (flags
& BLOCK_FLAG_RAID1
) {
427 type
= BLOCK_FLAG_RAID1
;
429 } else if (flags
& BLOCK_FLAG_RAID10
) {
431 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
433 type
= BLOCK_FLAG_RAID10
;
435 } else if (flags
& BLOCK_FLAG_RAID5
) {
437 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
439 type
= BLOCK_FLAG_RAID5
;
441 } else if (flags
& BLOCK_FLAG_RAID6
) {
445 type
= BLOCK_FLAG_RAID6
;
455 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * max_stripes
, ALLOC_TAG
);
457 ERR("out of memory\n");
458 Status
= STATUS_INSUFFICIENT_RESOURCES
;
464 if (type
== BLOCK_FLAG_DUPLICATE
) {
465 if (!find_new_dup_stripes(Vcb
, stripes
, max_stripe_size
, full_size
)) {
466 Status
= STATUS_DISK_FULL
;
470 num_stripes
= max_stripes
;
472 for (i
= 0; i
< max_stripes
; i
++) {
473 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, FALSE
, full_size
))
480 if (num_stripes
< min_stripes
&& Vcb
->options
.allow_degraded
&& allowed_missing
> 0) {
481 UINT16 added_missing
= 0;
483 for (i
= num_stripes
; i
< max_stripes
; i
++) {
484 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, TRUE
, full_size
))
488 if (added_missing
>= allowed_missing
)
493 num_stripes
+= added_missing
;
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
;
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
;
507 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
509 ERR("out of memory\n");
510 Status
= STATUS_INSUFFICIENT_RESOURCES
;
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
;
522 stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
524 if (type
== BLOCK_FLAG_DUPLICATE
&& stripes
[1].dh
== stripes
[0].dh
)
525 stripe_size
= min(stripes
[0].dh
->size
/ 2, max_stripe_size
);
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
;
534 if (type
== 0 || type
== BLOCK_FLAG_DUPLICATE
|| type
== BLOCK_FLAG_RAID1
)
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;
545 if (stripe_size
* factor
> max_chunk_size
)
546 stripe_size
= max_chunk_size
/ factor
;
548 if (stripe_size
% stripe_length
> 0)
549 stripe_size
-= stripe_size
% stripe_length
;
551 if (stripe_size
== 0) {
552 Status
= STATUS_INTERNAL_ERROR
;
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
;
566 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
568 ERR("out of memory\n");
569 Status
= STATUS_INSUFFICIENT_RESOURCES
;
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
;
577 if (type
== BLOCK_FLAG_DUPLICATE
&& i
== 1 && stripes
[i
].dh
== stripes
[0].dh
)
578 cis
[i
].offset
= stripes
[0].dh
->address
+ stripe_size
;
580 cis
[i
].offset
= stripes
[i
].dh
->address
;
582 cis
[i
].dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
584 c
->devices
[i
] = stripes
[i
].device
;
587 logaddr
= find_new_chunk_address(Vcb
, c
->chunk_item
->size
);
589 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
593 c
->used
= c
->oldused
= 0;
594 c
->cache
= c
->old_cache
= NULL
;
597 c
->last_alloc_set
= FALSE
;
599 c
->cache_loaded
= TRUE
;
601 c
->space_changed
= FALSE
;
604 InitializeListHead(&c
->space
);
605 InitializeListHead(&c
->space_size
);
606 InitializeListHead(&c
->deleting
);
607 InitializeListHead(&c
->changed_extents
);
609 InitializeListHead(&c
->range_locks
);
610 ExInitializeResourceLite(&c
->range_locks_lock
);
611 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
613 InitializeListHead(&c
->partial_stripes
);
614 ExInitializeResourceLite(&c
->partial_stripes_lock
);
616 ExInitializeResourceLite(&c
->lock
);
617 ExInitializeResourceLite(&c
->changed_extents_lock
);
619 s
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(space
), ALLOC_TAG
);
621 ERR("out of memory\n");
622 Status
= STATUS_INSUFFICIENT_RESOURCES
;
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
);
631 protect_superblocks(c
);
633 for (i
= 0; i
< num_stripes
; i
++) {
634 stripes
[i
].device
->devitem
.bytes_used
+= stripe_size
;
636 space_list_subtract2(&stripes
[i
].device
->space
, NULL
, cis
[i
].offset
, stripe_size
, NULL
, NULL
);
639 Status
= STATUS_SUCCESS
;
641 if (flags
& BLOCK_FLAG_RAID5
|| flags
& BLOCK_FLAG_RAID6
)
642 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_RAID56
;
648 if (!NT_SUCCESS(Status
)) {
651 ExFreePool(c
->devices
);
654 ExFreePool(c
->chunk_item
);
659 if (s
) ExFreePool(s
);
663 le
= Vcb
->chunks
.Flink
;
664 while (le
!= &Vcb
->chunks
) {
665 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
667 if (c2
->offset
> c
->offset
) {
668 InsertHeadList(le
->Blink
, &c
->list_entry
);
677 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
681 c
->space_changed
= TRUE
;
682 c
->list_entry_balance
.Flink
= NULL
;
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
;
696 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
700 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
702 ERR("out of memory\n");
703 return STATUS_INSUFFICIENT_RESOURCES
;
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
);
710 master_mdl
= Irp
->MdlAddress
;
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
);
717 ERR("out of memory\n");
718 return STATUS_INSUFFICIENT_RESOURCES
;
721 RtlCopyMemory(wtc
->scratch
, data
, length
);
723 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
725 ERR("out of memory\n");
726 return STATUS_INSUFFICIENT_RESOURCES
;
729 MmBuildMdlForNonPagedPool(master_mdl
);
731 wtc
->mdl
= master_mdl
;
733 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
735 NTSTATUS Status
= STATUS_SUCCESS
;
737 master_mdl
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
739 ERR("out of memory\n");
740 return STATUS_INSUFFICIENT_RESOURCES
;
744 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
745 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
746 Status
= _SEH2_GetExceptionCode();
749 if (!NT_SUCCESS(Status
)) {
750 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
751 IoFreeMdl(master_mdl
);
755 wtc
->mdl
= master_mdl
;
757 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
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
;
766 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
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;
773 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
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
;
786 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
788 stripenum
= startoffstripe
;
790 while (pos
< length
) {
791 PFN_NUMBER
* stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripenum
].mdl
+ 1);
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
));
797 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
799 stripeoff
[stripenum
] += 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
));
805 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
807 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
808 pos
+= c
->chunk_item
->stripe_length
;
811 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
814 ExFreePool(stripeoff
);
816 return STATUS_SUCCESS
;
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
;
826 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
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
);
833 stripenum
= startoffstripe
;
834 startoffstripe
*= c
->chunk_item
->sub_stripes
;
835 endoffstripe
*= c
->chunk_item
->sub_stripes
;
838 master_mdl
= Irp
->MdlAddress
;
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
);
845 ERR("out of memory\n");
846 return STATUS_INSUFFICIENT_RESOURCES
;
849 RtlCopyMemory(wtc
->scratch
, data
, length
);
851 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
853 ERR("out of memory\n");
854 return STATUS_INSUFFICIENT_RESOURCES
;
857 MmBuildMdlForNonPagedPool(master_mdl
);
859 wtc
->mdl
= master_mdl
;
861 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
863 NTSTATUS Status
= STATUS_SUCCESS
;
865 master_mdl
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
867 ERR("out of memory\n");
868 return STATUS_INSUFFICIENT_RESOURCES
;
872 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
873 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
874 Status
= _SEH2_GetExceptionCode();
877 if (!NT_SUCCESS(Status
)) {
878 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
879 IoFreeMdl(master_mdl
);
883 wtc
->mdl
= master_mdl
;
885 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
888 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= c
->chunk_item
->sub_stripes
) {
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
;
896 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
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;
903 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
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
;
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
;
921 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, ALLOC_TAG
);
923 ERR("out of memory\n");
924 return STATUS_INSUFFICIENT_RESOURCES
;
927 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
929 while (pos
< length
) {
930 PFN_NUMBER
* stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].mdl
+ 1);
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
));
936 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
938 stripeoff
[stripenum
] += 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
));
944 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
946 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
947 pos
+= c
->chunk_item
->stripe_length
;
950 stripenum
= (stripenum
+ 1) % (c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
953 ExFreePool(stripeoff
);
955 return STATUS_SUCCESS
;
958 static NTSTATUS
add_partial_stripe(device_extension
* Vcb
, chunk
*c
, UINT64 address
, UINT32 length
, void* data
) {
963 UINT16 num_data_stripes
;
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
));
969 ExAcquireResourceExclusiveLite(&c
->partial_stripes_lock
, TRUE
);
971 le
= c
->partial_stripes
.Flink
;
972 while (le
!= &c
->partial_stripes
) {
973 ps
= CONTAINING_RECORD(le
, partial_stripe
, list_entry
);
975 if (ps
->address
== stripe_addr
) {
976 // update existing entry
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
);
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
);
989 RemoveEntryList(&ps
->list_entry
);
992 ExFreePool(ps
->bmparr
);
997 Status
= STATUS_SUCCESS
;
999 } else if (ps
->address
> stripe_addr
)
1007 ps
= ExAllocatePoolWithTag(NonPagedPool
, offsetof(partial_stripe
, data
[0]) + (ULONG
)(num_data_stripes
* c
->chunk_item
->stripe_length
), ALLOC_TAG
);
1009 ERR("out of memory\n");
1010 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1014 bmplen
= (ULONG
)sector_align(((num_data_stripes
* c
->chunk_item
->stripe_length
) / (8 * Vcb
->superblock
.sector_size
) + 1), sizeof(ULONG
));
1016 ps
->address
= stripe_addr
;
1017 ps
->bmparr
= ExAllocatePoolWithTag(NonPagedPool
, bmplen
, ALLOC_TAG
);
1019 ERR("out of memory\n");
1021 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1025 RtlInitializeBitMap(&ps
->bmp
, ps
->bmparr
, (ULONG
)((num_data_stripes
* c
->chunk_item
->stripe_length
) / Vcb
->superblock
.sector_size
));
1026 RtlSetAllBits(&ps
->bmp
);
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
);
1031 InsertHeadList(le
->Blink
, &ps
->list_entry
);
1033 Status
= STATUS_SUCCESS
;
1036 ExReleaseResourceLite(&c
->partial_stripes_lock
);
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
;
1052 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
1055 PFN_NUMBER
*pfns
, *parity_pfns
;
1056 log_stripe
* log_stripes
= NULL
;
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
);
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
);
1068 length
-= (UINT32
)delta
;
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
));
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
);
1081 length
-= (UINT32
)delta
;
1082 irp_offset
+= delta
;
1086 Status
= STATUS_SUCCESS
;
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
);
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
;
1098 UINT16 stripe
= (parity
+ startoffstripe
+ 1) % c
->chunk_item
->num_stripes
;
1099 ULONG skip
, writelen
;
1102 while (stripe
!= parity
) {
1103 if (i
== startoffstripe
) {
1104 writelen
= (ULONG
)min(length
, c
->chunk_item
->stripe_length
- (startoff
% c
->chunk_item
->stripe_length
));
1106 stripes
[stripe
].start
= startoff
;
1107 stripes
[stripe
].end
= startoff
+ writelen
;
1114 writelen
= (ULONG
)min(length
- pos
, c
->chunk_item
->stripe_length
);
1116 stripes
[stripe
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1117 stripes
[stripe
].end
= stripes
[stripe
].start
+ writelen
;
1126 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1132 for (i
= 0; i
< startoffstripe
; i
++) {
1133 stripe
= (parity
+ i
+ 1) % c
->chunk_item
->num_stripes
;
1135 stripes
[stripe
].start
= stripes
[stripe
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1138 stripes
[parity
].start
= stripes
[parity
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
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);
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
;
1147 pos
+= skip
* num_data_stripes
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
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
;
1154 pos
+= c
->chunk_item
->stripe_length
* num_data_stripes
;
1156 UINT16 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1159 while (stripe
!= parity
) {
1160 if (endoffstripe
== i
) {
1161 stripes
[stripe
].end
= endoff
+ 1;
1163 } else if (endoffstripe
> i
)
1164 stripes
[stripe
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1167 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1174 parity_start
= 0xffffffffffffffff;
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
);
1184 if (parity_end
== parity_start
) {
1185 Status
= STATUS_SUCCESS
;
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
;
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
;
1195 log_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(log_stripe
) * num_data_stripes
, ALLOC_TAG
);
1197 ERR("out of memory\n");
1198 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1202 RtlZeroMemory(log_stripes
, sizeof(log_stripe
) * num_data_stripes
);
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
;
1212 log_stripes
[i
].mdl
->MdlFlags
|= MDL_PARTIAL
;
1213 log_stripes
[i
].pfns
= (PFN_NUMBER
*)(log_stripes
[i
].mdl
+ 1);
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
;
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
;
1230 MmBuildMdlForNonPagedPool(wtc
->parity1_mdl
);
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
;
1242 RtlCopyMemory(wtc
->scratch
, (UINT8
*)data
+ irp_offset
, length
);
1244 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
1246 ERR("out of memory\n");
1247 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1251 MmBuildMdlForNonPagedPool(master_mdl
);
1253 wtc
->mdl
= master_mdl
;
1255 master_mdl
= IoAllocateMdl((UINT8
*)data
+ irp_offset
, length
, FALSE
, FALSE
, NULL
);
1257 ERR("out of memory\n");
1258 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1262 Status
= STATUS_SUCCESS
;
1265 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
1266 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1267 Status
= _SEH2_GetExceptionCode();
1270 if (!NT_SUCCESS(Status
)) {
1271 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1272 IoFreeMdl(master_mdl
);
1276 wtc
->mdl
= master_mdl
;
1279 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
1280 parity_pfns
= (PFN_NUMBER
*)(wtc
->parity1_mdl
+ 1);
1283 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
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
;
1295 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1297 ERR("out of memory\n");
1298 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1302 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
1307 while (pos
< length
) {
1308 PFN_NUMBER
* stripe_pfns
;
1310 parity
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
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
;
1318 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1320 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1322 RtlCopyMemory(log_stripes
[startoffstripe
].pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1323 log_stripes
[startoffstripe
].pfns
+= writelen
>> PAGE_SHIFT
;
1325 stripeoff
[stripe
] = writelen
;
1328 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1329 i
= startoffstripe
+ 1;
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
));
1338 if (writelen
> maxwritelen
)
1339 maxwritelen
= writelen
;
1341 RtlCopyMemory(stripe_pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1343 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1344 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1346 stripeoff
[stripe
] = writelen
;
1349 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1353 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
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
;
1362 while (stripe
!= parity
) {
1363 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1365 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
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
;
1370 stripeoff
[stripe
] += c
->chunk_item
->stripe_length
;
1371 pos
+= c
->chunk_item
->stripe_length
;
1373 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1377 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
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
;
1383 UINT16 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1384 UINT32 writelen
, maxwritelen
= 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
));
1394 if (writelen
> maxwritelen
)
1395 maxwritelen
= writelen
;
1397 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1399 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1400 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1402 stripeoff
[stripe
] += writelen
;
1405 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1409 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
1411 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity
] >> PAGE_SHIFT
], &parity_pfns
[parity_pos
>> PAGE_SHIFT
], maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1415 for (i
= 0; i
< num_data_stripes
; i
++) {
1416 UINT8
* ss
= MmGetSystemAddressForMdlSafe(log_stripes
[i
].mdl
, priority
);
1419 RtlCopyMemory(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1421 do_xor(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1424 Status
= STATUS_SUCCESS
;
1428 for (i
= 0; i
< num_data_stripes
; i
++) {
1429 if (log_stripes
[i
].mdl
)
1430 IoFreeMdl(log_stripes
[i
].mdl
);
1433 ExFreePool(log_stripes
);
1437 ExFreePool(stripeoff
);
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
;
1448 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
1451 PFN_NUMBER
*pfns
, *parity1_pfns
, *parity2_pfns
;
1452 log_stripe
* log_stripes
= NULL
;
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
);
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
);
1464 length
-= (UINT32
)delta
;
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
));
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
);
1477 length
-= (UINT32
)delta
;
1478 irp_offset
+= delta
;
1482 Status
= STATUS_SUCCESS
;
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
);
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
;
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
;
1499 while (stripe
!= parity1
) {
1500 if (i
== startoffstripe
) {
1501 writelen
= (ULONG
)min(length
, c
->chunk_item
->stripe_length
- (startoff
% c
->chunk_item
->stripe_length
));
1503 stripes
[stripe
].start
= startoff
;
1504 stripes
[stripe
].end
= startoff
+ writelen
;
1511 writelen
= (ULONG
)min(length
- pos
, c
->chunk_item
->stripe_length
);
1513 stripes
[stripe
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1514 stripes
[stripe
].end
= stripes
[stripe
].start
+ writelen
;
1523 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1529 for (i
= 0; i
< startoffstripe
; i
++) {
1530 stripe
= (parity1
+ i
+ 2) % c
->chunk_item
->num_stripes
;
1532 stripes
[stripe
].start
= stripes
[stripe
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
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
;
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);
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
;
1545 pos
+= skip
* num_data_stripes
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
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
;
1552 pos
+= c
->chunk_item
->stripe_length
* num_data_stripes
;
1554 UINT16 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
1557 while (stripe
!= parity1
) {
1558 if (endoffstripe
== i
) {
1559 stripes
[stripe
].end
= endoff
+ 1;
1561 } else if (endoffstripe
> i
)
1562 stripes
[stripe
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1565 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1572 parity_start
= 0xffffffffffffffff;
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
);
1582 if (parity_end
== parity_start
) {
1583 Status
= STATUS_SUCCESS
;
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
;
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
;
1593 log_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(log_stripe
) * num_data_stripes
, ALLOC_TAG
);
1595 ERR("out of memory\n");
1596 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1600 RtlZeroMemory(log_stripes
, sizeof(log_stripe
) * num_data_stripes
);
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
;
1610 log_stripes
[i
].mdl
->MdlFlags
|= MDL_PARTIAL
;
1611 log_stripes
[i
].pfns
= (PFN_NUMBER
*)(log_stripes
[i
].mdl
+ 1);
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
;
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
;
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
;
1635 MmBuildMdlForNonPagedPool(wtc
->parity1_mdl
);
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
;
1644 MmBuildMdlForNonPagedPool(wtc
->parity2_mdl
);
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
;
1656 RtlCopyMemory(wtc
->scratch
, (UINT8
*)data
+ irp_offset
, length
);
1658 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
1660 ERR("out of memory\n");
1661 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1665 MmBuildMdlForNonPagedPool(master_mdl
);
1667 wtc
->mdl
= master_mdl
;
1669 master_mdl
= IoAllocateMdl((UINT8
*)data
+ irp_offset
, length
, FALSE
, FALSE
, NULL
);
1671 ERR("out of memory\n");
1672 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1676 Status
= STATUS_SUCCESS
;
1679 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
1680 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1681 Status
= _SEH2_GetExceptionCode();
1684 if (!NT_SUCCESS(Status
)) {
1685 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1686 IoFreeMdl(master_mdl
);
1690 wtc
->mdl
= master_mdl
;
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);
1698 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
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
;
1710 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1712 ERR("out of memory\n");
1713 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1717 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
1722 while (pos
< length
) {
1723 PFN_NUMBER
* stripe_pfns
;
1725 parity1
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
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
;
1733 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1735 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1737 RtlCopyMemory(log_stripes
[startoffstripe
].pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1738 log_stripes
[startoffstripe
].pfns
+= writelen
>> PAGE_SHIFT
;
1740 stripeoff
[stripe
] = writelen
;
1743 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1744 i
= startoffstripe
+ 1;
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
));
1753 if (writelen
> maxwritelen
)
1754 maxwritelen
= writelen
;
1756 RtlCopyMemory(stripe_pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1758 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1759 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1761 stripeoff
[stripe
] = writelen
;
1764 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
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
;
1772 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
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
;
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
;
1783 while (stripe
!= parity1
) {
1784 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1786 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
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
;
1791 stripeoff
[stripe
] += c
->chunk_item
->stripe_length
;
1792 pos
+= c
->chunk_item
->stripe_length
;
1794 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
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
;
1802 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
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
;
1808 parity_pos
+= c
->chunk_item
->stripe_length
;
1810 UINT16 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
, parity2
;
1811 UINT32 writelen
, maxwritelen
= 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
));
1821 if (writelen
> maxwritelen
)
1822 maxwritelen
= writelen
;
1824 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1826 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1827 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1829 stripeoff
[stripe
] += writelen
;
1832 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
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
);
1839 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
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
);
1846 for (i
= 0; i
< num_data_stripes
; i
++) {
1847 UINT8
* ss
= MmGetSystemAddressForMdlSafe(log_stripes
[c
->chunk_item
->num_stripes
- 3 - i
].mdl
, priority
);
1850 RtlCopyMemory(wtc
->parity1
, ss
, (ULONG
)(parity_end
- parity_start
));
1851 RtlCopyMemory(wtc
->parity2
, ss
, (ULONG
)(parity_end
- parity_start
));
1853 do_xor(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1855 galois_double(wtc
->parity2
, (UINT32
)(parity_end
- parity_start
));
1856 do_xor(wtc
->parity2
, ss
, (UINT32
)(parity_end
- parity_start
));
1860 Status
= STATUS_SUCCESS
;
1864 for (i
= 0; i
< num_data_stripes
; i
++) {
1865 if (log_stripes
[i
].mdl
)
1866 IoFreeMdl(log_stripes
[i
].mdl
);
1869 ExFreePool(log_stripes
);
1873 ExFreePool(stripeoff
);
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
) {
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
;
1888 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
1891 c
= get_chunk_from_address(Vcb
, address
);
1893 ERR("could not get chunk for address %llx\n", address
);
1894 return STATUS_INTERNAL_ERROR
;
1898 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1900 ERR("out of memory\n");
1901 return STATUS_INSUFFICIENT_RESOURCES
;
1904 RtlZeroMemory(stripes
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
);
1906 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
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
;
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
;
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
;
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
;
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
;
1947 if (c
->devices
[i
]->devobj
) {
1950 ULONG writelen
= (ULONG
)(stripes
[i
].end
- stripes
[i
].start
);
1952 va
= (UINT8
*)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + stripes
[i
].irp_offset
;
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
;
1961 IoBuildPartialMdl(Irp
->MdlAddress
, stripes
[i
].mdl
, va
, writelen
);
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
;
1970 Status
= STATUS_SUCCESS
;
1973 MmProbeAndLockPages(stripes
[i
].mdl
, KernelMode
, IoReadAccess
);
1974 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1975 Status
= _SEH2_GetExceptionCode();
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
;
1988 allowed_missing
= c
->chunk_item
->num_stripes
- 1;
1992 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1993 if (!c
->devices
[i
]->devobj
)
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
;
2003 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2004 PIO_STACK_LOCATION IrpSp
;
2006 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_stripe
), ALLOC_TAG
);
2008 ERR("out of memory\n");
2009 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2013 if (stripes
[i
].start
== stripes
[i
].end
|| !c
->devices
[i
]->devobj
) {
2014 stripe
->status
= WriteDataStatus_Ignore
;
2016 stripe
->buf
= stripes
[i
].data
;
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
;
2027 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
2030 ERR("IoAllocateIrp failed\n");
2031 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2035 stripe
->Irp
= IoMakeAssociatedIrp(Irp
, stripe
->device
->devobj
->StackSize
);
2038 ERR("IoMakeAssociatedIrp failed\n");
2039 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2044 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
2045 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
2047 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
2048 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= MmGetSystemAddressForMdlSafe(stripes
[i
].mdl
, priority
);
2050 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
2051 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
)
2052 stripe
->Irp
->MdlAddress
= stripe
->mdl
;
2054 stripe
->Irp
->UserBuffer
= MmGetSystemAddressForMdlSafe(stripes
[i
].mdl
, priority
);
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
);
2063 IrpSp
->Parameters
.Write
.Length
= (ULONG
)(stripes
[i
].end
- stripes
[i
].start
);
2064 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= stripes
[i
].start
+ cis
[i
].offset
;
2066 total_writing
+= IrpSp
->Parameters
.Write
.Length
;
2068 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
2069 wtc
->stripes_left
++;
2071 IoSetCompletionRoutine(stripe
->Irp
, write_data_completion
, stripe
, TRUE
, TRUE
, TRUE
);
2074 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
2078 fFsRtlUpdateDiskCounters(0, total_writing
);
2080 Status
= STATUS_SUCCESS
;
2084 if (stripes
) ExFreePool(stripes
);
2086 if (!NT_SUCCESS(Status
))
2087 free_write_data_stripes(wtc
);
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
);
2097 IoFreeMdl(stripes
[i
].mdl
);
2101 if (wtc
->parity1_mdl
) {
2102 if (wtc
->parity1_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2103 MmUnlockPages(wtc
->parity1_mdl
);
2105 IoFreeMdl(wtc
->parity1_mdl
);
2106 wtc
->parity1_mdl
= NULL
;
2109 if (wtc
->parity2_mdl
) {
2110 if (wtc
->parity2_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2111 MmUnlockPages(wtc
->parity2_mdl
);
2113 IoFreeMdl(wtc
->parity2_mdl
);
2114 wtc
->parity2_mdl
= NULL
;
2118 if (wtc
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2119 MmUnlockPages(wtc
->mdl
);
2121 IoFreeMdl(wtc
->mdl
);
2126 ExFreePool(wtc
->parity1
);
2127 wtc
->parity1
= NULL
;
2131 ExFreePool(wtc
->parity2
);
2132 wtc
->parity2
= NULL
;
2136 ExFreePool(wtc
->scratch
);
2137 wtc
->scratch
= NULL
;
2140 ExFreePool(stripes
);
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
;
2148 datastripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
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
);
2153 startoff
-= startoff
% c
->chunk_item
->stripe_length
;
2154 endoff
= sector_align(endoff
, c
->chunk_item
->stripe_length
);
2156 *lockaddr
= c
->offset
+ (startoff
* datastripes
);
2157 *locklen
= (endoff
- startoff
) * datastripes
;
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
;
2163 UINT64 lockaddr
, locklen
;
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
;
2172 c
= get_chunk_from_address(Vcb
, address
);
2174 ERR("could not get chunk for address %llx\n", address
);
2175 return STATUS_INTERNAL_ERROR
;
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
);
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();
2190 if (!NT_SUCCESS(Status
)) {
2191 ERR("write_data returned %08x\n", Status
);
2193 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
2194 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
2196 free_write_data_stripes(&wtc
);
2200 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
2201 // launch writes and wait
2202 LIST_ENTRY
* le
= wtc
.stripes
.Flink
;
2203 BOOL no_wait
= TRUE
;
2205 while (le
!= &wtc
.stripes
) {
2206 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2208 if (stripe
->status
!= WriteDataStatus_Ignore
) {
2209 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2217 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2219 le
= wtc
.stripes
.Flink
;
2220 while (le
!= &wtc
.stripes
) {
2221 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2223 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
2224 Status
= stripe
->iosb
.Status
;
2226 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
2233 free_write_data_stripes(&wtc
);
2236 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
2237 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
2242 _Function_class_(IO_COMPLETION_ROUTINE
)
2244 static NTSTATUS NTAPI
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2246 static NTSTATUS
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2248 write_data_stripe
* stripe
= conptr
;
2249 write_data_context
* context
= (write_data_context
*)stripe
->context
;
2252 UNUSED(DeviceObject
);
2254 // FIXME - we need a lock here
2256 if (stripe
->status
== WriteDataStatus_Cancelling
) {
2257 stripe
->status
= WriteDataStatus_Cancelled
;
2261 stripe
->iosb
= Irp
->IoStatus
;
2263 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
2264 stripe
->status
= WriteDataStatus_Success
;
2266 le
= context
->stripes
.Flink
;
2268 stripe
->status
= WriteDataStatus_Error
;
2270 while (le
!= &context
->stripes
) {
2271 write_data_stripe
* s2
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2273 if (s2
->status
== WriteDataStatus_Pending
) {
2274 s2
->status
= WriteDataStatus_Cancelling
;
2275 IoCancelIrp(s2
->Irp
);
2283 if (InterlockedDecrement(&context
->stripes_left
) == 0)
2284 KeSetEvent(&context
->Event
, 0, FALSE
);
2286 return STATUS_MORE_PROCESSING_REQUIRED
;
2289 void free_write_data_stripes(write_data_context
* wtc
) {
2291 PMDL last_mdl
= NULL
;
2293 if (wtc
->parity1_mdl
) {
2294 if (wtc
->parity1_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2295 MmUnlockPages(wtc
->parity1_mdl
);
2297 IoFreeMdl(wtc
->parity1_mdl
);
2300 if (wtc
->parity2_mdl
) {
2301 if (wtc
->parity2_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2302 MmUnlockPages(wtc
->parity2_mdl
);
2304 IoFreeMdl(wtc
->parity2_mdl
);
2308 if (wtc
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2309 MmUnlockPages(wtc
->mdl
);
2311 IoFreeMdl(wtc
->mdl
);
2315 ExFreePool(wtc
->parity1
);
2318 ExFreePool(wtc
->parity2
);
2321 ExFreePool(wtc
->scratch
);
2323 le
= wtc
->stripes
.Flink
;
2324 while (le
!= &wtc
->stripes
) {
2325 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2327 if (stripe
->mdl
&& stripe
->mdl
!= last_mdl
) {
2328 if (stripe
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2329 MmUnlockPages(stripe
->mdl
);
2331 IoFreeMdl(stripe
->mdl
);
2334 last_mdl
= stripe
->mdl
;
2338 IoFreeIrp(stripe
->Irp
);
2343 while (!IsListEmpty(&wtc
->stripes
)) {
2344 write_data_stripe
* stripe
= CONTAINING_RECORD(RemoveHeadList(&wtc
->stripes
), write_data_stripe
, list_entry
);
2350 void add_extent(_In_ fcb
* fcb
, _In_ LIST_ENTRY
* prevextle
, _In_ __drv_aliasesMem extent
* newext
) {
2351 LIST_ENTRY
* le
= prevextle
->Flink
;
2353 while (le
!= &fcb
->extents
) {
2354 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2356 if (ext
->offset
>= newext
->offset
) {
2357 InsertHeadList(ext
->list_entry
.Blink
, &newext
->list_entry
);
2364 InsertTailList(&fcb
->extents
, &newext
->list_entry
);
2367 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2371 le
= fcb
->extents
.Flink
;
2373 while (le
!= &fcb
->extents
) {
2374 LIST_ENTRY
* le2
= le
->Flink
;
2375 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2376 EXTENT_DATA
* ed
= &ext
->extent_data
;
2377 EXTENT_DATA2
* ed2
= NULL
;
2381 if (ed
->type
!= EXTENT_TYPE_INLINE
)
2382 ed2
= (EXTENT_DATA2
*)ed
->data
;
2384 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
2386 if (ext
->offset
< end_data
&& ext
->offset
+ len
> start_data
) {
2387 if (ed
->type
== EXTENT_TYPE_INLINE
) {
2388 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
2389 remove_fcb_extent(fcb
, ext
, rollback
);
2391 fcb
->inode_item
.st_blocks
-= len
;
2392 fcb
->inode_item_changed
= TRUE
;
2394 ERR("trying to split inline extent\n");
2395 #ifdef DEBUG_PARANOID
2398 return STATUS_INTERNAL_ERROR
;
2400 } else if (ed
->type
!= EXTENT_TYPE_INLINE
) {
2401 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
2402 if (ed2
->size
!= 0) {
2405 fcb
->inode_item
.st_blocks
-= len
;
2406 fcb
->inode_item_changed
= TRUE
;
2408 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2411 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2413 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
2414 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2415 if (!NT_SUCCESS(Status
)) {
2416 ERR("update_changed_extent_ref returned %08x\n", Status
);
2422 remove_fcb_extent(fcb
, ext
, rollback
);
2423 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
2427 if (ed2
->size
!= 0) {
2428 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
2429 fcb
->inode_item_changed
= TRUE
;
2432 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2434 ERR("out of memory\n");
2435 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2439 ned2
= (EXTENT_DATA2
*)newext
->extent_data
.data
;
2441 newext
->extent_data
.generation
= Vcb
->superblock
.generation
;
2442 newext
->extent_data
.decoded_size
= ed
->decoded_size
;
2443 newext
->extent_data
.compression
= ed
->compression
;
2444 newext
->extent_data
.encryption
= ed
->encryption
;
2445 newext
->extent_data
.encoding
= ed
->encoding
;
2446 newext
->extent_data
.type
= ed
->type
;
2447 ned2
->address
= ed2
->address
;
2448 ned2
->size
= ed2
->size
;
2449 ned2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2450 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- ext
->offset
);
2452 newext
->offset
= end_data
;
2453 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2454 newext
->unique
= ext
->unique
;
2455 newext
->ignore
= FALSE
;
2456 newext
->inserted
= TRUE
;
2459 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2460 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2461 if (!newext
->csum
) {
2462 ERR("out of memory\n");
2463 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2468 RtlCopyMemory(newext
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2469 (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2471 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2472 if (!newext
->csum
) {
2473 ERR("out of memory\n");
2474 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2479 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2482 newext
->csum
= NULL
;
2484 add_extent(fcb
, &ext
->list_entry
, newext
);
2486 remove_fcb_extent(fcb
, ext
, rollback
);
2487 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
2491 if (ed2
->size
!= 0) {
2492 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
2493 fcb
->inode_item_changed
= TRUE
;
2496 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2498 ERR("out of memory\n");
2499 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2503 ned2
= (EXTENT_DATA2
*)newext
->extent_data
.data
;
2505 newext
->extent_data
.generation
= Vcb
->superblock
.generation
;
2506 newext
->extent_data
.decoded_size
= ed
->decoded_size
;
2507 newext
->extent_data
.compression
= ed
->compression
;
2508 newext
->extent_data
.encryption
= ed
->encryption
;
2509 newext
->extent_data
.encoding
= ed
->encoding
;
2510 newext
->extent_data
.type
= ed
->type
;
2511 ned2
->address
= ed2
->address
;
2512 ned2
->size
= ed2
->size
;
2513 ned2
->offset
= ed2
->offset
;
2514 ned2
->num_bytes
= start_data
- ext
->offset
;
2516 newext
->offset
= ext
->offset
;
2517 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2518 newext
->unique
= ext
->unique
;
2519 newext
->ignore
= FALSE
;
2520 newext
->inserted
= TRUE
;
2523 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2524 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2525 if (!newext
->csum
) {
2526 ERR("out of memory\n");
2527 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2532 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2534 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2535 if (!newext
->csum
) {
2536 ERR("out of memory\n");
2537 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2542 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2545 newext
->csum
= NULL
;
2547 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2549 remove_fcb_extent(fcb
, ext
, rollback
);
2550 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
2551 EXTENT_DATA2
*neda2
, *nedb2
;
2552 extent
*newext1
, *newext2
;
2554 if (ed2
->size
!= 0) {
2557 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
2558 fcb
->inode_item_changed
= TRUE
;
2560 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2563 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2565 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
2566 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2567 if (!NT_SUCCESS(Status
)) {
2568 ERR("update_changed_extent_ref returned %08x\n", Status
);
2574 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2576 ERR("out of memory\n");
2577 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2581 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2583 ERR("out of memory\n");
2584 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2585 ExFreePool(newext1
);
2589 neda2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
2591 newext1
->extent_data
.generation
= Vcb
->superblock
.generation
;
2592 newext1
->extent_data
.decoded_size
= ed
->decoded_size
;
2593 newext1
->extent_data
.compression
= ed
->compression
;
2594 newext1
->extent_data
.encryption
= ed
->encryption
;
2595 newext1
->extent_data
.encoding
= ed
->encoding
;
2596 newext1
->extent_data
.type
= ed
->type
;
2597 neda2
->address
= ed2
->address
;
2598 neda2
->size
= ed2
->size
;
2599 neda2
->offset
= ed2
->offset
;
2600 neda2
->num_bytes
= start_data
- ext
->offset
;
2602 nedb2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
2604 newext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
2605 newext2
->extent_data
.decoded_size
= ed
->decoded_size
;
2606 newext2
->extent_data
.compression
= ed
->compression
;
2607 newext2
->extent_data
.encryption
= ed
->encryption
;
2608 newext2
->extent_data
.encoding
= ed
->encoding
;
2609 newext2
->extent_data
.type
= ed
->type
;
2610 nedb2
->address
= ed2
->address
;
2611 nedb2
->size
= ed2
->size
;
2612 nedb2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2613 nedb2
->num_bytes
= ext
->offset
+ len
- end_data
;
2615 newext1
->offset
= ext
->offset
;
2616 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2617 newext1
->unique
= ext
->unique
;
2618 newext1
->ignore
= FALSE
;
2619 newext1
->inserted
= TRUE
;
2621 newext2
->offset
= end_data
;
2622 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2623 newext2
->unique
= ext
->unique
;
2624 newext2
->ignore
= FALSE
;
2625 newext2
->inserted
= TRUE
;
2628 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2629 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2630 if (!newext1
->csum
) {
2631 ERR("out of memory\n");
2632 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2633 ExFreePool(newext1
);
2634 ExFreePool(newext2
);
2638 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2639 if (!newext2
->csum
) {
2640 ERR("out of memory\n");
2641 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2642 ExFreePool(newext1
->csum
);
2643 ExFreePool(newext1
);
2644 ExFreePool(newext2
);
2648 RtlCopyMemory(newext1
->csum
, ext
->csum
, (ULONG
)(neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2649 RtlCopyMemory(newext2
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2650 (ULONG
)(nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2652 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2653 if (!newext1
->csum
) {
2654 ERR("out of memory\n");
2655 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2656 ExFreePool(newext1
);
2657 ExFreePool(newext2
);
2661 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2662 if (!newext2
->csum
) {
2663 ERR("out of memory\n");
2664 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2665 ExFreePool(newext1
->csum
);
2666 ExFreePool(newext1
);
2667 ExFreePool(newext2
);
2671 RtlCopyMemory(newext1
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2672 RtlCopyMemory(newext2
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2675 newext1
->csum
= NULL
;
2676 newext2
->csum
= NULL
;
2679 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
2680 add_extent(fcb
, &newext1
->list_entry
, newext2
);
2682 remove_fcb_extent(fcb
, ext
, rollback
);
2691 Status
= STATUS_SUCCESS
;
2694 fcb
->extents_changed
= TRUE
;
2695 mark_fcb_dirty(fcb
);
2700 void add_insert_extent_rollback(LIST_ENTRY
* rollback
, fcb
* fcb
, extent
* ext
) {
2701 rollback_extent
* re
;
2703 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2705 ERR("out of memory\n");
2712 add_rollback(rollback
, ROLLBACK_INSERT_EXTENT
, re
);
2716 #pragma warning(push)
2717 #pragma warning(suppress: 28194)
2719 NTSTATUS
add_extent_to_fcb(_In_ fcb
* fcb
, _In_ UINT64 offset
, _In_reads_bytes_(edsize
) EXTENT_DATA
* ed
, _In_ UINT16 edsize
,
2720 _In_ BOOL unique
, _In_opt_
_When_(return >= 0, __drv_aliasesMem
) UINT32
* csum
, _In_ LIST_ENTRY
* rollback
) {
2724 ext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + edsize
, ALLOC_TAG
);
2726 ERR("out of memory\n");
2727 return STATUS_INSUFFICIENT_RESOURCES
;
2730 ext
->offset
= offset
;
2731 ext
->datalen
= edsize
;
2732 ext
->unique
= unique
;
2733 ext
->ignore
= FALSE
;
2734 ext
->inserted
= TRUE
;
2737 RtlCopyMemory(&ext
->extent_data
, ed
, edsize
);
2739 le
= fcb
->extents
.Flink
;
2740 while (le
!= &fcb
->extents
) {
2741 extent
* oldext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2743 if (oldext
->offset
>= offset
) {
2744 InsertHeadList(le
->Blink
, &ext
->list_entry
);
2751 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
2754 add_insert_extent_rollback(rollback
, fcb
, ext
);
2756 return STATUS_SUCCESS
;
2759 #pragma warning(pop)
2762 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
) {
2764 rollback_extent
* re
;
2768 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2770 ERR("out of memory\n");
2777 add_rollback(rollback
, ROLLBACK_DELETE_EXTENT
, re
);
2781 NTSTATUS
calc_csum(_In_ device_extension
* Vcb
, _In_reads_bytes_(sectors
*Vcb
->superblock
.sector_size
) UINT8
* data
,
2782 _In_ UINT32 sectors
, _Out_writes_bytes_(sectors
*sizeof(UINT32
)) UINT32
* csum
) {
2786 // From experimenting, it seems that 40 sectors is roughly the crossover
2787 // point where offloading the crc32 calculation becomes worth it.
2789 if (sectors
< 40 || KeQueryActiveProcessorCount(NULL
) < 2) {
2792 for (j
= 0; j
< sectors
; j
++) {
2793 csum
[j
] = ~calc_crc32c(0xffffffff, data
+ (j
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
2796 return STATUS_SUCCESS
;
2799 Status
= add_calc_job(Vcb
, data
, sectors
, csum
, &cj
);
2800 if (!NT_SUCCESS(Status
)) {
2801 ERR("add_calc_job returned %08x\n", Status
);
2805 KeWaitForSingleObject(&cj
->event
, Executive
, KernelMode
, FALSE
, NULL
);
2808 return STATUS_SUCCESS
;
2811 _Requires_lock_held_(c
->lock
)
2812 _When_(return != 0, _Releases_lock_(c
->lock
))
2813 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
,
2814 _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
, _In_ UINT8 compression
, _In_ UINT64 decoded_size
, _In_ BOOL file_write
, _In_ UINT64 irp_offset
) {
2819 UINT16 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
));
2820 UINT32
* csum
= NULL
;
2822 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
);
2824 if (!find_data_address_in_chunk(Vcb
, c
, length
, &address
))
2827 // add extent data to inode
2828 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
2830 ERR("out of memory\n");
2834 ed
->generation
= Vcb
->superblock
.generation
;
2835 ed
->decoded_size
= decoded_size
;
2836 ed
->compression
= compression
;
2837 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
2838 ed
->encoding
= BTRFS_ENCODING_NONE
;
2839 ed
->type
= prealloc
? EXTENT_TYPE_PREALLOC
: EXTENT_TYPE_REGULAR
;
2841 ed2
= (EXTENT_DATA2
*)ed
->data
;
2842 ed2
->address
= address
;
2845 ed2
->num_bytes
= decoded_size
;
2847 if (!prealloc
&& data
&& !(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
2848 ULONG sl
= (ULONG
)(length
/ Vcb
->superblock
.sector_size
);
2850 csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
2852 ERR("out of memory\n");
2857 Status
= calc_csum(Vcb
, data
, sl
, csum
);
2858 if (!NT_SUCCESS(Status
)) {
2859 ERR("calc_csum returned %08x\n", Status
);
2866 Status
= add_extent_to_fcb(fcb
, start_data
, ed
, edsize
, TRUE
, csum
, rollback
);
2867 if (!NT_SUCCESS(Status
)) {
2868 ERR("add_extent_to_fcb returned %08x\n", Status
);
2869 if (csum
) ExFreePool(csum
);
2877 space_list_subtract(c
, FALSE
, address
, length
, rollback
);
2879 fcb
->inode_item
.st_blocks
+= decoded_size
;
2881 fcb
->extents_changed
= TRUE
;
2882 fcb
->inode_item_changed
= TRUE
;
2883 mark_fcb_dirty(fcb
);
2885 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
2887 add_changed_extent_ref(c
, address
, length
, fcb
->subvol
->id
, fcb
->inode
, start_data
, 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
2889 ExReleaseResourceLite(&c
->changed_extents_lock
);
2891 ExReleaseResourceLite(&c
->lock
);
2894 Status
= write_data_complete(Vcb
, address
, data
, (UINT32
)length
, Irp
, NULL
, file_write
, irp_offset
,
2895 fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
);
2896 if (!NT_SUCCESS(Status
))
2897 ERR("write_data_complete returned %08x\n", Status
);
2903 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
2904 PIRP Irp
, UINT64
* written
, BOOL file_write
, UINT64 irp_offset
, LIST_ENTRY
* rollback
) {
2905 BOOL success
= FALSE
;
2912 le
= fcb
->extents
.Flink
;
2914 while (le
!= &fcb
->extents
) {
2915 extent
* nextext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2917 if (!nextext
->ignore
) {
2918 if (nextext
->offset
== start_data
) {
2921 } else if (nextext
->offset
> start_data
)
2933 ed
= &ext
->extent_data
;
2935 if (ed
->type
!= EXTENT_TYPE_REGULAR
&& ed
->type
!= EXTENT_TYPE_PREALLOC
) {
2936 TRACE("not extending extent which is not regular or prealloc\n");
2940 ed2
= (EXTENT_DATA2
*)ed
->data
;
2942 if (ext
->offset
+ ed2
->num_bytes
!= start_data
) {
2943 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext
->offset
, ed2
->num_bytes
, start_data
);
2947 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2949 if (c
->reloc
|| c
->readonly
|| c
->chunk_item
->type
!= Vcb
->data_flags
)
2952 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2954 if (length
> c
->chunk_item
->size
- c
->used
) {
2955 ExReleaseResourceLite(&c
->lock
);
2959 if (!c
->cache_loaded
) {
2960 NTSTATUS Status
= load_cache_chunk(Vcb
, c
, NULL
);
2962 if (!NT_SUCCESS(Status
)) {
2963 ERR("load_cache_chunk returned %08x\n", Status
);
2964 ExReleaseResourceLite(&c
->lock
);
2969 le
= c
->space
.Flink
;
2970 while (le
!= &c
->space
) {
2971 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
2973 if (s
->address
== ed2
->address
+ ed2
->size
) {
2974 UINT64 newlen
= min(min(s
->size
, length
), MAX_EXTENT_SIZE
);
2976 success
= insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
);
2981 ExReleaseResourceLite(&c
->lock
);
2984 } else if (s
->address
> ed2
->address
+ ed2
->size
)
2990 ExReleaseResourceLite(&c
->lock
);
2995 static NTSTATUS
insert_chunk_fragmented(fcb
* fcb
, UINT64 start
, UINT64 length
, UINT8
* data
, BOOL prealloc
, LIST_ENTRY
* rollback
) {
2997 UINT64 flags
= fcb
->Vcb
->data_flags
;
2998 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
3002 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3004 // first create as many chunks as we can
3006 Status
= alloc_chunk(fcb
->Vcb
, flags
, &c
, FALSE
);
3007 } while (NT_SUCCESS(Status
));
3009 if (Status
!= STATUS_DISK_FULL
) {
3010 ERR("alloc_chunk returned %08x\n", Status
);
3011 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3015 le
= fcb
->Vcb
->chunks
.Flink
;
3016 while (le
!= &fcb
->Vcb
->chunks
) {
3017 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3019 if (!c
->readonly
&& !c
->reloc
) {
3020 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3022 if (c
->chunk_item
->type
== flags
) {
3023 while (!IsListEmpty(&c
->space_size
) && length
> 0) {
3024 space
* s
= CONTAINING_RECORD(c
->space_size
.Flink
, space
, list_entry_size
);
3025 UINT64 extlen
= min(length
, s
->size
);
3027 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, prealloc
&& !page_file
, data
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0)) {
3030 if (data
) data
+= extlen
;
3032 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3037 ExReleaseResourceLite(&c
->lock
);
3046 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3048 return length
== 0 ? STATUS_SUCCESS
: STATUS_DISK_FULL
;
3051 static NTSTATUS
insert_prealloc_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
3056 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
3058 flags
= fcb
->Vcb
->data_flags
;
3061 UINT64 extlen
= min(MAX_EXTENT_SIZE
, length
);
3063 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3065 le
= fcb
->Vcb
->chunks
.Flink
;
3066 while (le
!= &fcb
->Vcb
->chunks
) {
3067 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3069 if (!c
->readonly
&& !c
->reloc
) {
3070 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3072 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
3073 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0)) {
3074 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3079 ExReleaseResourceLite(&c
->lock
);
3085 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3087 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3089 Status
= alloc_chunk(fcb
->Vcb
, flags
, &c
, FALSE
);
3091 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3093 if (!NT_SUCCESS(Status
)) {
3094 ERR("alloc_chunk returned %08x\n", Status
);
3098 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3100 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
3101 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0))
3105 ExReleaseResourceLite(&c
->lock
);
3107 Status
= insert_chunk_fragmented(fcb
, start
, length
, NULL
, TRUE
, rollback
);
3108 if (!NT_SUCCESS(Status
))
3109 ERR("insert_chunk_fragmented returned %08x\n", Status
);
3116 } while (length
> 0);
3118 Status
= STATUS_SUCCESS
;
3124 static NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
3125 PIRP Irp
, BOOL file_write
, UINT64 irp_offset
, LIST_ENTRY
* rollback
) {
3129 UINT64 flags
, orig_length
= length
, written
= 0;
3131 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
);
3133 if (start_data
> 0) {
3134 try_extend_data(Vcb
, fcb
, start_data
, length
, data
, Irp
, &written
, file_write
, irp_offset
, rollback
);
3136 if (written
== length
)
3137 return STATUS_SUCCESS
;
3138 else if (written
> 0) {
3139 start_data
+= written
;
3140 irp_offset
+= written
;
3142 data
= &((UINT8
*)data
)[written
];
3146 flags
= Vcb
->data_flags
;
3148 while (written
< orig_length
) {
3149 UINT64 newlen
= min(length
, MAX_EXTENT_SIZE
);
3152 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
3153 // First, see if we can write the extent part to an existing chunk.
3155 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
3157 le
= Vcb
->chunks
.Flink
;
3158 while (le
!= &Vcb
->chunks
) {
3159 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3161 if (!c
->readonly
&& !c
->reloc
) {
3162 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3164 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
3165 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
)) {
3168 if (written
== orig_length
) {
3169 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3170 return STATUS_SUCCESS
;
3173 start_data
+= newlen
;
3174 irp_offset
+= newlen
;
3176 data
= &((UINT8
*)data
)[newlen
];
3180 ExReleaseResourceLite(&c
->lock
);
3186 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3190 // Otherwise, see if we can put it in a new chunk.
3192 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
3194 Status
= alloc_chunk(Vcb
, flags
, &c
, FALSE
);
3196 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3198 if (!NT_SUCCESS(Status
)) {
3199 ERR("alloc_chunk returned %08x\n", Status
);
3204 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3206 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
3207 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
)) {
3210 if (written
== orig_length
)
3211 return STATUS_SUCCESS
;
3214 start_data
+= newlen
;
3215 irp_offset
+= newlen
;
3217 data
= &((UINT8
*)data
)[newlen
];
3220 ExReleaseResourceLite(&c
->lock
);
3224 Status
= insert_chunk_fragmented(fcb
, start_data
, length
, data
, FALSE
, rollback
);
3225 if (!NT_SUCCESS(Status
))
3226 ERR("insert_chunk_fragmented returned %08x\n", Status
);
3232 return STATUS_DISK_FULL
;
3235 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3238 // FIXME - convert into inline extent if short enough
3240 if (end
> 0 && fcb_is_inline(fcb
)) {
3242 BOOL make_inline
= end
<= fcb
->Vcb
->options
.max_inline
;
3244 buf
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(make_inline
? (offsetof(EXTENT_DATA
, data
[0]) + end
) : sector_align(end
, fcb
->Vcb
->superblock
.sector_size
)), ALLOC_TAG
);
3246 ERR("out of memory\n");
3247 return STATUS_INSUFFICIENT_RESOURCES
;
3250 Status
= read_file(fcb
, make_inline
? (buf
+ offsetof(EXTENT_DATA
, data
[0])) : buf
, 0, end
, NULL
, Irp
);
3251 if (!NT_SUCCESS(Status
)) {
3252 ERR("read_file returned %08x\n", Status
);
3257 Status
= excise_extents(fcb
->Vcb
, fcb
, 0, fcb
->inode_item
.st_size
, Irp
, rollback
);
3258 if (!NT_SUCCESS(Status
)) {
3259 ERR("excise_extents returned %08x\n", Status
);
3265 RtlZeroMemory(buf
+ end
, (ULONG
)(sector_align(end
, fcb
->Vcb
->superblock
.sector_size
) - end
));
3267 Status
= do_write_file(fcb
, 0, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
), buf
, Irp
, FALSE
, 0, rollback
);
3268 if (!NT_SUCCESS(Status
)) {
3269 ERR("do_write_file returned %08x\n", Status
);
3274 EXTENT_DATA
* ed
= (EXTENT_DATA
*)buf
;
3276 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3277 ed
->decoded_size
= end
;
3278 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3279 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3280 ed
->encoding
= BTRFS_ENCODING_NONE
;
3281 ed
->type
= EXTENT_TYPE_INLINE
;
3283 Status
= add_extent_to_fcb(fcb
, 0, ed
, (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
), FALSE
, NULL
, rollback
);
3284 if (!NT_SUCCESS(Status
)) {
3285 ERR("add_extent_to_fcb returned %08x\n", Status
);
3290 fcb
->inode_item
.st_blocks
+= end
;
3294 return STATUS_SUCCESS
;
3297 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
3298 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
3299 if (!NT_SUCCESS(Status
)) {
3300 ERR("excise_extents returned %08x\n", Status
);
3304 fcb
->inode_item
.st_size
= end
;
3305 fcb
->inode_item_changed
= TRUE
;
3306 TRACE("setting st_size to %llx\n", end
);
3308 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
3309 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
3310 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
3311 // FIXME - inform cache manager of this
3313 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
3315 return STATUS_SUCCESS
;
3318 NTSTATUS
extend_file(fcb
* fcb
, file_ref
* fileref
, UINT64 end
, BOOL prealloc
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3319 UINT64 oldalloc
, newalloc
;
3323 TRACE("(%p, %p, %x, %u)\n", fcb
, fileref
, end
, prealloc
);
3327 return STATUS_DISK_FULL
;
3329 return stream_set_end_of_file_information(fcb
->Vcb
, (UINT16
)end
, fcb
, fileref
, FALSE
);
3334 le
= fcb
->extents
.Blink
;
3335 while (le
!= &fcb
->extents
) {
3336 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
3338 if (!ext2
->ignore
) {
3348 EXTENT_DATA
* ed
= &ext
->extent_data
;
3349 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3351 oldalloc
= ext
->offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
3352 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
3354 if (cur_inline
&& end
> fcb
->Vcb
->options
.max_inline
) {
3355 UINT64 origlength
, length
;
3358 TRACE("giving inline file proper extents\n");
3360 origlength
= ed
->decoded_size
;
3364 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
3366 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)length
, ALLOC_TAG
);
3368 ERR("could not allocate %llx bytes for data\n", length
);
3369 return STATUS_INSUFFICIENT_RESOURCES
;
3372 Status
= read_file(fcb
, data
, 0, origlength
, NULL
, Irp
);
3373 if (!NT_SUCCESS(Status
)) {
3374 ERR("read_file returned %08x\n", Status
);
3379 RtlZeroMemory(data
+ origlength
, (ULONG
)(length
- origlength
));
3381 Status
= excise_extents(fcb
->Vcb
, fcb
, 0, fcb
->inode_item
.st_size
, Irp
, rollback
);
3382 if (!NT_SUCCESS(Status
)) {
3383 ERR("excise_extents returned %08x\n", Status
);
3388 Status
= do_write_file(fcb
, 0, length
, data
, Irp
, FALSE
, 0, rollback
);
3389 if (!NT_SUCCESS(Status
)) {
3390 ERR("do_write_file returned %08x\n", Status
);
3395 oldalloc
= ext
->offset
+ length
;
3403 if (end
> oldalloc
) {
3404 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
- ext
->offset
);
3405 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3408 ERR("out of memory\n");
3409 return STATUS_INSUFFICIENT_RESOURCES
;
3412 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3413 ed
->decoded_size
= end
- ext
->offset
;
3414 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3415 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3416 ed
->encoding
= BTRFS_ENCODING_NONE
;
3417 ed
->type
= EXTENT_TYPE_INLINE
;
3419 Status
= read_file(fcb
, ed
->data
, ext
->offset
, oldalloc
, NULL
, Irp
);
3420 if (!NT_SUCCESS(Status
)) {
3421 ERR("read_file returned %08x\n", Status
);
3426 RtlZeroMemory(ed
->data
+ oldalloc
- ext
->offset
, (ULONG
)(end
- oldalloc
));
3428 remove_fcb_extent(fcb
, ext
, rollback
);
3430 Status
= add_extent_to_fcb(fcb
, ext
->offset
, ed
, edsize
, ext
->unique
, NULL
, rollback
);
3431 if (!NT_SUCCESS(Status
)) {
3432 ERR("add_extent_to_fcb returned %08x\n", Status
);
3439 fcb
->extents_changed
= TRUE
;
3440 mark_fcb_dirty(fcb
);
3443 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
3445 fcb
->inode_item
.st_size
= end
;
3446 TRACE("setting st_size to %llx\n", end
);
3448 fcb
->inode_item
.st_blocks
= end
;
3450 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3452 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3454 if (newalloc
> oldalloc
) {
3456 // FIXME - try and extend previous extent first
3458 Status
= insert_prealloc_extent(fcb
, oldalloc
, newalloc
- oldalloc
, rollback
);
3460 if (!NT_SUCCESS(Status
)) {
3461 ERR("insert_prealloc_extent returned %08x\n", Status
);
3466 fcb
->extents_changed
= TRUE
;
3469 fcb
->inode_item
.st_size
= end
;
3470 fcb
->inode_item_changed
= TRUE
;
3471 mark_fcb_dirty(fcb
);
3473 TRACE("setting st_size to %llx\n", end
);
3475 TRACE("newalloc = %llx\n", newalloc
);
3477 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3478 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3481 if (end
> fcb
->Vcb
->options
.max_inline
) {
3482 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3485 Status
= insert_prealloc_extent(fcb
, 0, newalloc
, rollback
);
3487 if (!NT_SUCCESS(Status
)) {
3488 ERR("insert_prealloc_extent returned %08x\n", Status
);
3493 fcb
->extents_changed
= TRUE
;
3494 fcb
->inode_item_changed
= TRUE
;
3495 mark_fcb_dirty(fcb
);
3497 fcb
->inode_item
.st_size
= end
;
3498 TRACE("setting st_size to %llx\n", end
);
3500 TRACE("newalloc = %llx\n", newalloc
);
3502 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3503 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3508 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
);
3509 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3512 ERR("out of memory\n");
3513 return STATUS_INSUFFICIENT_RESOURCES
;
3516 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3517 ed
->decoded_size
= end
;
3518 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3519 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3520 ed
->encoding
= BTRFS_ENCODING_NONE
;
3521 ed
->type
= EXTENT_TYPE_INLINE
;
3523 RtlZeroMemory(ed
->data
, (ULONG
)end
);
3525 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
);
3526 if (!NT_SUCCESS(Status
)) {
3527 ERR("add_extent_to_fcb returned %08x\n", Status
);
3534 fcb
->extents_changed
= TRUE
;
3535 fcb
->inode_item_changed
= TRUE
;
3536 mark_fcb_dirty(fcb
);
3538 fcb
->inode_item
.st_size
= end
;
3539 TRACE("setting st_size to %llx\n", end
);
3541 fcb
->inode_item
.st_blocks
= end
;
3543 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3548 return STATUS_SUCCESS
;
3551 static NTSTATUS
do_write_file_prealloc(fcb
* fcb
, extent
* ext
, UINT64 start_data
, UINT64 end_data
, void* data
, UINT64
* written
,
3552 PIRP Irp
, BOOL file_write
, UINT64 irp_offset
, ULONG priority
, LIST_ENTRY
* rollback
) {
3553 EXTENT_DATA
* ed
= &ext
->extent_data
;
3554 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3558 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace all
3561 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3563 ERR("out of memory\n");
3564 return STATUS_INSUFFICIENT_RESOURCES
;
3567 RtlCopyMemory(&newext
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3569 newext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3571 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, (UINT32
)ed2
->num_bytes
, Irp
,
3572 NULL
, file_write
, irp_offset
+ ext
->offset
- start_data
, priority
);
3573 if (!NT_SUCCESS(Status
)) {
3574 ERR("write_data_complete returned %08x\n", Status
);
3578 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3579 ULONG sl
= (ULONG
)(ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
);
3580 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3583 ERR("out of memory\n");
3585 return STATUS_INSUFFICIENT_RESOURCES
;
3588 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3589 if (!NT_SUCCESS(Status
)) {
3590 ERR("calc_csum returned %08x\n", Status
);
3596 newext
->csum
= csum
;
3598 newext
->csum
= NULL
;
3600 *written
= ed2
->num_bytes
;
3602 newext
->offset
= ext
->offset
;
3603 newext
->datalen
= ext
->datalen
;
3604 newext
->unique
= ext
->unique
;
3605 newext
->ignore
= FALSE
;
3606 newext
->inserted
= TRUE
;
3607 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
3609 add_insert_extent_rollback(rollback
, fcb
, newext
);
3611 remove_fcb_extent(fcb
, ext
, rollback
);
3613 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3614 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace beginning
3616 extent
*newext1
, *newext2
;
3618 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3620 ERR("out of memory\n");
3621 return STATUS_INSUFFICIENT_RESOURCES
;
3624 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3626 ERR("out of memory\n");
3627 ExFreePool(newext1
);
3628 return STATUS_INSUFFICIENT_RESOURCES
;
3631 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3632 newext1
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3633 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3634 ned2
->num_bytes
= end_data
- ext
->offset
;
3636 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3637 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3638 ned2
->offset
+= end_data
- ext
->offset
;
3639 ned2
->num_bytes
-= end_data
- ext
->offset
;
3641 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, (UINT32
)(end_data
- ext
->offset
),
3642 Irp
, NULL
, file_write
, irp_offset
+ ext
->offset
- start_data
, priority
);
3643 if (!NT_SUCCESS(Status
)) {
3644 ERR("write_data_complete returned %08x\n", Status
);
3648 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3649 ULONG sl
= (ULONG
)((end_data
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
);
3650 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3653 ERR("out of memory\n");
3654 ExFreePool(newext1
);
3655 ExFreePool(newext2
);
3656 return STATUS_INSUFFICIENT_RESOURCES
;
3659 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3660 if (!NT_SUCCESS(Status
)) {
3661 ERR("calc_csum returned %08x\n", Status
);
3662 ExFreePool(newext1
);
3663 ExFreePool(newext2
);
3668 newext1
->csum
= csum
;
3670 newext1
->csum
= NULL
;
3672 *written
= end_data
- ext
->offset
;
3674 newext1
->offset
= ext
->offset
;
3675 newext1
->datalen
= ext
->datalen
;
3676 newext1
->unique
= ext
->unique
;
3677 newext1
->ignore
= FALSE
;
3678 newext1
->inserted
= TRUE
;
3679 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3681 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3683 newext2
->offset
= end_data
;
3684 newext2
->datalen
= ext
->datalen
;
3685 newext2
->unique
= ext
->unique
;
3686 newext2
->ignore
= FALSE
;
3687 newext2
->inserted
= TRUE
;
3688 newext2
->csum
= NULL
;
3689 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3691 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3693 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3696 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3698 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3699 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3701 if (!NT_SUCCESS(Status
)) {
3702 ERR("update_changed_extent_ref returned %08x\n", Status
);
3707 remove_fcb_extent(fcb
, ext
, rollback
);
3708 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace end
3710 extent
*newext1
, *newext2
;
3712 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3714 ERR("out of memory\n");
3715 return STATUS_INSUFFICIENT_RESOURCES
;
3718 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3720 ERR("out of memory\n");
3721 ExFreePool(newext1
);
3722 return STATUS_INSUFFICIENT_RESOURCES
;
3725 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3727 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3728 ned2
->num_bytes
= start_data
- ext
->offset
;
3730 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3732 newext2
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3733 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3734 ned2
->offset
+= start_data
- ext
->offset
;
3735 ned2
->num_bytes
= ext
->offset
+ ed2
->num_bytes
- start_data
;
3737 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, (UINT32
)ned2
->num_bytes
, Irp
, NULL
, file_write
, irp_offset
, priority
);
3738 if (!NT_SUCCESS(Status
)) {
3739 ERR("write_data_complete returned %08x\n", Status
);
3743 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3744 ULONG sl
= (ULONG
)(ned2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
);
3745 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3748 ERR("out of memory\n");
3749 ExFreePool(newext1
);
3750 ExFreePool(newext2
);
3751 return STATUS_INSUFFICIENT_RESOURCES
;
3754 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3755 if (!NT_SUCCESS(Status
)) {
3756 ERR("calc_csum returned %08x\n", Status
);
3757 ExFreePool(newext1
);
3758 ExFreePool(newext2
);
3763 newext2
->csum
= csum
;
3765 newext2
->csum
= NULL
;
3767 *written
= ned2
->num_bytes
;
3769 newext1
->offset
= ext
->offset
;
3770 newext1
->datalen
= ext
->datalen
;
3771 newext1
->unique
= ext
->unique
;
3772 newext1
->ignore
= FALSE
;
3773 newext1
->inserted
= TRUE
;
3774 newext1
->csum
= NULL
;
3775 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3777 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3779 newext2
->offset
= start_data
;
3780 newext2
->datalen
= ext
->datalen
;
3781 newext2
->unique
= ext
->unique
;
3782 newext2
->ignore
= FALSE
;
3783 newext2
->inserted
= TRUE
;
3784 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3786 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3788 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3791 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3793 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3794 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3796 if (!NT_SUCCESS(Status
)) {
3797 ERR("update_changed_extent_ref returned %08x\n", Status
);
3802 remove_fcb_extent(fcb
, ext
, rollback
);
3803 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace middle
3805 extent
*newext1
, *newext2
, *newext3
;
3807 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3809 ERR("out of memory\n");
3810 return STATUS_INSUFFICIENT_RESOURCES
;
3813 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3815 ERR("out of memory\n");
3816 ExFreePool(newext1
);
3817 return STATUS_INSUFFICIENT_RESOURCES
;
3820 newext3
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3822 ERR("out of memory\n");
3823 ExFreePool(newext1
);
3824 ExFreePool(newext2
);
3825 return STATUS_INSUFFICIENT_RESOURCES
;
3828 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3829 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3830 RtlCopyMemory(&newext3
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3832 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3833 ned2
->num_bytes
= start_data
- ext
->offset
;
3835 newext2
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3836 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3837 ned2
->offset
+= start_data
- ext
->offset
;
3838 ned2
->num_bytes
= end_data
- start_data
;
3840 ned2
= (EXTENT_DATA2
*)newext3
->extent_data
.data
;
3841 ned2
->offset
+= end_data
- ext
->offset
;
3842 ned2
->num_bytes
-= end_data
- ext
->offset
;
3844 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3845 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, (UINT32
)(end_data
- start_data
), Irp
, NULL
, file_write
, irp_offset
, priority
);
3846 if (!NT_SUCCESS(Status
)) {
3847 ERR("write_data_complete returned %08x\n", Status
);
3848 ExFreePool(newext1
);
3849 ExFreePool(newext2
);
3850 ExFreePool(newext3
);
3854 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3855 ULONG sl
= (ULONG
)((end_data
- start_data
) / fcb
->Vcb
->superblock
.sector_size
);
3856 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3859 ERR("out of memory\n");
3860 ExFreePool(newext1
);
3861 ExFreePool(newext2
);
3862 ExFreePool(newext3
);
3863 return STATUS_INSUFFICIENT_RESOURCES
;
3866 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3867 if (!NT_SUCCESS(Status
)) {
3868 ERR("calc_csum returned %08x\n", Status
);
3869 ExFreePool(newext1
);
3870 ExFreePool(newext2
);
3871 ExFreePool(newext3
);
3876 newext2
->csum
= csum
;
3878 newext2
->csum
= NULL
;
3880 *written
= end_data
- start_data
;
3882 newext1
->offset
= ext
->offset
;
3883 newext1
->datalen
= ext
->datalen
;
3884 newext1
->unique
= ext
->unique
;
3885 newext1
->ignore
= FALSE
;
3886 newext1
->inserted
= TRUE
;
3887 newext1
->csum
= NULL
;
3888 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3890 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3892 newext2
->offset
= start_data
;
3893 newext2
->datalen
= ext
->datalen
;
3894 newext2
->unique
= ext
->unique
;
3895 newext2
->ignore
= FALSE
;
3896 newext2
->inserted
= TRUE
;
3897 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3899 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3901 newext3
->offset
= end_data
;
3902 newext3
->datalen
= ext
->datalen
;
3903 newext3
->unique
= ext
->unique
;
3904 newext3
->ignore
= FALSE
;
3905 newext3
->inserted
= TRUE
;
3906 newext3
->csum
= NULL
;
3907 add_extent(fcb
, &newext2
->list_entry
, newext3
);
3909 add_insert_extent_rollback(rollback
, fcb
, newext3
);
3911 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3914 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3916 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 2,
3917 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3919 if (!NT_SUCCESS(Status
)) {
3920 ERR("update_changed_extent_ref returned %08x\n", Status
);
3925 remove_fcb_extent(fcb
, ext
, rollback
);
3931 return STATUS_SUCCESS
;
3934 NTSTATUS
do_write_file(fcb
* fcb
, UINT64 start
, UINT64 end_data
, void* data
, PIRP Irp
, BOOL file_write
, UINT32 irp_offset
, LIST_ENTRY
* rollback
) {
3936 LIST_ENTRY
*le
, *le2
;
3937 UINT64 written
= 0, length
= end_data
- start
;
3938 UINT64 last_cow_start
;
3939 ULONG priority
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
;
3940 #ifdef DEBUG_PARANOID
3946 le
= fcb
->extents
.Flink
;
3947 while (le
!= &fcb
->extents
) {
3948 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3953 EXTENT_DATA
* ed
= &ext
->extent_data
;
3954 EXTENT_DATA2
* ed2
= ed
->type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ed
->data
;
3957 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
3959 if (ext
->offset
+ len
<= start
)
3962 if (ext
->offset
> start
+ written
+ length
)
3965 if ((fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ext
->unique
&& ed
->compression
== BTRFS_COMPRESSION_NONE
) {
3966 if (max(last_cow_start
, start
+ written
) < ext
->offset
) {
3967 UINT64 start_write
= max(last_cow_start
, start
+ written
);
3969 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, ext
->offset
, Irp
, rollback
);
3970 if (!NT_SUCCESS(Status
)) {
3971 ERR("excise_extents returned %08x\n", Status
);
3975 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, ext
->offset
- start_write
, (UINT8
*)data
+ written
, Irp
, file_write
, irp_offset
+ written
, rollback
);
3976 if (!NT_SUCCESS(Status
)) {
3977 ERR("insert_extent returned %08x\n", Status
);
3981 written
+= ext
->offset
- start_write
;
3982 length
-= ext
->offset
- start_write
;
3988 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
3989 UINT64 writeaddr
= ed2
->address
+ ed2
->offset
+ start
+ written
- ext
->offset
;
3990 UINT64 write_len
= min(len
, length
);
3993 TRACE("doing non-COW write to %llx\n", writeaddr
);
3995 Status
= write_data_complete(fcb
->Vcb
, writeaddr
, (UINT8
*)data
+ written
, (UINT32
)write_len
, Irp
, NULL
, file_write
, irp_offset
+ written
, priority
);
3996 if (!NT_SUCCESS(Status
)) {
3997 ERR("write_data_complete returned %08x\n", Status
);
4001 c
= get_chunk_from_address(fcb
->Vcb
, writeaddr
);
4005 // This shouldn't ever get called - nocow files should always also be nosum.
4006 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
4007 calc_csum(fcb
->Vcb
, (UINT8
*)data
+ written
, (UINT32
)(write_len
/ fcb
->Vcb
->superblock
.sector_size
),
4008 &ext
->csum
[(start
+ written
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
]);
4010 ext
->inserted
= TRUE
;
4013 written
+= write_len
;
4014 length
-= write_len
;
4018 } else if (ed
->type
== EXTENT_TYPE_PREALLOC
) {
4021 Status
= do_write_file_prealloc(fcb
, ext
, start
+ written
, end_data
, (UINT8
*)data
+ written
, &write_len
,
4022 Irp
, file_write
, irp_offset
+ written
, priority
, rollback
);
4023 if (!NT_SUCCESS(Status
)) {
4024 ERR("do_write_file_prealloc returned %08x\n", Status
);
4028 written
+= write_len
;
4029 length
-= write_len
;
4035 last_cow_start
= ext
->offset
+ len
;
4044 UINT64 start_write
= max(last_cow_start
, start
+ written
);
4046 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, end_data
, Irp
, rollback
);
4047 if (!NT_SUCCESS(Status
)) {
4048 ERR("excise_extents returned %08x\n", Status
);
4052 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, end_data
- start_write
, (UINT8
*)data
+ written
, Irp
, file_write
, irp_offset
+ written
, rollback
);
4053 if (!NT_SUCCESS(Status
)) {
4054 ERR("insert_extent returned %08x\n", Status
);
4059 #ifdef DEBUG_PARANOID
4060 last_off
= 0xffffffffffffffff;
4062 le
= fcb
->extents
.Flink
;
4063 while (le
!= &fcb
->extents
) {
4064 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4067 if (ext
->offset
== last_off
) {
4068 ERR("offset %llx duplicated\n", ext
->offset
);
4070 } else if (ext
->offset
< last_off
&& last_off
!= 0xffffffffffffffff) {
4071 ERR("offsets out of order\n");
4075 last_off
= ext
->offset
;
4082 fcb
->extents_changed
= TRUE
;
4083 mark_fcb_dirty(fcb
);
4085 return STATUS_SUCCESS
;
4088 NTSTATUS
write_compressed(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4092 for (i
= 0; i
< sector_align(end_data
- start_data
, COMPRESSED_EXTENT_SIZE
) / COMPRESSED_EXTENT_SIZE
; i
++) {
4096 s2
= start_data
+ (i
* COMPRESSED_EXTENT_SIZE
);
4097 e2
= min(s2
+ COMPRESSED_EXTENT_SIZE
, end_data
);
4099 Status
= write_compressed_bit(fcb
, s2
, e2
, (UINT8
*)data
+ (i
* COMPRESSED_EXTENT_SIZE
), &compressed
, Irp
, rollback
);
4101 if (!NT_SUCCESS(Status
)) {
4102 ERR("write_compressed_bit returned %08x\n", Status
);
4106 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
4107 // bother with the rest of it.
4108 if (s2
== 0 && e2
== COMPRESSED_EXTENT_SIZE
&& !compressed
&& !fcb
->Vcb
->options
.compress_force
) {
4109 fcb
->inode_item
.flags
|= BTRFS_INODE_NOCOMPRESS
;
4110 fcb
->inode_item_changed
= TRUE
;
4111 mark_fcb_dirty(fcb
);
4113 // write subsequent data non-compressed
4114 if (e2
< end_data
) {
4115 Status
= do_write_file(fcb
, e2
, end_data
, (UINT8
*)data
+ e2
, Irp
, FALSE
, 0, rollback
);
4117 if (!NT_SUCCESS(Status
)) {
4118 ERR("do_write_file returned %08x\n", Status
);
4123 return STATUS_SUCCESS
;
4127 return STATUS_SUCCESS
;
4130 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOLEAN paging_io
, BOOLEAN no_cache
,
4131 BOOLEAN wait
, BOOLEAN deferred_write
, BOOLEAN write_irp
, LIST_ENTRY
* rollback
) {
4132 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4133 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4135 UINT64 off64
, newlength
, start_data
, end_data
;
4140 BOOL changed_length
= FALSE
;
4147 BOOL paging_lock
= FALSE
, fcb_lock
= FALSE
, tree_lock
= FALSE
, pagefile
;
4150 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
4153 WARN("returning success for zero-length write\n");
4154 return STATUS_SUCCESS
;
4158 ERR("error - FileObject was NULL\n");
4159 return STATUS_ACCESS_DENIED
;
4162 fcb
= FileObject
->FsContext
;
4163 ccb
= FileObject
->FsContext2
;
4164 fileref
= ccb
? ccb
->fileref
: NULL
;
4166 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
4167 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
);
4168 return STATUS_INVALID_DEVICE_REQUEST
;
4171 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1)
4172 offset
= fcb
->Header
.FileSize
;
4174 off64
= offset
.QuadPart
;
4176 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
4178 if (!no_cache
&& !CcCanIWrite(FileObject
, *length
, wait
, deferred_write
))
4179 return STATUS_PENDING
;
4181 if (!wait
&& no_cache
)
4182 return STATUS_PENDING
;
4184 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
4185 IO_STATUS_BLOCK iosb
;
4187 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
4189 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
4191 if (!NT_SUCCESS(iosb
.Status
)) {
4192 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
4193 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
4199 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
4203 if (!ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, wait
)) {
4204 Status
= STATUS_PENDING
;
4210 pagefile
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
&& paging_io
;
4212 if (!pagefile
&& !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
4213 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
4214 Status
= STATUS_PENDING
;
4222 if (!ExAcquireResourceSharedLite(fcb
->Header
.Resource
, wait
)) {
4223 Status
= STATUS_PENDING
;
4227 } else if (!ExIsResourceAcquiredExclusiveLite(fcb
->Header
.Resource
)) {
4228 if (!ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, wait
)) {
4229 Status
= STATUS_PENDING
;
4236 newlength
= fcb
->ads
? fcb
->adsdata
.Length
: fcb
->inode_item
.st_size
;
4241 TRACE("newlength = %llx\n", newlength
);
4243 if (off64
+ *length
> newlength
) {
4245 if (off64
>= newlength
) {
4246 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, off64
, *length
);
4247 TRACE("filename %S\n", file_desc(FileObject
));
4248 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
4249 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
4250 Status
= STATUS_SUCCESS
;
4254 *length
= (ULONG
)(newlength
- off64
);
4256 newlength
= off64
+ *length
;
4257 changed_length
= TRUE
;
4259 TRACE("extending length to %llx\n", newlength
);
4264 make_inline
= FALSE
;
4265 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4266 make_inline
= newlength
<= (Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - offsetof(EXTENT_DATA
, data
[0]));
4268 make_inline
= newlength
<= fcb
->Vcb
->options
.max_inline
;
4270 if (changed_length
) {
4271 if (newlength
> (UINT64
)fcb
->Header
.AllocationSize
.QuadPart
) {
4273 // We need to acquire the tree lock if we don't have it already -
4274 // we can't give an inline file proper extents at the same time as we're
4276 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
4277 Status
= STATUS_PENDING
;
4283 Status
= extend_file(fcb
, fileref
, newlength
, FALSE
, Irp
, rollback
);
4284 if (!NT_SUCCESS(Status
)) {
4285 ERR("extend_file returned %08x\n", Status
);
4288 } else if (!fcb
->ads
)
4289 fcb
->inode_item
.st_size
= newlength
;
4291 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4292 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4294 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
4295 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
4296 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
4300 Status
= STATUS_SUCCESS
;
4303 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
4306 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4307 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4308 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4310 if (!FileObject
->PrivateCacheMap
)
4311 init_file_cache(FileObject
, &ccfs
);
4313 CcSetFileSizes(FileObject
, &ccfs
);
4316 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
4317 CcPrepareMdlWrite(FileObject
, &offset
, *length
, &Irp
->MdlAddress
, &Irp
->IoStatus
);
4319 Status
= Irp
->IoStatus
.Status
;
4322 if (fCcCopyWriteEx
) {
4323 TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject
, off64
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
);
4324 if (!fCcCopyWriteEx(FileObject
, &offset
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
)) {
4325 Status
= STATUS_PENDING
;
4328 TRACE("CcCopyWriteEx finished\n");
4330 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, off64
, *length
, wait
, buf
);
4331 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
4332 Status
= STATUS_PENDING
;
4335 TRACE("CcCopyWrite finished\n");
4338 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4339 Status
= _SEH2_GetExceptionCode();
4342 if (changed_length
) {
4343 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, fcb
->ads
? FILE_NOTIFY_CHANGE_STREAM_SIZE
: FILE_NOTIFY_CHANGE_SIZE
,
4344 fcb
->ads
? FILE_ACTION_MODIFIED_STREAM
: FILE_ACTION_MODIFIED
, fcb
->ads
&& fileref
->dc
? &fileref
->dc
->name
: NULL
);
4351 if (changed_length
) {
4354 if (newlength
> fcb
->adsmaxlen
) {
4355 ERR("error - xattr too long (%llu > %u)\n", newlength
, fcb
->adsmaxlen
);
4356 Status
= STATUS_DISK_FULL
;
4360 data2
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)newlength
, ALLOC_TAG
);
4362 ERR("out of memory\n");
4363 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4367 if (fcb
->adsdata
.Buffer
) {
4368 RtlCopyMemory(data2
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
4369 ExFreePool(fcb
->adsdata
.Buffer
);
4372 if (newlength
> fcb
->adsdata
.Length
)
4373 RtlZeroMemory(&data2
[fcb
->adsdata
.Length
], (ULONG
)(newlength
- fcb
->adsdata
.Length
));
4376 fcb
->adsdata
.Buffer
= data2
;
4377 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= (USHORT
)newlength
;
4379 fcb
->Header
.AllocationSize
.QuadPart
= newlength
;
4380 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4381 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4385 RtlCopyMemory(&fcb
->adsdata
.Buffer
[off64
], buf
, *length
);
4387 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4389 mark_fcb_dirty(fcb
);
4392 mark_fileref_dirty(fileref
);
4394 BOOL compress
= write_fcb_compressed(fcb
), no_buf
= FALSE
;
4398 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
4399 bufhead
= sizeof(EXTENT_DATA
) - 1;
4400 } else if (compress
) {
4401 start_data
= off64
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
4402 end_data
= min(sector_align(off64
+ *length
, COMPRESSED_EXTENT_SIZE
),
4403 sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
));
4406 start_data
= off64
& ~(UINT64
)(fcb
->Vcb
->superblock
.sector_size
- 1);
4407 end_data
= sector_align(off64
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
4411 if (fcb_is_inline(fcb
))
4412 end_data
= max(end_data
, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
4414 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4415 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
4417 if (!make_inline
&& !compress
&& off64
== start_data
&& off64
+ *length
== end_data
) {
4421 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(end_data
- start_data
+ bufhead
), ALLOC_TAG
);
4423 ERR("out of memory\n");
4424 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4428 RtlZeroMemory(data
+ bufhead
, (ULONG
)(end_data
- start_data
));
4430 TRACE("start_data = %llx\n", start_data
);
4431 TRACE("end_data = %llx\n", end_data
);
4433 if (off64
> start_data
|| off64
+ *length
< end_data
) {
4434 if (changed_length
) {
4435 if (fcb
->inode_item
.st_size
> start_data
)
4436 Status
= read_file(fcb
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
, Irp
);
4438 Status
= STATUS_SUCCESS
;
4440 Status
= read_file(fcb
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
, Irp
);
4442 if (!NT_SUCCESS(Status
)) {
4443 ERR("read_file returned %08x\n", Status
);
4449 RtlCopyMemory(data
+ bufhead
+ off64
- start_data
, buf
, *length
);
4453 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, Irp
, rollback
);
4454 if (!NT_SUCCESS(Status
)) {
4455 ERR("error - excise_extents returned %08x\n", Status
);
4460 ed2
= (EXTENT_DATA
*)data
;
4461 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
4462 ed2
->decoded_size
= newlength
;
4463 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
4464 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
4465 ed2
->encoding
= BTRFS_ENCODING_NONE
;
4466 ed2
->type
= EXTENT_TYPE_INLINE
;
4468 Status
= add_extent_to_fcb(fcb
, 0, ed2
, (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + newlength
), FALSE
, NULL
, rollback
);
4469 if (!NT_SUCCESS(Status
)) {
4470 ERR("add_extent_to_fcb returned %08x\n", Status
);
4475 fcb
->inode_item
.st_blocks
+= newlength
;
4476 } else if (compress
) {
4477 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
4479 if (!NT_SUCCESS(Status
)) {
4480 ERR("write_compressed returned %08x\n", Status
);
4487 if (write_irp
&& Irp
->MdlAddress
&& no_buf
) {
4488 BOOL locked
= Irp
->MdlAddress
->MdlFlags
& MDL_PAGES_LOCKED
;
4491 Status
= STATUS_SUCCESS
;
4494 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoReadAccess
);
4495 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4496 Status
= _SEH2_GetExceptionCode();
4499 if (!NT_SUCCESS(Status
)) {
4500 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
4506 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, TRUE
, 0, rollback
);
4507 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4508 Status
= _SEH2_GetExceptionCode();
4512 MmUnlockPages(Irp
->MdlAddress
);
4515 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, FALSE
, 0, rollback
);
4516 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4517 Status
= _SEH2_GetExceptionCode();
4521 if (!NT_SUCCESS(Status
)) {
4522 ERR("do_write_file returned %08x\n", Status
);
4523 if (!no_buf
) ExFreePool(data
);
4532 KeQuerySystemTime(&time
);
4533 win_time_to_unix(time
, &now
);
4537 if (fileref
&& fileref
->parent
)
4538 origii
= &fileref
->parent
->fcb
->inode_item
;
4540 ERR("no parent fcb found for stream\n");
4541 Status
= STATUS_INTERNAL_ERROR
;
4545 origii
= &fcb
->inode_item
;
4547 origii
->transid
= Vcb
->superblock
.generation
;
4550 if (!ccb
->user_set_change_time
)
4551 origii
->st_ctime
= now
;
4554 if (changed_length
) {
4555 TRACE("setting st_size to %llx\n", newlength
);
4556 origii
->st_size
= newlength
;
4557 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
4560 fcb
->inode_item_changed
= TRUE
;
4562 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
4565 filter
|= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
4567 filter
|= FILE_NOTIFY_CHANGE_STREAM_WRITE
;
4570 if (!ccb
->user_set_write_time
) {
4571 origii
->st_mtime
= now
;
4572 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
4575 mark_fcb_dirty(fcb
->ads
? fileref
->parent
->fcb
: fcb
);
4578 if (changed_length
) {
4581 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4582 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4583 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4586 CcSetFileSizes(FileObject
, &ccfs
);
4587 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4588 Status
= _SEH2_GetExceptionCode();
4593 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
4594 fcb
->subvol
->root_item
.ctime
= now
;
4596 Status
= STATUS_SUCCESS
;
4599 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, filter
, fcb
->ads
? FILE_ACTION_MODIFIED_STREAM
: FILE_ACTION_MODIFIED
,
4600 fcb
->ads
&& fileref
->dc
? &fileref
->dc
->name
: NULL
);
4603 if (NT_SUCCESS(Status
) && FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
4604 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4605 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
4606 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4610 ExReleaseResourceLite(fcb
->Header
.Resource
);
4613 ExReleaseResourceLite(&Vcb
->tree_lock
);
4616 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
4621 NTSTATUS
write_file(device_extension
* Vcb
, PIRP Irp
, BOOLEAN wait
, BOOLEAN deferred_write
) {
4622 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4625 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
4626 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4627 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4628 LIST_ENTRY rollback
;
4630 InitializeListHead(&rollback
);
4634 Irp
->IoStatus
.Information
= 0;
4636 TRACE("offset = %llx\n", offset
.QuadPart
);
4637 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
4639 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
4640 buf
= map_user_buffer(Irp
, fcb
&& fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
);
4642 if (Irp
->MdlAddress
&& !buf
) {
4643 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
4644 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4648 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
4650 TRACE("buf = %p\n", buf
);
4652 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
4653 WARN("tried to write to locked region\n");
4654 Status
= STATUS_FILE_LOCK_CONFLICT
;
4658 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
,
4659 wait
, deferred_write
, TRUE
, &rollback
);
4661 if (Status
== STATUS_PENDING
)
4663 else if (!NT_SUCCESS(Status
)) {
4664 ERR("write_file2 returned %08x\n", Status
);
4668 if (NT_SUCCESS(Status
)) {
4669 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
4671 if (diskacc
&& Status
!= STATUS_PENDING
&& Irp
->Flags
& IRP_NOCACHE
) {
4672 PETHREAD thread
= NULL
;
4674 if (Irp
->Tail
.Overlay
.Thread
&& !IoIsSystemThread(Irp
->Tail
.Overlay
.Thread
))
4675 thread
= Irp
->Tail
.Overlay
.Thread
;
4676 else if (!IoIsSystemThread(PsGetCurrentThread()))
4677 thread
= PsGetCurrentThread();
4678 else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp
)
4679 thread
= PsGetCurrentThread();
4682 fPsUpdateDiskCounters(PsGetThreadProcess(thread
), 0, IrpSp
->Parameters
.Write
.Length
, 0, 1, 0);
4687 if (NT_SUCCESS(Status
))
4688 clear_rollback(&rollback
);
4690 do_rollback(Vcb
, &rollback
);
4695 _Dispatch_type_(IRP_MJ_WRITE
)
4696 _Function_class_(DRIVER_DISPATCH
)
4697 NTSTATUS NTAPI
drv_write(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4700 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4701 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4702 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4703 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4704 ccb
* ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
4705 BOOLEAN wait
= FileObject
? IoIsOperationSynchronous(Irp
) : TRUE
;
4707 FsRtlEnterFileSystem();
4709 top_level
= is_top_level(Irp
);
4711 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4712 Status
= vol_write(DeviceObject
, Irp
);
4714 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4715 Status
= STATUS_INVALID_PARAMETER
;
4720 ERR("fcb was NULL\n");
4721 Status
= STATUS_INVALID_PARAMETER
;
4726 ERR("ccb was NULL\n");
4727 Status
= STATUS_INVALID_PARAMETER
;
4731 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
4732 WARN("insufficient permissions\n");
4733 Status
= STATUS_ACCESS_DENIED
;
4737 if (fcb
== Vcb
->volume_fcb
) {
4738 if (!Vcb
->locked
|| Vcb
->locked_fileobj
!= FileObject
) {
4739 ERR("trying to write to volume when not locked, or locked with another FileObject\n");
4740 Status
= STATUS_ACCESS_DENIED
;
4744 TRACE("writing directly to volume\n");
4746 IoSkipCurrentIrpStackLocation(Irp
);
4748 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
4752 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
4753 Status
= STATUS_ACCESS_DENIED
;
4757 if (Vcb
->readonly
) {
4758 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4763 if (IrpSp
->MinorFunction
& IRP_MN_COMPLETE
) {
4764 CcMdlWriteComplete(IrpSp
->FileObject
, &IrpSp
->Parameters
.Write
.ByteOffset
, Irp
->MdlAddress
);
4766 Irp
->MdlAddress
= NULL
;
4767 Status
= STATUS_SUCCESS
;
4769 // Don't offload jobs when doing paging IO - otherwise this can lead to
4770 // deadlocks in CcCopyWrite.
4771 if (Irp
->Flags
& IRP_PAGING_IO
)
4774 Status
= write_file(Vcb
, Irp
, wait
, FALSE
);
4776 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4777 Status
= _SEH2_GetExceptionCode();
4781 Irp
->IoStatus
.Status
= Status
;
4783 TRACE("wrote %u bytes\n", Irp
->IoStatus
.Information
);
4785 if (Status
!= STATUS_PENDING
)
4786 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4788 IoMarkIrpPending(Irp
);
4790 if (!add_thread_job(Vcb
, Irp
))
4791 do_write_job(Vcb
, Irp
);
4796 IoSetTopLevelIrp(NULL
);
4798 TRACE("returning %08x\n", Status
);
4800 FsRtlExitFileSystem();