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 if (flags
& BLOCK_FLAG_DUPLICATE
) {
411 type
= BLOCK_FLAG_DUPLICATE
;
413 } else if (flags
& BLOCK_FLAG_RAID0
) {
415 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
417 type
= BLOCK_FLAG_RAID0
;
419 } else if (flags
& BLOCK_FLAG_RAID1
) {
423 type
= BLOCK_FLAG_RAID1
;
425 } else if (flags
& BLOCK_FLAG_RAID10
) {
427 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
429 type
= BLOCK_FLAG_RAID10
;
431 } else if (flags
& BLOCK_FLAG_RAID5
) {
433 max_stripes
= (UINT16
)min(0xffff, Vcb
->superblock
.num_devices
);
435 type
= BLOCK_FLAG_RAID5
;
437 } else if (flags
& BLOCK_FLAG_RAID6
) {
441 type
= BLOCK_FLAG_RAID6
;
451 if (max_chunk_size
> total_size
/ 10) { // cap at 10%
452 max_chunk_size
= total_size
/ 10;
453 max_stripe_size
= max_chunk_size
/ min_stripes
;
456 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
458 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * max_stripes
, ALLOC_TAG
);
460 ERR("out of memory\n");
461 Status
= STATUS_INSUFFICIENT_RESOURCES
;
467 if (type
== BLOCK_FLAG_DUPLICATE
) {
468 if (!find_new_dup_stripes(Vcb
, stripes
, max_stripe_size
, full_size
)) {
469 Status
= STATUS_DISK_FULL
;
473 num_stripes
= max_stripes
;
475 for (i
= 0; i
< max_stripes
; i
++) {
476 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, FALSE
, full_size
))
483 if (num_stripes
< min_stripes
&& Vcb
->options
.allow_degraded
&& allowed_missing
> 0) {
484 UINT16 added_missing
= 0;
486 for (i
= num_stripes
; i
< max_stripes
; i
++) {
487 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, TRUE
, full_size
))
491 if (added_missing
>= allowed_missing
)
496 num_stripes
+= added_missing
;
499 // for RAID10, round down to an even number of stripes
500 if (type
== BLOCK_FLAG_RAID10
&& (num_stripes
% sub_stripes
) != 0) {
501 num_stripes
-= num_stripes
% sub_stripes
;
504 if (num_stripes
< min_stripes
) {
505 WARN("found %u stripes, needed at least %u\n", num_stripes
, min_stripes
);
506 Status
= STATUS_DISK_FULL
;
510 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
512 ERR("out of memory\n");
513 Status
= STATUS_INSUFFICIENT_RESOURCES
;
519 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
520 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, cisize
, ALLOC_TAG
);
521 if (!c
->chunk_item
) {
522 ERR("out of memory\n");
523 Status
= STATUS_INSUFFICIENT_RESOURCES
;
527 stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
529 if (type
== BLOCK_FLAG_DUPLICATE
&& stripes
[1].dh
== stripes
[0].dh
)
530 stripe_size
= min(stripes
[0].dh
->size
/ 2, max_stripe_size
);
532 stripe_size
= max_stripe_size
;
533 for (i
= 0; i
< num_stripes
; i
++) {
534 if (stripes
[i
].dh
->size
< stripe_size
)
535 stripe_size
= stripes
[i
].dh
->size
;
539 if (type
== 0 || type
== BLOCK_FLAG_DUPLICATE
|| type
== BLOCK_FLAG_RAID1
)
541 else if (type
== BLOCK_FLAG_RAID0
)
542 factor
= num_stripes
;
543 else if (type
== BLOCK_FLAG_RAID10
)
544 factor
= num_stripes
/ sub_stripes
;
545 else if (type
== BLOCK_FLAG_RAID5
)
546 factor
= num_stripes
- 1;
547 else if (type
== BLOCK_FLAG_RAID6
)
548 factor
= num_stripes
- 2;
550 if (stripe_size
* factor
> max_chunk_size
)
551 stripe_size
= max_chunk_size
/ factor
;
553 if (stripe_size
% stripe_length
> 0)
554 stripe_size
-= stripe_size
% stripe_length
;
556 if (stripe_size
== 0) {
557 ERR("not enough free space found (stripe_size == 0)\n");
558 Status
= STATUS_DISK_FULL
;
562 c
->chunk_item
->size
= stripe_size
* factor
;
563 c
->chunk_item
->root_id
= Vcb
->extent_root
->id
;
564 c
->chunk_item
->stripe_length
= stripe_length
;
565 c
->chunk_item
->type
= flags
;
566 c
->chunk_item
->opt_io_alignment
= (UINT32
)c
->chunk_item
->stripe_length
;
567 c
->chunk_item
->opt_io_width
= (UINT32
)c
->chunk_item
->stripe_length
;
568 c
->chunk_item
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
569 c
->chunk_item
->num_stripes
= num_stripes
;
570 c
->chunk_item
->sub_stripes
= sub_stripes
;
572 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
574 ERR("out of memory\n");
575 Status
= STATUS_INSUFFICIENT_RESOURCES
;
579 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
580 for (i
= 0; i
< num_stripes
; i
++) {
581 cis
[i
].dev_id
= stripes
[i
].device
->devitem
.dev_id
;
583 if (type
== BLOCK_FLAG_DUPLICATE
&& i
== 1 && stripes
[i
].dh
== stripes
[0].dh
)
584 cis
[i
].offset
= stripes
[0].dh
->address
+ stripe_size
;
586 cis
[i
].offset
= stripes
[i
].dh
->address
;
588 cis
[i
].dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
590 c
->devices
[i
] = stripes
[i
].device
;
593 logaddr
= find_new_chunk_address(Vcb
, c
->chunk_item
->size
);
595 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
599 c
->used
= c
->oldused
= 0;
600 c
->cache
= c
->old_cache
= NULL
;
603 c
->last_alloc_set
= FALSE
;
605 c
->cache_loaded
= TRUE
;
607 c
->space_changed
= FALSE
;
610 InitializeListHead(&c
->space
);
611 InitializeListHead(&c
->space_size
);
612 InitializeListHead(&c
->deleting
);
613 InitializeListHead(&c
->changed_extents
);
615 InitializeListHead(&c
->range_locks
);
616 ExInitializeResourceLite(&c
->range_locks_lock
);
617 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
619 InitializeListHead(&c
->partial_stripes
);
620 ExInitializeResourceLite(&c
->partial_stripes_lock
);
622 ExInitializeResourceLite(&c
->lock
);
623 ExInitializeResourceLite(&c
->changed_extents_lock
);
625 s
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(space
), ALLOC_TAG
);
627 ERR("out of memory\n");
628 Status
= STATUS_INSUFFICIENT_RESOURCES
;
632 s
->address
= c
->offset
;
633 s
->size
= c
->chunk_item
->size
;
634 InsertTailList(&c
->space
, &s
->list_entry
);
635 InsertTailList(&c
->space_size
, &s
->list_entry_size
);
637 protect_superblocks(c
);
639 for (i
= 0; i
< num_stripes
; i
++) {
640 stripes
[i
].device
->devitem
.bytes_used
+= stripe_size
;
642 space_list_subtract2(&stripes
[i
].device
->space
, NULL
, cis
[i
].offset
, stripe_size
, NULL
, NULL
);
645 Status
= STATUS_SUCCESS
;
647 if (flags
& BLOCK_FLAG_RAID5
|| flags
& BLOCK_FLAG_RAID6
)
648 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_RAID56
;
654 if (!NT_SUCCESS(Status
)) {
657 ExFreePool(c
->devices
);
660 ExFreePool(c
->chunk_item
);
665 if (s
) ExFreePool(s
);
669 le
= Vcb
->chunks
.Flink
;
670 while (le
!= &Vcb
->chunks
) {
671 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
673 if (c2
->offset
> c
->offset
) {
674 InsertHeadList(le
->Blink
, &c
->list_entry
);
683 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
687 c
->space_changed
= TRUE
;
688 c
->list_entry_balance
.Flink
= NULL
;
696 static NTSTATUS
prepare_raid0_write(_Pre_satisfies_(_Curr_
->chunk_item
->num_stripes
>0) _In_ chunk
* c
, _In_ UINT64 address
, _In_reads_bytes_(length
) void* data
,
697 _In_ UINT32 length
, _In_ write_stripe
* stripes
, _In_ PIRP Irp
, _In_ UINT64 irp_offset
, _In_ write_data_context
* wtc
) {
698 UINT64 startoff
, endoff
;
699 UINT16 startoffstripe
, endoffstripe
, stripenum
;
700 UINT64 pos
, *stripeoff
;
702 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
706 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
708 ERR("out of memory\n");
709 return STATUS_INSUFFICIENT_RESOURCES
;
712 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &startoff
, &startoffstripe
);
713 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &endoff
, &endoffstripe
);
716 master_mdl
= Irp
->MdlAddress
;
718 pfns
= (PFN_NUMBER
*)(Irp
->MdlAddress
+ 1);
719 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
720 } else if (((ULONG_PTR
)data
% PAGE_SIZE
) != 0) {
721 wtc
->scratch
= ExAllocatePoolWithTag(NonPagedPool
, length
, ALLOC_TAG
);
723 ERR("out of memory\n");
724 return STATUS_INSUFFICIENT_RESOURCES
;
727 RtlCopyMemory(wtc
->scratch
, data
, length
);
729 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
731 ERR("out of memory\n");
732 return STATUS_INSUFFICIENT_RESOURCES
;
735 MmBuildMdlForNonPagedPool(master_mdl
);
737 wtc
->mdl
= master_mdl
;
739 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
741 NTSTATUS Status
= STATUS_SUCCESS
;
743 master_mdl
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
745 ERR("out of memory\n");
746 return STATUS_INSUFFICIENT_RESOURCES
;
750 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
751 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
752 Status
= _SEH2_GetExceptionCode();
755 if (!NT_SUCCESS(Status
)) {
756 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
757 IoFreeMdl(master_mdl
);
761 wtc
->mdl
= master_mdl
;
763 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
766 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
767 if (startoffstripe
> i
)
768 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
769 else if (startoffstripe
== i
)
770 stripes
[i
].start
= startoff
;
772 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
774 if (endoffstripe
> i
)
775 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
776 else if (endoffstripe
== i
)
777 stripes
[i
].end
= endoff
+ 1;
779 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
781 if (stripes
[i
].start
!= stripes
[i
].end
) {
782 stripes
[i
].mdl
= IoAllocateMdl(NULL
, (ULONG
)(stripes
[i
].end
- stripes
[i
].start
), FALSE
, FALSE
, NULL
);
783 if (!stripes
[i
].mdl
) {
784 ERR("IoAllocateMdl failed\n");
785 ExFreePool(stripeoff
);
786 return STATUS_INSUFFICIENT_RESOURCES
;
792 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
794 stripenum
= startoffstripe
;
796 while (pos
< length
) {
797 PFN_NUMBER
* stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripenum
].mdl
+ 1);
800 UINT32 writelen
= (UINT32
)min(stripes
[stripenum
].end
- stripes
[stripenum
].start
,
801 c
->chunk_item
->stripe_length
- (stripes
[stripenum
].start
% c
->chunk_item
->stripe_length
));
803 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
805 stripeoff
[stripenum
] += writelen
;
807 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
808 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)((length
- pos
) * sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
811 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
813 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
814 pos
+= c
->chunk_item
->stripe_length
;
817 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
820 ExFreePool(stripeoff
);
822 return STATUS_SUCCESS
;
825 static NTSTATUS
prepare_raid10_write(_Pre_satisfies_(_Curr_
->chunk_item
->sub_stripes
>0&&_Curr_
->chunk_item
->num_stripes
>=_Curr_
->chunk_item
->sub_stripes
) _In_ chunk
* c
,
826 _In_ UINT64 address
, _In_reads_bytes_(length
) void* data
, _In_ UINT32 length
, _In_ write_stripe
* stripes
,
827 _In_ PIRP Irp
, _In_ UINT64 irp_offset
, _In_ write_data_context
* wtc
) {
828 UINT64 startoff
, endoff
;
829 UINT16 startoffstripe
, endoffstripe
, stripenum
;
830 UINT64 pos
, *stripeoff
;
832 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
836 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, &startoff
, &startoffstripe
);
837 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, &endoff
, &endoffstripe
);
839 stripenum
= startoffstripe
;
840 startoffstripe
*= c
->chunk_item
->sub_stripes
;
841 endoffstripe
*= c
->chunk_item
->sub_stripes
;
844 master_mdl
= Irp
->MdlAddress
;
846 pfns
= (PFN_NUMBER
*)(Irp
->MdlAddress
+ 1);
847 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
848 } else if (((ULONG_PTR
)data
% PAGE_SIZE
) != 0) {
849 wtc
->scratch
= ExAllocatePoolWithTag(NonPagedPool
, length
, ALLOC_TAG
);
851 ERR("out of memory\n");
852 return STATUS_INSUFFICIENT_RESOURCES
;
855 RtlCopyMemory(wtc
->scratch
, data
, length
);
857 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
859 ERR("out of memory\n");
860 return STATUS_INSUFFICIENT_RESOURCES
;
863 MmBuildMdlForNonPagedPool(master_mdl
);
865 wtc
->mdl
= master_mdl
;
867 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
869 NTSTATUS Status
= STATUS_SUCCESS
;
871 master_mdl
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
873 ERR("out of memory\n");
874 return STATUS_INSUFFICIENT_RESOURCES
;
878 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
879 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
880 Status
= _SEH2_GetExceptionCode();
883 if (!NT_SUCCESS(Status
)) {
884 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
885 IoFreeMdl(master_mdl
);
889 wtc
->mdl
= master_mdl
;
891 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
894 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= c
->chunk_item
->sub_stripes
) {
897 if (startoffstripe
> i
)
898 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
899 else if (startoffstripe
== i
)
900 stripes
[i
].start
= startoff
;
902 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
904 if (endoffstripe
> i
)
905 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
906 else if (endoffstripe
== i
)
907 stripes
[i
].end
= endoff
+ 1;
909 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
911 stripes
[i
].mdl
= IoAllocateMdl(NULL
, (ULONG
)(stripes
[i
].end
- stripes
[i
].start
), FALSE
, FALSE
, NULL
);
912 if (!stripes
[i
].mdl
) {
913 ERR("IoAllocateMdl failed\n");
914 return STATUS_INSUFFICIENT_RESOURCES
;
917 for (j
= 1; j
< c
->chunk_item
->sub_stripes
; j
++) {
918 stripes
[i
+j
].start
= stripes
[i
].start
;
919 stripes
[i
+j
].end
= stripes
[i
].end
;
920 stripes
[i
+j
].data
= stripes
[i
].data
;
921 stripes
[i
+j
].mdl
= stripes
[i
].mdl
;
927 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, ALLOC_TAG
);
929 ERR("out of memory\n");
930 return STATUS_INSUFFICIENT_RESOURCES
;
933 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
935 while (pos
< length
) {
936 PFN_NUMBER
* stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].mdl
+ 1);
939 UINT32 writelen
= (UINT32
)min(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].end
- stripes
[stripenum
* c
->chunk_item
->sub_stripes
].start
,
940 c
->chunk_item
->stripe_length
- (stripes
[stripenum
* c
->chunk_item
->sub_stripes
].start
% c
->chunk_item
->stripe_length
));
942 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
944 stripeoff
[stripenum
] += writelen
;
946 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
947 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)((length
- pos
) * sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
950 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripenum
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
952 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
953 pos
+= c
->chunk_item
->stripe_length
;
956 stripenum
= (stripenum
+ 1) % (c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
959 ExFreePool(stripeoff
);
961 return STATUS_SUCCESS
;
964 static NTSTATUS
add_partial_stripe(device_extension
* Vcb
, chunk
*c
, UINT64 address
, UINT32 length
, void* data
) {
969 UINT16 num_data_stripes
;
972 num_data_stripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
973 stripe_addr
= address
- ((address
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
));
975 ExAcquireResourceExclusiveLite(&c
->partial_stripes_lock
, TRUE
);
977 le
= c
->partial_stripes
.Flink
;
978 while (le
!= &c
->partial_stripes
) {
979 ps
= CONTAINING_RECORD(le
, partial_stripe
, list_entry
);
981 if (ps
->address
== stripe_addr
) {
982 // update existing entry
984 RtlCopyMemory(ps
->data
+ address
- stripe_addr
, data
, length
);
985 RtlClearBits(&ps
->bmp
, (ULONG
)((address
- stripe_addr
) / Vcb
->superblock
.sector_size
), length
/ Vcb
->superblock
.sector_size
);
987 // if now filled, flush
988 if (RtlAreBitsClear(&ps
->bmp
, 0, (ULONG
)((num_data_stripes
* c
->chunk_item
->stripe_length
) / Vcb
->superblock
.sector_size
))) {
989 Status
= flush_partial_stripe(Vcb
, c
, ps
);
990 if (!NT_SUCCESS(Status
)) {
991 ERR("flush_partial_stripe returned %08x\n", Status
);
995 RemoveEntryList(&ps
->list_entry
);
998 ExFreePool(ps
->bmparr
);
1003 Status
= STATUS_SUCCESS
;
1005 } else if (ps
->address
> stripe_addr
)
1013 ps
= ExAllocatePoolWithTag(NonPagedPool
, offsetof(partial_stripe
, data
[0]) + (ULONG
)(num_data_stripes
* c
->chunk_item
->stripe_length
), ALLOC_TAG
);
1015 ERR("out of memory\n");
1016 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1020 bmplen
= (ULONG
)sector_align(((num_data_stripes
* c
->chunk_item
->stripe_length
) / (8 * Vcb
->superblock
.sector_size
) + 1), sizeof(ULONG
));
1022 ps
->address
= stripe_addr
;
1023 ps
->bmparr
= ExAllocatePoolWithTag(NonPagedPool
, bmplen
, ALLOC_TAG
);
1025 ERR("out of memory\n");
1027 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1031 RtlInitializeBitMap(&ps
->bmp
, ps
->bmparr
, (ULONG
)((num_data_stripes
* c
->chunk_item
->stripe_length
) / Vcb
->superblock
.sector_size
));
1032 RtlSetAllBits(&ps
->bmp
);
1034 RtlCopyMemory(ps
->data
+ address
- stripe_addr
, data
, length
);
1035 RtlClearBits(&ps
->bmp
, (ULONG
)((address
- stripe_addr
) / Vcb
->superblock
.sector_size
), length
/ Vcb
->superblock
.sector_size
);
1037 InsertHeadList(le
->Blink
, &ps
->list_entry
);
1039 Status
= STATUS_SUCCESS
;
1042 ExReleaseResourceLite(&c
->partial_stripes_lock
);
1052 static NTSTATUS
prepare_raid5_write(device_extension
* Vcb
, chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
, PIRP Irp
,
1053 UINT64 irp_offset
, ULONG priority
, write_data_context
* wtc
) {
1054 UINT64 startoff
, endoff
, parity_start
, parity_end
;
1055 UINT16 startoffstripe
, endoffstripe
, parity
, num_data_stripes
= c
->chunk_item
->num_stripes
- 1;
1056 UINT64 pos
, parity_pos
, *stripeoff
= NULL
;
1058 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
1061 PFN_NUMBER
*pfns
, *parity_pfns
;
1062 log_stripe
* log_stripes
= NULL
;
1064 if ((address
+ length
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
) > 0) {
1065 UINT64 delta
= (address
+ length
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
);
1067 delta
= min(irp_offset
+ length
, delta
);
1068 Status
= add_partial_stripe(Vcb
, c
, address
+ length
- delta
, (UINT32
)delta
, (UINT8
*)data
+ irp_offset
+ length
- delta
);
1069 if (!NT_SUCCESS(Status
)) {
1070 ERR("add_partial_stripe returned %08x\n", Status
);
1074 length
-= (UINT32
)delta
;
1077 if (length
> 0 && (address
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
) > 0) {
1078 UINT64 delta
= (num_data_stripes
* c
->chunk_item
->stripe_length
) - ((address
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
));
1080 Status
= add_partial_stripe(Vcb
, c
, address
, (UINT32
)delta
, (UINT8
*)data
+ irp_offset
);
1081 if (!NT_SUCCESS(Status
)) {
1082 ERR("add_partial_stripe returned %08x\n", Status
);
1087 length
-= (UINT32
)delta
;
1088 irp_offset
+= delta
;
1092 Status
= STATUS_SUCCESS
;
1096 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, num_data_stripes
, &startoff
, &startoffstripe
);
1097 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, num_data_stripes
, &endoff
, &endoffstripe
);
1100 while (pos
< length
) {
1101 parity
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1104 UINT16 stripe
= (parity
+ startoffstripe
+ 1) % c
->chunk_item
->num_stripes
;
1105 ULONG skip
, writelen
;
1108 while (stripe
!= parity
) {
1109 if (i
== startoffstripe
) {
1110 writelen
= (ULONG
)min(length
, c
->chunk_item
->stripe_length
- (startoff
% c
->chunk_item
->stripe_length
));
1112 stripes
[stripe
].start
= startoff
;
1113 stripes
[stripe
].end
= startoff
+ writelen
;
1120 writelen
= (ULONG
)min(length
- pos
, c
->chunk_item
->stripe_length
);
1122 stripes
[stripe
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1123 stripes
[stripe
].end
= stripes
[stripe
].start
+ writelen
;
1132 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1138 for (i
= 0; i
< startoffstripe
; i
++) {
1139 stripe
= (parity
+ i
+ 1) % c
->chunk_item
->num_stripes
;
1141 stripes
[stripe
].start
= stripes
[stripe
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1144 stripes
[parity
].start
= stripes
[parity
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1146 if (length
- pos
> c
->chunk_item
->num_stripes
* num_data_stripes
* c
->chunk_item
->stripe_length
) {
1147 skip
= (ULONG
)(((length
- pos
) / (c
->chunk_item
->num_stripes
* num_data_stripes
* c
->chunk_item
->stripe_length
)) - 1);
1149 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1150 stripes
[i
].end
+= skip
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
1153 pos
+= skip
* num_data_stripes
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
1155 } else if (length
- pos
>= c
->chunk_item
->stripe_length
* num_data_stripes
) {
1156 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1157 stripes
[i
].end
+= c
->chunk_item
->stripe_length
;
1160 pos
+= c
->chunk_item
->stripe_length
* num_data_stripes
;
1162 UINT16 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1165 while (stripe
!= parity
) {
1166 if (endoffstripe
== i
) {
1167 stripes
[stripe
].end
= endoff
+ 1;
1169 } else if (endoffstripe
> i
)
1170 stripes
[stripe
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1173 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1180 parity_start
= 0xffffffffffffffff;
1183 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1184 if (stripes
[i
].start
!= 0 || stripes
[i
].end
!= 0) {
1185 parity_start
= min(stripes
[i
].start
, parity_start
);
1186 parity_end
= max(stripes
[i
].end
, parity_end
);
1190 if (parity_end
== parity_start
) {
1191 Status
= STATUS_SUCCESS
;
1195 parity
= (((address
- c
->offset
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1196 stripes
[parity
].start
= parity_start
;
1198 parity
= (((address
- c
->offset
+ length
- 1) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1199 stripes
[parity
].end
= parity_end
;
1201 log_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(log_stripe
) * num_data_stripes
, ALLOC_TAG
);
1203 ERR("out of memory\n");
1204 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1208 RtlZeroMemory(log_stripes
, sizeof(log_stripe
) * num_data_stripes
);
1210 for (i
= 0; i
< num_data_stripes
; i
++) {
1211 log_stripes
[i
].mdl
= IoAllocateMdl(NULL
, (ULONG
)(parity_end
- parity_start
), FALSE
, FALSE
, NULL
);
1212 if (!log_stripes
[i
].mdl
) {
1213 ERR("out of memory\n");
1214 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1218 log_stripes
[i
].mdl
->MdlFlags
|= MDL_PARTIAL
;
1219 log_stripes
[i
].pfns
= (PFN_NUMBER
*)(log_stripes
[i
].mdl
+ 1);
1222 wtc
->parity1
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(parity_end
- parity_start
), ALLOC_TAG
);
1223 if (!wtc
->parity1
) {
1224 ERR("out of memory\n");
1225 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1229 wtc
->parity1_mdl
= IoAllocateMdl(wtc
->parity1
, (ULONG
)(parity_end
- parity_start
), FALSE
, FALSE
, NULL
);
1230 if (!wtc
->parity1_mdl
) {
1231 ERR("out of memory\n");
1232 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1236 MmBuildMdlForNonPagedPool(wtc
->parity1_mdl
);
1239 master_mdl
= Irp
->MdlAddress
;
1240 else if (((ULONG_PTR
)data
% PAGE_SIZE
) != 0) {
1241 wtc
->scratch
= ExAllocatePoolWithTag(NonPagedPool
, length
, ALLOC_TAG
);
1242 if (!wtc
->scratch
) {
1243 ERR("out of memory\n");
1244 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1248 RtlCopyMemory(wtc
->scratch
, (UINT8
*)data
+ irp_offset
, length
);
1250 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
1252 ERR("out of memory\n");
1253 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1257 MmBuildMdlForNonPagedPool(master_mdl
);
1259 wtc
->mdl
= master_mdl
;
1261 master_mdl
= IoAllocateMdl((UINT8
*)data
+ irp_offset
, length
, FALSE
, FALSE
, NULL
);
1263 ERR("out of memory\n");
1264 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1268 Status
= STATUS_SUCCESS
;
1271 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
1272 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1273 Status
= _SEH2_GetExceptionCode();
1276 if (!NT_SUCCESS(Status
)) {
1277 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1278 IoFreeMdl(master_mdl
);
1282 wtc
->mdl
= master_mdl
;
1285 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
1286 parity_pfns
= (PFN_NUMBER
*)(wtc
->parity1_mdl
+ 1);
1289 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
1291 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1292 if (stripes
[i
].start
!= stripes
[i
].end
) {
1293 stripes
[i
].mdl
= IoAllocateMdl((UINT8
*)MmGetMdlVirtualAddress(master_mdl
) + irp_offset
, (ULONG
)(stripes
[i
].end
- stripes
[i
].start
), FALSE
, FALSE
, NULL
);
1294 if (!stripes
[i
].mdl
) {
1295 ERR("IoAllocateMdl failed\n");
1296 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1302 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1304 ERR("out of memory\n");
1305 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1309 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
1314 while (pos
< length
) {
1315 PFN_NUMBER
* stripe_pfns
;
1317 parity
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1320 UINT16 stripe
= (parity
+ startoffstripe
+ 1) % c
->chunk_item
->num_stripes
;
1321 UINT32 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
,
1322 c
->chunk_item
->stripe_length
- (stripes
[stripe
].start
% c
->chunk_item
->stripe_length
)));
1323 UINT32 maxwritelen
= writelen
;
1325 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1327 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1329 RtlCopyMemory(log_stripes
[startoffstripe
].pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1330 log_stripes
[startoffstripe
].pfns
+= writelen
>> PAGE_SHIFT
;
1332 stripeoff
[stripe
] = writelen
;
1335 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1336 i
= startoffstripe
+ 1;
1338 while (stripe
!= parity
) {
1339 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1340 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
, c
->chunk_item
->stripe_length
));
1345 if (writelen
> maxwritelen
)
1346 maxwritelen
= writelen
;
1348 RtlCopyMemory(stripe_pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1350 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1351 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1353 stripeoff
[stripe
] = writelen
;
1356 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1360 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
1362 RtlCopyMemory(stripe_pfns
, parity_pfns
, maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1363 stripeoff
[parity
] = maxwritelen
;
1364 parity_pos
= maxwritelen
;
1365 } else if (length
- pos
>= c
->chunk_item
->stripe_length
* num_data_stripes
) {
1366 UINT16 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1369 while (stripe
!= parity
) {
1370 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1372 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1374 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1375 log_stripes
[i
].pfns
+= c
->chunk_item
->stripe_length
>> PAGE_SHIFT
;
1377 stripeoff
[stripe
] += c
->chunk_item
->stripe_length
;
1378 pos
+= c
->chunk_item
->stripe_length
;
1380 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1384 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
1386 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity
] >> PAGE_SHIFT
], &parity_pfns
[parity_pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1387 stripeoff
[parity
] += c
->chunk_item
->stripe_length
;
1388 parity_pos
+= c
->chunk_item
->stripe_length
;
1390 UINT16 stripe
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1391 UINT32 writelen
, maxwritelen
= 0;
1394 while (pos
< length
) {
1395 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1396 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
, c
->chunk_item
->stripe_length
));
1401 if (writelen
> maxwritelen
)
1402 maxwritelen
= writelen
;
1404 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1406 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1407 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1409 stripeoff
[stripe
] += writelen
;
1412 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1416 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity
].mdl
+ 1);
1418 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity
] >> PAGE_SHIFT
], &parity_pfns
[parity_pos
>> PAGE_SHIFT
], maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1422 for (i
= 0; i
< num_data_stripes
; i
++) {
1423 UINT8
* ss
= MmGetSystemAddressForMdlSafe(log_stripes
[i
].mdl
, priority
);
1426 RtlCopyMemory(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1428 do_xor(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1431 Status
= STATUS_SUCCESS
;
1435 for (i
= 0; i
< num_data_stripes
; i
++) {
1436 if (log_stripes
[i
].mdl
)
1437 IoFreeMdl(log_stripes
[i
].mdl
);
1440 ExFreePool(log_stripes
);
1444 ExFreePool(stripeoff
);
1449 static NTSTATUS
prepare_raid6_write(device_extension
* Vcb
, chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
, PIRP Irp
,
1450 UINT64 irp_offset
, ULONG priority
, write_data_context
* wtc
) {
1451 UINT64 startoff
, endoff
, parity_start
, parity_end
;
1452 UINT16 startoffstripe
, endoffstripe
, parity1
, num_data_stripes
= c
->chunk_item
->num_stripes
- 2;
1453 UINT64 pos
, parity_pos
, *stripeoff
= NULL
;
1455 BOOL file_write
= Irp
&& Irp
->MdlAddress
&& (Irp
->MdlAddress
->ByteOffset
== 0);
1458 PFN_NUMBER
*pfns
, *parity1_pfns
, *parity2_pfns
;
1459 log_stripe
* log_stripes
= NULL
;
1461 if ((address
+ length
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
) > 0) {
1462 UINT64 delta
= (address
+ length
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
);
1464 delta
= min(irp_offset
+ length
, delta
);
1465 Status
= add_partial_stripe(Vcb
, c
, address
+ length
- delta
, (UINT32
)delta
, (UINT8
*)data
+ irp_offset
+ length
- delta
);
1466 if (!NT_SUCCESS(Status
)) {
1467 ERR("add_partial_stripe returned %08x\n", Status
);
1471 length
-= (UINT32
)delta
;
1474 if (length
> 0 && (address
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
) > 0) {
1475 UINT64 delta
= (num_data_stripes
* c
->chunk_item
->stripe_length
) - ((address
- c
->offset
) % (num_data_stripes
* c
->chunk_item
->stripe_length
));
1477 Status
= add_partial_stripe(Vcb
, c
, address
, (UINT32
)delta
, (UINT8
*)data
+ irp_offset
);
1478 if (!NT_SUCCESS(Status
)) {
1479 ERR("add_partial_stripe returned %08x\n", Status
);
1484 length
-= (UINT32
)delta
;
1485 irp_offset
+= delta
;
1489 Status
= STATUS_SUCCESS
;
1493 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, num_data_stripes
, &startoff
, &startoffstripe
);
1494 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, num_data_stripes
, &endoff
, &endoffstripe
);
1497 while (pos
< length
) {
1498 parity1
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1501 UINT16 stripe
= (parity1
+ startoffstripe
+ 2) % c
->chunk_item
->num_stripes
;
1502 UINT16 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1503 ULONG skip
, writelen
;
1506 while (stripe
!= parity1
) {
1507 if (i
== startoffstripe
) {
1508 writelen
= (ULONG
)min(length
, c
->chunk_item
->stripe_length
- (startoff
% c
->chunk_item
->stripe_length
));
1510 stripes
[stripe
].start
= startoff
;
1511 stripes
[stripe
].end
= startoff
+ writelen
;
1518 writelen
= (ULONG
)min(length
- pos
, c
->chunk_item
->stripe_length
);
1520 stripes
[stripe
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1521 stripes
[stripe
].end
= stripes
[stripe
].start
+ writelen
;
1530 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1536 for (i
= 0; i
< startoffstripe
; i
++) {
1537 stripe
= (parity1
+ i
+ 2) % c
->chunk_item
->num_stripes
;
1539 stripes
[stripe
].start
= stripes
[stripe
].end
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1542 stripes
[parity1
].start
= stripes
[parity1
].end
= stripes
[parity2
].start
= stripes
[parity2
].end
=
1543 startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1545 if (length
- pos
> c
->chunk_item
->num_stripes
* num_data_stripes
* c
->chunk_item
->stripe_length
) {
1546 skip
= (ULONG
)(((length
- pos
) / (c
->chunk_item
->num_stripes
* num_data_stripes
* c
->chunk_item
->stripe_length
)) - 1);
1548 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1549 stripes
[i
].end
+= skip
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
1552 pos
+= skip
* num_data_stripes
* c
->chunk_item
->num_stripes
* c
->chunk_item
->stripe_length
;
1554 } else if (length
- pos
>= c
->chunk_item
->stripe_length
* num_data_stripes
) {
1555 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1556 stripes
[i
].end
+= c
->chunk_item
->stripe_length
;
1559 pos
+= c
->chunk_item
->stripe_length
* num_data_stripes
;
1561 UINT16 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
1564 while (stripe
!= parity1
) {
1565 if (endoffstripe
== i
) {
1566 stripes
[stripe
].end
= endoff
+ 1;
1568 } else if (endoffstripe
> i
)
1569 stripes
[stripe
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1572 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1579 parity_start
= 0xffffffffffffffff;
1582 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1583 if (stripes
[i
].start
!= 0 || stripes
[i
].end
!= 0) {
1584 parity_start
= min(stripes
[i
].start
, parity_start
);
1585 parity_end
= max(stripes
[i
].end
, parity_end
);
1589 if (parity_end
== parity_start
) {
1590 Status
= STATUS_SUCCESS
;
1594 parity1
= (((address
- c
->offset
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1595 stripes
[parity1
].start
= stripes
[(parity1
+ 1) % c
->chunk_item
->num_stripes
].start
= parity_start
;
1597 parity1
= (((address
- c
->offset
+ length
- 1) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1598 stripes
[parity1
].end
= stripes
[(parity1
+ 1) % c
->chunk_item
->num_stripes
].end
= parity_end
;
1600 log_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(log_stripe
) * num_data_stripes
, ALLOC_TAG
);
1602 ERR("out of memory\n");
1603 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1607 RtlZeroMemory(log_stripes
, sizeof(log_stripe
) * num_data_stripes
);
1609 for (i
= 0; i
< num_data_stripes
; i
++) {
1610 log_stripes
[i
].mdl
= IoAllocateMdl(NULL
, (ULONG
)(parity_end
- parity_start
), FALSE
, FALSE
, NULL
);
1611 if (!log_stripes
[i
].mdl
) {
1612 ERR("out of memory\n");
1613 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1617 log_stripes
[i
].mdl
->MdlFlags
|= MDL_PARTIAL
;
1618 log_stripes
[i
].pfns
= (PFN_NUMBER
*)(log_stripes
[i
].mdl
+ 1);
1621 wtc
->parity1
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(parity_end
- parity_start
), ALLOC_TAG
);
1622 if (!wtc
->parity1
) {
1623 ERR("out of memory\n");
1624 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1628 wtc
->parity2
= ExAllocatePoolWithTag(NonPagedPool
, (ULONG
)(parity_end
- parity_start
), ALLOC_TAG
);
1629 if (!wtc
->parity2
) {
1630 ERR("out of memory\n");
1631 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1635 wtc
->parity1_mdl
= IoAllocateMdl(wtc
->parity1
, (ULONG
)(parity_end
- parity_start
), FALSE
, FALSE
, NULL
);
1636 if (!wtc
->parity1_mdl
) {
1637 ERR("out of memory\n");
1638 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1642 MmBuildMdlForNonPagedPool(wtc
->parity1_mdl
);
1644 wtc
->parity2_mdl
= IoAllocateMdl(wtc
->parity2
, (ULONG
)(parity_end
- parity_start
), FALSE
, FALSE
, NULL
);
1645 if (!wtc
->parity2_mdl
) {
1646 ERR("out of memory\n");
1647 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1651 MmBuildMdlForNonPagedPool(wtc
->parity2_mdl
);
1654 master_mdl
= Irp
->MdlAddress
;
1655 else if (((ULONG_PTR
)data
% PAGE_SIZE
) != 0) {
1656 wtc
->scratch
= ExAllocatePoolWithTag(NonPagedPool
, length
, ALLOC_TAG
);
1657 if (!wtc
->scratch
) {
1658 ERR("out of memory\n");
1659 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1663 RtlCopyMemory(wtc
->scratch
, (UINT8
*)data
+ irp_offset
, length
);
1665 master_mdl
= IoAllocateMdl(wtc
->scratch
, length
, FALSE
, FALSE
, NULL
);
1667 ERR("out of memory\n");
1668 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1672 MmBuildMdlForNonPagedPool(master_mdl
);
1674 wtc
->mdl
= master_mdl
;
1676 master_mdl
= IoAllocateMdl((UINT8
*)data
+ irp_offset
, length
, FALSE
, FALSE
, NULL
);
1678 ERR("out of memory\n");
1679 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1683 Status
= STATUS_SUCCESS
;
1686 MmProbeAndLockPages(master_mdl
, KernelMode
, IoReadAccess
);
1687 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1688 Status
= _SEH2_GetExceptionCode();
1691 if (!NT_SUCCESS(Status
)) {
1692 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1693 IoFreeMdl(master_mdl
);
1697 wtc
->mdl
= master_mdl
;
1700 pfns
= (PFN_NUMBER
*)(master_mdl
+ 1);
1701 parity1_pfns
= (PFN_NUMBER
*)(wtc
->parity1_mdl
+ 1);
1702 parity2_pfns
= (PFN_NUMBER
*)(wtc
->parity2_mdl
+ 1);
1705 pfns
= &pfns
[irp_offset
>> PAGE_SHIFT
];
1707 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1708 if (stripes
[i
].start
!= stripes
[i
].end
) {
1709 stripes
[i
].mdl
= IoAllocateMdl((UINT8
*)MmGetMdlVirtualAddress(master_mdl
) + irp_offset
, (ULONG
)(stripes
[i
].end
- stripes
[i
].start
), FALSE
, FALSE
, NULL
);
1710 if (!stripes
[i
].mdl
) {
1711 ERR("IoAllocateMdl failed\n");
1712 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1718 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1720 ERR("out of memory\n");
1721 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1725 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
1730 while (pos
< length
) {
1731 PFN_NUMBER
* stripe_pfns
;
1733 parity1
= (((address
- c
->offset
+ pos
) / (num_data_stripes
* c
->chunk_item
->stripe_length
)) + num_data_stripes
) % c
->chunk_item
->num_stripes
;
1736 UINT16 stripe
= (parity1
+ startoffstripe
+ 2) % c
->chunk_item
->num_stripes
, parity2
;
1737 UINT32 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
,
1738 c
->chunk_item
->stripe_length
- (stripes
[stripe
].start
% c
->chunk_item
->stripe_length
)));
1739 UINT32 maxwritelen
= writelen
;
1741 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1743 RtlCopyMemory(stripe_pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1745 RtlCopyMemory(log_stripes
[startoffstripe
].pfns
, pfns
, writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1746 log_stripes
[startoffstripe
].pfns
+= writelen
>> PAGE_SHIFT
;
1748 stripeoff
[stripe
] = writelen
;
1751 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1752 i
= startoffstripe
+ 1;
1754 while (stripe
!= parity1
) {
1755 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1756 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
, c
->chunk_item
->stripe_length
));
1761 if (writelen
> maxwritelen
)
1762 maxwritelen
= writelen
;
1764 RtlCopyMemory(stripe_pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1766 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1767 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1769 stripeoff
[stripe
] = writelen
;
1772 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1776 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity1
].mdl
+ 1);
1777 RtlCopyMemory(stripe_pfns
, parity1_pfns
, maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1778 stripeoff
[parity1
] = maxwritelen
;
1780 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1782 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity2
].mdl
+ 1);
1783 RtlCopyMemory(stripe_pfns
, parity2_pfns
, maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1784 stripeoff
[parity2
] = maxwritelen
;
1786 parity_pos
= maxwritelen
;
1787 } else if (length
- pos
>= c
->chunk_item
->stripe_length
* num_data_stripes
) {
1788 UINT16 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
, parity2
;
1791 while (stripe
!= parity1
) {
1792 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1794 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1796 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1797 log_stripes
[i
].pfns
+= c
->chunk_item
->stripe_length
>> PAGE_SHIFT
;
1799 stripeoff
[stripe
] += c
->chunk_item
->stripe_length
;
1800 pos
+= c
->chunk_item
->stripe_length
;
1802 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1806 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity1
].mdl
+ 1);
1807 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity1
] >> PAGE_SHIFT
], &parity1_pfns
[parity_pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1808 stripeoff
[parity1
] += c
->chunk_item
->stripe_length
;
1810 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1812 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity2
].mdl
+ 1);
1813 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity2
] >> PAGE_SHIFT
], &parity2_pfns
[parity_pos
>> PAGE_SHIFT
], (ULONG
)(c
->chunk_item
->stripe_length
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
));
1814 stripeoff
[parity2
] += c
->chunk_item
->stripe_length
;
1816 parity_pos
+= c
->chunk_item
->stripe_length
;
1818 UINT16 stripe
= (parity1
+ 2) % c
->chunk_item
->num_stripes
, parity2
;
1819 UINT32 writelen
, maxwritelen
= 0;
1822 while (pos
< length
) {
1823 stripe_pfns
= (PFN_NUMBER
*)(stripes
[stripe
].mdl
+ 1);
1824 writelen
= (UINT32
)min(length
- pos
, min(stripes
[stripe
].end
- stripes
[stripe
].start
, c
->chunk_item
->stripe_length
));
1829 if (writelen
> maxwritelen
)
1830 maxwritelen
= writelen
;
1832 RtlCopyMemory(&stripe_pfns
[stripeoff
[stripe
] >> PAGE_SHIFT
], &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1834 RtlCopyMemory(log_stripes
[i
].pfns
, &pfns
[pos
>> PAGE_SHIFT
], writelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1835 log_stripes
[i
].pfns
+= writelen
>> PAGE_SHIFT
;
1837 stripeoff
[stripe
] += writelen
;
1840 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
1844 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity1
].mdl
+ 1);
1845 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity1
] >> PAGE_SHIFT
], &parity1_pfns
[parity_pos
>> PAGE_SHIFT
], maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1847 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1849 stripe_pfns
= (PFN_NUMBER
*)(stripes
[parity2
].mdl
+ 1);
1850 RtlCopyMemory(&stripe_pfns
[stripeoff
[parity2
] >> PAGE_SHIFT
], &parity2_pfns
[parity_pos
>> PAGE_SHIFT
], maxwritelen
* sizeof(PFN_NUMBER
) >> PAGE_SHIFT
);
1854 for (i
= 0; i
< num_data_stripes
; i
++) {
1855 UINT8
* ss
= MmGetSystemAddressForMdlSafe(log_stripes
[c
->chunk_item
->num_stripes
- 3 - i
].mdl
, priority
);
1858 RtlCopyMemory(wtc
->parity1
, ss
, (ULONG
)(parity_end
- parity_start
));
1859 RtlCopyMemory(wtc
->parity2
, ss
, (ULONG
)(parity_end
- parity_start
));
1861 do_xor(wtc
->parity1
, ss
, (UINT32
)(parity_end
- parity_start
));
1863 galois_double(wtc
->parity2
, (UINT32
)(parity_end
- parity_start
));
1864 do_xor(wtc
->parity2
, ss
, (UINT32
)(parity_end
- parity_start
));
1868 Status
= STATUS_SUCCESS
;
1872 for (i
= 0; i
< num_data_stripes
; i
++) {
1873 if (log_stripes
[i
].mdl
)
1874 IoFreeMdl(log_stripes
[i
].mdl
);
1877 ExFreePool(log_stripes
);
1881 ExFreePool(stripeoff
);
1886 NTSTATUS
write_data(_In_ device_extension
* Vcb
, _In_ UINT64 address
, _In_reads_bytes_(length
) void* data
, _In_ UINT32 length
, _In_ write_data_context
* wtc
,
1887 _In_opt_ PIRP Irp
, _In_opt_ chunk
* c
, _In_ BOOL file_write
, _In_ UINT64 irp_offset
, _In_ ULONG priority
) {
1890 CHUNK_ITEM_STRIPE
* cis
;
1891 write_stripe
* stripes
= NULL
;
1892 UINT64 total_writing
= 0;
1893 ULONG allowed_missing
, missing
;
1895 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
1898 c
= get_chunk_from_address(Vcb
, address
);
1900 ERR("could not get chunk for address %llx\n", address
);
1901 return STATUS_INTERNAL_ERROR
;
1905 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1907 ERR("out of memory\n");
1908 return STATUS_INSUFFICIENT_RESOURCES
;
1911 RtlZeroMemory(stripes
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
);
1913 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1915 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
1916 Status
= prepare_raid0_write(c
, address
, data
, length
, stripes
, file_write
? Irp
: NULL
, irp_offset
, wtc
);
1917 if (!NT_SUCCESS(Status
)) {
1918 ERR("prepare_raid0_write returned %08x\n", Status
);
1919 goto prepare_failed
;
1922 allowed_missing
= 0;
1923 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
1924 Status
= prepare_raid10_write(c
, address
, data
, length
, stripes
, file_write
? Irp
: NULL
, irp_offset
, wtc
);
1925 if (!NT_SUCCESS(Status
)) {
1926 ERR("prepare_raid10_write returned %08x\n", Status
);
1927 goto prepare_failed
;
1930 allowed_missing
= 1;
1931 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
1932 Status
= prepare_raid5_write(Vcb
, c
, address
, data
, length
, stripes
, file_write
? Irp
: NULL
, irp_offset
, priority
, wtc
);
1933 if (!NT_SUCCESS(Status
)) {
1934 ERR("prepare_raid5_write returned %08x\n", Status
);
1935 goto prepare_failed
;
1938 allowed_missing
= 1;
1939 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
1940 Status
= prepare_raid6_write(Vcb
, c
, address
, data
, length
, stripes
, file_write
? Irp
: NULL
, irp_offset
, priority
, wtc
);
1941 if (!NT_SUCCESS(Status
)) {
1942 ERR("prepare_raid6_write returned %08x\n", Status
);
1943 goto prepare_failed
;
1946 allowed_missing
= 2;
1947 } else { // write same data to every location - SINGLE, DUP, RAID1
1948 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1949 stripes
[i
].start
= address
- c
->offset
;
1950 stripes
[i
].end
= stripes
[i
].start
+ length
;
1951 stripes
[i
].data
= data
;
1952 stripes
[i
].irp_offset
= irp_offset
;
1954 if (c
->devices
[i
]->devobj
) {
1957 ULONG writelen
= (ULONG
)(stripes
[i
].end
- stripes
[i
].start
);
1959 va
= (UINT8
*)MmGetMdlVirtualAddress(Irp
->MdlAddress
) + stripes
[i
].irp_offset
;
1961 stripes
[i
].mdl
= IoAllocateMdl(va
, writelen
, FALSE
, FALSE
, NULL
);
1962 if (!stripes
[i
].mdl
) {
1963 ERR("IoAllocateMdl failed\n");
1964 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1965 goto prepare_failed
;
1968 IoBuildPartialMdl(Irp
->MdlAddress
, stripes
[i
].mdl
, va
, writelen
);
1970 stripes
[i
].mdl
= IoAllocateMdl(stripes
[i
].data
, (ULONG
)(stripes
[i
].end
- stripes
[i
].start
), FALSE
, FALSE
, NULL
);
1971 if (!stripes
[i
].mdl
) {
1972 ERR("IoAllocateMdl failed\n");
1973 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1974 goto prepare_failed
;
1977 Status
= STATUS_SUCCESS
;
1980 MmProbeAndLockPages(stripes
[i
].mdl
, KernelMode
, IoReadAccess
);
1981 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
1982 Status
= _SEH2_GetExceptionCode();
1985 if (!NT_SUCCESS(Status
)) {
1986 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
1987 IoFreeMdl(stripes
[i
].mdl
);
1988 stripes
[i
].mdl
= NULL
;
1989 goto prepare_failed
;
1995 allowed_missing
= c
->chunk_item
->num_stripes
- 1;
1999 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2000 if (!c
->devices
[i
]->devobj
)
2004 if (missing
> allowed_missing
) {
2005 ERR("cannot write as %u missing devices (maximum %u)\n", missing
, allowed_missing
);
2006 Status
= STATUS_DEVICE_NOT_READY
;
2007 goto prepare_failed
;
2010 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2011 write_data_stripe
* stripe
;
2012 PIO_STACK_LOCATION IrpSp
;
2014 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_stripe
), ALLOC_TAG
);
2016 ERR("out of memory\n");
2017 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2021 if (stripes
[i
].start
== stripes
[i
].end
|| !c
->devices
[i
]->devobj
) {
2022 stripe
->status
= WriteDataStatus_Ignore
;
2024 stripe
->buf
= stripes
[i
].data
;
2027 stripe
->context
= (struct _write_data_context
*)wtc
;
2028 stripe
->buf
= stripes
[i
].data
;
2029 stripe
->device
= c
->devices
[i
];
2030 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
2031 stripe
->status
= WriteDataStatus_Pending
;
2032 stripe
->mdl
= stripes
[i
].mdl
;
2035 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
2038 ERR("IoAllocateIrp failed\n");
2040 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2044 stripe
->Irp
= IoMakeAssociatedIrp(Irp
, stripe
->device
->devobj
->StackSize
);
2047 ERR("IoMakeAssociatedIrp failed\n");
2049 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2054 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
2055 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
2057 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
2058 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= MmGetSystemAddressForMdlSafe(stripes
[i
].mdl
, priority
);
2060 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
2061 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
)
2062 stripe
->Irp
->MdlAddress
= stripe
->mdl
;
2064 stripe
->Irp
->UserBuffer
= MmGetSystemAddressForMdlSafe(stripes
[i
].mdl
, priority
);
2066 #ifdef DEBUG_PARANOID
2067 if (stripes
[i
].end
< stripes
[i
].start
) {
2068 ERR("trying to write stripe with negative length (%llx < %llx)\n", stripes
[i
].end
, stripes
[i
].start
);
2073 IrpSp
->Parameters
.Write
.Length
= (ULONG
)(stripes
[i
].end
- stripes
[i
].start
);
2074 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= stripes
[i
].start
+ cis
[i
].offset
;
2076 total_writing
+= IrpSp
->Parameters
.Write
.Length
;
2078 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
2079 wtc
->stripes_left
++;
2081 IoSetCompletionRoutine(stripe
->Irp
, write_data_completion
, stripe
, TRUE
, TRUE
, TRUE
);
2084 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
2088 fFsRtlUpdateDiskCounters(0, total_writing
);
2090 Status
= STATUS_SUCCESS
;
2094 if (stripes
) ExFreePool(stripes
);
2096 if (!NT_SUCCESS(Status
))
2097 free_write_data_stripes(wtc
);
2102 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2103 if (stripes
[i
].mdl
&& (i
== 0 || stripes
[i
].mdl
!= stripes
[i
-1].mdl
)) {
2104 if (stripes
[i
].mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2105 MmUnlockPages(stripes
[i
].mdl
);
2107 IoFreeMdl(stripes
[i
].mdl
);
2111 if (wtc
->parity1_mdl
) {
2112 if (wtc
->parity1_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2113 MmUnlockPages(wtc
->parity1_mdl
);
2115 IoFreeMdl(wtc
->parity1_mdl
);
2116 wtc
->parity1_mdl
= NULL
;
2119 if (wtc
->parity2_mdl
) {
2120 if (wtc
->parity2_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2121 MmUnlockPages(wtc
->parity2_mdl
);
2123 IoFreeMdl(wtc
->parity2_mdl
);
2124 wtc
->parity2_mdl
= NULL
;
2128 if (wtc
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2129 MmUnlockPages(wtc
->mdl
);
2131 IoFreeMdl(wtc
->mdl
);
2136 ExFreePool(wtc
->parity1
);
2137 wtc
->parity1
= NULL
;
2141 ExFreePool(wtc
->parity2
);
2142 wtc
->parity2
= NULL
;
2146 ExFreePool(wtc
->scratch
);
2147 wtc
->scratch
= NULL
;
2150 ExFreePool(stripes
);
2154 void get_raid56_lock_range(chunk
* c
, UINT64 address
, UINT64 length
, UINT64
* lockaddr
, UINT64
* locklen
) {
2155 UINT64 startoff
, endoff
;
2156 UINT16 startoffstripe
, endoffstripe
, datastripes
;
2158 datastripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
2160 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, datastripes
, &startoff
, &startoffstripe
);
2161 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, datastripes
, &endoff
, &endoffstripe
);
2163 startoff
-= startoff
% c
->chunk_item
->stripe_length
;
2164 endoff
= sector_align(endoff
, c
->chunk_item
->stripe_length
);
2166 *lockaddr
= c
->offset
+ (startoff
* datastripes
);
2167 *locklen
= (endoff
- startoff
) * datastripes
;
2170 NTSTATUS
write_data_complete(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
, PIRP Irp
, chunk
* c
, BOOL file_write
, UINT64 irp_offset
, ULONG priority
) {
2171 write_data_context wtc
;
2173 UINT64 lockaddr
, locklen
;
2175 KeInitializeEvent(&wtc
.Event
, NotificationEvent
, FALSE
);
2176 InitializeListHead(&wtc
.stripes
);
2177 wtc
.stripes_left
= 0;
2178 wtc
.parity1
= wtc
.parity2
= wtc
.scratch
= NULL
;
2179 wtc
.mdl
= wtc
.parity1_mdl
= wtc
.parity2_mdl
= NULL
;
2182 c
= get_chunk_from_address(Vcb
, address
);
2184 ERR("could not get chunk for address %llx\n", address
);
2185 return STATUS_INTERNAL_ERROR
;
2189 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2190 get_raid56_lock_range(c
, address
, length
, &lockaddr
, &locklen
);
2191 chunk_lock_range(Vcb
, c
, lockaddr
, locklen
);
2195 Status
= write_data(Vcb
, address
, data
, length
, &wtc
, Irp
, c
, file_write
, irp_offset
, priority
);
2196 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
2197 Status
= _SEH2_GetExceptionCode();
2200 if (!NT_SUCCESS(Status
)) {
2201 ERR("write_data returned %08x\n", Status
);
2203 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
2204 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
2206 free_write_data_stripes(&wtc
);
2210 if (wtc
.stripes
.Flink
!= &wtc
.stripes
) {
2211 // launch writes and wait
2212 LIST_ENTRY
* le
= wtc
.stripes
.Flink
;
2213 BOOL no_wait
= TRUE
;
2215 while (le
!= &wtc
.stripes
) {
2216 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2218 if (stripe
->status
!= WriteDataStatus_Ignore
) {
2219 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2227 KeWaitForSingleObject(&wtc
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2229 le
= wtc
.stripes
.Flink
;
2230 while (le
!= &wtc
.stripes
) {
2231 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2233 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
2234 Status
= stripe
->iosb
.Status
;
2236 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
2243 free_write_data_stripes(&wtc
);
2246 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
2247 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
2252 _Function_class_(IO_COMPLETION_ROUTINE
)
2254 static NTSTATUS NTAPI
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2256 static NTSTATUS
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2258 write_data_stripe
* stripe
= conptr
;
2259 write_data_context
* context
= (write_data_context
*)stripe
->context
;
2262 UNUSED(DeviceObject
);
2264 // FIXME - we need a lock here
2266 if (stripe
->status
== WriteDataStatus_Cancelling
) {
2267 stripe
->status
= WriteDataStatus_Cancelled
;
2271 stripe
->iosb
= Irp
->IoStatus
;
2273 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
2274 stripe
->status
= WriteDataStatus_Success
;
2276 le
= context
->stripes
.Flink
;
2278 stripe
->status
= WriteDataStatus_Error
;
2280 while (le
!= &context
->stripes
) {
2281 write_data_stripe
* s2
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2283 if (s2
->status
== WriteDataStatus_Pending
) {
2284 s2
->status
= WriteDataStatus_Cancelling
;
2285 IoCancelIrp(s2
->Irp
);
2293 if (InterlockedDecrement(&context
->stripes_left
) == 0)
2294 KeSetEvent(&context
->Event
, 0, FALSE
);
2296 return STATUS_MORE_PROCESSING_REQUIRED
;
2299 void free_write_data_stripes(write_data_context
* wtc
) {
2301 PMDL last_mdl
= NULL
;
2303 if (wtc
->parity1_mdl
) {
2304 if (wtc
->parity1_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2305 MmUnlockPages(wtc
->parity1_mdl
);
2307 IoFreeMdl(wtc
->parity1_mdl
);
2310 if (wtc
->parity2_mdl
) {
2311 if (wtc
->parity2_mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2312 MmUnlockPages(wtc
->parity2_mdl
);
2314 IoFreeMdl(wtc
->parity2_mdl
);
2318 if (wtc
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2319 MmUnlockPages(wtc
->mdl
);
2321 IoFreeMdl(wtc
->mdl
);
2325 ExFreePool(wtc
->parity1
);
2328 ExFreePool(wtc
->parity2
);
2331 ExFreePool(wtc
->scratch
);
2333 le
= wtc
->stripes
.Flink
;
2334 while (le
!= &wtc
->stripes
) {
2335 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2337 if (stripe
->mdl
&& stripe
->mdl
!= last_mdl
) {
2338 if (stripe
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2339 MmUnlockPages(stripe
->mdl
);
2341 IoFreeMdl(stripe
->mdl
);
2344 last_mdl
= stripe
->mdl
;
2347 IoFreeIrp(stripe
->Irp
);
2352 while (!IsListEmpty(&wtc
->stripes
)) {
2353 write_data_stripe
* stripe
= CONTAINING_RECORD(RemoveHeadList(&wtc
->stripes
), write_data_stripe
, list_entry
);
2359 void add_extent(_In_ fcb
* fcb
, _In_ LIST_ENTRY
* prevextle
, _In_ __drv_aliasesMem extent
* newext
) {
2360 LIST_ENTRY
* le
= prevextle
->Flink
;
2362 while (le
!= &fcb
->extents
) {
2363 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2365 if (ext
->offset
>= newext
->offset
) {
2366 InsertHeadList(ext
->list_entry
.Blink
, &newext
->list_entry
);
2373 InsertTailList(&fcb
->extents
, &newext
->list_entry
);
2376 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2380 le
= fcb
->extents
.Flink
;
2382 while (le
!= &fcb
->extents
) {
2383 LIST_ENTRY
* le2
= le
->Flink
;
2384 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2385 EXTENT_DATA
* ed
= &ext
->extent_data
;
2386 EXTENT_DATA2
* ed2
= NULL
;
2390 if (ed
->type
!= EXTENT_TYPE_INLINE
)
2391 ed2
= (EXTENT_DATA2
*)ed
->data
;
2393 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
2395 if (ext
->offset
< end_data
&& ext
->offset
+ len
> start_data
) {
2396 if (ed
->type
== EXTENT_TYPE_INLINE
) {
2397 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
2398 remove_fcb_extent(fcb
, ext
, rollback
);
2400 fcb
->inode_item
.st_blocks
-= len
;
2401 fcb
->inode_item_changed
= TRUE
;
2403 ERR("trying to split inline extent\n");
2404 #ifdef DEBUG_PARANOID
2407 return STATUS_INTERNAL_ERROR
;
2409 } else if (ed
->type
!= EXTENT_TYPE_INLINE
) {
2410 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
2411 if (ed2
->size
!= 0) {
2414 fcb
->inode_item
.st_blocks
-= len
;
2415 fcb
->inode_item_changed
= TRUE
;
2417 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2420 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2422 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
2423 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2424 if (!NT_SUCCESS(Status
)) {
2425 ERR("update_changed_extent_ref returned %08x\n", Status
);
2431 remove_fcb_extent(fcb
, ext
, rollback
);
2432 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
2436 if (ed2
->size
!= 0) {
2437 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
2438 fcb
->inode_item_changed
= TRUE
;
2441 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2443 ERR("out of memory\n");
2444 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2448 ned2
= (EXTENT_DATA2
*)newext
->extent_data
.data
;
2450 newext
->extent_data
.generation
= Vcb
->superblock
.generation
;
2451 newext
->extent_data
.decoded_size
= ed
->decoded_size
;
2452 newext
->extent_data
.compression
= ed
->compression
;
2453 newext
->extent_data
.encryption
= ed
->encryption
;
2454 newext
->extent_data
.encoding
= ed
->encoding
;
2455 newext
->extent_data
.type
= ed
->type
;
2456 ned2
->address
= ed2
->address
;
2457 ned2
->size
= ed2
->size
;
2458 ned2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2459 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- ext
->offset
);
2461 newext
->offset
= end_data
;
2462 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2463 newext
->unique
= ext
->unique
;
2464 newext
->ignore
= FALSE
;
2465 newext
->inserted
= TRUE
;
2468 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2469 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2470 if (!newext
->csum
) {
2471 ERR("out of memory\n");
2472 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2477 RtlCopyMemory(newext
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2478 (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2480 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2481 if (!newext
->csum
) {
2482 ERR("out of memory\n");
2483 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2488 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2491 newext
->csum
= NULL
;
2493 add_extent(fcb
, &ext
->list_entry
, newext
);
2495 remove_fcb_extent(fcb
, ext
, rollback
);
2496 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
2500 if (ed2
->size
!= 0) {
2501 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
2502 fcb
->inode_item_changed
= TRUE
;
2505 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2507 ERR("out of memory\n");
2508 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2512 ned2
= (EXTENT_DATA2
*)newext
->extent_data
.data
;
2514 newext
->extent_data
.generation
= Vcb
->superblock
.generation
;
2515 newext
->extent_data
.decoded_size
= ed
->decoded_size
;
2516 newext
->extent_data
.compression
= ed
->compression
;
2517 newext
->extent_data
.encryption
= ed
->encryption
;
2518 newext
->extent_data
.encoding
= ed
->encoding
;
2519 newext
->extent_data
.type
= ed
->type
;
2520 ned2
->address
= ed2
->address
;
2521 ned2
->size
= ed2
->size
;
2522 ned2
->offset
= ed2
->offset
;
2523 ned2
->num_bytes
= start_data
- ext
->offset
;
2525 newext
->offset
= ext
->offset
;
2526 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2527 newext
->unique
= ext
->unique
;
2528 newext
->ignore
= FALSE
;
2529 newext
->inserted
= TRUE
;
2532 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2533 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2534 if (!newext
->csum
) {
2535 ERR("out of memory\n");
2536 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2541 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2543 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2544 if (!newext
->csum
) {
2545 ERR("out of memory\n");
2546 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2551 RtlCopyMemory(newext
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2554 newext
->csum
= NULL
;
2556 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2558 remove_fcb_extent(fcb
, ext
, rollback
);
2559 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
2560 EXTENT_DATA2
*neda2
, *nedb2
;
2561 extent
*newext1
, *newext2
;
2563 if (ed2
->size
!= 0) {
2566 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
2567 fcb
->inode_item_changed
= TRUE
;
2569 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2572 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2574 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
2575 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2576 if (!NT_SUCCESS(Status
)) {
2577 ERR("update_changed_extent_ref returned %08x\n", Status
);
2583 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2585 ERR("out of memory\n");
2586 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2590 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2592 ERR("out of memory\n");
2593 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2594 ExFreePool(newext1
);
2598 neda2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
2600 newext1
->extent_data
.generation
= Vcb
->superblock
.generation
;
2601 newext1
->extent_data
.decoded_size
= ed
->decoded_size
;
2602 newext1
->extent_data
.compression
= ed
->compression
;
2603 newext1
->extent_data
.encryption
= ed
->encryption
;
2604 newext1
->extent_data
.encoding
= ed
->encoding
;
2605 newext1
->extent_data
.type
= ed
->type
;
2606 neda2
->address
= ed2
->address
;
2607 neda2
->size
= ed2
->size
;
2608 neda2
->offset
= ed2
->offset
;
2609 neda2
->num_bytes
= start_data
- ext
->offset
;
2611 nedb2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
2613 newext2
->extent_data
.generation
= Vcb
->superblock
.generation
;
2614 newext2
->extent_data
.decoded_size
= ed
->decoded_size
;
2615 newext2
->extent_data
.compression
= ed
->compression
;
2616 newext2
->extent_data
.encryption
= ed
->encryption
;
2617 newext2
->extent_data
.encoding
= ed
->encoding
;
2618 newext2
->extent_data
.type
= ed
->type
;
2619 nedb2
->address
= ed2
->address
;
2620 nedb2
->size
= ed2
->size
;
2621 nedb2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2622 nedb2
->num_bytes
= ext
->offset
+ len
- end_data
;
2624 newext1
->offset
= ext
->offset
;
2625 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2626 newext1
->unique
= ext
->unique
;
2627 newext1
->ignore
= FALSE
;
2628 newext1
->inserted
= TRUE
;
2630 newext2
->offset
= end_data
;
2631 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2632 newext2
->unique
= ext
->unique
;
2633 newext2
->ignore
= FALSE
;
2634 newext2
->inserted
= TRUE
;
2637 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2638 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2639 if (!newext1
->csum
) {
2640 ERR("out of memory\n");
2641 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2642 ExFreePool(newext1
);
2643 ExFreePool(newext2
);
2647 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2648 if (!newext2
->csum
) {
2649 ERR("out of memory\n");
2650 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2651 ExFreePool(newext1
->csum
);
2652 ExFreePool(newext1
);
2653 ExFreePool(newext2
);
2657 RtlCopyMemory(newext1
->csum
, ext
->csum
, (ULONG
)(neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2658 RtlCopyMemory(newext2
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2659 (ULONG
)(nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2661 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2662 if (!newext1
->csum
) {
2663 ERR("out of memory\n");
2664 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2665 ExFreePool(newext1
);
2666 ExFreePool(newext2
);
2670 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
), ALLOC_TAG
);
2671 if (!newext2
->csum
) {
2672 ERR("out of memory\n");
2673 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2674 ExFreePool(newext1
->csum
);
2675 ExFreePool(newext1
);
2676 ExFreePool(newext2
);
2680 RtlCopyMemory(newext1
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2681 RtlCopyMemory(newext2
->csum
, ext
->csum
, (ULONG
)(ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
));
2684 newext1
->csum
= NULL
;
2685 newext2
->csum
= NULL
;
2688 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
2689 add_extent(fcb
, &newext1
->list_entry
, newext2
);
2691 remove_fcb_extent(fcb
, ext
, rollback
);
2700 Status
= STATUS_SUCCESS
;
2703 fcb
->extents_changed
= TRUE
;
2704 mark_fcb_dirty(fcb
);
2709 void add_insert_extent_rollback(LIST_ENTRY
* rollback
, fcb
* fcb
, extent
* ext
) {
2710 rollback_extent
* re
;
2712 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2714 ERR("out of memory\n");
2721 add_rollback(rollback
, ROLLBACK_INSERT_EXTENT
, re
);
2725 #pragma warning(push)
2726 #pragma warning(suppress: 28194)
2728 NTSTATUS
add_extent_to_fcb(_In_ fcb
* fcb
, _In_ UINT64 offset
, _In_reads_bytes_(edsize
) EXTENT_DATA
* ed
, _In_ UINT16 edsize
,
2729 _In_ BOOL unique
, _In_opt_
_When_(return >= 0, __drv_aliasesMem
) UINT32
* csum
, _In_ LIST_ENTRY
* rollback
) {
2733 ext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + edsize
, ALLOC_TAG
);
2735 ERR("out of memory\n");
2736 return STATUS_INSUFFICIENT_RESOURCES
;
2739 ext
->offset
= offset
;
2740 ext
->datalen
= edsize
;
2741 ext
->unique
= unique
;
2742 ext
->ignore
= FALSE
;
2743 ext
->inserted
= TRUE
;
2746 RtlCopyMemory(&ext
->extent_data
, ed
, edsize
);
2748 le
= fcb
->extents
.Flink
;
2749 while (le
!= &fcb
->extents
) {
2750 extent
* oldext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2752 if (oldext
->offset
>= offset
) {
2753 InsertHeadList(le
->Blink
, &ext
->list_entry
);
2760 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
2763 add_insert_extent_rollback(rollback
, fcb
, ext
);
2765 return STATUS_SUCCESS
;
2768 #pragma warning(pop)
2771 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
) {
2773 rollback_extent
* re
;
2777 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2779 ERR("out of memory\n");
2786 add_rollback(rollback
, ROLLBACK_DELETE_EXTENT
, re
);
2790 NTSTATUS
calc_csum(_In_ device_extension
* Vcb
, _In_reads_bytes_(sectors
*Vcb
->superblock
.sector_size
) UINT8
* data
,
2791 _In_ UINT32 sectors
, _Out_writes_bytes_(sectors
*sizeof(UINT32
)) UINT32
* csum
) {
2795 // From experimenting, it seems that 40 sectors is roughly the crossover
2796 // point where offloading the crc32 calculation becomes worth it.
2798 if (sectors
< 40 || KeQueryActiveProcessorCount(NULL
) < 2) {
2801 for (j
= 0; j
< sectors
; j
++) {
2802 csum
[j
] = ~calc_crc32c(0xffffffff, data
+ (j
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
2805 return STATUS_SUCCESS
;
2808 Status
= add_calc_job(Vcb
, data
, sectors
, csum
, &cj
);
2809 if (!NT_SUCCESS(Status
)) {
2810 ERR("add_calc_job returned %08x\n", Status
);
2814 KeWaitForSingleObject(&cj
->event
, Executive
, KernelMode
, FALSE
, NULL
);
2817 return STATUS_SUCCESS
;
2820 _Requires_lock_held_(c
->lock
)
2821 _When_(return != 0, _Releases_lock_(c
->lock
))
2822 BOOL
insert_extent_chunk(_In_ device_extension
* Vcb
, _In_ fcb
* fcb
, _In_ chunk
* c
, _In_ UINT64 start_data
, _In_ UINT64 length
, _In_ BOOL prealloc
, _In_opt_
void* data
,
2823 _In_opt_ PIRP Irp
, _In_ LIST_ENTRY
* rollback
, _In_ UINT8 compression
, _In_ UINT64 decoded_size
, _In_ BOOL file_write
, _In_ UINT64 irp_offset
) {
2828 UINT16 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + sizeof(EXTENT_DATA2
));
2829 UINT32
* csum
= NULL
;
2831 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, c
->offset
, start_data
, length
, prealloc
, data
, rollback
);
2833 if (!find_data_address_in_chunk(Vcb
, c
, length
, &address
))
2836 // add extent data to inode
2837 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
2839 ERR("out of memory\n");
2843 ed
->generation
= Vcb
->superblock
.generation
;
2844 ed
->decoded_size
= decoded_size
;
2845 ed
->compression
= compression
;
2846 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
2847 ed
->encoding
= BTRFS_ENCODING_NONE
;
2848 ed
->type
= prealloc
? EXTENT_TYPE_PREALLOC
: EXTENT_TYPE_REGULAR
;
2850 ed2
= (EXTENT_DATA2
*)ed
->data
;
2851 ed2
->address
= address
;
2854 ed2
->num_bytes
= decoded_size
;
2856 if (!prealloc
&& data
&& !(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
2857 ULONG sl
= (ULONG
)(length
/ Vcb
->superblock
.sector_size
);
2859 csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
2861 ERR("out of memory\n");
2866 Status
= calc_csum(Vcb
, data
, sl
, csum
);
2867 if (!NT_SUCCESS(Status
)) {
2868 ERR("calc_csum returned %08x\n", Status
);
2875 Status
= add_extent_to_fcb(fcb
, start_data
, ed
, edsize
, TRUE
, csum
, rollback
);
2876 if (!NT_SUCCESS(Status
)) {
2877 ERR("add_extent_to_fcb returned %08x\n", Status
);
2878 if (csum
) ExFreePool(csum
);
2886 space_list_subtract(c
, FALSE
, address
, length
, rollback
);
2888 fcb
->inode_item
.st_blocks
+= decoded_size
;
2890 fcb
->extents_changed
= TRUE
;
2891 fcb
->inode_item_changed
= TRUE
;
2892 mark_fcb_dirty(fcb
);
2894 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
2896 add_changed_extent_ref(c
, address
, length
, fcb
->subvol
->id
, fcb
->inode
, start_data
, 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
2898 ExReleaseResourceLite(&c
->changed_extents_lock
);
2900 release_chunk_lock(c
, Vcb
);
2903 Status
= write_data_complete(Vcb
, address
, data
, (UINT32
)length
, Irp
, NULL
, file_write
, irp_offset
,
2904 fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
);
2905 if (!NT_SUCCESS(Status
))
2906 ERR("write_data_complete returned %08x\n", Status
);
2912 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
2913 PIRP Irp
, UINT64
* written
, BOOL file_write
, UINT64 irp_offset
, LIST_ENTRY
* rollback
) {
2914 BOOL success
= FALSE
;
2921 le
= fcb
->extents
.Flink
;
2923 while (le
!= &fcb
->extents
) {
2924 extent
* nextext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2926 if (!nextext
->ignore
) {
2927 if (nextext
->offset
== start_data
) {
2930 } else if (nextext
->offset
> start_data
)
2942 ed
= &ext
->extent_data
;
2944 if (ed
->type
!= EXTENT_TYPE_REGULAR
&& ed
->type
!= EXTENT_TYPE_PREALLOC
) {
2945 TRACE("not extending extent which is not regular or prealloc\n");
2949 ed2
= (EXTENT_DATA2
*)ed
->data
;
2951 if (ext
->offset
+ ed2
->num_bytes
!= start_data
) {
2952 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext
->offset
, ed2
->num_bytes
, start_data
);
2956 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2958 if (c
->reloc
|| c
->readonly
|| c
->chunk_item
->type
!= Vcb
->data_flags
)
2961 acquire_chunk_lock(c
, Vcb
);
2963 if (length
> c
->chunk_item
->size
- c
->used
) {
2964 release_chunk_lock(c
, Vcb
);
2968 if (!c
->cache_loaded
) {
2969 NTSTATUS Status
= load_cache_chunk(Vcb
, c
, NULL
);
2971 if (!NT_SUCCESS(Status
)) {
2972 ERR("load_cache_chunk returned %08x\n", Status
);
2973 release_chunk_lock(c
, Vcb
);
2978 le
= c
->space
.Flink
;
2979 while (le
!= &c
->space
) {
2980 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
2982 if (s
->address
== ed2
->address
+ ed2
->size
) {
2983 UINT64 newlen
= min(min(s
->size
, length
), MAX_EXTENT_SIZE
);
2985 success
= insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
);
2990 release_chunk_lock(c
, Vcb
);
2993 } else if (s
->address
> ed2
->address
+ ed2
->size
)
2999 release_chunk_lock(c
, Vcb
);
3004 static NTSTATUS
insert_chunk_fragmented(fcb
* fcb
, UINT64 start
, UINT64 length
, UINT8
* data
, BOOL prealloc
, LIST_ENTRY
* rollback
) {
3006 UINT64 flags
= fcb
->Vcb
->data_flags
;
3007 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
3011 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3013 // first create as many chunks as we can
3015 Status
= alloc_chunk(fcb
->Vcb
, flags
, &c
, FALSE
);
3016 } while (NT_SUCCESS(Status
));
3018 if (Status
!= STATUS_DISK_FULL
) {
3019 ERR("alloc_chunk returned %08x\n", Status
);
3020 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3024 le
= fcb
->Vcb
->chunks
.Flink
;
3025 while (le
!= &fcb
->Vcb
->chunks
) {
3026 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3028 if (!c
->readonly
&& !c
->reloc
) {
3029 acquire_chunk_lock(c
, fcb
->Vcb
);
3031 if (c
->chunk_item
->type
== flags
) {
3032 while (!IsListEmpty(&c
->space_size
) && length
> 0) {
3033 space
* s
= CONTAINING_RECORD(c
->space_size
.Flink
, space
, list_entry_size
);
3034 UINT64 extlen
= min(length
, s
->size
);
3036 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, prealloc
&& !page_file
, data
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0)) {
3039 if (data
) data
+= extlen
;
3041 acquire_chunk_lock(c
, fcb
->Vcb
);
3046 release_chunk_lock(c
, fcb
->Vcb
);
3055 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3057 return length
== 0 ? STATUS_SUCCESS
: STATUS_DISK_FULL
;
3060 static NTSTATUS
insert_prealloc_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
3065 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
3067 flags
= fcb
->Vcb
->data_flags
;
3070 UINT64 extlen
= min(MAX_EXTENT_SIZE
, length
);
3072 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3074 le
= fcb
->Vcb
->chunks
.Flink
;
3075 while (le
!= &fcb
->Vcb
->chunks
) {
3076 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3078 if (!c
->readonly
&& !c
->reloc
) {
3079 acquire_chunk_lock(c
, fcb
->Vcb
);
3081 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
3082 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0)) {
3083 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3088 release_chunk_lock(c
, fcb
->Vcb
);
3094 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3096 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
3098 Status
= alloc_chunk(fcb
->Vcb
, flags
, &c
, FALSE
);
3100 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
3102 if (!NT_SUCCESS(Status
)) {
3103 ERR("alloc_chunk returned %08x\n", Status
);
3107 acquire_chunk_lock(c
, fcb
->Vcb
);
3109 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
3110 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
, FALSE
, 0))
3114 release_chunk_lock(c
, fcb
->Vcb
);
3116 Status
= insert_chunk_fragmented(fcb
, start
, length
, NULL
, TRUE
, rollback
);
3117 if (!NT_SUCCESS(Status
))
3118 ERR("insert_chunk_fragmented returned %08x\n", Status
);
3125 } while (length
> 0);
3127 Status
= STATUS_SUCCESS
;
3133 static NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
3134 PIRP Irp
, BOOL file_write
, UINT64 irp_offset
, LIST_ENTRY
* rollback
) {
3138 UINT64 flags
, orig_length
= length
, written
= 0;
3140 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
);
3142 if (start_data
> 0) {
3143 try_extend_data(Vcb
, fcb
, start_data
, length
, data
, Irp
, &written
, file_write
, irp_offset
, rollback
);
3145 if (written
== length
)
3146 return STATUS_SUCCESS
;
3147 else if (written
> 0) {
3148 start_data
+= written
;
3149 irp_offset
+= written
;
3151 data
= &((UINT8
*)data
)[written
];
3155 flags
= Vcb
->data_flags
;
3157 while (written
< orig_length
) {
3158 UINT64 newlen
= min(length
, MAX_EXTENT_SIZE
);
3161 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
3162 // First, see if we can write the extent part to an existing chunk.
3164 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
3166 le
= Vcb
->chunks
.Flink
;
3167 while (le
!= &Vcb
->chunks
) {
3168 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3170 if (!c
->readonly
&& !c
->reloc
) {
3171 acquire_chunk_lock(c
, Vcb
);
3173 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
3174 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
)) {
3177 if (written
== orig_length
) {
3178 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3179 return STATUS_SUCCESS
;
3182 start_data
+= newlen
;
3183 irp_offset
+= newlen
;
3185 data
= &((UINT8
*)data
)[newlen
];
3189 release_chunk_lock(c
, Vcb
);
3195 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3199 // Otherwise, see if we can put it in a new chunk.
3201 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
3203 Status
= alloc_chunk(Vcb
, flags
, &c
, FALSE
);
3205 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3207 if (!NT_SUCCESS(Status
)) {
3208 ERR("alloc_chunk returned %08x\n", Status
);
3213 acquire_chunk_lock(c
, Vcb
);
3215 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
3216 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
, file_write
, irp_offset
)) {
3219 if (written
== orig_length
)
3220 return STATUS_SUCCESS
;
3223 start_data
+= newlen
;
3224 irp_offset
+= newlen
;
3226 data
= &((UINT8
*)data
)[newlen
];
3229 release_chunk_lock(c
, Vcb
);
3233 Status
= insert_chunk_fragmented(fcb
, start_data
, length
, data
, FALSE
, rollback
);
3234 if (!NT_SUCCESS(Status
))
3235 ERR("insert_chunk_fragmented returned %08x\n", Status
);
3241 return STATUS_DISK_FULL
;
3244 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3247 // FIXME - convert into inline extent if short enough
3249 if (end
> 0 && fcb_is_inline(fcb
)) {
3251 BOOL make_inline
= end
<= fcb
->Vcb
->options
.max_inline
;
3253 buf
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(make_inline
? (offsetof(EXTENT_DATA
, data
[0]) + end
) : sector_align(end
, fcb
->Vcb
->superblock
.sector_size
)), ALLOC_TAG
);
3255 ERR("out of memory\n");
3256 return STATUS_INSUFFICIENT_RESOURCES
;
3259 Status
= read_file(fcb
, make_inline
? (buf
+ offsetof(EXTENT_DATA
, data
[0])) : buf
, 0, end
, NULL
, Irp
);
3260 if (!NT_SUCCESS(Status
)) {
3261 ERR("read_file returned %08x\n", Status
);
3266 Status
= excise_extents(fcb
->Vcb
, fcb
, 0, fcb
->inode_item
.st_size
, Irp
, rollback
);
3267 if (!NT_SUCCESS(Status
)) {
3268 ERR("excise_extents returned %08x\n", Status
);
3274 RtlZeroMemory(buf
+ end
, (ULONG
)(sector_align(end
, fcb
->Vcb
->superblock
.sector_size
) - end
));
3276 Status
= do_write_file(fcb
, 0, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
), buf
, Irp
, FALSE
, 0, rollback
);
3277 if (!NT_SUCCESS(Status
)) {
3278 ERR("do_write_file returned %08x\n", Status
);
3283 EXTENT_DATA
* ed
= (EXTENT_DATA
*)buf
;
3285 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3286 ed
->decoded_size
= end
;
3287 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3288 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3289 ed
->encoding
= BTRFS_ENCODING_NONE
;
3290 ed
->type
= EXTENT_TYPE_INLINE
;
3292 Status
= add_extent_to_fcb(fcb
, 0, ed
, (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
), FALSE
, NULL
, rollback
);
3293 if (!NT_SUCCESS(Status
)) {
3294 ERR("add_extent_to_fcb returned %08x\n", Status
);
3299 fcb
->inode_item
.st_blocks
+= end
;
3303 return STATUS_SUCCESS
;
3306 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
3307 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
3308 if (!NT_SUCCESS(Status
)) {
3309 ERR("excise_extents returned %08x\n", Status
);
3313 fcb
->inode_item
.st_size
= end
;
3314 fcb
->inode_item_changed
= TRUE
;
3315 TRACE("setting st_size to %llx\n", end
);
3317 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
3318 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
3319 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
3320 // FIXME - inform cache manager of this
3322 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
3324 return STATUS_SUCCESS
;
3327 NTSTATUS
extend_file(fcb
* fcb
, file_ref
* fileref
, UINT64 end
, BOOL prealloc
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3328 UINT64 oldalloc
, newalloc
;
3332 TRACE("(%p, %p, %x, %u)\n", fcb
, fileref
, end
, prealloc
);
3336 return STATUS_DISK_FULL
;
3338 return stream_set_end_of_file_information(fcb
->Vcb
, (UINT16
)end
, fcb
, fileref
, FALSE
);
3343 le
= fcb
->extents
.Blink
;
3344 while (le
!= &fcb
->extents
) {
3345 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
3347 if (!ext2
->ignore
) {
3357 EXTENT_DATA
* ed
= &ext
->extent_data
;
3358 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3360 oldalloc
= ext
->offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
3361 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
3363 if (cur_inline
&& end
> fcb
->Vcb
->options
.max_inline
) {
3364 UINT64 origlength
, length
;
3367 TRACE("giving inline file proper extents\n");
3369 origlength
= ed
->decoded_size
;
3373 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
3375 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)length
, ALLOC_TAG
);
3377 ERR("could not allocate %llx bytes for data\n", length
);
3378 return STATUS_INSUFFICIENT_RESOURCES
;
3381 Status
= read_file(fcb
, data
, 0, origlength
, NULL
, Irp
);
3382 if (!NT_SUCCESS(Status
)) {
3383 ERR("read_file returned %08x\n", Status
);
3388 RtlZeroMemory(data
+ origlength
, (ULONG
)(length
- origlength
));
3390 Status
= excise_extents(fcb
->Vcb
, fcb
, 0, fcb
->inode_item
.st_size
, Irp
, rollback
);
3391 if (!NT_SUCCESS(Status
)) {
3392 ERR("excise_extents returned %08x\n", Status
);
3397 Status
= do_write_file(fcb
, 0, length
, data
, Irp
, FALSE
, 0, rollback
);
3398 if (!NT_SUCCESS(Status
)) {
3399 ERR("do_write_file returned %08x\n", Status
);
3404 oldalloc
= ext
->offset
+ length
;
3412 if (end
> oldalloc
) {
3413 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
- ext
->offset
);
3414 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3417 ERR("out of memory\n");
3418 return STATUS_INSUFFICIENT_RESOURCES
;
3421 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3422 ed
->decoded_size
= end
- ext
->offset
;
3423 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3424 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3425 ed
->encoding
= BTRFS_ENCODING_NONE
;
3426 ed
->type
= EXTENT_TYPE_INLINE
;
3428 Status
= read_file(fcb
, ed
->data
, ext
->offset
, oldalloc
, NULL
, Irp
);
3429 if (!NT_SUCCESS(Status
)) {
3430 ERR("read_file returned %08x\n", Status
);
3435 RtlZeroMemory(ed
->data
+ oldalloc
- ext
->offset
, (ULONG
)(end
- oldalloc
));
3437 remove_fcb_extent(fcb
, ext
, rollback
);
3439 Status
= add_extent_to_fcb(fcb
, ext
->offset
, ed
, edsize
, ext
->unique
, NULL
, rollback
);
3440 if (!NT_SUCCESS(Status
)) {
3441 ERR("add_extent_to_fcb returned %08x\n", Status
);
3448 fcb
->extents_changed
= TRUE
;
3449 mark_fcb_dirty(fcb
);
3452 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
3454 fcb
->inode_item
.st_size
= end
;
3455 TRACE("setting st_size to %llx\n", end
);
3457 fcb
->inode_item
.st_blocks
= end
;
3459 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3461 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3463 if (newalloc
> oldalloc
) {
3465 // FIXME - try and extend previous extent first
3467 Status
= insert_prealloc_extent(fcb
, oldalloc
, newalloc
- oldalloc
, rollback
);
3469 if (!NT_SUCCESS(Status
)) {
3470 ERR("insert_prealloc_extent returned %08x\n", Status
);
3475 fcb
->extents_changed
= TRUE
;
3478 fcb
->inode_item
.st_size
= end
;
3479 fcb
->inode_item_changed
= TRUE
;
3480 mark_fcb_dirty(fcb
);
3482 TRACE("setting st_size to %llx\n", end
);
3484 TRACE("newalloc = %llx\n", newalloc
);
3486 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3487 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3490 if (end
> fcb
->Vcb
->options
.max_inline
) {
3491 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3494 Status
= insert_prealloc_extent(fcb
, 0, newalloc
, rollback
);
3496 if (!NT_SUCCESS(Status
)) {
3497 ERR("insert_prealloc_extent returned %08x\n", Status
);
3502 fcb
->extents_changed
= TRUE
;
3503 fcb
->inode_item_changed
= TRUE
;
3504 mark_fcb_dirty(fcb
);
3506 fcb
->inode_item
.st_size
= end
;
3507 TRACE("setting st_size to %llx\n", end
);
3509 TRACE("newalloc = %llx\n", newalloc
);
3511 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3512 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3517 edsize
= (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + end
);
3518 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3521 ERR("out of memory\n");
3522 return STATUS_INSUFFICIENT_RESOURCES
;
3525 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3526 ed
->decoded_size
= end
;
3527 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3528 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3529 ed
->encoding
= BTRFS_ENCODING_NONE
;
3530 ed
->type
= EXTENT_TYPE_INLINE
;
3532 RtlZeroMemory(ed
->data
, (ULONG
)end
);
3534 Status
= add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
);
3535 if (!NT_SUCCESS(Status
)) {
3536 ERR("add_extent_to_fcb returned %08x\n", Status
);
3543 fcb
->extents_changed
= TRUE
;
3544 fcb
->inode_item_changed
= TRUE
;
3545 mark_fcb_dirty(fcb
);
3547 fcb
->inode_item
.st_size
= end
;
3548 TRACE("setting st_size to %llx\n", end
);
3550 fcb
->inode_item
.st_blocks
= end
;
3552 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3557 return STATUS_SUCCESS
;
3560 static NTSTATUS
do_write_file_prealloc(fcb
* fcb
, extent
* ext
, UINT64 start_data
, UINT64 end_data
, void* data
, UINT64
* written
,
3561 PIRP Irp
, BOOL file_write
, UINT64 irp_offset
, ULONG priority
, LIST_ENTRY
* rollback
) {
3562 EXTENT_DATA
* ed
= &ext
->extent_data
;
3563 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3567 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace all
3570 newext
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3572 ERR("out of memory\n");
3573 return STATUS_INSUFFICIENT_RESOURCES
;
3576 RtlCopyMemory(&newext
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3578 newext
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3580 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, (UINT32
)ed2
->num_bytes
, Irp
,
3581 NULL
, file_write
, irp_offset
+ ext
->offset
- start_data
, priority
);
3582 if (!NT_SUCCESS(Status
)) {
3583 ERR("write_data_complete returned %08x\n", Status
);
3587 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3588 ULONG sl
= (ULONG
)(ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
);
3589 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3592 ERR("out of memory\n");
3594 return STATUS_INSUFFICIENT_RESOURCES
;
3597 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3598 if (!NT_SUCCESS(Status
)) {
3599 ERR("calc_csum returned %08x\n", Status
);
3605 newext
->csum
= csum
;
3607 newext
->csum
= NULL
;
3609 *written
= ed2
->num_bytes
;
3611 newext
->offset
= ext
->offset
;
3612 newext
->datalen
= ext
->datalen
;
3613 newext
->unique
= ext
->unique
;
3614 newext
->ignore
= FALSE
;
3615 newext
->inserted
= TRUE
;
3616 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
3618 add_insert_extent_rollback(rollback
, fcb
, newext
);
3620 remove_fcb_extent(fcb
, ext
, rollback
);
3622 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3623 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace beginning
3625 extent
*newext1
, *newext2
;
3627 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3629 ERR("out of memory\n");
3630 return STATUS_INSUFFICIENT_RESOURCES
;
3633 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3635 ERR("out of memory\n");
3636 ExFreePool(newext1
);
3637 return STATUS_INSUFFICIENT_RESOURCES
;
3640 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3641 newext1
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3642 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3643 ned2
->num_bytes
= end_data
- ext
->offset
;
3645 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3646 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3647 ned2
->offset
+= end_data
- ext
->offset
;
3648 ned2
->num_bytes
-= end_data
- ext
->offset
;
3650 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, (UINT32
)(end_data
- ext
->offset
),
3651 Irp
, NULL
, file_write
, irp_offset
+ ext
->offset
- start_data
, priority
);
3652 if (!NT_SUCCESS(Status
)) {
3653 ERR("write_data_complete returned %08x\n", Status
);
3654 ExFreePool(newext1
);
3655 ExFreePool(newext2
);
3659 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3660 ULONG sl
= (ULONG
)((end_data
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
);
3661 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3664 ERR("out of memory\n");
3665 ExFreePool(newext1
);
3666 ExFreePool(newext2
);
3667 return STATUS_INSUFFICIENT_RESOURCES
;
3670 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3671 if (!NT_SUCCESS(Status
)) {
3672 ERR("calc_csum returned %08x\n", Status
);
3673 ExFreePool(newext1
);
3674 ExFreePool(newext2
);
3679 newext1
->csum
= csum
;
3681 newext1
->csum
= NULL
;
3683 *written
= end_data
- ext
->offset
;
3685 newext1
->offset
= ext
->offset
;
3686 newext1
->datalen
= ext
->datalen
;
3687 newext1
->unique
= ext
->unique
;
3688 newext1
->ignore
= FALSE
;
3689 newext1
->inserted
= TRUE
;
3690 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3692 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3694 newext2
->offset
= end_data
;
3695 newext2
->datalen
= ext
->datalen
;
3696 newext2
->unique
= ext
->unique
;
3697 newext2
->ignore
= FALSE
;
3698 newext2
->inserted
= TRUE
;
3699 newext2
->csum
= NULL
;
3700 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3702 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3704 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3707 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3709 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3710 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3712 if (!NT_SUCCESS(Status
)) {
3713 ERR("update_changed_extent_ref returned %08x\n", Status
);
3718 remove_fcb_extent(fcb
, ext
, rollback
);
3719 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace end
3721 extent
*newext1
, *newext2
;
3723 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3725 ERR("out of memory\n");
3726 return STATUS_INSUFFICIENT_RESOURCES
;
3729 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3731 ERR("out of memory\n");
3732 ExFreePool(newext1
);
3733 return STATUS_INSUFFICIENT_RESOURCES
;
3736 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3738 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3739 ned2
->num_bytes
= start_data
- ext
->offset
;
3741 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3743 newext2
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3744 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3745 ned2
->offset
+= start_data
- ext
->offset
;
3746 ned2
->num_bytes
= ext
->offset
+ ed2
->num_bytes
- start_data
;
3748 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, (UINT32
)ned2
->num_bytes
, Irp
, NULL
, file_write
, irp_offset
, priority
);
3749 if (!NT_SUCCESS(Status
)) {
3750 ERR("write_data_complete returned %08x\n", Status
);
3751 ExFreePool(newext1
);
3752 ExFreePool(newext2
);
3756 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3757 ULONG sl
= (ULONG
)(ned2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
);
3758 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3761 ERR("out of memory\n");
3762 ExFreePool(newext1
);
3763 ExFreePool(newext2
);
3764 return STATUS_INSUFFICIENT_RESOURCES
;
3767 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3768 if (!NT_SUCCESS(Status
)) {
3769 ERR("calc_csum returned %08x\n", Status
);
3770 ExFreePool(newext1
);
3771 ExFreePool(newext2
);
3776 newext2
->csum
= csum
;
3778 newext2
->csum
= NULL
;
3780 *written
= ned2
->num_bytes
;
3782 newext1
->offset
= ext
->offset
;
3783 newext1
->datalen
= ext
->datalen
;
3784 newext1
->unique
= ext
->unique
;
3785 newext1
->ignore
= FALSE
;
3786 newext1
->inserted
= TRUE
;
3787 newext1
->csum
= NULL
;
3788 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3790 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3792 newext2
->offset
= start_data
;
3793 newext2
->datalen
= ext
->datalen
;
3794 newext2
->unique
= ext
->unique
;
3795 newext2
->ignore
= FALSE
;
3796 newext2
->inserted
= TRUE
;
3797 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3799 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3801 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3804 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3806 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3807 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3809 if (!NT_SUCCESS(Status
)) {
3810 ERR("update_changed_extent_ref returned %08x\n", Status
);
3815 remove_fcb_extent(fcb
, ext
, rollback
);
3816 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace middle
3818 extent
*newext1
, *newext2
, *newext3
;
3820 newext1
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3822 ERR("out of memory\n");
3823 return STATUS_INSUFFICIENT_RESOURCES
;
3826 newext2
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3828 ERR("out of memory\n");
3829 ExFreePool(newext1
);
3830 return STATUS_INSUFFICIENT_RESOURCES
;
3833 newext3
= ExAllocatePoolWithTag(PagedPool
, offsetof(extent
, extent_data
) + ext
->datalen
, ALLOC_TAG
);
3835 ERR("out of memory\n");
3836 ExFreePool(newext1
);
3837 ExFreePool(newext2
);
3838 return STATUS_INSUFFICIENT_RESOURCES
;
3841 RtlCopyMemory(&newext1
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3842 RtlCopyMemory(&newext2
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3843 RtlCopyMemory(&newext3
->extent_data
, &ext
->extent_data
, ext
->datalen
);
3845 ned2
= (EXTENT_DATA2
*)newext1
->extent_data
.data
;
3846 ned2
->num_bytes
= start_data
- ext
->offset
;
3848 newext2
->extent_data
.type
= EXTENT_TYPE_REGULAR
;
3849 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3850 ned2
->offset
+= start_data
- ext
->offset
;
3851 ned2
->num_bytes
= end_data
- start_data
;
3853 ned2
= (EXTENT_DATA2
*)newext3
->extent_data
.data
;
3854 ned2
->offset
+= end_data
- ext
->offset
;
3855 ned2
->num_bytes
-= end_data
- ext
->offset
;
3857 ned2
= (EXTENT_DATA2
*)newext2
->extent_data
.data
;
3858 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, (UINT32
)(end_data
- start_data
), Irp
, NULL
, file_write
, irp_offset
, priority
);
3859 if (!NT_SUCCESS(Status
)) {
3860 ERR("write_data_complete returned %08x\n", Status
);
3861 ExFreePool(newext1
);
3862 ExFreePool(newext2
);
3863 ExFreePool(newext3
);
3867 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3868 ULONG sl
= (ULONG
)((end_data
- start_data
) / fcb
->Vcb
->superblock
.sector_size
);
3869 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3872 ERR("out of memory\n");
3873 ExFreePool(newext1
);
3874 ExFreePool(newext2
);
3875 ExFreePool(newext3
);
3876 return STATUS_INSUFFICIENT_RESOURCES
;
3879 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3880 if (!NT_SUCCESS(Status
)) {
3881 ERR("calc_csum returned %08x\n", Status
);
3882 ExFreePool(newext1
);
3883 ExFreePool(newext2
);
3884 ExFreePool(newext3
);
3889 newext2
->csum
= csum
;
3891 newext2
->csum
= NULL
;
3893 *written
= end_data
- start_data
;
3895 newext1
->offset
= ext
->offset
;
3896 newext1
->datalen
= ext
->datalen
;
3897 newext1
->unique
= ext
->unique
;
3898 newext1
->ignore
= FALSE
;
3899 newext1
->inserted
= TRUE
;
3900 newext1
->csum
= NULL
;
3901 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3903 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3905 newext2
->offset
= start_data
;
3906 newext2
->datalen
= ext
->datalen
;
3907 newext2
->unique
= ext
->unique
;
3908 newext2
->ignore
= FALSE
;
3909 newext2
->inserted
= TRUE
;
3910 add_extent(fcb
, &newext1
->list_entry
, newext2
);
3912 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3914 newext3
->offset
= end_data
;
3915 newext3
->datalen
= ext
->datalen
;
3916 newext3
->unique
= ext
->unique
;
3917 newext3
->ignore
= FALSE
;
3918 newext3
->inserted
= TRUE
;
3919 newext3
->csum
= NULL
;
3920 add_extent(fcb
, &newext2
->list_entry
, newext3
);
3922 add_insert_extent_rollback(rollback
, fcb
, newext3
);
3924 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3927 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3929 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 2,
3930 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3932 if (!NT_SUCCESS(Status
)) {
3933 ERR("update_changed_extent_ref returned %08x\n", Status
);
3938 remove_fcb_extent(fcb
, ext
, rollback
);
3944 return STATUS_SUCCESS
;
3947 NTSTATUS
do_write_file(fcb
* fcb
, UINT64 start
, UINT64 end_data
, void* data
, PIRP Irp
, BOOL file_write
, UINT32 irp_offset
, LIST_ENTRY
* rollback
) {
3949 LIST_ENTRY
*le
, *le2
;
3950 UINT64 written
= 0, length
= end_data
- start
;
3951 UINT64 last_cow_start
;
3952 ULONG priority
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
;
3953 #ifdef DEBUG_PARANOID
3959 le
= fcb
->extents
.Flink
;
3960 while (le
!= &fcb
->extents
) {
3961 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3966 EXTENT_DATA
* ed
= &ext
->extent_data
;
3967 EXTENT_DATA2
* ed2
= ed
->type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ed
->data
;
3970 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
3972 if (ext
->offset
+ len
<= start
)
3975 if (ext
->offset
> start
+ written
+ length
)
3978 if ((fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ext
->unique
&& ed
->compression
== BTRFS_COMPRESSION_NONE
) {
3979 if (max(last_cow_start
, start
+ written
) < ext
->offset
) {
3980 UINT64 start_write
= max(last_cow_start
, start
+ written
);
3982 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, ext
->offset
, Irp
, rollback
);
3983 if (!NT_SUCCESS(Status
)) {
3984 ERR("excise_extents returned %08x\n", Status
);
3988 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, ext
->offset
- start_write
, (UINT8
*)data
+ written
, Irp
, file_write
, irp_offset
+ written
, rollback
);
3989 if (!NT_SUCCESS(Status
)) {
3990 ERR("insert_extent returned %08x\n", Status
);
3994 written
+= ext
->offset
- start_write
;
3995 length
-= ext
->offset
- start_write
;
4001 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
4002 UINT64 writeaddr
= ed2
->address
+ ed2
->offset
+ start
+ written
- ext
->offset
;
4003 UINT64 write_len
= min(len
, length
);
4006 TRACE("doing non-COW write to %llx\n", writeaddr
);
4008 Status
= write_data_complete(fcb
->Vcb
, writeaddr
, (UINT8
*)data
+ written
, (UINT32
)write_len
, Irp
, NULL
, file_write
, irp_offset
+ written
, priority
);
4009 if (!NT_SUCCESS(Status
)) {
4010 ERR("write_data_complete returned %08x\n", Status
);
4014 c
= get_chunk_from_address(fcb
->Vcb
, writeaddr
);
4018 // This shouldn't ever get called - nocow files should always also be nosum.
4019 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
4020 calc_csum(fcb
->Vcb
, (UINT8
*)data
+ written
, (UINT32
)(write_len
/ fcb
->Vcb
->superblock
.sector_size
),
4021 &ext
->csum
[(start
+ written
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
]);
4023 ext
->inserted
= TRUE
;
4026 written
+= write_len
;
4027 length
-= write_len
;
4031 } else if (ed
->type
== EXTENT_TYPE_PREALLOC
) {
4034 Status
= do_write_file_prealloc(fcb
, ext
, start
+ written
, end_data
, (UINT8
*)data
+ written
, &write_len
,
4035 Irp
, file_write
, irp_offset
+ written
, priority
, rollback
);
4036 if (!NT_SUCCESS(Status
)) {
4037 ERR("do_write_file_prealloc returned %08x\n", Status
);
4041 written
+= write_len
;
4042 length
-= write_len
;
4048 last_cow_start
= ext
->offset
+ len
;
4057 UINT64 start_write
= max(last_cow_start
, start
+ written
);
4059 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, end_data
, Irp
, rollback
);
4060 if (!NT_SUCCESS(Status
)) {
4061 ERR("excise_extents returned %08x\n", Status
);
4065 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, end_data
- start_write
, (UINT8
*)data
+ written
, Irp
, file_write
, irp_offset
+ written
, rollback
);
4066 if (!NT_SUCCESS(Status
)) {
4067 ERR("insert_extent returned %08x\n", Status
);
4072 #ifdef DEBUG_PARANOID
4073 last_off
= 0xffffffffffffffff;
4075 le
= fcb
->extents
.Flink
;
4076 while (le
!= &fcb
->extents
) {
4077 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4080 if (ext
->offset
== last_off
) {
4081 ERR("offset %llx duplicated\n", ext
->offset
);
4083 } else if (ext
->offset
< last_off
&& last_off
!= 0xffffffffffffffff) {
4084 ERR("offsets out of order\n");
4088 last_off
= ext
->offset
;
4095 fcb
->extents_changed
= TRUE
;
4096 mark_fcb_dirty(fcb
);
4098 return STATUS_SUCCESS
;
4101 NTSTATUS
write_compressed(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4105 for (i
= 0; i
< sector_align(end_data
- start_data
, COMPRESSED_EXTENT_SIZE
) / COMPRESSED_EXTENT_SIZE
; i
++) {
4109 s2
= start_data
+ (i
* COMPRESSED_EXTENT_SIZE
);
4110 e2
= min(s2
+ COMPRESSED_EXTENT_SIZE
, end_data
);
4112 Status
= write_compressed_bit(fcb
, s2
, e2
, (UINT8
*)data
+ (i
* COMPRESSED_EXTENT_SIZE
), &compressed
, Irp
, rollback
);
4114 if (!NT_SUCCESS(Status
)) {
4115 ERR("write_compressed_bit returned %08x\n", Status
);
4119 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
4120 // bother with the rest of it.
4121 if (s2
== 0 && e2
== COMPRESSED_EXTENT_SIZE
&& !compressed
&& !fcb
->Vcb
->options
.compress_force
) {
4122 fcb
->inode_item
.flags
|= BTRFS_INODE_NOCOMPRESS
;
4123 fcb
->inode_item_changed
= TRUE
;
4124 mark_fcb_dirty(fcb
);
4126 // write subsequent data non-compressed
4127 if (e2
< end_data
) {
4128 Status
= do_write_file(fcb
, e2
, end_data
, (UINT8
*)data
+ e2
, Irp
, FALSE
, 0, rollback
);
4130 if (!NT_SUCCESS(Status
)) {
4131 ERR("do_write_file returned %08x\n", Status
);
4136 return STATUS_SUCCESS
;
4140 return STATUS_SUCCESS
;
4143 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOLEAN paging_io
, BOOLEAN no_cache
,
4144 BOOLEAN wait
, BOOLEAN deferred_write
, BOOLEAN write_irp
, LIST_ENTRY
* rollback
) {
4145 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4146 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4148 UINT64 off64
, newlength
, start_data
, end_data
;
4153 BOOL changed_length
= FALSE
;
4160 BOOL paging_lock
= FALSE
, fcb_lock
= FALSE
, tree_lock
= FALSE
, pagefile
;
4163 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
4166 WARN("returning success for zero-length write\n");
4167 return STATUS_SUCCESS
;
4171 ERR("error - FileObject was NULL\n");
4172 return STATUS_ACCESS_DENIED
;
4175 fcb
= FileObject
->FsContext
;
4176 ccb
= FileObject
->FsContext2
;
4177 fileref
= ccb
? ccb
->fileref
: NULL
;
4179 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
4180 WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb
->inode
, fcb
->type
, &fcb
->type
, fcb
);
4181 return STATUS_INVALID_DEVICE_REQUEST
;
4184 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1)
4185 offset
= fcb
->Header
.FileSize
;
4187 off64
= offset
.QuadPart
;
4189 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
4191 if (!no_cache
&& !CcCanIWrite(FileObject
, *length
, wait
, deferred_write
))
4192 return STATUS_PENDING
;
4194 if (!wait
&& no_cache
)
4195 return STATUS_PENDING
;
4197 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
4198 IO_STATUS_BLOCK iosb
;
4200 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
4202 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
4204 if (!NT_SUCCESS(iosb
.Status
)) {
4205 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
4206 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
4212 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
4216 if (!ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, wait
)) {
4217 Status
= STATUS_PENDING
;
4223 pagefile
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
&& paging_io
;
4225 if (!pagefile
&& !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
4226 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
4227 Status
= STATUS_PENDING
;
4235 if (!ExAcquireResourceSharedLite(fcb
->Header
.Resource
, wait
)) {
4236 Status
= STATUS_PENDING
;
4240 } else if (!ExIsResourceAcquiredExclusiveLite(fcb
->Header
.Resource
)) {
4241 if (!ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, wait
)) {
4242 Status
= STATUS_PENDING
;
4249 newlength
= fcb
->ads
? fcb
->adsdata
.Length
: fcb
->inode_item
.st_size
;
4254 TRACE("newlength = %llx\n", newlength
);
4256 if (off64
+ *length
> newlength
) {
4258 if (off64
>= newlength
) {
4259 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, off64
, *length
);
4260 TRACE("filename %S\n", file_desc(FileObject
));
4261 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
4262 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
4263 Status
= STATUS_SUCCESS
;
4267 *length
= (ULONG
)(newlength
- off64
);
4269 newlength
= off64
+ *length
;
4270 changed_length
= TRUE
;
4272 TRACE("extending length to %llx\n", newlength
);
4277 make_inline
= FALSE
;
4278 else if (fcb
->type
== BTRFS_TYPE_SYMLINK
)
4279 make_inline
= newlength
<= (Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
) - offsetof(EXTENT_DATA
, data
[0]));
4281 make_inline
= newlength
<= fcb
->Vcb
->options
.max_inline
;
4283 if (changed_length
) {
4284 if (newlength
> (UINT64
)fcb
->Header
.AllocationSize
.QuadPart
) {
4286 // We need to acquire the tree lock if we don't have it already -
4287 // we can't give an inline file proper extents at the same time as we're
4289 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
4290 Status
= STATUS_PENDING
;
4296 Status
= extend_file(fcb
, fileref
, newlength
, FALSE
, Irp
, rollback
);
4297 if (!NT_SUCCESS(Status
)) {
4298 ERR("extend_file returned %08x\n", Status
);
4301 } else if (!fcb
->ads
)
4302 fcb
->inode_item
.st_size
= newlength
;
4304 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4305 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4307 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
4308 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
4309 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
4313 Status
= STATUS_SUCCESS
;
4316 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
4319 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4320 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4321 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4323 if (!FileObject
->PrivateCacheMap
)
4324 init_file_cache(FileObject
, &ccfs
);
4326 CcSetFileSizes(FileObject
, &ccfs
);
4329 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
4330 CcPrepareMdlWrite(FileObject
, &offset
, *length
, &Irp
->MdlAddress
, &Irp
->IoStatus
);
4332 Status
= Irp
->IoStatus
.Status
;
4335 if (fCcCopyWriteEx
) {
4336 TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject
, off64
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
);
4337 if (!fCcCopyWriteEx(FileObject
, &offset
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
)) {
4338 Status
= STATUS_PENDING
;
4341 TRACE("CcCopyWriteEx finished\n");
4343 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, off64
, *length
, wait
, buf
);
4344 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
4345 Status
= STATUS_PENDING
;
4348 TRACE("CcCopyWrite finished\n");
4351 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4352 Status
= _SEH2_GetExceptionCode();
4355 if (changed_length
) {
4356 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, fcb
->ads
? FILE_NOTIFY_CHANGE_STREAM_SIZE
: FILE_NOTIFY_CHANGE_SIZE
,
4357 fcb
->ads
? FILE_ACTION_MODIFIED_STREAM
: FILE_ACTION_MODIFIED
, fcb
->ads
&& fileref
->dc
? &fileref
->dc
->name
: NULL
);
4364 if (changed_length
) {
4367 if (newlength
> fcb
->adsmaxlen
) {
4368 ERR("error - xattr too long (%llu > %u)\n", newlength
, fcb
->adsmaxlen
);
4369 Status
= STATUS_DISK_FULL
;
4373 data2
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)newlength
, ALLOC_TAG
);
4375 ERR("out of memory\n");
4376 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4380 if (fcb
->adsdata
.Buffer
) {
4381 RtlCopyMemory(data2
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
4382 ExFreePool(fcb
->adsdata
.Buffer
);
4385 if (newlength
> fcb
->adsdata
.Length
)
4386 RtlZeroMemory(&data2
[fcb
->adsdata
.Length
], (ULONG
)(newlength
- fcb
->adsdata
.Length
));
4389 fcb
->adsdata
.Buffer
= data2
;
4390 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= (USHORT
)newlength
;
4392 fcb
->Header
.AllocationSize
.QuadPart
= newlength
;
4393 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4394 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4398 RtlCopyMemory(&fcb
->adsdata
.Buffer
[off64
], buf
, *length
);
4400 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4402 mark_fcb_dirty(fcb
);
4405 mark_fileref_dirty(fileref
);
4407 BOOL compress
= write_fcb_compressed(fcb
), no_buf
= FALSE
;
4411 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
4412 bufhead
= sizeof(EXTENT_DATA
) - 1;
4413 } else if (compress
) {
4414 start_data
= off64
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
4415 end_data
= min(sector_align(off64
+ *length
, COMPRESSED_EXTENT_SIZE
),
4416 sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
));
4419 start_data
= off64
& ~(UINT64
)(fcb
->Vcb
->superblock
.sector_size
- 1);
4420 end_data
= sector_align(off64
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
4424 if (fcb_is_inline(fcb
))
4425 end_data
= max(end_data
, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
));
4427 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4428 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
4430 if (!make_inline
&& !compress
&& off64
== start_data
&& off64
+ *length
== end_data
) {
4434 data
= ExAllocatePoolWithTag(PagedPool
, (ULONG
)(end_data
- start_data
+ bufhead
), ALLOC_TAG
);
4436 ERR("out of memory\n");
4437 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4441 RtlZeroMemory(data
+ bufhead
, (ULONG
)(end_data
- start_data
));
4443 TRACE("start_data = %llx\n", start_data
);
4444 TRACE("end_data = %llx\n", end_data
);
4446 if (off64
> start_data
|| off64
+ *length
< end_data
) {
4447 if (changed_length
) {
4448 if (fcb
->inode_item
.st_size
> start_data
)
4449 Status
= read_file(fcb
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
, Irp
);
4451 Status
= STATUS_SUCCESS
;
4453 Status
= read_file(fcb
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
, Irp
);
4455 if (!NT_SUCCESS(Status
)) {
4456 ERR("read_file returned %08x\n", Status
);
4462 RtlCopyMemory(data
+ bufhead
+ off64
- start_data
, buf
, *length
);
4466 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, Irp
, rollback
);
4467 if (!NT_SUCCESS(Status
)) {
4468 ERR("error - excise_extents returned %08x\n", Status
);
4473 ed2
= (EXTENT_DATA
*)data
;
4474 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
4475 ed2
->decoded_size
= newlength
;
4476 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
4477 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
4478 ed2
->encoding
= BTRFS_ENCODING_NONE
;
4479 ed2
->type
= EXTENT_TYPE_INLINE
;
4481 Status
= add_extent_to_fcb(fcb
, 0, ed2
, (UINT16
)(offsetof(EXTENT_DATA
, data
[0]) + newlength
), FALSE
, NULL
, rollback
);
4482 if (!NT_SUCCESS(Status
)) {
4483 ERR("add_extent_to_fcb returned %08x\n", Status
);
4488 fcb
->inode_item
.st_blocks
+= newlength
;
4489 } else if (compress
) {
4490 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
4492 if (!NT_SUCCESS(Status
)) {
4493 ERR("write_compressed returned %08x\n", Status
);
4500 if (write_irp
&& Irp
->MdlAddress
&& no_buf
) {
4501 BOOL locked
= Irp
->MdlAddress
->MdlFlags
& MDL_PAGES_LOCKED
;
4504 Status
= STATUS_SUCCESS
;
4507 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoReadAccess
);
4508 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4509 Status
= _SEH2_GetExceptionCode();
4512 if (!NT_SUCCESS(Status
)) {
4513 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
4519 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, TRUE
, 0, rollback
);
4520 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4521 Status
= _SEH2_GetExceptionCode();
4525 MmUnlockPages(Irp
->MdlAddress
);
4528 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, FALSE
, 0, rollback
);
4529 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4530 Status
= _SEH2_GetExceptionCode();
4534 if (!NT_SUCCESS(Status
)) {
4535 ERR("do_write_file returned %08x\n", Status
);
4536 if (!no_buf
) ExFreePool(data
);
4545 KeQuerySystemTime(&time
);
4546 win_time_to_unix(time
, &now
);
4550 if (fileref
&& fileref
->parent
)
4551 origii
= &fileref
->parent
->fcb
->inode_item
;
4553 ERR("no parent fcb found for stream\n");
4554 Status
= STATUS_INTERNAL_ERROR
;
4558 origii
= &fcb
->inode_item
;
4560 origii
->transid
= Vcb
->superblock
.generation
;
4563 if (!ccb
->user_set_change_time
)
4564 origii
->st_ctime
= now
;
4567 if (changed_length
) {
4568 TRACE("setting st_size to %llx\n", newlength
);
4569 origii
->st_size
= newlength
;
4570 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
4573 fcb
->inode_item_changed
= TRUE
;
4575 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
4578 filter
|= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
4580 filter
|= FILE_NOTIFY_CHANGE_STREAM_WRITE
;
4583 if (!ccb
->user_set_write_time
) {
4584 origii
->st_mtime
= now
;
4585 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
4588 mark_fcb_dirty(fcb
->ads
? fileref
->parent
->fcb
: fcb
);
4591 if (changed_length
) {
4594 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4595 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4596 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4599 CcSetFileSizes(FileObject
, &ccfs
);
4600 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4601 Status
= _SEH2_GetExceptionCode();
4606 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
4607 fcb
->subvol
->root_item
.ctime
= now
;
4609 Status
= STATUS_SUCCESS
;
4612 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, filter
, fcb
->ads
? FILE_ACTION_MODIFIED_STREAM
: FILE_ACTION_MODIFIED
,
4613 fcb
->ads
&& fileref
->dc
? &fileref
->dc
->name
: NULL
);
4616 if (NT_SUCCESS(Status
) && FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
4617 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4618 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
4619 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4623 ExReleaseResourceLite(fcb
->Header
.Resource
);
4626 ExReleaseResourceLite(&Vcb
->tree_lock
);
4629 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
4634 NTSTATUS
write_file(device_extension
* Vcb
, PIRP Irp
, BOOLEAN wait
, BOOLEAN deferred_write
) {
4635 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4638 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
4639 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4640 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4641 LIST_ENTRY rollback
;
4643 InitializeListHead(&rollback
);
4647 Irp
->IoStatus
.Information
= 0;
4649 TRACE("offset = %llx\n", offset
.QuadPart
);
4650 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
4652 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
4653 buf
= map_user_buffer(Irp
, fcb
&& fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
? HighPagePriority
: NormalPagePriority
);
4655 if (Irp
->MdlAddress
&& !buf
) {
4656 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
4657 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4661 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
4663 TRACE("buf = %p\n", buf
);
4665 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
4666 WARN("tried to write to locked region\n");
4667 Status
= STATUS_FILE_LOCK_CONFLICT
;
4671 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
,
4672 wait
, deferred_write
, TRUE
, &rollback
);
4674 if (Status
== STATUS_PENDING
)
4676 else if (!NT_SUCCESS(Status
)) {
4677 ERR("write_file2 returned %08x\n", Status
);
4681 if (NT_SUCCESS(Status
)) {
4682 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
4684 if (diskacc
&& Status
!= STATUS_PENDING
&& Irp
->Flags
& IRP_NOCACHE
) {
4685 PETHREAD thread
= NULL
;
4687 if (Irp
->Tail
.Overlay
.Thread
&& !IoIsSystemThread(Irp
->Tail
.Overlay
.Thread
))
4688 thread
= Irp
->Tail
.Overlay
.Thread
;
4689 else if (!IoIsSystemThread(PsGetCurrentThread()))
4690 thread
= PsGetCurrentThread();
4691 else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp
)
4692 thread
= PsGetCurrentThread();
4695 fPsUpdateDiskCounters(PsGetThreadProcess(thread
), 0, IrpSp
->Parameters
.Write
.Length
, 0, 1, 0);
4700 if (NT_SUCCESS(Status
))
4701 clear_rollback(&rollback
);
4703 do_rollback(Vcb
, &rollback
);
4708 _Dispatch_type_(IRP_MJ_WRITE
)
4709 _Function_class_(DRIVER_DISPATCH
)
4710 NTSTATUS NTAPI
drv_write(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4713 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4714 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4715 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4716 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4717 ccb
* ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
4718 BOOLEAN wait
= FileObject
? IoIsOperationSynchronous(Irp
) : TRUE
;
4720 FsRtlEnterFileSystem();
4722 top_level
= is_top_level(Irp
);
4724 if (Vcb
&& Vcb
->type
== VCB_TYPE_VOLUME
) {
4725 Status
= vol_write(DeviceObject
, Irp
);
4727 } else if (!Vcb
|| Vcb
->type
!= VCB_TYPE_FS
) {
4728 Status
= STATUS_INVALID_PARAMETER
;
4733 ERR("fcb was NULL\n");
4734 Status
= STATUS_INVALID_PARAMETER
;
4739 ERR("ccb was NULL\n");
4740 Status
= STATUS_INVALID_PARAMETER
;
4744 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
4745 WARN("insufficient permissions\n");
4746 Status
= STATUS_ACCESS_DENIED
;
4750 if (fcb
== Vcb
->volume_fcb
) {
4751 if (!Vcb
->locked
|| Vcb
->locked_fileobj
!= FileObject
) {
4752 ERR("trying to write to volume when not locked, or locked with another FileObject\n");
4753 Status
= STATUS_ACCESS_DENIED
;
4757 TRACE("writing directly to volume\n");
4759 IoSkipCurrentIrpStackLocation(Irp
);
4761 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
4765 if (is_subvol_readonly(fcb
->subvol
, Irp
)) {
4766 Status
= STATUS_ACCESS_DENIED
;
4770 if (Vcb
->readonly
) {
4771 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4776 if (IrpSp
->MinorFunction
& IRP_MN_COMPLETE
) {
4777 CcMdlWriteComplete(IrpSp
->FileObject
, &IrpSp
->Parameters
.Write
.ByteOffset
, Irp
->MdlAddress
);
4779 Irp
->MdlAddress
= NULL
;
4780 Status
= STATUS_SUCCESS
;
4782 // Don't offload jobs when doing paging IO - otherwise this can lead to
4783 // deadlocks in CcCopyWrite.
4784 if (Irp
->Flags
& IRP_PAGING_IO
)
4787 Status
= write_file(Vcb
, Irp
, wait
, FALSE
);
4789 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4790 Status
= _SEH2_GetExceptionCode();
4794 Irp
->IoStatus
.Status
= Status
;
4796 TRACE("wrote %u bytes\n", Irp
->IoStatus
.Information
);
4798 if (Status
!= STATUS_PENDING
)
4799 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4801 IoMarkIrpPending(Irp
);
4803 if (!add_thread_job(Vcb
, Irp
))
4804 do_write_job(Vcb
, Irp
);
4809 IoSetTopLevelIrp(NULL
);
4811 TRACE("returning %08x\n", Status
);
4813 FsRtlExitFileSystem();