1 /* Copyright (c) Mark Harmstone 2016
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"
21 BOOL chunk_test
= FALSE
;
38 PDEVICE_OBJECT devobj
;
40 read_stripe_master
* master
;
43 // static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
44 static NTSTATUS STDCALL
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
);
45 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
);
47 extern tPsUpdateDiskCounters PsUpdateDiskCounters
;
48 extern tCcCopyWriteEx CcCopyWriteEx
;
51 BOOL
find_data_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64 length
, UINT64
* address
) {
55 TRACE("(%p, %llx, %llx, %p)\n", Vcb
, c
->offset
, length
, address
);
57 if (IsListEmpty(&c
->space_size
))
60 le
= c
->space_size
.Flink
;
61 while (le
!= &c
->space_size
) {
62 s
= CONTAINING_RECORD(le
, space
, list_entry_size
);
64 if (s
->size
== length
) {
65 *address
= s
->address
;
67 } else if (s
->size
< length
) {
68 if (le
== c
->space_size
.Flink
)
71 s
= CONTAINING_RECORD(le
->Blink
, space
, list_entry_size
);
73 *address
= s
->address
;
80 s
= CONTAINING_RECORD(c
->space_size
.Blink
, space
, list_entry_size
);
82 if (s
->size
> length
) {
83 *address
= s
->address
;
90 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
94 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
96 le2
= Vcb
->chunks
.Flink
;
97 while (le2
!= &Vcb
->chunks
) {
98 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
100 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
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
) {
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
) {
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
191 le
= Vcb
->devices
.Flink
;
192 while (le
!= &Vcb
->devices
) {
193 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
195 if (!dev
->readonly
&& !dev
->reloc
) {
196 if (!IsListEmpty(&dev
->space
)) {
198 space
*dh1
= NULL
, *dh2
= NULL
;
200 le2
= dev
->space
.Flink
;
201 while (le2
!= &dev
->space
) {
202 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
204 if (!dh1
|| !dh2
|| dh
->size
< dh1
->size
) {
216 devsize
= max(dh1
->size
/ 2, min(dh1
->size
, dh2
->size
));
218 devsize
= min(dh1
->size
, dh2
->size
);
220 if (devsize
> size
) {
224 if (dh2
&& min(dh1
->size
, dh2
->size
) > dh1
->size
/ 2)
242 stripes
[0].device
= stripes
[1].device
= dev2
;
243 stripes
[0].dh
= devdh1
;
244 stripes
[1].dh
= devdh2
;
249 static BOOL
find_new_stripe(device_extension
* Vcb
, stripe
* stripes
, UINT16 i
, UINT64 max_stripe_size
, UINT16 type
) {
250 UINT64 k
, devusage
= 0xffffffffffffffff;
255 le
= Vcb
->devices
.Flink
;
256 while (le
!= &Vcb
->devices
) {
257 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
261 if (dev
->readonly
|| dev
->reloc
) {
266 // skip this device if it already has a stripe
268 for (k
= 0; k
< i
; k
++) {
269 if (stripes
[k
].device
== dev
) {
277 usage
= (dev
->devitem
.bytes_used
* 4096) / dev
->devitem
.num_bytes
;
279 // favour devices which have been used the least
280 if (usage
< devusage
) {
281 if (!IsListEmpty(&dev
->space
)) {
284 le2
= dev
->space
.Flink
;
285 while (le2
!= &dev
->space
) {
286 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
288 if ((dev2
!= dev
&& dh
->size
>= max_stripe_size
) ||
289 (dev2
== dev
&& dh
->size
>= max_stripe_size
&& dh
->size
< devdh
->size
)
306 // Can't find hole of at least max_stripe_size; look for the largest one we can find
308 le
= Vcb
->devices
.Flink
;
309 while (le
!= &Vcb
->devices
) {
310 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
313 if (dev
->readonly
|| dev
->reloc
) {
318 // skip this device if it already has a stripe
320 for (k
= 0; k
< i
; k
++) {
321 if (stripes
[k
].device
== dev
) {
329 if (!IsListEmpty(&dev
->space
)) {
332 le2
= dev
->space
.Flink
;
333 while (le2
!= &dev
->space
) {
334 space
* dh
= CONTAINING_RECORD(le2
, space
, list_entry
);
336 if (!devdh
|| devdh
->size
< dh
->size
) {
353 stripes
[i
].dh
= devdh
;
354 stripes
[i
].device
= dev2
;
359 chunk
* alloc_chunk(device_extension
* Vcb
, UINT64 flags
) {
360 UINT64 max_stripe_size
, max_chunk_size
, stripe_size
, stripe_length
, factor
;
361 UINT64 total_size
= 0, i
, logaddr
;
362 UINT16 type
, num_stripes
, sub_stripes
, max_stripes
, min_stripes
;
363 stripe
* stripes
= NULL
;
365 CHUNK_ITEM_STRIPE
* cis
;
368 BOOL success
= FALSE
;
371 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
373 le
= Vcb
->devices
.Flink
;
374 while (le
!= &Vcb
->devices
) {
375 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
376 total_size
+= dev
->devitem
.num_bytes
;
381 TRACE("total_size = %llx\n", total_size
);
383 // We purposely check for DATA first - mixed blocks have the same size
385 if (flags
& BLOCK_FLAG_DATA
) {
386 max_stripe_size
= 0x40000000; // 1 GB
387 max_chunk_size
= 10 * max_stripe_size
;
388 } else if (flags
& BLOCK_FLAG_METADATA
) {
389 if (total_size
> 0xC80000000) // 50 GB
390 max_stripe_size
= 0x40000000; // 1 GB
392 max_stripe_size
= 0x10000000; // 256 MB
394 max_chunk_size
= max_stripe_size
;
395 } else if (flags
& BLOCK_FLAG_SYSTEM
) {
396 max_stripe_size
= 0x2000000; // 32 MB
397 max_chunk_size
= 2 * max_stripe_size
;
400 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
402 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
404 if (flags
& BLOCK_FLAG_DUPLICATE
) {
408 type
= BLOCK_FLAG_DUPLICATE
;
409 } else if (flags
& BLOCK_FLAG_RAID0
) {
411 max_stripes
= Vcb
->superblock
.num_devices
;
413 type
= BLOCK_FLAG_RAID0
;
414 } else if (flags
& BLOCK_FLAG_RAID1
) {
418 type
= BLOCK_FLAG_RAID1
;
419 } else if (flags
& BLOCK_FLAG_RAID10
) {
421 max_stripes
= Vcb
->superblock
.num_devices
;
423 type
= BLOCK_FLAG_RAID10
;
424 } else if (flags
& BLOCK_FLAG_RAID5
) {
426 max_stripes
= Vcb
->superblock
.num_devices
;
428 type
= BLOCK_FLAG_RAID5
;
429 } else if (flags
& BLOCK_FLAG_RAID6
) {
433 type
= BLOCK_FLAG_RAID6
;
441 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * max_stripes
, ALLOC_TAG
);
443 ERR("out of memory\n");
449 if (type
== BLOCK_FLAG_DUPLICATE
) {
450 if (!find_new_dup_stripes(Vcb
, stripes
, max_stripe_size
))
453 num_stripes
= max_stripes
;
455 for (i
= 0; i
< max_stripes
; i
++) {
456 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, type
))
463 // for RAID10, round down to an even number of stripes
464 if (type
== BLOCK_FLAG_RAID10
&& (num_stripes
% sub_stripes
) != 0) {
465 num_stripes
-= num_stripes
% sub_stripes
;
468 if (num_stripes
< min_stripes
) {
469 WARN("found %u stripes, needed at least %u\n", num_stripes
, min_stripes
);
473 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
475 ERR("out of memory\n");
479 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
480 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, cisize
, ALLOC_TAG
);
481 if (!c
->chunk_item
) {
482 ERR("out of memory\n");
486 stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
488 if (type
== BLOCK_FLAG_DUPLICATE
&& stripes
[1].dh
== stripes
[0].dh
)
489 stripe_size
= min(stripes
[0].dh
->size
/ 2, max_stripe_size
);
491 stripe_size
= max_stripe_size
;
492 for (i
= 0; i
< num_stripes
; i
++) {
493 if (stripes
[i
].dh
->size
< stripe_size
)
494 stripe_size
= stripes
[i
].dh
->size
;
498 if (type
== 0 || type
== BLOCK_FLAG_DUPLICATE
|| type
== BLOCK_FLAG_RAID1
)
500 else if (type
== BLOCK_FLAG_RAID0
)
501 factor
= num_stripes
;
502 else if (type
== BLOCK_FLAG_RAID10
)
503 factor
= num_stripes
/ sub_stripes
;
504 else if (type
== BLOCK_FLAG_RAID5
)
505 factor
= num_stripes
- 1;
506 else if (type
== BLOCK_FLAG_RAID6
)
507 factor
= num_stripes
- 2;
509 if (stripe_size
* factor
> max_chunk_size
)
510 stripe_size
= max_chunk_size
/ factor
;
512 if (stripe_size
% stripe_length
> 0)
513 stripe_size
-= stripe_size
% stripe_length
;
515 if (stripe_size
== 0)
518 c
->chunk_item
->size
= stripe_size
* factor
;
519 c
->chunk_item
->root_id
= Vcb
->extent_root
->id
;
520 c
->chunk_item
->stripe_length
= stripe_length
;
521 c
->chunk_item
->type
= flags
;
522 c
->chunk_item
->opt_io_alignment
= c
->chunk_item
->stripe_length
;
523 c
->chunk_item
->opt_io_width
= c
->chunk_item
->stripe_length
;
524 c
->chunk_item
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
525 c
->chunk_item
->num_stripes
= num_stripes
;
526 c
->chunk_item
->sub_stripes
= sub_stripes
;
528 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
530 ERR("out of memory\n");
534 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
535 for (i
= 0; i
< num_stripes
; i
++) {
536 cis
[i
].dev_id
= stripes
[i
].device
->devitem
.dev_id
;
538 if (type
== BLOCK_FLAG_DUPLICATE
&& i
== 1 && stripes
[i
].dh
== stripes
[0].dh
)
539 cis
[i
].offset
= stripes
[0].dh
->address
+ stripe_size
;
541 cis
[i
].offset
= stripes
[i
].dh
->address
;
543 cis
[i
].dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
545 c
->devices
[i
] = stripes
[i
].device
;
548 logaddr
= find_new_chunk_address(Vcb
, c
->chunk_item
->size
);
550 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
554 c
->used
= c
->oldused
= 0;
558 c
->last_alloc_set
= FALSE
;
560 InitializeListHead(&c
->space
);
561 InitializeListHead(&c
->space_size
);
562 InitializeListHead(&c
->deleting
);
563 InitializeListHead(&c
->changed_extents
);
565 InitializeListHead(&c
->range_locks
);
566 KeInitializeSpinLock(&c
->range_locks_spinlock
);
567 KeInitializeEvent(&c
->range_locks_event
, NotificationEvent
, FALSE
);
569 ExInitializeResourceLite(&c
->lock
);
570 ExInitializeResourceLite(&c
->changed_extents_lock
);
572 s
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(space
), ALLOC_TAG
);
574 ERR("out of memory\n");
578 s
->address
= c
->offset
;
579 s
->size
= c
->chunk_item
->size
;
580 InsertTailList(&c
->space
, &s
->list_entry
);
581 InsertTailList(&c
->space_size
, &s
->list_entry_size
);
583 protect_superblocks(Vcb
, c
);
585 for (i
= 0; i
< num_stripes
; i
++) {
586 stripes
[i
].device
->devitem
.bytes_used
+= stripe_size
;
588 space_list_subtract2(Vcb
, &stripes
[i
].device
->space
, NULL
, cis
[i
].offset
, stripe_size
, NULL
);
593 if (flags
& BLOCK_FLAG_RAID5
|| flags
& BLOCK_FLAG_RAID6
)
594 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_RAID56
;
601 if (c
&& c
->chunk_item
) ExFreePool(c
->chunk_item
);
602 if (c
) ExFreePool(c
);
603 if (s
) ExFreePool(s
);
608 le
= Vcb
->chunks
.Flink
;
609 while (le
!= &Vcb
->chunks
) {
610 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
612 if (c2
->offset
> c
->offset
) {
613 InsertHeadList(le
->Blink
, &c
->list_entry
);
622 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
625 InsertTailList(&Vcb
->chunks_changed
, &c
->list_entry_changed
);
626 c
->list_entry_balance
.Flink
= NULL
;
629 ExReleaseResourceLite(&Vcb
->chunk_lock
);
631 return success
? c
: NULL
;
634 static NTSTATUS
prepare_raid0_write(chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
) {
635 UINT64 startoff
, endoff
;
636 UINT16 startoffstripe
, endoffstripe
, stripenum
;
637 UINT64 pos
, *stripeoff
;
640 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
642 ERR("out of memory\n");
643 return STATUS_INSUFFICIENT_RESOURCES
;
646 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &startoff
, &startoffstripe
);
647 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &endoff
, &endoffstripe
);
649 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
650 if (startoffstripe
> i
) {
651 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
652 } else if (startoffstripe
== i
) {
653 stripes
[i
].start
= startoff
;
655 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
658 if (endoffstripe
> i
) {
659 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
660 } else if (endoffstripe
== i
) {
661 stripes
[i
].end
= endoff
+ 1;
663 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
666 if (stripes
[i
].start
!= stripes
[i
].end
) {
667 stripes
[i
].data
= ExAllocatePoolWithTag(NonPagedPool
, stripes
[i
].end
- stripes
[i
].start
, ALLOC_TAG
);
669 if (!stripes
[i
].data
) {
670 ERR("out of memory\n");
671 ExFreePool(stripeoff
);
672 return STATUS_INSUFFICIENT_RESOURCES
;
678 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
680 stripenum
= startoffstripe
;
681 while (pos
< length
) {
683 UINT32 writelen
= min(stripes
[stripenum
].end
- stripes
[stripenum
].start
,
684 c
->chunk_item
->stripe_length
- (stripes
[stripenum
].start
% c
->chunk_item
->stripe_length
));
686 RtlCopyMemory(stripes
[stripenum
].data
, data
, writelen
);
687 stripeoff
[stripenum
] += writelen
;
689 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
690 RtlCopyMemory(stripes
[stripenum
].data
+ stripeoff
[stripenum
], (UINT8
*)data
+ pos
, length
- pos
);
693 RtlCopyMemory(stripes
[stripenum
].data
+ stripeoff
[stripenum
], (UINT8
*)data
+ pos
, c
->chunk_item
->stripe_length
);
694 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
695 pos
+= c
->chunk_item
->stripe_length
;
698 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
701 ExFreePool(stripeoff
);
703 return STATUS_SUCCESS
;
706 static NTSTATUS
prepare_raid10_write(chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
) {
707 UINT64 startoff
, endoff
;
708 UINT16 startoffstripe
, endoffstripe
, stripenum
;
709 UINT64 pos
, *stripeoff
;
712 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, ALLOC_TAG
);
714 ERR("out of memory\n");
715 return STATUS_INSUFFICIENT_RESOURCES
;
718 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, &startoff
, &startoffstripe
);
719 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
);
721 startoffstripe
*= c
->chunk_item
->sub_stripes
;
722 endoffstripe
*= c
->chunk_item
->sub_stripes
;
724 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= c
->chunk_item
->sub_stripes
) {
727 if (startoffstripe
> i
) {
728 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
729 } else if (startoffstripe
== i
) {
730 stripes
[i
].start
= startoff
;
732 stripes
[i
].start
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
735 if (endoffstripe
> i
) {
736 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
737 } else if (endoffstripe
== i
) {
738 stripes
[i
].end
= endoff
+ 1;
740 stripes
[i
].end
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
743 if (stripes
[i
].start
!= stripes
[i
].end
) {
744 stripes
[i
].data
= ExAllocatePoolWithTag(NonPagedPool
, stripes
[i
].end
- stripes
[i
].start
, ALLOC_TAG
);
746 if (!stripes
[i
].data
) {
747 ERR("out of memory\n");
748 ExFreePool(stripeoff
);
749 return STATUS_INSUFFICIENT_RESOURCES
;
753 for (j
= 1; j
< c
->chunk_item
->sub_stripes
; j
++) {
754 stripes
[i
+j
].start
= stripes
[i
].start
;
755 stripes
[i
+j
].end
= stripes
[i
].end
;
756 stripes
[i
+j
].data
= stripes
[i
].data
;
761 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
763 stripenum
= startoffstripe
/ c
->chunk_item
->sub_stripes
;
764 while (pos
< length
) {
766 UINT32 writelen
= min(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].end
- stripes
[stripenum
* c
->chunk_item
->sub_stripes
].start
,
767 c
->chunk_item
->stripe_length
- (stripes
[stripenum
* c
->chunk_item
->sub_stripes
].start
% c
->chunk_item
->stripe_length
));
769 RtlCopyMemory(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].data
, data
, writelen
);
770 stripeoff
[stripenum
] += writelen
;
772 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
773 RtlCopyMemory(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].data
+ stripeoff
[stripenum
], (UINT8
*)data
+ pos
, length
- pos
);
776 RtlCopyMemory(stripes
[stripenum
* c
->chunk_item
->sub_stripes
].data
+ stripeoff
[stripenum
], (UINT8
*)data
+ pos
, c
->chunk_item
->stripe_length
);
777 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
778 pos
+= c
->chunk_item
->stripe_length
;
781 stripenum
= (stripenum
+ 1) % (c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
784 ExFreePool(stripeoff
);
786 return STATUS_SUCCESS
;
789 static NTSTATUS STDCALL
read_stripe_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID ptr
) {
790 read_stripe
* stripe
= ptr
;
791 read_stripe_master
* master
= stripe
->master
;
792 ULONG stripes_left
= InterlockedDecrement(&master
->stripes_left
);
794 stripe
->iosb
= Irp
->IoStatus
;
796 if (stripes_left
== 0)
797 KeSetEvent(&master
->event
, 0, FALSE
);
799 return STATUS_MORE_PROCESSING_REQUIRED
;
802 static NTSTATUS
make_read_irp(PIRP old_irp
, read_stripe
* stripe
, UINT64 offset
, void* data
, UINT32 length
) {
803 PIO_STACK_LOCATION IrpSp
;
807 Irp
= IoAllocateIrp(stripe
->devobj
->StackSize
, FALSE
);
810 ERR("IoAllocateIrp failed\n");
811 return STATUS_INSUFFICIENT_RESOURCES
;
814 Irp
= IoMakeAssociatedIrp(old_irp
, stripe
->devobj
->StackSize
);
817 ERR("IoMakeAssociatedIrp failed\n");
818 return STATUS_INSUFFICIENT_RESOURCES
;
822 IrpSp
= IoGetNextIrpStackLocation(Irp
);
823 IrpSp
->MajorFunction
= IRP_MJ_READ
;
825 if (stripe
->devobj
->Flags
& DO_BUFFERED_IO
) {
826 FIXME("FIXME - buffered IO\n");
828 return STATUS_INTERNAL_ERROR
;
829 } else if (stripe
->devobj
->Flags
& DO_DIRECT_IO
) {
830 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
831 if (!Irp
->MdlAddress
) {
832 ERR("IoAllocateMdl failed\n");
834 return STATUS_INSUFFICIENT_RESOURCES
;
837 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
839 Irp
->UserBuffer
= data
;
842 IrpSp
->Parameters
.Read
.Length
= length
;
843 IrpSp
->Parameters
.Read
.ByteOffset
.QuadPart
= offset
;
845 Irp
->UserIosb
= &stripe
->iosb
;
847 IoSetCompletionRoutine(Irp
, read_stripe_completion
, stripe
, TRUE
, TRUE
, TRUE
);
851 return STATUS_SUCCESS
;
854 static NTSTATUS
prepare_raid5_write(PIRP Irp
, chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
) {
855 UINT64 startoff
, endoff
;
856 UINT16 startoffstripe
, endoffstripe
, stripenum
, parity
, logstripe
;
857 UINT64 start
= 0xffffffffffffffff, end
= 0;
858 UINT64 pos
, stripepos
;
859 UINT32 firststripesize
, laststripesize
;
861 UINT8
* data2
= (UINT8
*)data
;
863 BOOL same_stripe
= FALSE
, multiple_stripes
;
865 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
- 1, &startoff
, &startoffstripe
);
866 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
- 1, &endoff
, &endoffstripe
);
868 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
869 UINT64 ststart
, stend
;
871 if (startoffstripe
> i
) {
872 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
873 } else if (startoffstripe
== i
) {
876 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
879 if (endoffstripe
> i
) {
880 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
881 } else if (endoffstripe
== i
) {
884 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
887 if (ststart
!= stend
) {
888 stripes
[i
].start
= ststart
;
889 stripes
[i
].end
= stend
;
891 if (ststart
< start
) {
893 firststripesize
= c
->chunk_item
->stripe_length
- (ststart
% c
->chunk_item
->stripe_length
);
898 laststripesize
= stend
% c
->chunk_item
->stripe_length
;
899 if (laststripesize
== 0)
900 laststripesize
= c
->chunk_item
->stripe_length
;
906 ERR("error: start == end (%llx)\n", start
);
907 return STATUS_INTERNAL_ERROR
;
910 if (startoffstripe
== endoffstripe
&& start
/ c
->chunk_item
->stripe_length
== end
/ c
->chunk_item
->stripe_length
) {
911 firststripesize
= end
- start
;
912 laststripesize
= firststripesize
;
915 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
916 stripes
[i
].data
= ExAllocatePoolWithTag(NonPagedPool
, end
- start
, ALLOC_TAG
);
917 if (!stripes
[i
].data
) {
918 ERR("out of memory\n");
919 return STATUS_INSUFFICIENT_RESOURCES
;
922 if (i
< c
->chunk_item
->num_stripes
- 1) {
923 if (stripes
[i
].start
== 0 && stripes
[i
].end
== 0)
924 stripes
[i
].start
= stripes
[i
].end
= start
;
929 multiple_stripes
= (end
- 1) / c
->chunk_item
->stripe_length
!= start
/ c
->chunk_item
->stripe_length
;
931 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
932 if (stripes
[i
].start
== stripes
[i
].end
) {
935 if (multiple_stripes
)
938 if (stripes
[i
].start
> start
)
941 if (stripes
[i
].end
< end
)
948 read_stripe_master
* master
;
949 read_stripe
* read_stripes
;
950 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
953 master
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_stripe_master
), ALLOC_TAG
);
955 ERR("out of memory\n");
956 return STATUS_INSUFFICIENT_RESOURCES
;
959 read_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_stripe
) * num_reads
, ALLOC_TAG
);
961 ERR("out of memory\n");
963 return STATUS_INSUFFICIENT_RESOURCES
;
966 parity
= (((address
- c
->offset
) / ((c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
967 stripenum
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
970 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
971 if (stripes
[i
].start
> start
|| stripes
[i
].start
== stripes
[i
].end
) {
974 read_stripes
[j
].Irp
= NULL
;
975 read_stripes
[j
].devobj
= c
->devices
[stripenum
]->devobj
;
976 read_stripes
[j
].master
= master
;
978 if (stripes
[i
].start
!= stripes
[i
].end
)
979 readlen
= stripes
[i
].start
- start
;
981 readlen
= firststripesize
;
983 Status
= make_read_irp(Irp
, &read_stripes
[j
], start
+ cis
[stripenum
].offset
, stripes
[stripenum
].data
, readlen
);
985 if (!NT_SUCCESS(Status
)) {
986 ERR("make_read_irp returned %08x\n", Status
);
991 stripes
[stripenum
].skip_start
= readlen
;
994 if (j
== num_reads
) break;
997 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1000 if (j
< num_reads
) {
1001 parity
= (((address
+ length
- 1 - c
->offset
) / ((c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
1002 stripenum
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1004 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
1005 if ((stripes
[i
].start
!= stripes
[i
].end
&& stripes
[i
].end
< end
) || (stripes
[i
].start
== stripes
[i
].end
&& multiple_stripes
)) {
1006 read_stripes
[j
].Irp
= NULL
;
1007 read_stripes
[j
].devobj
= c
->devices
[stripenum
]->devobj
;
1008 read_stripes
[j
].master
= master
;
1010 if (stripes
[i
].start
== stripes
[i
].end
) {
1011 Status
= make_read_irp(Irp
, &read_stripes
[j
], start
+ firststripesize
+ cis
[stripenum
].offset
, &stripes
[stripenum
].data
[firststripesize
], laststripesize
);
1012 stripes
[stripenum
].skip_end
= laststripesize
;
1014 Status
= make_read_irp(Irp
, &read_stripes
[j
], stripes
[i
].end
+ cis
[stripenum
].offset
, &stripes
[stripenum
].data
[stripes
[i
].end
- start
], end
- stripes
[i
].end
);
1015 stripes
[stripenum
].skip_end
= end
- stripes
[i
].end
;
1018 if (!NT_SUCCESS(Status
)) {
1019 ERR("make_read_irp returned %08x\n", Status
);
1025 if (j
== num_reads
) break;
1028 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1032 master
->stripes_left
= j
;
1033 KeInitializeEvent(&master
->event
, NotificationEvent
, FALSE
);
1035 for (i
= 0; i
< j
; i
++) {
1036 Status
= IoCallDriver(read_stripes
[i
].devobj
, read_stripes
[i
].Irp
);
1037 if (!NT_SUCCESS(Status
)) {
1038 ERR("IoCallDriver returned %08x\n", Status
);
1043 KeWaitForSingleObject(&master
->event
, Executive
, KernelMode
, FALSE
, NULL
);
1045 for (i
= 0; i
< j
; i
++) {
1046 if (!NT_SUCCESS(read_stripes
[i
].iosb
.Status
)) {
1047 Status
= read_stripes
[i
].iosb
.Status
;
1052 Status
= STATUS_SUCCESS
;
1055 for (i
= 0; i
< j
; i
++) {
1056 if (read_stripes
[i
].Irp
) {
1057 if (read_stripes
[i
].devobj
->Flags
& DO_DIRECT_IO
) {
1058 MmUnlockPages(read_stripes
[i
].Irp
->MdlAddress
);
1059 IoFreeMdl(read_stripes
[i
].Irp
->MdlAddress
);
1062 IoFreeIrp(read_stripes
[i
].Irp
); // FIXME - what if IoCallDriver fails and other Irps are still running?
1066 ExFreePool(read_stripes
);
1069 if (!NT_SUCCESS(Status
))
1075 parity
= (((address
- c
->offset
) / ((c
->chunk_item
->num_stripes
- 1) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
1078 if ((address
- c
->offset
) % (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 1)) > 0) {
1082 stripenum
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1084 for (logstripe
= 0; logstripe
< c
->chunk_item
->num_stripes
- 1; logstripe
++) {
1090 if (stripes
[logstripe
].start
< start
+ firststripesize
&& stripes
[logstripe
].start
!= stripes
[logstripe
].end
) {
1091 copylen
= min(start
+ firststripesize
- stripes
[logstripe
].start
, length
- pos
);
1093 if (!first
&& copylen
< c
->chunk_item
->stripe_length
) {
1098 RtlCopyMemory(&stripes
[stripenum
].data
[firststripesize
- copylen
], &data2
[pos
], copylen
);
1104 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1107 firstdata
= parity
== 0 ? 1 : 0;
1109 RtlCopyMemory(stripes
[parity
].data
, stripes
[firstdata
].data
, firststripesize
);
1111 for (i
= firstdata
+ 1; i
< c
->chunk_item
->num_stripes
; i
++) {
1113 do_xor(&stripes
[parity
].data
[0], &stripes
[i
].data
[0], firststripesize
);
1117 stripepos
= firststripesize
;
1118 parity
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1122 while (length
>= pos
+ c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 1)) {
1125 stripenum
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1127 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
1128 RtlCopyMemory(&stripes
[stripenum
].data
[stripepos
], &data2
[pos
], c
->chunk_item
->stripe_length
);
1130 pos
+= c
->chunk_item
->stripe_length
;
1131 stripenum
= (stripenum
+1) % c
->chunk_item
->num_stripes
;
1134 firstdata
= parity
== 0 ? 1 : 0;
1136 RtlCopyMemory(&stripes
[parity
].data
[stripepos
], &stripes
[firstdata
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1138 for (i
= firstdata
+ 1; i
< c
->chunk_item
->num_stripes
; i
++) {
1140 do_xor(&stripes
[parity
].data
[stripepos
], &stripes
[i
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1143 parity
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1144 stripepos
+= c
->chunk_item
->stripe_length
;
1151 stripenum
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
1156 while (pos
< length
) {
1159 copylen
= min(stripes
[i
].end
- start
- stripepos
, length
- pos
);
1161 RtlCopyMemory(&stripes
[stripenum
].data
[stripepos
], &data2
[pos
], copylen
);
1164 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1168 firstdata
= parity
== 0 ? 1 : 0;
1170 RtlCopyMemory(&stripes
[parity
].data
[stripepos
], &stripes
[firstdata
].data
[stripepos
], laststripesize
);
1172 for (i
= firstdata
+ 1; i
< c
->chunk_item
->num_stripes
; i
++) {
1174 do_xor(&stripes
[parity
].data
[stripepos
], &stripes
[i
].data
[stripepos
], laststripesize
);
1178 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1179 stripes
[i
].start
= start
;
1180 stripes
[i
].end
= end
;
1183 return STATUS_SUCCESS
;
1186 static NTSTATUS
prepare_raid6_write(PIRP Irp
, chunk
* c
, UINT64 address
, void* data
, UINT32 length
, write_stripe
* stripes
) {
1187 UINT64 startoff
, endoff
;
1188 UINT16 startoffstripe
, endoffstripe
, stripenum
, parity1
, parity2
, logstripe
;
1189 UINT64 start
= 0xffffffffffffffff, end
= 0;
1190 UINT64 pos
, stripepos
;
1191 UINT32 firststripesize
, laststripesize
;
1193 UINT8
* data2
= (UINT8
*)data
;
1195 BOOL same_stripe
= FALSE
, multiple_stripes
;
1197 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
- 2, &startoff
, &startoffstripe
);
1198 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
- 2, &endoff
, &endoffstripe
);
1200 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 2; i
++) {
1201 UINT64 ststart
, stend
;
1203 if (startoffstripe
> i
) {
1204 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1205 } else if (startoffstripe
== i
) {
1208 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1211 if (endoffstripe
> i
) {
1212 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1213 } else if (endoffstripe
== i
) {
1216 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
1219 if (ststart
!= stend
) {
1220 stripes
[i
].start
= ststart
;
1221 stripes
[i
].end
= stend
;
1223 if (ststart
< start
) {
1225 firststripesize
= c
->chunk_item
->stripe_length
- (ststart
% c
->chunk_item
->stripe_length
);
1230 laststripesize
= stend
% c
->chunk_item
->stripe_length
;
1231 if (laststripesize
== 0)
1232 laststripesize
= c
->chunk_item
->stripe_length
;
1238 ERR("error: start == end (%llx)\n", start
);
1239 return STATUS_INTERNAL_ERROR
;
1242 if (startoffstripe
== endoffstripe
&& start
/ c
->chunk_item
->stripe_length
== end
/ c
->chunk_item
->stripe_length
) {
1243 firststripesize
= end
- start
;
1244 laststripesize
= firststripesize
;
1247 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1248 stripes
[i
].data
= ExAllocatePoolWithTag(NonPagedPool
, end
- start
, ALLOC_TAG
);
1249 if (!stripes
[i
].data
) {
1250 ERR("out of memory\n");
1251 return STATUS_INSUFFICIENT_RESOURCES
;
1254 if (i
< c
->chunk_item
->num_stripes
- 2) {
1255 if (stripes
[i
].start
== 0 && stripes
[i
].end
== 0)
1256 stripes
[i
].start
= stripes
[i
].end
= start
;
1261 multiple_stripes
= (end
- 1) / c
->chunk_item
->stripe_length
!= start
/ c
->chunk_item
->stripe_length
;
1263 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 2; i
++) {
1264 if (stripes
[i
].start
== stripes
[i
].end
) {
1267 if (multiple_stripes
)
1270 if (stripes
[i
].start
> start
)
1273 if (stripes
[i
].end
< end
)
1278 if (num_reads
> 0) {
1280 read_stripe_master
* master
;
1281 read_stripe
* read_stripes
;
1282 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1285 master
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_stripe_master
), ALLOC_TAG
);
1287 ERR("out of memory\n");
1288 return STATUS_INSUFFICIENT_RESOURCES
;
1291 read_stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_stripe
) * num_reads
, ALLOC_TAG
);
1292 if (!read_stripes
) {
1293 ERR("out of memory\n");
1295 return STATUS_INSUFFICIENT_RESOURCES
;
1298 parity1
= (((address
- c
->offset
) / ((c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 2) % c
->chunk_item
->num_stripes
;
1299 stripenum
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
1302 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 2; i
++) {
1303 if (stripes
[i
].start
> start
|| stripes
[i
].start
== stripes
[i
].end
) {
1306 read_stripes
[j
].Irp
= NULL
;
1307 read_stripes
[j
].devobj
= c
->devices
[stripenum
]->devobj
;
1308 read_stripes
[j
].master
= master
;
1310 if (stripes
[i
].start
!= stripes
[i
].end
)
1311 readlen
= stripes
[i
].start
- start
;
1313 readlen
= firststripesize
;
1315 Status
= make_read_irp(Irp
, &read_stripes
[j
], start
+ cis
[stripenum
].offset
, stripes
[stripenum
].data
, readlen
);
1317 if (!NT_SUCCESS(Status
)) {
1318 ERR("make_read_irp returned %08x\n", Status
);
1323 stripes
[stripenum
].skip_start
= readlen
;
1326 if (j
== num_reads
) break;
1329 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1332 if (j
< num_reads
) {
1333 parity1
= (((address
+ length
- 1 - c
->offset
) / ((c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 2) % c
->chunk_item
->num_stripes
;
1334 stripenum
= (parity1
+ 2) % c
->chunk_item
->num_stripes
;
1336 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 2; i
++) {
1337 if ((stripes
[i
].start
!= stripes
[i
].end
&& stripes
[i
].end
< end
) || (stripes
[i
].start
== stripes
[i
].end
&& multiple_stripes
)) {
1338 read_stripes
[j
].Irp
= NULL
;
1339 read_stripes
[j
].devobj
= c
->devices
[stripenum
]->devobj
;
1340 read_stripes
[j
].master
= master
;
1342 if (stripes
[i
].start
== stripes
[i
].end
) {
1343 Status
= make_read_irp(Irp
, &read_stripes
[j
], start
+ firststripesize
+ cis
[stripenum
].offset
, &stripes
[stripenum
].data
[firststripesize
], laststripesize
);
1344 stripes
[stripenum
].skip_end
= laststripesize
;
1346 Status
= make_read_irp(Irp
, &read_stripes
[j
], stripes
[i
].end
+ cis
[stripenum
].offset
, &stripes
[stripenum
].data
[stripes
[i
].end
- start
], end
- stripes
[i
].end
);
1347 stripes
[stripenum
].skip_end
= end
- stripes
[i
].end
;
1350 if (!NT_SUCCESS(Status
)) {
1351 ERR("make_read_irp returned %08x\n", Status
);
1357 if (j
== num_reads
) break;
1360 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1364 master
->stripes_left
= j
;
1365 KeInitializeEvent(&master
->event
, NotificationEvent
, FALSE
);
1367 for (i
= 0; i
< j
; i
++) {
1368 Status
= IoCallDriver(read_stripes
[i
].devobj
, read_stripes
[i
].Irp
);
1369 if (!NT_SUCCESS(Status
)) {
1370 ERR("IoCallDriver returned %08x\n", Status
);
1375 KeWaitForSingleObject(&master
->event
, Executive
, KernelMode
, FALSE
, NULL
);
1377 for (i
= 0; i
< j
; i
++) {
1378 if (!NT_SUCCESS(read_stripes
[i
].iosb
.Status
)) {
1379 Status
= read_stripes
[i
].iosb
.Status
;
1384 Status
= STATUS_SUCCESS
;
1387 for (i
= 0; i
< j
; i
++) {
1388 if (read_stripes
[i
].Irp
) {
1389 if (read_stripes
[i
].devobj
->Flags
& DO_DIRECT_IO
) {
1390 MmUnlockPages(read_stripes
[i
].Irp
->MdlAddress
);
1391 IoFreeMdl(read_stripes
[i
].Irp
->MdlAddress
);
1394 IoFreeIrp(read_stripes
[i
].Irp
); // FIXME - what if IoCallDriver fails and other Irps are still running?
1398 ExFreePool(read_stripes
);
1401 if (!NT_SUCCESS(Status
))
1407 parity1
= (((address
- c
->offset
) / ((c
->chunk_item
->num_stripes
- 2) * c
->chunk_item
->stripe_length
)) + c
->chunk_item
->num_stripes
- 2) % c
->chunk_item
->num_stripes
;
1408 parity2
= (parity1
+ 1) % c
->chunk_item
->num_stripes
;
1411 if ((address
- c
->offset
) % (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 2)) > 0) {
1414 stripenum
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
1416 for (logstripe
= 0; logstripe
< c
->chunk_item
->num_stripes
- 2; logstripe
++) {
1422 if (stripes
[logstripe
].start
< start
+ firststripesize
&& stripes
[logstripe
].start
!= stripes
[logstripe
].end
) {
1423 copylen
= min(start
+ firststripesize
- stripes
[logstripe
].start
, length
- pos
);
1425 if (!first
&& copylen
< c
->chunk_item
->stripe_length
) {
1430 RtlCopyMemory(&stripes
[stripenum
].data
[firststripesize
- copylen
], &data2
[pos
], copylen
);
1436 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1439 i
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
1440 RtlCopyMemory(stripes
[parity1
].data
, stripes
[i
].data
, firststripesize
);
1441 RtlCopyMemory(stripes
[parity2
].data
, stripes
[i
].data
, firststripesize
);
1442 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1445 do_xor(stripes
[parity1
].data
, stripes
[i
].data
, firststripesize
);
1447 galois_double(stripes
[parity2
].data
, firststripesize
);
1448 do_xor(stripes
[parity2
].data
, stripes
[i
].data
, firststripesize
);
1450 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1451 } while (i
!= parity2
);
1454 stripepos
= firststripesize
;
1456 parity2
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
1460 while (length
>= pos
+ c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 2)) {
1461 stripenum
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
1463 for (i
= 0; i
< c
->chunk_item
->num_stripes
- 2; i
++) {
1464 RtlCopyMemory(&stripes
[stripenum
].data
[stripepos
], &data2
[pos
], c
->chunk_item
->stripe_length
);
1466 pos
+= c
->chunk_item
->stripe_length
;
1467 stripenum
= (stripenum
+1) % c
->chunk_item
->num_stripes
;
1470 i
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
1471 RtlCopyMemory(&stripes
[parity1
].data
[stripepos
], &stripes
[i
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1472 RtlCopyMemory(&stripes
[parity2
].data
[stripepos
], &stripes
[i
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1473 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1476 do_xor(&stripes
[parity1
].data
[stripepos
], &stripes
[i
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1478 galois_double(&stripes
[parity2
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1479 do_xor(&stripes
[parity2
].data
[stripepos
], &stripes
[i
].data
[stripepos
], c
->chunk_item
->stripe_length
);
1481 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1482 } while (i
!= parity2
);
1485 parity2
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
1486 stripepos
+= c
->chunk_item
->stripe_length
;
1491 stripenum
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
1496 while (pos
< length
) {
1499 copylen
= min(stripes
[i
].end
- start
- stripepos
, length
- pos
);
1501 RtlCopyMemory(&stripes
[stripenum
].data
[stripepos
], &data2
[pos
], copylen
);
1504 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
1508 i
= parity1
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (parity1
- 1);
1509 RtlCopyMemory(&stripes
[parity1
].data
[stripepos
], &stripes
[i
].data
[stripepos
], laststripesize
);
1510 RtlCopyMemory(&stripes
[parity2
].data
[stripepos
], &stripes
[i
].data
[stripepos
], laststripesize
);
1511 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1514 do_xor(&stripes
[parity1
].data
[stripepos
], &stripes
[i
].data
[stripepos
], laststripesize
);
1516 galois_double(&stripes
[parity2
].data
[stripepos
], laststripesize
);
1517 do_xor(&stripes
[parity2
].data
[stripepos
], &stripes
[i
].data
[stripepos
], laststripesize
);
1519 i
= i
== 0 ? (c
->chunk_item
->num_stripes
- 1) : (i
- 1);
1520 } while (i
!= parity2
);
1523 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1524 stripes
[i
].start
= start
;
1525 stripes
[i
].end
= end
;
1528 return STATUS_SUCCESS
;
1531 NTSTATUS STDCALL
write_data(device_extension
* Vcb
, UINT64 address
, void* data
, BOOL need_free
, UINT32 length
, write_data_context
* wtc
, PIRP Irp
, chunk
* c
) {
1534 CHUNK_ITEM_STRIPE
* cis
;
1535 write_data_stripe
* stripe
;
1536 write_stripe
* stripes
= NULL
;
1539 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
1542 c
= get_chunk_from_address(Vcb
, address
);
1544 ERR("could not get chunk for address %llx\n", address
);
1545 return STATUS_INTERNAL_ERROR
;
1549 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
1551 ERR("out of memory\n");
1552 return STATUS_INSUFFICIENT_RESOURCES
;
1555 RtlZeroMemory(stripes
, sizeof(write_stripe
) * c
->chunk_item
->num_stripes
);
1557 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1559 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
1560 Status
= prepare_raid0_write(c
, address
, data
, length
, stripes
);
1561 if (!NT_SUCCESS(Status
)) {
1562 ERR("prepare_raid0_write returned %08x\n", Status
);
1563 ExFreePool(stripes
);
1571 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
1572 Status
= prepare_raid10_write(c
, address
, data
, length
, stripes
);
1573 if (!NT_SUCCESS(Status
)) {
1574 ERR("prepare_raid10_write returned %08x\n", Status
);
1575 ExFreePool(stripes
);
1583 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
1584 Status
= prepare_raid5_write(Irp
, c
, address
, data
, length
, stripes
);
1585 if (!NT_SUCCESS(Status
)) {
1586 ERR("prepare_raid5_write returned %08x\n", Status
);
1587 ExFreePool(stripes
);
1595 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
1596 Status
= prepare_raid6_write(Irp
, c
, address
, data
, length
, stripes
);
1597 if (!NT_SUCCESS(Status
)) {
1598 ERR("prepare_raid6_write returned %08x\n", Status
);
1599 ExFreePool(stripes
);
1607 } else { // write same data to every location - SINGLE, DUP, RAID1
1608 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1609 stripes
[i
].start
= address
- c
->offset
;
1610 stripes
[i
].end
= stripes
[i
].start
+ length
;
1611 stripes
[i
].data
= data
;
1613 need_free2
= need_free
;
1616 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1617 PIO_STACK_LOCATION IrpSp
;
1619 // FIXME - handle missing devices
1621 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_stripe
), ALLOC_TAG
);
1623 ERR("out of memory\n");
1624 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1628 if (stripes
[i
].start
+ stripes
[i
].skip_start
== stripes
[i
].end
- stripes
[i
].skip_end
|| stripes
[i
].start
== stripes
[i
].end
) {
1629 stripe
->status
= WriteDataStatus_Ignore
;
1631 stripe
->buf
= stripes
[i
].data
;
1632 stripe
->need_free
= need_free2
;
1634 stripe
->context
= (struct _write_data_context
*)wtc
;
1635 stripe
->buf
= stripes
[i
].data
;
1636 stripe
->need_free
= need_free2
;
1637 stripe
->device
= c
->devices
[i
];
1638 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
1639 stripe
->status
= WriteDataStatus_Pending
;
1642 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
1645 ERR("IoAllocateIrp failed\n");
1646 Status
= STATUS_INTERNAL_ERROR
;
1650 stripe
->Irp
= IoMakeAssociatedIrp(Irp
, stripe
->device
->devobj
->StackSize
);
1653 ERR("IoMakeAssociatedIrp failed\n");
1654 Status
= STATUS_INTERNAL_ERROR
;
1659 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
1660 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
1662 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
1663 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= stripes
[i
].data
+ stripes
[i
].skip_start
;
1665 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
1666 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1667 stripe
->Irp
->MdlAddress
= IoAllocateMdl(stripes
[i
].data
+ stripes
[i
].skip_start
,
1668 stripes
[i
].end
- stripes
[i
].start
- stripes
[i
].skip_start
- stripes
[i
].skip_end
, FALSE
, FALSE
, NULL
);
1669 if (!stripe
->Irp
->MdlAddress
) {
1670 ERR("IoAllocateMdl failed\n");
1671 Status
= STATUS_INTERNAL_ERROR
;
1675 MmProbeAndLockPages(stripe
->Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
1677 stripe
->Irp
->UserBuffer
= stripes
[i
].data
+ stripes
[i
].skip_start
;
1680 #ifdef DEBUG_PARANOID
1681 if (stripes
[i
].end
< stripes
[i
].start
+ stripes
[i
].skip_start
+ stripes
[i
].skip_end
) {
1682 ERR("trying to write stripe with negative length (%llx < %llx + %x + %x)\n",
1683 stripes
[i
].end
, stripes
[i
].start
, stripes
[i
].skip_start
, stripes
[i
].skip_end
);
1688 IrpSp
->Parameters
.Write
.Length
= stripes
[i
].end
- stripes
[i
].start
- stripes
[i
].skip_start
- stripes
[i
].skip_end
;
1689 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= stripes
[i
].start
+ cis
[i
].offset
+ stripes
[i
].skip_start
;
1691 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
1692 wtc
->stripes_left
++;
1694 IoSetCompletionRoutine(stripe
->Irp
, write_data_completion
, stripe
, TRUE
, TRUE
, TRUE
);
1697 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
1700 Status
= STATUS_SUCCESS
;
1704 if (stripes
) ExFreePool(stripes
);
1706 if (!NT_SUCCESS(Status
)) {
1707 free_write_data_stripes(wtc
);
1714 void get_raid56_lock_range(chunk
* c
, UINT64 address
, UINT64 length
, UINT64
* lockaddr
, UINT64
* locklen
) {
1715 UINT64 startoff
, endoff
;
1716 UINT16 startoffstripe
, endoffstripe
, datastripes
;
1717 UINT64 start
= 0xffffffffffffffff, end
= 0, logend
;
1720 datastripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
1722 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, datastripes
, &startoff
, &startoffstripe
);
1723 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, datastripes
, &endoff
, &endoffstripe
);
1725 for (i
= 0; i
< datastripes
; i
++) {
1726 UINT64 ststart
, stend
;
1728 if (startoffstripe
> i
) {
1729 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1730 } else if (startoffstripe
== i
) {
1733 ststart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
1736 if (endoffstripe
> i
) {
1737 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
1738 } else if (endoffstripe
== i
) {
1741 stend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
1744 if (ststart
!= stend
) {
1745 if (ststart
< start
)
1753 *lockaddr
= c
->offset
+ ((start
/ c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
* datastripes
) +
1754 start
% c
->chunk_item
->stripe_length
;
1756 logend
= c
->offset
+ ((end
/ c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
* datastripes
);
1757 logend
+= c
->chunk_item
->stripe_length
* (datastripes
- 1);
1758 logend
+= end
% c
->chunk_item
->stripe_length
== 0 ? c
->chunk_item
->stripe_length
: (end
% c
->chunk_item
->stripe_length
);
1759 *locklen
= logend
- *lockaddr
;
1762 NTSTATUS STDCALL
write_data_complete(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
, PIRP Irp
, chunk
* c
) {
1763 write_data_context
* wtc
;
1765 UINT64 lockaddr
, locklen
;
1766 // #ifdef DEBUG_PARANOID
1770 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_context
), ALLOC_TAG
);
1772 ERR("out of memory\n");
1773 return STATUS_INSUFFICIENT_RESOURCES
;
1776 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
1777 InitializeListHead(&wtc
->stripes
);
1779 wtc
->stripes_left
= 0;
1782 c
= get_chunk_from_address(Vcb
, address
);
1784 ERR("could not get chunk for address %llx\n", address
);
1785 return STATUS_INTERNAL_ERROR
;
1789 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
1790 get_raid56_lock_range(c
, address
, length
, &lockaddr
, &locklen
);
1791 chunk_lock_range(Vcb
, c
, lockaddr
, locklen
);
1794 Status
= write_data(Vcb
, address
, data
, FALSE
, length
, wtc
, Irp
, c
);
1795 if (!NT_SUCCESS(Status
)) {
1796 ERR("write_data returned %08x\n", Status
);
1798 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1799 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
1801 free_write_data_stripes(wtc
);
1806 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
1807 // launch writes and wait
1808 LIST_ENTRY
* le
= wtc
->stripes
.Flink
;
1809 while (le
!= &wtc
->stripes
) {
1810 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1812 if (stripe
->status
!= WriteDataStatus_Ignore
)
1813 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
1818 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
1820 le
= wtc
->stripes
.Flink
;
1821 while (le
!= &wtc
->stripes
) {
1822 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1824 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
1825 Status
= stripe
->iosb
.Status
;
1832 free_write_data_stripes(wtc
);
1835 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
1836 chunk_unlock_range(Vcb
, c
, lockaddr
, locklen
);
1840 // #ifdef DEBUG_PARANOID
1841 // buf2 = ExAllocatePoolWithTag(NonPagedPool, length, ALLOC_TAG);
1842 // Status = read_data(Vcb, address, length, NULL, FALSE, buf2, NULL, Irp);
1844 // if (!NT_SUCCESS(Status) || RtlCompareMemory(buf2, data, length) != length)
1847 // ExFreePool(buf2);
1850 return STATUS_SUCCESS
;
1853 static NTSTATUS STDCALL
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1854 write_data_stripe
* stripe
= conptr
;
1855 write_data_context
* context
= (write_data_context
*)stripe
->context
;
1858 // FIXME - we need a lock here
1860 if (stripe
->status
== WriteDataStatus_Cancelling
) {
1861 stripe
->status
= WriteDataStatus_Cancelled
;
1865 stripe
->iosb
= Irp
->IoStatus
;
1867 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
1868 stripe
->status
= WriteDataStatus_Success
;
1870 le
= context
->stripes
.Flink
;
1872 stripe
->status
= WriteDataStatus_Error
;
1874 while (le
!= &context
->stripes
) {
1875 write_data_stripe
* s2
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1877 if (s2
->status
== WriteDataStatus_Pending
) {
1878 s2
->status
= WriteDataStatus_Cancelling
;
1879 IoCancelIrp(s2
->Irp
);
1887 if (InterlockedDecrement(&context
->stripes_left
) == 0)
1888 KeSetEvent(&context
->Event
, 0, FALSE
);
1890 return STATUS_MORE_PROCESSING_REQUIRED
;
1893 void free_write_data_stripes(write_data_context
* wtc
) {
1894 LIST_ENTRY
*le
, *le2
, *nextle
;
1896 le
= wtc
->stripes
.Flink
;
1897 while (le
!= &wtc
->stripes
) {
1898 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1901 if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1902 MmUnlockPages(stripe
->Irp
->MdlAddress
);
1903 IoFreeMdl(stripe
->Irp
->MdlAddress
);
1910 le
= wtc
->stripes
.Flink
;
1911 while (le
!= &wtc
->stripes
) {
1912 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1916 if (stripe
->buf
&& stripe
->need_free
) {
1917 ExFreePool(stripe
->buf
);
1920 while (le2
!= &wtc
->stripes
) {
1921 write_data_stripe
* s2
= CONTAINING_RECORD(le2
, write_data_stripe
, list_entry
);
1923 if (s2
->buf
== stripe
->buf
)
1937 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1941 le
= fcb
->extents
.Flink
;
1943 while (le
!= &fcb
->extents
) {
1944 LIST_ENTRY
* le2
= le
->Flink
;
1945 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1946 EXTENT_DATA
* ed
= ext
->data
;
1951 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
1952 ERR("extent at %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
1953 Status
= STATUS_INTERNAL_ERROR
;
1957 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1958 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1959 ERR("extent at %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
1960 Status
= STATUS_INTERNAL_ERROR
;
1964 ed2
= (EXTENT_DATA2
*)ed
->data
;
1967 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
1969 if (ext
->offset
< end_data
&& ext
->offset
+ len
> start_data
) {
1970 if (ed
->type
== EXTENT_TYPE_INLINE
) {
1971 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
1972 remove_fcb_extent(fcb
, ext
, rollback
);
1974 fcb
->inode_item
.st_blocks
-= len
;
1975 fcb
->inode_item_changed
= TRUE
;
1976 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
1981 size
= len
- (end_data
- ext
->offset
);
1983 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
1985 ERR("out of memory\n");
1986 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1990 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
1992 ERR("out of memory\n");
1993 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1998 ned
->generation
= Vcb
->superblock
.generation
;
1999 ned
->decoded_size
= size
;
2000 ned
->compression
= ed
->compression
;
2001 ned
->encryption
= ed
->encryption
;
2002 ned
->encoding
= ed
->encoding
;
2003 ned
->type
= ed
->type
;
2005 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- ext
->offset
], size
);
2007 newext
->offset
= end_data
;
2009 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
2010 newext
->unique
= ext
->unique
;
2011 newext
->ignore
= FALSE
;
2012 newext
->inserted
= TRUE
;
2013 newext
->csum
= NULL
;
2014 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2016 remove_fcb_extent(fcb
, ext
, rollback
);
2018 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
2019 fcb
->inode_item_changed
= TRUE
;
2020 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
2025 size
= start_data
- ext
->offset
;
2027 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
2029 ERR("out of memory\n");
2030 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2034 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2036 ERR("out of memory\n");
2037 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2042 ned
->generation
= Vcb
->superblock
.generation
;
2043 ned
->decoded_size
= size
;
2044 ned
->compression
= ed
->compression
;
2045 ned
->encryption
= ed
->encryption
;
2046 ned
->encoding
= ed
->encoding
;
2047 ned
->type
= ed
->type
;
2049 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
2051 newext
->offset
= ext
->offset
;
2053 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
2054 newext
->unique
= ext
->unique
;
2055 newext
->ignore
= FALSE
;
2056 newext
->inserted
= TRUE
;
2057 newext
->csum
= NULL
;
2058 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2060 remove_fcb_extent(fcb
, ext
, rollback
);
2062 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
2063 fcb
->inode_item_changed
= TRUE
;
2064 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
2065 EXTENT_DATA
*ned1
, *ned2
;
2067 extent
*newext1
, *newext2
;
2069 size
= start_data
- ext
->offset
;
2071 ned1
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
2073 ERR("out of memory\n");
2074 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2078 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2080 ERR("out of memory\n");
2081 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2086 ned1
->generation
= Vcb
->superblock
.generation
;
2087 ned1
->decoded_size
= size
;
2088 ned1
->compression
= ed
->compression
;
2089 ned1
->encryption
= ed
->encryption
;
2090 ned1
->encoding
= ed
->encoding
;
2091 ned1
->type
= ed
->type
;
2093 RtlCopyMemory(&ned1
->data
[0], &ed
->data
[0], size
);
2095 newext1
->offset
= ext
->offset
;
2096 newext1
->data
= ned1
;
2097 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
2098 newext1
->unique
= ext
->unique
;
2099 newext1
->ignore
= FALSE
;
2100 newext1
->inserted
= TRUE
;
2101 newext1
->csum
= NULL
;
2103 size
= ext
->offset
+ len
- end_data
;
2105 ned2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
2107 ERR("out of memory\n");
2108 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2110 ExFreePool(newext1
);
2114 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2116 ERR("out of memory\n");
2117 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2119 ExFreePool(newext1
);
2124 ned2
->generation
= Vcb
->superblock
.generation
;
2125 ned2
->decoded_size
= size
;
2126 ned2
->compression
= ed
->compression
;
2127 ned2
->encryption
= ed
->encryption
;
2128 ned2
->encoding
= ed
->encoding
;
2129 ned2
->type
= ed
->type
;
2131 RtlCopyMemory(&ned2
->data
[0], &ed
->data
[end_data
- ext
->offset
], size
);
2133 newext2
->offset
= end_data
;
2134 newext2
->data
= ned2
;
2135 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
2136 newext2
->unique
= ext
->unique
;
2137 newext2
->ignore
= FALSE
;
2138 newext2
->inserted
= TRUE
;
2139 newext2
->csum
= NULL
;
2141 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
2142 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
2144 remove_fcb_extent(fcb
, ext
, rollback
);
2146 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
2147 fcb
->inode_item_changed
= TRUE
;
2149 } else if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
2150 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
2151 if (ed2
->size
!= 0) {
2154 fcb
->inode_item
.st_blocks
-= len
;
2155 fcb
->inode_item_changed
= TRUE
;
2157 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2160 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2162 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
2163 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2164 if (!NT_SUCCESS(Status
)) {
2165 ERR("update_changed_extent_ref returned %08x\n", Status
);
2171 remove_fcb_extent(fcb
, ext
, rollback
);
2172 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
2177 if (ed2
->size
!= 0) {
2178 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
2179 fcb
->inode_item_changed
= TRUE
;
2182 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2184 ERR("out of memory\n");
2185 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2189 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2191 ERR("out of memory\n");
2192 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2197 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
2199 ned
->generation
= Vcb
->superblock
.generation
;
2200 ned
->decoded_size
= ed
->decoded_size
;
2201 ned
->compression
= ed
->compression
;
2202 ned
->encryption
= ed
->encryption
;
2203 ned
->encoding
= ed
->encoding
;
2204 ned
->type
= ed
->type
;
2205 ned2
->address
= ed2
->address
;
2206 ned2
->size
= ed2
->size
;
2207 ned2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2208 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- ext
->offset
);
2210 newext
->offset
= end_data
;
2212 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2213 newext
->unique
= ext
->unique
;
2214 newext
->ignore
= FALSE
;
2215 newext
->inserted
= TRUE
;
2218 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2219 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2220 if (!newext
->csum
) {
2221 ERR("out of memory\n");
2222 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2228 RtlCopyMemory(newext
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2229 ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2231 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2232 if (!newext
->csum
) {
2233 ERR("out of memory\n");
2234 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2240 RtlCopyMemory(newext
->csum
, ext
->csum
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2243 newext
->csum
= NULL
;
2245 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2247 remove_fcb_extent(fcb
, ext
, rollback
);
2248 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
2253 if (ed2
->size
!= 0) {
2254 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
2255 fcb
->inode_item_changed
= TRUE
;
2258 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2260 ERR("out of memory\n");
2261 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2265 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2267 ERR("out of memory\n");
2268 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2273 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
2275 ned
->generation
= Vcb
->superblock
.generation
;
2276 ned
->decoded_size
= ed
->decoded_size
;
2277 ned
->compression
= ed
->compression
;
2278 ned
->encryption
= ed
->encryption
;
2279 ned
->encoding
= ed
->encoding
;
2280 ned
->type
= ed
->type
;
2281 ned2
->address
= ed2
->address
;
2282 ned2
->size
= ed2
->size
;
2283 ned2
->offset
= ed2
->offset
;
2284 ned2
->num_bytes
= start_data
- ext
->offset
;
2286 newext
->offset
= ext
->offset
;
2288 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2289 newext
->unique
= ext
->unique
;
2290 newext
->ignore
= FALSE
;
2291 newext
->inserted
= TRUE
;
2294 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2295 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2296 if (!newext
->csum
) {
2297 ERR("out of memory\n");
2298 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2304 RtlCopyMemory(newext
->csum
, ext
->csum
, ned2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2306 newext
->csum
= ExAllocatePoolWithTag(PagedPool
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2307 if (!newext
->csum
) {
2308 ERR("out of memory\n");
2309 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2315 RtlCopyMemory(newext
->csum
, ext
->csum
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2318 newext
->csum
= NULL
;
2320 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
2322 remove_fcb_extent(fcb
, ext
, rollback
);
2323 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
2324 EXTENT_DATA
*neda
, *nedb
;
2325 EXTENT_DATA2
*neda2
, *nedb2
;
2326 extent
*newext1
, *newext2
;
2328 if (ed2
->size
!= 0) {
2331 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
2332 fcb
->inode_item_changed
= TRUE
;
2334 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2337 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
2339 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
2340 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
2341 if (!NT_SUCCESS(Status
)) {
2342 ERR("update_changed_extent_ref returned %08x\n", Status
);
2348 neda
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2350 ERR("out of memory\n");
2351 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2355 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2357 ERR("out of memory\n");
2358 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2363 nedb
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
2365 ERR("out of memory\n");
2366 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2368 ExFreePool(newext1
);
2372 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2374 ERR("out of memory\n");
2375 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2377 ExFreePool(newext1
);
2382 neda2
= (EXTENT_DATA2
*)&neda
->data
[0];
2384 neda
->generation
= Vcb
->superblock
.generation
;
2385 neda
->decoded_size
= ed
->decoded_size
;
2386 neda
->compression
= ed
->compression
;
2387 neda
->encryption
= ed
->encryption
;
2388 neda
->encoding
= ed
->encoding
;
2389 neda
->type
= ed
->type
;
2390 neda2
->address
= ed2
->address
;
2391 neda2
->size
= ed2
->size
;
2392 neda2
->offset
= ed2
->offset
;
2393 neda2
->num_bytes
= start_data
- ext
->offset
;
2395 nedb2
= (EXTENT_DATA2
*)&nedb
->data
[0];
2397 nedb
->generation
= Vcb
->superblock
.generation
;
2398 nedb
->decoded_size
= ed
->decoded_size
;
2399 nedb
->compression
= ed
->compression
;
2400 nedb
->encryption
= ed
->encryption
;
2401 nedb
->encoding
= ed
->encoding
;
2402 nedb
->type
= ed
->type
;
2403 nedb2
->address
= ed2
->address
;
2404 nedb2
->size
= ed2
->size
;
2405 nedb2
->offset
= ed2
->offset
+ (end_data
- ext
->offset
);
2406 nedb2
->num_bytes
= ext
->offset
+ len
- end_data
;
2408 newext1
->offset
= ext
->offset
;
2409 newext1
->data
= neda
;
2410 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2411 newext1
->unique
= ext
->unique
;
2412 newext1
->ignore
= FALSE
;
2413 newext1
->inserted
= TRUE
;
2415 newext2
->offset
= end_data
;
2416 newext2
->data
= nedb
;
2417 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2418 newext2
->unique
= ext
->unique
;
2419 newext2
->ignore
= FALSE
;
2420 newext2
->inserted
= TRUE
;
2423 if (ed
->compression
== BTRFS_COMPRESSION_NONE
) {
2424 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2425 if (!newext1
->csum
) {
2426 ERR("out of memory\n");
2427 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2429 ExFreePool(newext1
);
2431 ExFreePool(newext2
);
2435 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2436 if (!newext2
->csum
) {
2437 ERR("out of memory\n");
2438 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2439 ExFreePool(newext1
->csum
);
2441 ExFreePool(newext1
);
2443 ExFreePool(newext2
);
2447 RtlCopyMemory(newext1
->csum
, ext
->csum
, neda2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2448 RtlCopyMemory(newext2
->csum
, &ext
->csum
[(end_data
- ext
->offset
) / Vcb
->superblock
.sector_size
],
2449 nedb2
->num_bytes
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2451 newext1
->csum
= ExAllocatePoolWithTag(PagedPool
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2452 if (!newext1
->csum
) {
2453 ERR("out of memory\n");
2454 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2456 ExFreePool(newext1
);
2458 ExFreePool(newext2
);
2462 newext2
->csum
= ExAllocatePoolWithTag(PagedPool
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
, ALLOC_TAG
);
2463 if (!newext1
->csum
) {
2464 ERR("out of memory\n");
2465 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2466 ExFreePool(newext1
->csum
);
2468 ExFreePool(newext1
);
2470 ExFreePool(newext2
);
2474 RtlCopyMemory(newext1
->csum
, ext
->csum
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2475 RtlCopyMemory(newext2
->csum
, ext
->csum
, ed2
->size
* sizeof(UINT32
) / Vcb
->superblock
.sector_size
);
2478 newext1
->csum
= NULL
;
2479 newext2
->csum
= NULL
;
2482 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
2483 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
2485 remove_fcb_extent(fcb
, ext
, rollback
);
2494 Status
= STATUS_SUCCESS
;
2497 fcb
->extents_changed
= TRUE
;
2498 mark_fcb_dirty(fcb
);
2503 static void add_insert_extent_rollback(LIST_ENTRY
* rollback
, fcb
* fcb
, extent
* ext
) {
2504 rollback_extent
* re
;
2506 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2508 ERR("out of memory\n");
2515 add_rollback(fcb
->Vcb
, rollback
, ROLLBACK_INSERT_EXTENT
, re
);
2518 static BOOL
add_extent_to_fcb(fcb
* fcb
, UINT64 offset
, EXTENT_DATA
* ed
, ULONG edsize
, BOOL unique
, UINT32
* csum
, LIST_ENTRY
* rollback
) {
2522 ext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
2524 ERR("out of memory\n");
2528 ext
->offset
= offset
;
2530 ext
->datalen
= edsize
;
2531 ext
->unique
= unique
;
2532 ext
->ignore
= FALSE
;
2533 ext
->inserted
= TRUE
;
2536 le
= fcb
->extents
.Flink
;
2537 while (le
!= &fcb
->extents
) {
2538 extent
* oldext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2540 if (!oldext
->ignore
) {
2541 if (oldext
->offset
> offset
) {
2542 InsertHeadList(le
->Blink
, &ext
->list_entry
);
2550 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
2553 add_insert_extent_rollback(rollback
, fcb
, ext
);
2558 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
) {
2560 rollback_extent
* re
;
2564 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
2566 ERR("out of memory\n");
2573 add_rollback(fcb
->Vcb
, rollback
, ROLLBACK_DELETE_EXTENT
, re
);
2577 static NTSTATUS
calc_csum(device_extension
* Vcb
, UINT8
* data
, UINT32 sectors
, UINT32
* csum
) {
2581 // From experimenting, it seems that 40 sectors is roughly the crossover
2582 // point where offloading the crc32 calculation becomes worth it.
2587 for (j
= 0; j
< sectors
; j
++) {
2588 csum
[j
] = ~calc_crc32c(0xffffffff, data
+ (j
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
2591 return STATUS_SUCCESS
;
2594 Status
= add_calc_job(Vcb
, data
, sectors
, csum
, &cj
);
2595 if (!NT_SUCCESS(Status
)) {
2596 ERR("add_calc_job returned %08x\n", Status
);
2600 KeWaitForSingleObject(&cj
->event
, Executive
, KernelMode
, FALSE
, NULL
);
2603 return STATUS_SUCCESS
;
2606 BOOL
insert_extent_chunk(device_extension
* Vcb
, fcb
* fcb
, chunk
* c
, UINT64 start_data
, UINT64 length
, BOOL prealloc
, void* data
,
2607 PIRP Irp
, LIST_ENTRY
* rollback
, UINT8 compression
, UINT64 decoded_size
) {
2612 ULONG edsize
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
2613 UINT32
* csum
= NULL
;
2614 // #ifdef DEBUG_PARANOID
2619 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
);
2621 if (!find_data_address_in_chunk(Vcb
, c
, length
, &address
))
2624 // #ifdef DEBUG_PARANOID
2625 // searchkey.obj_id = address;
2626 // searchkey.obj_type = TYPE_EXTENT_ITEM;
2627 // searchkey.offset = 0xffffffffffffffff;
2629 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
2630 // if (!NT_SUCCESS(Status)) {
2631 // ERR("error - find_item returned %08x\n", Status);
2632 // } else if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
2633 // ERR("address %llx already allocated\n", address);
2638 // add extent data to inode
2639 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
2641 ERR("out of memory\n");
2645 ed
->generation
= Vcb
->superblock
.generation
;
2646 ed
->decoded_size
= decoded_size
;
2647 ed
->compression
= compression
;
2648 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
2649 ed
->encoding
= BTRFS_ENCODING_NONE
;
2650 ed
->type
= prealloc
? EXTENT_TYPE_PREALLOC
: EXTENT_TYPE_REGULAR
;
2652 ed2
= (EXTENT_DATA2
*)ed
->data
;
2653 ed2
->address
= address
;
2656 ed2
->num_bytes
= decoded_size
;
2658 if (!prealloc
&& data
&& !(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
2659 ULONG sl
= length
/ Vcb
->superblock
.sector_size
;
2661 csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
2663 ERR("out of memory\n");
2667 Status
= calc_csum(Vcb
, data
, sl
, csum
);
2668 if (!NT_SUCCESS(Status
)) {
2669 ERR("calc_csum returned %08x\n", Status
);
2674 if (!add_extent_to_fcb(fcb
, start_data
, ed
, edsize
, TRUE
, csum
, rollback
)) {
2675 ERR("add_extent_to_fcb failed\n");
2680 increase_chunk_usage(c
, length
);
2681 space_list_subtract(Vcb
, c
, FALSE
, address
, length
, rollback
);
2683 fcb
->inode_item
.st_blocks
+= decoded_size
;
2685 fcb
->extents_changed
= TRUE
;
2686 fcb
->inode_item_changed
= TRUE
;
2687 mark_fcb_dirty(fcb
);
2689 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
2691 add_changed_extent_ref(c
, address
, length
, fcb
->subvol
->id
, fcb
->inode
, start_data
, 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
2693 ExReleaseResourceLite(&c
->changed_extents_lock
);
2695 ExReleaseResourceLite(&c
->lock
);
2698 Status
= write_data_complete(Vcb
, address
, data
, length
, Irp
, NULL
);
2699 if (!NT_SUCCESS(Status
))
2700 ERR("write_data_complete returned %08x\n", Status
);
2706 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
2707 PIRP Irp
, UINT64
* written
, LIST_ENTRY
* rollback
) {
2708 BOOL success
= FALSE
;
2716 le
= fcb
->extents
.Flink
;
2718 while (le
!= &fcb
->extents
) {
2719 extent
* nextext
= CONTAINING_RECORD(le
, extent
, list_entry
);
2721 if (!nextext
->ignore
) {
2722 if (nextext
->offset
== start_data
) {
2725 } else if (nextext
->offset
> start_data
)
2739 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
2740 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
2744 if (ed
->type
!= EXTENT_TYPE_REGULAR
&& ed
->type
!= EXTENT_TYPE_PREALLOC
) {
2745 TRACE("not extending extent which is not regular or prealloc\n");
2749 ed2
= (EXTENT_DATA2
*)ed
->data
;
2751 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
2752 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
2756 if (ext
->offset
+ ed2
->num_bytes
!= start_data
) {
2757 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext
->offset
, ed2
->num_bytes
, start_data
);
2761 c
= get_chunk_from_address(Vcb
, ed2
->address
);
2763 if (c
->reloc
|| c
->readonly
|| c
->chunk_item
->type
!= Vcb
->data_flags
)
2766 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2768 le
= c
->space
.Flink
;
2769 while (le
!= &c
->space
) {
2770 s
= CONTAINING_RECORD(le
, space
, list_entry
);
2772 if (s
->address
== ed2
->address
+ ed2
->size
) {
2773 UINT64 newlen
= min(min(s
->size
, length
), MAX_EXTENT_SIZE
);
2775 success
= insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
);
2781 } else if (s
->address
> ed2
->address
+ ed2
->size
)
2787 ExReleaseResourceLite(&c
->lock
);
2792 static NTSTATUS
insert_prealloc_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
2798 UINT64 flags
, origlength
= length
;
2801 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
2803 flags
= fcb
->Vcb
->data_flags
;
2805 // FIXME - try and maximize contiguous ranges first. If we can't do that,
2806 // allocate all the free space we find until it's enough.
2809 UINT64 extlen
= min(MAX_EXTENT_SIZE
, length
);
2811 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
2813 le
= fcb
->Vcb
->chunks
.Flink
;
2814 while (le
!= &fcb
->Vcb
->chunks
) {
2815 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2817 if (!c
->readonly
&& !c
->reloc
) {
2818 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2820 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
2821 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
)) {
2822 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
2827 ExReleaseResourceLite(&c
->lock
);
2833 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
2835 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
2837 if ((c
= alloc_chunk(fcb
->Vcb
, flags
))) {
2838 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
2840 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2842 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
2843 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
))
2847 ExReleaseResourceLite(&c
->lock
);
2849 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
2851 WARN("couldn't find any data chunks with %llx bytes free\n", origlength
);
2852 Status
= STATUS_DISK_FULL
;
2858 } while (length
> 0);
2860 Status
= STATUS_SUCCESS
;
2866 // static void print_tree(tree* t) {
2867 // LIST_ENTRY* le = t->itemlist.Flink;
2868 // while (le != &t->itemlist) {
2869 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2870 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
2875 NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2878 UINT64 flags
, orig_length
= length
, written
= 0;
2880 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
);
2882 if (start_data
> 0) {
2883 try_extend_data(Vcb
, fcb
, start_data
, length
, data
, Irp
, &written
, rollback
);
2885 if (written
== length
)
2886 return STATUS_SUCCESS
;
2887 else if (written
> 0) {
2888 start_data
+= written
;
2890 data
= &((UINT8
*)data
)[written
];
2894 flags
= Vcb
->data_flags
;
2896 while (written
< orig_length
) {
2897 UINT64 newlen
= min(length
, MAX_EXTENT_SIZE
);
2900 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
2901 // First, see if we can write the extent part to an existing chunk.
2903 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
2905 le
= Vcb
->chunks
.Flink
;
2906 while (le
!= &Vcb
->chunks
) {
2907 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2909 if (!c
->readonly
&& !c
->reloc
) {
2910 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2912 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
2913 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
)) {
2916 if (written
== orig_length
) {
2917 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2918 return STATUS_SUCCESS
;
2921 start_data
+= newlen
;
2923 data
= &((UINT8
*)data
)[newlen
];
2927 ExReleaseResourceLite(&c
->lock
);
2933 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
2937 // Otherwise, see if we can put it in a new chunk.
2939 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
2941 if ((c
= alloc_chunk(Vcb
, flags
))) {
2942 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2944 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2946 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
&&
2947 insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
)) {
2950 if (written
== orig_length
)
2951 return STATUS_SUCCESS
;
2954 start_data
+= newlen
;
2956 data
= &((UINT8
*)data
)[newlen
];
2959 ExReleaseResourceLite(&c
->lock
);
2961 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2964 FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME
2969 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
2971 return STATUS_DISK_FULL
;
2974 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2977 // FIXME - convert into inline extent if short enough
2979 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
2980 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
2981 if (!NT_SUCCESS(Status
)) {
2982 ERR("error - excise_extents failed\n");
2986 fcb
->inode_item
.st_size
= end
;
2987 fcb
->inode_item_changed
= TRUE
;
2988 TRACE("setting st_size to %llx\n", end
);
2990 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
2991 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
2992 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
2993 // FIXME - inform cache manager of this
2995 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
2997 return STATUS_SUCCESS
;
3000 NTSTATUS
extend_file(fcb
* fcb
, file_ref
* fileref
, UINT64 end
, BOOL prealloc
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3001 UINT64 oldalloc
, newalloc
;
3005 TRACE("(%p, %p, %x, %u)\n", fcb
, fileref
, end
, prealloc
);
3008 return stream_set_end_of_file_information(fcb
->Vcb
, end
, fcb
, fileref
, NULL
, FALSE
, rollback
);
3013 le
= fcb
->extents
.Blink
;
3014 while (le
!= &fcb
->extents
) {
3015 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
3017 if (!ext2
->ignore
) {
3027 EXTENT_DATA
* ed
= ext
->data
;
3028 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3030 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
3031 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
3032 return STATUS_INTERNAL_ERROR
;
3035 oldalloc
= ext
->offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
3036 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
3038 if (cur_inline
&& end
> fcb
->Vcb
->options
.max_inline
) {
3039 UINT64 origlength
, length
;
3041 UINT64 offset
= ext
->offset
;
3043 TRACE("giving inline file proper extents\n");
3045 origlength
= ed
->decoded_size
;
3049 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
3051 data
= ExAllocatePoolWithTag(PagedPool
, length
, ALLOC_TAG
);
3053 ERR("could not allocate %llx bytes for data\n", length
);
3054 return STATUS_INSUFFICIENT_RESOURCES
;
3057 if (length
> origlength
)
3058 RtlZeroMemory(data
+ origlength
, length
- origlength
);
3060 RtlCopyMemory(data
, ed
->data
, origlength
);
3062 fcb
->inode_item
.st_blocks
-= origlength
;
3063 fcb
->inode_item_changed
= TRUE
;
3064 mark_fcb_dirty(fcb
);
3066 remove_fcb_extent(fcb
, ext
, rollback
);
3068 if (write_fcb_compressed(fcb
)) {
3069 Status
= write_compressed(fcb
, offset
, offset
+ length
, data
, Irp
, rollback
);
3070 if (!NT_SUCCESS(Status
)) {
3071 ERR("write_compressed returned %08x\n", Status
);
3076 Status
= insert_extent(fcb
->Vcb
, fcb
, offset
, length
, data
, Irp
, rollback
);
3077 if (!NT_SUCCESS(Status
)) {
3078 ERR("insert_extent returned %08x\n", Status
);
3084 oldalloc
= ext
->offset
+ length
;
3092 if (end
> oldalloc
) {
3093 edsize
= sizeof(EXTENT_DATA
) - 1 + end
- ext
->offset
;
3094 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3097 ERR("out of memory\n");
3098 return STATUS_INSUFFICIENT_RESOURCES
;
3101 RtlZeroMemory(ed
, edsize
);
3102 RtlCopyMemory(ed
, ext
->data
, ext
->datalen
);
3104 ed
->decoded_size
= end
- ext
->offset
;
3106 remove_fcb_extent(fcb
, ext
, rollback
);
3108 if (!add_extent_to_fcb(fcb
, ext
->offset
, ed
, edsize
, ext
->unique
, NULL
, rollback
)) {
3109 ERR("add_extent_to_fcb failed\n");
3111 return STATUS_INTERNAL_ERROR
;
3114 fcb
->extents_changed
= TRUE
;
3115 mark_fcb_dirty(fcb
);
3118 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
3120 fcb
->inode_item
.st_size
= end
;
3121 TRACE("setting st_size to %llx\n", end
);
3123 fcb
->inode_item
.st_blocks
= end
;
3125 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3127 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3129 if (newalloc
> oldalloc
) {
3131 // FIXME - try and extend previous extent first
3133 Status
= insert_prealloc_extent(fcb
, oldalloc
, newalloc
- oldalloc
, rollback
);
3135 if (!NT_SUCCESS(Status
)) {
3136 ERR("insert_prealloc_extent returned %08x\n", Status
);
3141 fcb
->extents_changed
= TRUE
;
3144 fcb
->inode_item
.st_size
= end
;
3145 fcb
->inode_item_changed
= TRUE
;
3146 mark_fcb_dirty(fcb
);
3148 TRACE("setting st_size to %llx\n", end
);
3150 TRACE("newalloc = %llx\n", newalloc
);
3152 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3153 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3156 if (end
> fcb
->Vcb
->options
.max_inline
) {
3157 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
3160 Status
= insert_prealloc_extent(fcb
, 0, newalloc
, rollback
);
3162 if (!NT_SUCCESS(Status
)) {
3163 ERR("insert_prealloc_extent returned %08x\n", Status
);
3168 fcb
->extents_changed
= TRUE
;
3169 fcb
->inode_item_changed
= TRUE
;
3170 mark_fcb_dirty(fcb
);
3172 fcb
->inode_item
.st_size
= end
;
3173 TRACE("setting st_size to %llx\n", end
);
3175 TRACE("newalloc = %llx\n", newalloc
);
3177 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
3178 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3183 edsize
= sizeof(EXTENT_DATA
) - 1 + end
;
3184 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
3187 ERR("out of memory\n");
3188 return STATUS_INSUFFICIENT_RESOURCES
;
3191 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3192 ed
->decoded_size
= end
;
3193 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3194 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3195 ed
->encoding
= BTRFS_ENCODING_NONE
;
3196 ed
->type
= EXTENT_TYPE_INLINE
;
3198 RtlZeroMemory(ed
->data
, end
);
3200 if (!add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, NULL
, rollback
)) {
3201 ERR("add_extent_to_fcb failed\n");
3203 return STATUS_INTERNAL_ERROR
;
3206 fcb
->extents_changed
= TRUE
;
3207 fcb
->inode_item_changed
= TRUE
;
3208 mark_fcb_dirty(fcb
);
3210 fcb
->inode_item
.st_size
= end
;
3211 TRACE("setting st_size to %llx\n", end
);
3213 fcb
->inode_item
.st_blocks
= end
;
3215 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
3220 return STATUS_SUCCESS
;
3223 static NTSTATUS
do_write_file_prealloc(fcb
* fcb
, extent
* ext
, UINT64 start_data
, UINT64 end_data
, void* data
, UINT64
* written
,
3224 PIRP Irp
, LIST_ENTRY
* rollback
) {
3225 EXTENT_DATA
* ed
= ext
->data
;
3226 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3230 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace all
3234 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3236 ERR("out of memory\n");
3237 return STATUS_INSUFFICIENT_RESOURCES
;
3240 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3242 ERR("out of memory\n");
3244 return STATUS_INSUFFICIENT_RESOURCES
;
3247 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
3249 ned
->type
= EXTENT_TYPE_REGULAR
;
3251 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, ed2
->num_bytes
, Irp
, NULL
);
3252 if (!NT_SUCCESS(Status
)) {
3253 ERR("write_data_complete returned %08x\n", Status
);
3257 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3258 ULONG sl
= ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
;
3259 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3262 ERR("out of memory\n");
3265 return STATUS_INSUFFICIENT_RESOURCES
;
3268 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3269 if (!NT_SUCCESS(Status
)) {
3270 ERR("calc_csum returned %08x\n", Status
);
3277 newext
->csum
= csum
;
3279 newext
->csum
= NULL
;
3281 *written
= ed2
->num_bytes
;
3283 newext
->offset
= ext
->offset
;
3285 newext
->datalen
= ext
->datalen
;
3286 newext
->unique
= ext
->unique
;
3287 newext
->ignore
= FALSE
;
3288 newext
->inserted
= TRUE
;
3289 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
3291 add_insert_extent_rollback(rollback
, fcb
, newext
);
3293 remove_fcb_extent(fcb
, ext
, rollback
);
3294 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace beginning
3295 EXTENT_DATA
*ned
, *nedb
;
3297 extent
*newext1
, *newext2
;
3299 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3301 ERR("out of memory\n");
3302 return STATUS_INSUFFICIENT_RESOURCES
;
3305 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3307 ERR("out of memory\n");
3309 return STATUS_INSUFFICIENT_RESOURCES
;
3312 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3314 ERR("out of memory\n");
3317 return STATUS_INSUFFICIENT_RESOURCES
;
3320 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3322 ERR("out of memory\n");
3325 ExFreePool(newext1
);
3326 return STATUS_INSUFFICIENT_RESOURCES
;
3329 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
3330 ned
->type
= EXTENT_TYPE_REGULAR
;
3331 ned2
= (EXTENT_DATA2
*)ned
->data
;
3332 ned2
->num_bytes
= end_data
- ext
->offset
;
3334 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
3335 ned2
= (EXTENT_DATA2
*)nedb
->data
;
3336 ned2
->offset
+= end_data
- ext
->offset
;
3337 ned2
->num_bytes
-= end_data
- ext
->offset
;
3339 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, end_data
- ext
->offset
, Irp
, NULL
);
3340 if (!NT_SUCCESS(Status
)) {
3341 ERR("write_data_complete returned %08x\n", Status
);
3345 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3346 ULONG sl
= (end_data
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
;
3347 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3350 ERR("out of memory\n");
3353 ExFreePool(newext1
);
3354 ExFreePool(newext2
);
3355 return STATUS_INSUFFICIENT_RESOURCES
;
3358 Status
= calc_csum(fcb
->Vcb
, (UINT8
*)data
+ ext
->offset
- start_data
, sl
, csum
);
3359 if (!NT_SUCCESS(Status
)) {
3360 ERR("calc_csum returned %08x\n", Status
);
3363 ExFreePool(newext1
);
3364 ExFreePool(newext2
);
3369 newext1
->csum
= csum
;
3371 newext1
->csum
= NULL
;
3373 *written
= end_data
- ext
->offset
;
3375 newext1
->offset
= ext
->offset
;
3376 newext1
->data
= ned
;
3377 newext1
->datalen
= ext
->datalen
;
3378 newext1
->unique
= ext
->unique
;
3379 newext1
->ignore
= FALSE
;
3380 newext1
->inserted
= TRUE
;
3381 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3383 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3385 newext2
->offset
= end_data
;
3386 newext2
->data
= nedb
;
3387 newext2
->datalen
= ext
->datalen
;
3388 newext2
->unique
= ext
->unique
;
3389 newext2
->ignore
= FALSE
;
3390 newext2
->inserted
= TRUE
;
3391 newext2
->csum
= NULL
;
3392 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
3394 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3396 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3399 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3401 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3402 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3404 if (!NT_SUCCESS(Status
)) {
3405 ERR("update_changed_extent_ref returned %08x\n", Status
);
3410 remove_fcb_extent(fcb
, ext
, rollback
);
3411 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace end
3412 EXTENT_DATA
*ned
, *nedb
;
3414 extent
*newext1
, *newext2
;
3416 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3418 ERR("out of memory\n");
3419 return STATUS_INSUFFICIENT_RESOURCES
;
3422 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3424 ERR("out of memory\n");
3426 return STATUS_INSUFFICIENT_RESOURCES
;
3429 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3431 ERR("out of memory\n");
3434 return STATUS_INSUFFICIENT_RESOURCES
;
3437 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3439 ERR("out of memory\n");
3442 ExFreePool(newext1
);
3443 return STATUS_INSUFFICIENT_RESOURCES
;
3446 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
3448 ned2
= (EXTENT_DATA2
*)ned
->data
;
3449 ned2
->num_bytes
= start_data
- ext
->offset
;
3451 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
3453 nedb
->type
= EXTENT_TYPE_REGULAR
;
3454 ned2
= (EXTENT_DATA2
*)nedb
->data
;
3455 ned2
->offset
+= start_data
- ext
->offset
;
3456 ned2
->num_bytes
= ext
->offset
+ ed2
->num_bytes
- start_data
;
3458 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, ned2
->num_bytes
, Irp
, NULL
);
3459 if (!NT_SUCCESS(Status
)) {
3460 ERR("write_data_complete returned %08x\n", Status
);
3464 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3465 ULONG sl
= ned2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
;
3466 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3469 ERR("out of memory\n");
3472 ExFreePool(newext1
);
3473 ExFreePool(newext2
);
3474 return STATUS_INSUFFICIENT_RESOURCES
;
3477 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3478 if (!NT_SUCCESS(Status
)) {
3479 ERR("calc_csum returned %08x\n", Status
);
3482 ExFreePool(newext1
);
3483 ExFreePool(newext2
);
3488 newext2
->csum
= csum
;
3490 newext2
->csum
= NULL
;
3492 *written
= ned2
->num_bytes
;
3494 newext1
->offset
= ext
->offset
;
3495 newext1
->data
= ned
;
3496 newext1
->datalen
= ext
->datalen
;
3497 newext1
->unique
= ext
->unique
;
3498 newext1
->ignore
= FALSE
;
3499 newext1
->inserted
= TRUE
;
3500 newext1
->csum
= NULL
;
3501 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3503 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3505 newext2
->offset
= start_data
;
3506 newext2
->data
= nedb
;
3507 newext2
->datalen
= ext
->datalen
;
3508 newext2
->unique
= ext
->unique
;
3509 newext2
->ignore
= FALSE
;
3510 newext2
->inserted
= TRUE
;
3511 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
3513 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3515 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3518 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3520 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
3521 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3523 if (!NT_SUCCESS(Status
)) {
3524 ERR("update_changed_extent_ref returned %08x\n", Status
);
3529 remove_fcb_extent(fcb
, ext
, rollback
);
3530 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace middle
3531 EXTENT_DATA
*ned
, *nedb
, *nedc
;
3533 extent
*newext1
, *newext2
, *newext3
;
3535 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3537 ERR("out of memory\n");
3538 return STATUS_INSUFFICIENT_RESOURCES
;
3541 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3543 ERR("out of memory\n");
3545 return STATUS_INSUFFICIENT_RESOURCES
;
3548 nedc
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
3550 ERR("out of memory\n");
3553 return STATUS_INSUFFICIENT_RESOURCES
;
3556 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3558 ERR("out of memory\n");
3562 return STATUS_INSUFFICIENT_RESOURCES
;
3565 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3567 ERR("out of memory\n");
3571 ExFreePool(newext1
);
3572 return STATUS_INSUFFICIENT_RESOURCES
;
3575 newext3
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
3577 ERR("out of memory\n");
3581 ExFreePool(newext1
);
3582 ExFreePool(newext2
);
3583 return STATUS_INSUFFICIENT_RESOURCES
;
3586 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
3587 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
3588 RtlCopyMemory(nedc
, ext
->data
, ext
->datalen
);
3590 ned2
= (EXTENT_DATA2
*)ned
->data
;
3591 ned2
->num_bytes
= start_data
- ext
->offset
;
3593 nedb
->type
= EXTENT_TYPE_REGULAR
;
3594 ned2
= (EXTENT_DATA2
*)nedb
->data
;
3595 ned2
->offset
+= start_data
- ext
->offset
;
3596 ned2
->num_bytes
= end_data
- start_data
;
3598 ned2
= (EXTENT_DATA2
*)nedc
->data
;
3599 ned2
->offset
+= end_data
- ext
->offset
;
3600 ned2
->num_bytes
-= end_data
- ext
->offset
;
3602 ned2
= (EXTENT_DATA2
*)nedb
->data
;
3603 Status
= write_data_complete(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, end_data
- start_data
, Irp
, NULL
);
3604 if (!NT_SUCCESS(Status
)) {
3605 ERR("write_data_complete returned %08x\n", Status
);
3609 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3610 ULONG sl
= (end_data
- start_data
) / fcb
->Vcb
->superblock
.sector_size
;
3611 UINT32
* csum
= ExAllocatePoolWithTag(PagedPool
, sl
* sizeof(UINT32
), ALLOC_TAG
);
3614 ERR("out of memory\n");
3618 ExFreePool(newext1
);
3619 ExFreePool(newext2
);
3620 ExFreePool(newext3
);
3621 return STATUS_INSUFFICIENT_RESOURCES
;
3624 Status
= calc_csum(fcb
->Vcb
, data
, sl
, csum
);
3625 if (!NT_SUCCESS(Status
)) {
3626 ERR("calc_csum returned %08x\n", Status
);
3630 ExFreePool(newext1
);
3631 ExFreePool(newext2
);
3632 ExFreePool(newext3
);
3637 newext2
->csum
= csum
;
3639 newext2
->csum
= NULL
;
3641 *written
= end_data
- start_data
;
3643 newext1
->offset
= ext
->offset
;
3644 newext1
->data
= ned
;
3645 newext1
->datalen
= ext
->datalen
;
3646 newext1
->unique
= ext
->unique
;
3647 newext1
->ignore
= FALSE
;
3648 newext1
->inserted
= TRUE
;
3649 newext1
->csum
= NULL
;
3650 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
3652 add_insert_extent_rollback(rollback
, fcb
, newext1
);
3654 newext2
->offset
= start_data
;
3655 newext2
->data
= nedb
;
3656 newext2
->datalen
= ext
->datalen
;
3657 newext2
->unique
= ext
->unique
;
3658 newext2
->ignore
= FALSE
;
3659 newext2
->inserted
= TRUE
;
3660 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
3662 add_insert_extent_rollback(rollback
, fcb
, newext2
);
3664 newext3
->offset
= end_data
;
3665 newext3
->data
= nedc
;
3666 newext3
->datalen
= ext
->datalen
;
3667 newext3
->unique
= ext
->unique
;
3668 newext3
->ignore
= FALSE
;
3669 newext3
->inserted
= TRUE
;
3670 newext3
->csum
= NULL
;
3671 InsertHeadList(&newext2
->list_entry
, &newext3
->list_entry
);
3673 add_insert_extent_rollback(rollback
, fcb
, newext3
);
3675 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
3678 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
3680 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 2,
3681 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
3683 if (!NT_SUCCESS(Status
)) {
3684 ERR("update_changed_extent_ref returned %08x\n", Status
);
3689 remove_fcb_extent(fcb
, ext
, rollback
);
3692 return STATUS_SUCCESS
;
3695 NTSTATUS
do_write_file(fcb
* fcb
, UINT64 start
, UINT64 end_data
, void* data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3697 LIST_ENTRY
*le
, *le2
;
3698 UINT64 written
= 0, length
= end_data
- start
;
3699 UINT64 last_cow_start
;
3700 #ifdef DEBUG_PARANOID
3706 le
= fcb
->extents
.Flink
;
3707 while (le
!= &fcb
->extents
) {
3708 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3713 EXTENT_DATA
* ed
= ext
->data
;
3714 EXTENT_DATA2
* ed2
= ed
->type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ed
->data
;
3717 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
3719 if (ext
->offset
+ len
<= start
)
3722 if (ext
->offset
> start
+ written
+ length
)
3725 if ((fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && ext
->unique
) {
3726 if (max(last_cow_start
, start
+ written
) < ext
->offset
) {
3727 UINT64 start_write
= max(last_cow_start
, start
+ written
);
3729 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, ext
->offset
, Irp
, rollback
);
3730 if (!NT_SUCCESS(Status
)) {
3731 ERR("excise_extents returned %08x\n", Status
);
3735 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, ext
->offset
- start_write
, data
, Irp
, rollback
);
3736 if (!NT_SUCCESS(Status
)) {
3737 ERR("insert_extent returned %08x\n", Status
);
3741 written
+= ext
->offset
- start_write
;
3742 length
-= ext
->offset
- start_write
;
3748 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
3749 UINT64 writeaddr
= ed2
->address
+ ed2
->offset
+ start
+ written
- ext
->offset
;
3750 UINT64 write_len
= min(len
, length
);
3752 TRACE("doing non-COW write to %llx\n", writeaddr
);
3754 Status
= write_data_complete(fcb
->Vcb
, writeaddr
, (UINT8
*)data
+ written
, write_len
, Irp
, NULL
);
3755 if (!NT_SUCCESS(Status
)) {
3756 ERR("write_data_complete returned %08x\n", Status
);
3760 // This shouldn't ever get called - nocow files should always also be nosum.
3761 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3762 calc_csum(fcb
->Vcb
, (UINT8
*)data
+ written
, write_len
/ fcb
->Vcb
->superblock
.sector_size
,
3763 &ext
->csum
[(start
+ written
- ext
->offset
) / fcb
->Vcb
->superblock
.sector_size
]);
3765 ext
->inserted
= TRUE
;
3768 written
+= write_len
;
3769 length
-= write_len
;
3773 } else if (ed
->type
== EXTENT_TYPE_PREALLOC
) {
3776 Status
= do_write_file_prealloc(fcb
, ext
, start
+ written
, end_data
, (UINT8
*)data
+ written
, &write_len
,
3778 if (!NT_SUCCESS(Status
)) {
3779 ERR("do_write_file_prealloc returned %08x\n", Status
);
3783 written
+= write_len
;
3784 length
-= write_len
;
3790 last_cow_start
= ext
->offset
+ len
;
3799 UINT64 start_write
= max(last_cow_start
, start
+ written
);
3801 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, end_data
, Irp
, rollback
);
3802 if (!NT_SUCCESS(Status
)) {
3803 ERR("excise_extents returned %08x\n", Status
);
3807 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, end_data
- start_write
, data
, Irp
, rollback
);
3808 if (!NT_SUCCESS(Status
)) {
3809 ERR("insert_extent returned %08x\n", Status
);
3814 #ifdef DEBUG_PARANOID
3815 last_off
= 0xffffffffffffffff;
3817 le
= fcb
->extents
.Flink
;
3818 while (le
!= &fcb
->extents
) {
3819 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3822 if (ext
->offset
== last_off
) {
3823 ERR("offset %llx duplicated\n", ext
->offset
);
3825 } else if (ext
->offset
< last_off
&& last_off
!= 0xffffffffffffffff) {
3826 ERR("offsets out of order\n");
3830 last_off
= ext
->offset
;
3837 fcb
->extents_changed
= TRUE
;
3838 mark_fcb_dirty(fcb
);
3840 return STATUS_SUCCESS
;
3843 NTSTATUS
write_compressed(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3847 for (i
= 0; i
< sector_align(end_data
- start_data
, COMPRESSED_EXTENT_SIZE
) / COMPRESSED_EXTENT_SIZE
; i
++) {
3851 s2
= start_data
+ (i
* COMPRESSED_EXTENT_SIZE
);
3852 e2
= min(s2
+ COMPRESSED_EXTENT_SIZE
, end_data
);
3854 Status
= write_compressed_bit(fcb
, s2
, e2
, (UINT8
*)data
+ (i
* COMPRESSED_EXTENT_SIZE
), &compressed
, Irp
, rollback
);
3856 if (!NT_SUCCESS(Status
)) {
3857 ERR("write_compressed_bit returned %08x\n", Status
);
3861 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
3862 // bother with the rest of it.
3863 if (s2
== 0 && e2
== COMPRESSED_EXTENT_SIZE
&& !compressed
&& !fcb
->Vcb
->options
.compress_force
) {
3864 fcb
->inode_item
.flags
|= BTRFS_INODE_NOCOMPRESS
;
3865 fcb
->inode_item_changed
= TRUE
;
3866 mark_fcb_dirty(fcb
);
3868 // write subsequent data non-compressed
3869 if (e2
< end_data
) {
3870 Status
= do_write_file(fcb
, e2
, end_data
, (UINT8
*)data
+ e2
, Irp
, rollback
);
3872 if (!NT_SUCCESS(Status
)) {
3873 ERR("do_write_file returned %08x\n", Status
);
3878 return STATUS_SUCCESS
;
3882 return STATUS_SUCCESS
;
3885 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOL paging_io
, BOOL no_cache
,
3886 BOOL wait
, BOOL deferred_write
, LIST_ENTRY
* rollback
) {
3887 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3888 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
3890 UINT64 newlength
, start_data
, end_data
;
3895 BOOL changed_length
= FALSE
/*, lazy_writer = FALSE, write_eof = FALSE*/;
3902 BOOL paging_lock
= FALSE
, fcb_lock
= FALSE
, tree_lock
= FALSE
, pagefile
;
3905 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
3908 WARN("returning success for zero-length write\n");
3909 return STATUS_SUCCESS
;
3913 ERR("error - FileObject was NULL\n");
3914 return STATUS_ACCESS_DENIED
;
3917 fcb
= FileObject
->FsContext
;
3918 ccb
= FileObject
->FsContext2
;
3919 fileref
= ccb
? ccb
->fileref
: NULL
;
3921 if (!fcb
->ads
&& fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
3922 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
);
3923 return STATUS_INVALID_DEVICE_REQUEST
;
3926 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1) {
3927 offset
= fcb
->Header
.FileSize
;
3928 // write_eof = TRUE;
3931 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
3933 if (!no_cache
&& !CcCanIWrite(FileObject
, *length
, wait
, deferred_write
))
3934 return STATUS_PENDING
;
3936 if (!wait
&& no_cache
)
3937 return STATUS_PENDING
;
3939 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
3940 IO_STATUS_BLOCK iosb
;
3942 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
3944 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
3946 if (!NT_SUCCESS(iosb
.Status
)) {
3947 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
3948 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
3954 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
3958 if (!ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, wait
)) {
3959 Status
= STATUS_PENDING
;
3965 pagefile
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
&& paging_io
;
3967 if (!pagefile
&& !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
3968 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
3969 Status
= STATUS_PENDING
;
3977 if (!ExAcquireResourceSharedLite(fcb
->Header
.Resource
, wait
)) {
3978 Status
= STATUS_PENDING
;
3982 } else if (!ExIsResourceAcquiredExclusiveLite(fcb
->Header
.Resource
)) {
3983 if (!ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, wait
)) {
3984 Status
= STATUS_PENDING
;
3991 newlength
= fcb
->ads
? fcb
->adsdata
.Length
: fcb
->inode_item
.st_size
;
3996 TRACE("newlength = %llx\n", newlength
);
3998 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
3999 // ERR("lazy writer on the TV\n");
4000 // lazy_writer = TRUE;
4003 if (offset
.QuadPart
+ *length
> newlength
) {
4005 if (offset
.QuadPart
>= newlength
) {
4006 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, offset
.QuadPart
, *length
);
4007 TRACE("filename %S\n", file_desc(FileObject
));
4008 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
4009 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
4010 Status
= STATUS_SUCCESS
;
4014 *length
= newlength
- offset
.QuadPart
;
4016 newlength
= offset
.QuadPart
+ *length
;
4017 changed_length
= TRUE
;
4019 TRACE("extending length to %llx\n", newlength
);
4023 make_inline
= fcb
->ads
? FALSE
: newlength
<= fcb
->Vcb
->options
.max_inline
;
4025 if (changed_length
) {
4026 if (newlength
> fcb
->Header
.AllocationSize
.QuadPart
) {
4028 // We need to acquire the tree lock if we don't have it already -
4029 // we can't give an inline file proper extents at the same as we're
4031 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
4032 Status
= STATUS_PENDING
;
4038 Status
= extend_file(fcb
, fileref
, newlength
, FALSE
, Irp
, rollback
);
4039 if (!NT_SUCCESS(Status
)) {
4040 ERR("extend_file returned %08x\n", Status
);
4043 } else if (!fcb
->ads
)
4044 fcb
->inode_item
.st_size
= newlength
;
4046 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4047 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4049 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
4050 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
4051 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
4055 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
4058 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4059 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4060 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4062 if (!FileObject
->PrivateCacheMap
)
4063 init_file_cache(FileObject
, &ccfs
);
4065 CcSetFileSizes(FileObject
, &ccfs
);
4068 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
4069 CcPrepareMdlWrite(FileObject
, &offset
, *length
, &Irp
->MdlAddress
, &Irp
->IoStatus
);
4071 Status
= Irp
->IoStatus
.Status
;
4074 if (CcCopyWriteEx
) {
4075 TRACE("CcCopyWriteEx(%p, %llx, %x, %u, %p, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
);
4076 if (!CcCopyWriteEx(FileObject
, &offset
, *length
, wait
, buf
, Irp
->Tail
.Overlay
.Thread
)) {
4077 Status
= STATUS_PENDING
;
4080 TRACE("CcCopyWriteEx finished\n");
4082 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
);
4083 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
4084 Status
= STATUS_PENDING
;
4087 TRACE("CcCopyWrite finished\n");
4091 Status
= STATUS_SUCCESS
;
4096 if (changed_length
) {
4099 if (newlength
> fcb
->adsmaxlen
) {
4100 ERR("error - xattr too long (%llu > %u)\n", newlength
, fcb
->adsmaxlen
);
4101 Status
= STATUS_DISK_FULL
;
4105 data2
= ExAllocatePoolWithTag(PagedPool
, newlength
, ALLOC_TAG
);
4107 ERR("out of memory\n");
4108 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4112 if (fcb
->adsdata
.Buffer
) {
4113 RtlCopyMemory(data2
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
4114 ExFreePool(fcb
->adsdata
.Buffer
);
4117 if (newlength
> fcb
->adsdata
.Length
)
4118 RtlZeroMemory(&data2
[fcb
->adsdata
.Length
], newlength
- fcb
->adsdata
.Length
);
4121 fcb
->adsdata
.Buffer
= data2
;
4122 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= newlength
;
4124 fcb
->Header
.AllocationSize
.QuadPart
= newlength
;
4125 fcb
->Header
.FileSize
.QuadPart
= newlength
;
4126 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4130 RtlCopyMemory(&fcb
->adsdata
.Buffer
[offset
.QuadPart
], buf
, *length
);
4132 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4134 mark_fcb_dirty(fcb
);
4137 mark_fileref_dirty(fileref
);
4139 BOOL compress
= write_fcb_compressed(fcb
);
4143 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
4144 bufhead
= sizeof(EXTENT_DATA
) - 1;
4145 } else if (compress
) {
4146 start_data
= offset
.QuadPart
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
4147 end_data
= min(sector_align(offset
.QuadPart
+ *length
, COMPRESSED_EXTENT_SIZE
),
4148 sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
));
4151 start_data
= offset
.QuadPart
& ~(UINT64
)(fcb
->Vcb
->superblock
.sector_size
- 1);
4152 end_data
= sector_align(offset
.QuadPart
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
4156 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
4157 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
4159 data
= ExAllocatePoolWithTag(PagedPool
, end_data
- start_data
+ bufhead
, ALLOC_TAG
);
4161 ERR("out of memory\n");
4162 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4166 RtlZeroMemory(data
+ bufhead
, end_data
- start_data
);
4168 TRACE("start_data = %llx\n", start_data
);
4169 TRACE("end_data = %llx\n", end_data
);
4171 if (offset
.QuadPart
> start_data
|| offset
.QuadPart
+ *length
< end_data
) {
4172 if (changed_length
) {
4173 if (fcb
->inode_item
.st_size
> start_data
)
4174 Status
= read_file(fcb
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
, Irp
, TRUE
);
4176 Status
= STATUS_SUCCESS
;
4178 Status
= read_file(fcb
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
, Irp
, TRUE
);
4180 if (!NT_SUCCESS(Status
)) {
4181 ERR("read_file returned %08x\n", Status
);
4187 RtlCopyMemory(data
+ bufhead
+ offset
.QuadPart
- start_data
, buf
, *length
);
4190 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, Irp
, rollback
);
4191 if (!NT_SUCCESS(Status
)) {
4192 ERR("error - excise_extents returned %08x\n", Status
);
4197 ed2
= (EXTENT_DATA
*)data
;
4198 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
4199 ed2
->decoded_size
= newlength
;
4200 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
4201 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
4202 ed2
->encoding
= BTRFS_ENCODING_NONE
;
4203 ed2
->type
= EXTENT_TYPE_INLINE
;
4205 if (!add_extent_to_fcb(fcb
, 0, ed2
, sizeof(EXTENT_DATA
) - 1 + newlength
, FALSE
, NULL
, rollback
)) {
4206 ERR("add_extent_to_fcb failed\n");
4208 Status
= STATUS_INTERNAL_ERROR
;
4212 fcb
->inode_item
.st_blocks
+= newlength
;
4213 } else if (compress
) {
4214 Status
= write_compressed(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
4216 if (!NT_SUCCESS(Status
)) {
4217 ERR("write_compressed returned %08x\n", Status
);
4224 Status
= do_write_file(fcb
, start_data
, end_data
, data
, Irp
, rollback
);
4226 if (!NT_SUCCESS(Status
)) {
4227 ERR("do_write_file returned %08x\n", Status
);
4237 KeQuerySystemTime(&time
);
4238 win_time_to_unix(time
, &now
);
4240 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
4243 // if (!FileObject->PrivateCacheMap) {
4244 // CC_FILE_SIZES ccfs;
4246 // ccfs.AllocationSize = fcb->Header.AllocationSize;
4247 // ccfs.FileSize = fcb->Header.FileSize;
4248 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
4250 // TRACE("calling CcInitializeCacheMap...\n");
4251 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
4253 // changed_length = FALSE;
4258 if (fileref
&& fileref
->parent
)
4259 origii
= &fileref
->parent
->fcb
->inode_item
;
4261 ERR("no parent fcb found for stream\n");
4262 Status
= STATUS_INTERNAL_ERROR
;
4266 origii
= &fcb
->inode_item
;
4268 origii
->transid
= Vcb
->superblock
.generation
;
4271 if (!ccb
->user_set_change_time
)
4272 origii
->st_ctime
= now
;
4275 if (changed_length
) {
4276 TRACE("setting st_size to %llx\n", newlength
);
4277 origii
->st_size
= newlength
;
4278 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
4281 if (!ccb
->user_set_write_time
) {
4282 origii
->st_mtime
= now
;
4283 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
4286 fcb
->inode_item_changed
= TRUE
;
4288 fileref
->parent
->fcb
->inode_item_changed
= TRUE
;
4290 mark_fcb_dirty(fcb
->ads
? fileref
->parent
->fcb
: fcb
);
4293 if (changed_length
) {
4296 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
4297 ccfs
.FileSize
= fcb
->Header
.FileSize
;
4298 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
4300 CcSetFileSizes(FileObject
, &ccfs
);
4303 // FIXME - make sure this still called if STATUS_PENDING and async
4305 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
4306 // ERR("CcCopyWrite failed.\n");
4310 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
4311 fcb
->subvol
->root_item
.ctime
= now
;
4313 Status
= STATUS_SUCCESS
;
4316 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, filter
, FILE_ACTION_MODIFIED
);
4319 if (NT_SUCCESS(Status
) && FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
4320 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4321 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
4322 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
4326 ExReleaseResourceLite(fcb
->Header
.Resource
);
4329 ExReleaseResourceLite(&Vcb
->tree_lock
);
4332 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
4337 NTSTATUS
write_file(device_extension
* Vcb
, PIRP Irp
, BOOL wait
, BOOL deferred_write
) {
4338 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4341 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
4342 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4343 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4344 // BOOL locked = FALSE;
4345 // LARGE_INTEGER freq, time1, time2;
4346 LIST_ENTRY rollback
;
4348 InitializeListHead(&rollback
);
4350 // time1 = KeQueryPerformanceCounter(&freq);
4354 Irp
->IoStatus
.Information
= 0;
4356 TRACE("offset = %llx\n", offset
.QuadPart
);
4357 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
4359 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
4360 buf
= map_user_buffer(Irp
);
4362 if (Irp
->MdlAddress
&& !buf
) {
4363 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
4364 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4368 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
4370 TRACE("buf = %p\n", buf
);
4372 // if (Irp->Flags & IRP_NOCACHE) {
4373 // if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
4374 // Status = STATUS_PENDING;
4380 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
4381 WARN("tried to write to locked region\n");
4382 Status
= STATUS_FILE_LOCK_CONFLICT
;
4386 // ERR("Irp->Flags = %x\n", Irp->Flags);
4387 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
,
4388 wait
, deferred_write
, &rollback
);
4390 if (Status
== STATUS_PENDING
)
4392 else if (!NT_SUCCESS(Status
)) {
4393 ERR("write_file2 returned %08x\n", Status
);
4398 // Status = consider_write(Vcb);
4400 if (NT_SUCCESS(Status
)) {
4401 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
4403 #ifdef DEBUG_PARANOID
4405 // check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
4407 // check_extent_tree_consistent(Vcb);
4410 if (diskacc
&& Status
!= STATUS_PENDING
&& Irp
->Flags
& IRP_NOCACHE
) {
4411 PETHREAD thread
= NULL
;
4413 if (Irp
->Tail
.Overlay
.Thread
&& !IoIsSystemThread(Irp
->Tail
.Overlay
.Thread
))
4414 thread
= Irp
->Tail
.Overlay
.Thread
;
4415 else if (!IoIsSystemThread(PsGetCurrentThread()))
4416 thread
= PsGetCurrentThread();
4417 else if (IoIsSystemThread(PsGetCurrentThread()) && IoGetTopLevelIrp() == Irp
)
4418 thread
= PsGetCurrentThread();
4421 PsUpdateDiskCounters(PsGetThreadProcess(thread
), 0, IrpSp
->Parameters
.Write
.Length
, 0, 1, 0);
4427 if (NT_SUCCESS(Status
))
4428 clear_rollback(Vcb
, &rollback
);
4430 do_rollback(Vcb
, &rollback
);
4432 // ExReleaseResourceLite(&Vcb->tree_lock);
4435 // time2 = KeQueryPerformanceCounter(NULL);
4437 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
4442 NTSTATUS STDCALL
drv_write(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4445 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4446 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4447 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4448 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
4449 ccb
* ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
4450 BOOL wait
= FileObject
? IoIsOperationSynchronous(Irp
) : TRUE
;
4452 FsRtlEnterFileSystem();
4454 top_level
= is_top_level(Irp
);
4456 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4457 Status
= part0_passthrough(DeviceObject
, Irp
);
4462 ERR("fcb was NULL\n");
4463 Status
= STATUS_INVALID_PARAMETER
;
4468 ERR("ccb was NULL\n");
4469 Status
= STATUS_INVALID_PARAMETER
;
4473 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
4474 WARN("insufficient permissions\n");
4475 Status
= STATUS_ACCESS_DENIED
;
4479 if (fcb
== Vcb
->volume_fcb
) {
4480 if (!Vcb
->locked
|| Vcb
->locked_fileobj
!= FileObject
) {
4481 ERR("trying to write to volume when not locked, or locked with another FileObject\n");
4482 Status
= STATUS_ACCESS_DENIED
;
4486 TRACE("writing directly to volume\n");
4488 IoSkipCurrentIrpStackLocation(Irp
);
4490 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
4494 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
) {
4495 Status
= STATUS_ACCESS_DENIED
;
4499 if (Vcb
->readonly
) {
4500 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
4504 // ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
4507 if (IrpSp
->MinorFunction
& IRP_MN_COMPLETE
) {
4508 CcMdlWriteComplete(IrpSp
->FileObject
, &IrpSp
->Parameters
.Write
.ByteOffset
, Irp
->MdlAddress
);
4510 Irp
->MdlAddress
= NULL
;
4511 Status
= STATUS_SUCCESS
;
4513 // Don't offload jobs when doing paging IO - otherwise this can lead to
4514 // deadlocks in CcCopyWrite.
4515 if (Irp
->Flags
& IRP_PAGING_IO
)
4518 Status
= write_file(Vcb
, Irp
, wait
, FALSE
);
4520 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
4521 Status
= _SEH2_GetExceptionCode();
4525 Irp
->IoStatus
.Status
= Status
;
4527 TRACE("wrote %u bytes\n", Irp
->IoStatus
.Information
);
4529 if (Status
!= STATUS_PENDING
)
4530 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4532 IoMarkIrpPending(Irp
);
4534 if (!add_thread_job(Vcb
, Irp
))
4535 do_write_job(Vcb
, Irp
);
4540 IoSetTopLevelIrp(NULL
);
4542 FsRtlExitFileSystem();
4544 TRACE("returning %08x\n", Status
);