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"
20 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
23 BOOL chunk_test
= FALSE
;
34 } EXTENT_ITEM_DATA_REF
;
46 } EXTENT_ITEM_SKINNY_METADATA
;
50 CHUNK_ITEM_STRIPE stripes
[1];
54 LIST_ENTRY list_entry
;
65 static NTSTATUS
convert_old_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
);
66 static BOOL
extent_item_is_shared(EXTENT_ITEM
* ei
, ULONG len
);
67 static NTSTATUS
convert_shared_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
);
69 static NTSTATUS STDCALL
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
70 write_context
* context
= conptr
;
72 context
->iosb
= Irp
->IoStatus
;
73 KeSetEvent(&context
->Event
, 0, FALSE
);
75 // return STATUS_SUCCESS;
76 return STATUS_MORE_PROCESSING_REQUIRED
;
79 static NTSTATUS STDCALL
write_data_phys(PDEVICE_OBJECT device
, UINT64 address
, void* data
, UINT32 length
) {
83 PIO_STACK_LOCATION IrpSp
;
84 write_context
* context
= NULL
;
86 TRACE("(%p, %llx, %p, %x)\n", device
, address
, data
, length
);
88 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_context
), ALLOC_TAG
);
90 ERR("out of memory\n");
91 return STATUS_INSUFFICIENT_RESOURCES
;
94 RtlZeroMemory(context
, sizeof(write_context
));
96 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
98 offset
.QuadPart
= address
;
100 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
102 Irp
= IoAllocateIrp(device
->StackSize
, FALSE
);
105 ERR("IoAllocateIrp failed\n");
106 Status
= STATUS_INTERNAL_ERROR
;
110 IrpSp
= IoGetNextIrpStackLocation(Irp
);
111 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
113 if (device
->Flags
& DO_BUFFERED_IO
) {
114 Irp
->AssociatedIrp
.SystemBuffer
= data
;
116 Irp
->Flags
= IRP_BUFFERED_IO
;
117 } else if (device
->Flags
& DO_DIRECT_IO
) {
118 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
119 if (!Irp
->MdlAddress
) {
120 DbgPrint("IoAllocateMdl failed\n");
124 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
126 Irp
->UserBuffer
= data
;
129 IrpSp
->Parameters
.Write
.Length
= length
;
130 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
132 Irp
->UserIosb
= &context
->iosb
;
134 Irp
->UserEvent
= &context
->Event
;
136 IoSetCompletionRoutine(Irp
, write_completion
, context
, TRUE
, TRUE
, TRUE
);
138 // FIXME - support multiple devices
139 Status
= IoCallDriver(device
, Irp
);
141 if (Status
== STATUS_PENDING
) {
142 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
143 Status
= context
->iosb
.Status
;
146 if (!NT_SUCCESS(Status
)) {
147 ERR("IoCallDriver returned %08x\n", Status
);
150 if (device
->Flags
& DO_DIRECT_IO
) {
151 MmUnlockPages(Irp
->MdlAddress
);
152 IoFreeMdl(Irp
->MdlAddress
);
165 static NTSTATUS STDCALL
write_superblock(device_extension
* Vcb
, device
* device
) {
171 Status
= STATUS_INTERNAL_ERROR
;
174 // FIXME - work with RAID
176 // FIXME - only write one superblock if on SSD (?)
177 while (superblock_addrs
[i
] > 0 && Vcb
->length
>= superblock_addrs
[i
] + sizeof(superblock
)) {
178 TRACE("writing superblock %u\n", i
);
180 Vcb
->superblock
.sb_phys_addr
= superblock_addrs
[i
];
181 RtlCopyMemory(&Vcb
->superblock
.dev_item
, &device
->devitem
, sizeof(DEV_ITEM
));
183 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
185 TRACE("crc32 is %08x\n", crc32
);
186 RtlCopyMemory(&Vcb
->superblock
.checksum
, &crc32
, sizeof(UINT32
));
188 Status
= write_data_phys(device
->devobj
, superblock_addrs
[i
], &Vcb
->superblock
, sizeof(superblock
));
190 if (!NT_SUCCESS(Status
))
199 static BOOL
find_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64 length
, UINT64
* address
) {
201 space
*s
, *bestfit
= NULL
;
203 TRACE("(%p, %llx, %llx, %p)\n", Vcb
, c
->offset
, length
, address
);
206 while (le
!= &c
->space
) {
207 s
= CONTAINING_RECORD(le
, space
, list_entry
);
209 if (s
->type
== SPACE_TYPE_FREE
) {
210 if (s
->size
== length
) {
211 *address
= s
->offset
;
212 TRACE("returning exact fit at %llx\n", s
->offset
);
214 } else if (s
->size
> length
&& (!bestfit
|| bestfit
->size
> s
->size
)) {
223 TRACE("returning best fit at %llx\n", bestfit
->offset
);
224 *address
= bestfit
->offset
;
231 void add_to_space_list(chunk
* c
, UINT64 offset
, UINT64 size
, UINT8 type
) {
232 LIST_ENTRY
*le
= c
->space
.Flink
, *nextle
, *insbef
;
234 #ifdef DEBUG_PARANOID
238 TRACE("(%p, %llx, %llx, %x)\n", c
, offset
, size
, type
);
240 #ifdef DEBUG_PARANOID
243 while (le
!= &c
->space
) {
244 s
= CONTAINING_RECORD(le
, space
, list_entry
);
246 TRACE("%llx,%llx,%x\n", s
->offset
, s
->size
, s
->type
);
252 c
->space_changed
= TRUE
;
256 while (le
!= &c
->space
) {
257 s
= CONTAINING_RECORD(le
, space
, list_entry
);
260 if (s
->offset
>= offset
+ size
) {
265 if (s
->offset
>= offset
&& s
->offset
+ s
->size
<= offset
+ size
) { // delete entirely
267 RemoveEntryList(&s
->list_entry
);
270 if (s
->offset
+ s
->size
== offset
+ size
) {
271 insbef
= s
->list_entry
.Flink
;
272 RemoveEntryList(&s
->list_entry
);
277 RemoveEntryList(&s
->list_entry
);
279 } else if (s
->offset
< offset
&& s
->offset
+ s
->size
> offset
+ size
) { // split in two
280 s3
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
282 ERR("out of memory\n");
286 s3
->offset
= offset
+ size
;
287 s3
->size
= s
->size
- size
- offset
+ s
->offset
;
289 InsertHeadList(&s
->list_entry
, &s3
->list_entry
);
290 insbef
= &s3
->list_entry
;
292 s
->size
= offset
- s
->offset
;
294 } else if (s
->offset
+ s
->size
> offset
&& s
->offset
+ s
->size
<= offset
+ size
) { // truncate before
295 s
->size
= offset
- s
->offset
;
296 } else if (s
->offset
< offset
+ size
&& s
->offset
+ s
->size
> offset
+ size
) { // truncate after
297 s
->size
-= s
->offset
- offset
+ size
;
298 s
->offset
= offset
+ size
;
307 s2
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
309 ERR("out of memory\n");
316 InsertTailList(insbef
, &s2
->list_entry
);
318 // merge entries if same type
320 if (s2
->list_entry
.Blink
!= &c
->space
) {
321 s
= CONTAINING_RECORD(s2
->list_entry
.Blink
, space
, list_entry
);
323 if (s
->type
== type
) {
326 RemoveEntryList(&s2
->list_entry
);
333 if (s2
->list_entry
.Flink
!= &c
->space
) {
334 s
= CONTAINING_RECORD(s2
->list_entry
.Flink
, space
, list_entry
);
336 if (s
->type
== type
) {
339 RemoveEntryList(&s
->list_entry
);
345 while (le
!= &c
->space
) {
346 s
= CONTAINING_RECORD(le
, space
, list_entry
);
348 TRACE("%llx,%llx,%x\n", s
->offset
, s
->size
, s
->type
);
353 #ifdef DEBUG_PARANOID
355 lastaddr
= c
->offset
;
357 while (le
!= &c
->space
) {
358 s
= CONTAINING_RECORD(le
, space
, list_entry
);
360 if (s
->offset
!= lastaddr
) {
361 ERR("inconsistency detected!\n");
365 lastaddr
= s
->offset
+ s
->size
;
370 if (lastaddr
!= c
->offset
+ c
->chunk_item
->size
) {
371 ERR("inconsistency detected - space doesn't run all the way to end of chunk\n");
377 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
381 le2
= Vcb
->chunks
.Flink
;
382 while (le2
!= &Vcb
->chunks
) {
383 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
385 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
387 if (address
>= c
->offset
&& address
< c
->offset
+ c
->chunk_item
->size
)
401 static void add_provisional_disk_hole(device_extension
* Vcb
, stripe
* s
, UINT64 max_stripe_size
) {
402 // LIST_ENTRY* le = s->device->disk_holes.Flink;
405 // ERR("old holes:\n");
406 // while (le != &s->device->disk_holes) {
407 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
409 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
414 if (s
->dh
->size
<= max_stripe_size
) {
415 s
->dh
->provisional
= TRUE
;
417 disk_hole
* newdh
= ExAllocatePoolWithTag(PagedPool
, sizeof(disk_hole
), ALLOC_TAG
);
419 ERR("out of memory\n");
423 newdh
->address
= s
->dh
->address
+ max_stripe_size
;
424 newdh
->size
= s
->dh
->size
- max_stripe_size
;
425 newdh
->provisional
= FALSE
;
426 InsertTailList(&s
->device
->disk_holes
, &newdh
->listentry
);
428 s
->dh
->size
= max_stripe_size
;
429 s
->dh
->provisional
= TRUE
;
432 // ERR("new holes:\n");
433 // le = s->device->disk_holes.Flink;
434 // while (le != &s->device->disk_holes) {
435 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
437 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
443 static UINT64
find_new_chunk_address(device_extension
* Vcb
, UINT64 size
) {
445 traverse_ptr tp
, next_tp
;
450 searchkey
.obj_id
= 0x100;
451 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
452 searchkey
.offset
= 0;
454 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
455 if (!NT_SUCCESS(Status
)) {
456 ERR("error - find_item returned %08x\n", Status
);
457 return 0xffffffffffffffff;
463 if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
464 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
465 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(CHUNK_ITEM
));
467 CHUNK_ITEM
* ci
= (CHUNK_ITEM
*)tp
.item
->data
;
469 if (tp
.item
->key
.offset
>= lastaddr
+ size
) {
470 free_traverse_ptr(&tp
);
474 lastaddr
= tp
.item
->key
.offset
+ ci
->size
;
478 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
480 free_traverse_ptr(&tp
);
483 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
488 free_traverse_ptr(&tp
);
493 static BOOL
increase_dev_item_used(device_extension
* Vcb
, device
* device
, UINT64 size
, LIST_ENTRY
* rollback
) {
499 searchkey
.obj_id
= 1;
500 searchkey
.obj_type
= TYPE_DEV_ITEM
;
501 searchkey
.offset
= device
->devitem
.dev_id
;
503 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
504 if (!NT_SUCCESS(Status
)) {
505 ERR("error - find_item returned %08x\n", Status
);
509 if (keycmp(&tp
.item
->key
, &searchkey
)) {
510 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
511 free_traverse_ptr(&tp
);
515 delete_tree_item(Vcb
, &tp
, rollback
);
517 free_traverse_ptr(&tp
);
519 device
->devitem
.bytes_used
+= size
;
521 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
523 ERR("out of memory\n");
527 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
529 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, rollback
)) {
530 ERR("insert_tree_item failed\n");
537 static void reset_disk_holes(device
* device
, BOOL commit
) {
538 LIST_ENTRY
* le
= device
->disk_holes
.Flink
;
541 // ERR("old holes:\n");
542 // while (le != &device->disk_holes) {
543 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
545 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
550 le
= device
->disk_holes
.Flink
;
551 while (le
!= &device
->disk_holes
) {
552 LIST_ENTRY
* le2
= le
->Flink
;
554 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
556 if (dh
->provisional
) {
561 dh
->provisional
= FALSE
;
569 le
= device
->disk_holes
.Flink
;
570 while (le
!= &device
->disk_holes
) {
571 LIST_ENTRY
* le2
= le
->Flink
;
573 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
575 while (le2
!= &device
->disk_holes
) {
576 disk_hole
* dh2
= CONTAINING_RECORD(le2
, disk_hole
, listentry
);
578 if (dh2
->address
== dh
->address
+ dh
->size
) {
579 LIST_ENTRY
* le3
= le2
->Flink
;
580 dh
->size
+= dh2
->size
;
582 RemoveEntryList(le2
);
594 // ERR("new holes:\n");
595 // le = device->disk_holes.Flink;
596 // while (le != &device->disk_holes) {
597 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
599 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
605 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, ULONG size
) {
610 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
611 ERR("error - bootstrap is full\n");
612 return STATUS_INTERNAL_ERROR
;
615 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
617 ERR("out of memory\n");
618 return STATUS_INSUFFICIENT_RESOURCES
;
621 sc
->key
.obj_id
= obj_id
;
622 sc
->key
.obj_type
= obj_type
;
623 sc
->key
.offset
= offset
;
625 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
627 ERR("out of memory\n");
629 return STATUS_INSUFFICIENT_RESOURCES
;
632 RtlCopyMemory(sc
->data
, data
, sc
->size
);
634 le
= Vcb
->sys_chunks
.Flink
;
635 while (le
!= &Vcb
->sys_chunks
) {
636 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
638 if (keycmp(&sc2
->key
, &sc
->key
) == 1)
643 InsertTailList(le
, &sc
->list_entry
);
645 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
648 le
= Vcb
->sys_chunks
.Flink
;
649 while (le
!= &Vcb
->sys_chunks
) {
650 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
652 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
654 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
657 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
663 return STATUS_SUCCESS
;
666 static chunk
* alloc_chunk(device_extension
* Vcb
, UINT64 flags
, LIST_ENTRY
* rollback
) {
667 UINT64 max_stripe_size
, max_chunk_size
, stripe_size
;
668 UINT64 total_size
= 0, i
, j
, logaddr
;
674 CHUNK_ITEM_STRIPE
* cis
;
677 BOOL success
= FALSE
;
678 BLOCK_GROUP_ITEM
* bgi
;
680 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
681 total_size
+= Vcb
->devices
[i
].devitem
.num_bytes
;
683 TRACE("total_size = %llx\n", total_size
);
685 if (flags
& BLOCK_FLAG_DATA
) {
686 max_stripe_size
= 0x40000000; // 1 GB
687 max_chunk_size
= 10 * max_stripe_size
;
688 } else if (flags
& BLOCK_FLAG_METADATA
) {
689 if (total_size
> 0xC80000000) // 50 GB
690 max_stripe_size
= 0x40000000; // 1 GB
692 max_stripe_size
= 0x10000000; // 256 MB
694 max_chunk_size
= max_stripe_size
;
695 } else if (flags
& BLOCK_FLAG_SYSTEM
) {
696 max_stripe_size
= 0x2000000; // 32 MB
697 max_chunk_size
= 2 * max_stripe_size
;
700 // FIXME - make sure whole number of sectors?
701 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
703 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
705 if (flags
& BLOCK_FLAG_DUPLICATE
) {
707 } else if (flags
& BLOCK_FLAG_RAID0
) {
708 FIXME("RAID0 not yet supported\n");
710 } else if (flags
& BLOCK_FLAG_RAID1
) {
711 FIXME("RAID1 not yet supported\n");
713 } else if (flags
& BLOCK_FLAG_RAID10
) {
714 FIXME("RAID10 not yet supported\n");
716 } else if (flags
& BLOCK_FLAG_RAID5
) {
717 FIXME("RAID5 not yet supported\n");
719 } else if (flags
& BLOCK_FLAG_RAID6
) {
720 FIXME("RAID6 not yet supported\n");
726 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * num_stripes
, ALLOC_TAG
);
728 ERR("out of memory\n");
732 for (i
= 0; i
< num_stripes
; i
++) {
733 stripes
[i
].dh
= NULL
;
735 for (j
= 0; j
< Vcb
->superblock
.num_devices
; j
++) {
736 LIST_ENTRY
* le
= Vcb
->devices
[j
].disk_holes
.Flink
;
738 while (le
!= &Vcb
->devices
[j
].disk_holes
) {
739 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
741 if (!dh
->provisional
) {
742 if (!stripes
[i
].dh
|| dh
->size
> stripes
[i
].dh
->size
) {
744 stripes
[i
].device
= &Vcb
->devices
[j
];
746 if (stripes
[i
].dh
->size
>= max_stripe_size
)
754 if (stripes
[i
].dh
&& stripes
[i
].dh
->size
>= max_stripe_size
)
759 TRACE("good DH: device %llx, address %llx, size %llx\n", stripes
[i
].device
->devitem
.dev_id
, stripes
[i
].dh
->address
, stripes
[i
].dh
->size
);
761 TRACE("good DH not found\n");
765 add_provisional_disk_hole(Vcb
, &stripes
[i
], max_stripe_size
);
768 stripe_size
= min(stripes
[0].dh
->size
, max_stripe_size
);
769 for (i
= 1; i
< num_stripes
; i
++) {
770 stripe_size
= min(stripe_size
, stripes
[1].dh
->size
);
772 // FIXME - make sure stripe_size aligned properly
773 // FIXME - obey max_chunk_size
775 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
777 ERR("out of memory\n");
781 // add CHUNK_ITEM to tree 3
783 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
784 ci
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
786 ERR("out of memory\n");
790 ci
->size
= stripe_size
; // FIXME for RAID
791 ci
->root_id
= Vcb
->extent_root
->id
;
792 ci
->stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
794 ci
->opt_io_alignment
= ci
->stripe_length
;
795 ci
->opt_io_width
= ci
->stripe_length
;
796 ci
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
797 ci
->num_stripes
= num_stripes
;
800 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
802 ERR("out of memory\n");
807 for (i
= 0; i
< num_stripes
; i
++) {
809 cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
813 cis
->dev_id
= stripes
[i
].device
->devitem
.dev_id
;
814 cis
->offset
= stripes
[i
].dh
->address
;
815 cis
->dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
817 c
->devices
[i
] = stripes
[i
].device
;
820 logaddr
= find_new_chunk_address(Vcb
, ci
->size
);
821 if (logaddr
== 0xffffffffffffffff) {
822 ERR("find_new_chunk_address failed\n");
827 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
, NULL
, rollback
)) {
828 ERR("insert_tree_item failed\n");
833 if (flags
& BLOCK_FLAG_SYSTEM
) {
834 NTSTATUS Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
);
835 if (!NT_SUCCESS(Status
)) {
836 ERR("add_to_bootstrap returned %08x\n", Status
);
841 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
843 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
844 if (!c
->chunk_item
) {
845 ERR("out of memory\n");
849 RtlCopyMemory(c
->chunk_item
, ci
, cisize
);
852 c
->used
= c
->oldused
= 0;
853 c
->space_changed
= FALSE
;
854 InitializeListHead(&c
->space
);
856 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
858 ERR("out of memory\n");
862 s
->offset
= c
->offset
;
863 s
->size
= c
->chunk_item
->size
;
864 s
->type
= SPACE_TYPE_FREE
;
865 InsertTailList(&c
->space
, &s
->list_entry
);
867 protect_superblocks(Vcb
, c
);
869 // add BLOCK_GROUP_ITEM to tree 2
871 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
873 ERR("out of memory\n");
878 bgi
->chunk_tree
= 0x100;
881 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, logaddr
, TYPE_BLOCK_GROUP_ITEM
, ci
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, rollback
)) {
882 ERR("insert_tree_item failed\n");
887 // add DEV_EXTENTs to tree 4
889 for (i
= 0; i
< num_stripes
; i
++) {
892 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
894 ERR("out of memory\n");
898 de
->chunktree
= Vcb
->chunk_root
->id
;
900 de
->address
= logaddr
;
901 de
->length
= ci
->size
;
902 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
904 if (!insert_tree_item(Vcb
, Vcb
->dev_root
, stripes
[i
].device
->devitem
.dev_id
, TYPE_DEV_EXTENT
, stripes
[i
].dh
->address
, de
, sizeof(DEV_EXTENT
), NULL
, rollback
)) {
905 ERR("insert_tree_item failed\n");
910 if (!increase_dev_item_used(Vcb
, stripes
[i
].device
, ci
->size
, rollback
)) {
911 ERR("increase_dev_item_used failed\n");
916 for (i
= 0; i
< num_stripes
; i
++) {
918 for (j
= 0; j
< i
; j
++) {
919 if (stripes
[j
].device
== stripes
[i
].device
)
924 reset_disk_holes(stripes
[i
].device
, TRUE
);
933 for (i
= 0; i
< num_stripes
; i
++) {
935 for (j
= 0; j
< i
; j
++) {
936 if (stripes
[j
].device
== stripes
[i
].device
)
941 reset_disk_holes(stripes
[i
].device
, FALSE
);
944 if (c
) ExFreePool(c
);
945 if (s
) ExFreePool(s
);
947 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
949 return success
? c
: NULL
;
952 static void decrease_chunk_usage(chunk
* c
, UINT64 delta
) {
955 TRACE("decreasing size of chunk %llx by %llx\n", c
->offset
, delta
);
958 static void increase_chunk_usage(chunk
* c
, UINT64 delta
) {
961 TRACE("increasing size of chunk %llx by %llx\n", c
->offset
, delta
);
964 static NTSTATUS STDCALL
write_data(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
) {
971 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
973 // FIXME - use version cached in Vcb
975 searchkey
.obj_id
= 0x100; // fixed?
976 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
977 searchkey
.offset
= address
;
979 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
980 if (!NT_SUCCESS(Status
)) {
981 ERR("error - find_item returned %08x\n", Status
);
985 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
986 ERR("error - unexpected item in chunk tree\n");
987 Status
= STATUS_INTERNAL_ERROR
;
991 if (tp
.item
->size
< sizeof(CHUNK_ITEM2
)) {
992 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(CHUNK_ITEM2
));
993 Status
= STATUS_INTERNAL_ERROR
;
997 ci
= (CHUNK_ITEM2
*)tp
.item
->data
;
999 if (tp
.item
->key
.offset
> address
|| tp
.item
->key
.offset
+ ci
->ci
.size
< address
) {
1000 ERR("error - address %llx was out of chunk bounds\n", address
);
1001 Status
= STATUS_INTERNAL_ERROR
;
1005 // FIXME - only do this for chunks marked DUPLICATE?
1006 // FIXME - for multiple writes, if PENDING do waits at the end
1007 // FIXME - work with RAID
1008 for (i
= 0; i
< ci
->ci
.num_stripes
; i
++) {
1009 Status
= write_data_phys(Vcb
->devices
[0].devobj
, address
- tp
.item
->key
.offset
+ ci
->stripes
[i
].offset
, data
, length
);
1010 if (!NT_SUCCESS(Status
)) {
1011 ERR("error - write_data_phys failed\n");
1017 free_traverse_ptr(&tp
);
1022 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
1023 LIST_ENTRY
*le
, *nextle
;
1027 // le = c->space.Flink;
1028 // while (le != &c->space) {
1029 // s = CONTAINING_RECORD(le, space, list_entry);
1031 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1036 le
= c
->space
.Flink
;
1037 while (le
!= &c
->space
) {
1038 s
= CONTAINING_RECORD(le
, space
, list_entry
);
1041 if (s
->type
== SPACE_TYPE_DELETING
)
1042 s
->type
= SPACE_TYPE_FREE
;
1043 else if (s
->type
== SPACE_TYPE_WRITING
)
1044 s
->type
= SPACE_TYPE_USED
;
1046 if (le
->Blink
!= &c
->space
) {
1047 s2
= CONTAINING_RECORD(le
->Blink
, space
, list_entry
);
1049 if (s2
->type
== s
->type
) { // do merge
1050 s2
->size
+= s
->size
;
1052 RemoveEntryList(&s
->list_entry
);
1060 // le = c->space.Flink;
1061 // while (le != &c->space) {
1062 // s = CONTAINING_RECORD(le, space, list_entry);
1064 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1070 static void clean_space_cache(device_extension
* Vcb
) {
1074 TRACE("(%p)\n", Vcb
);
1076 le
= Vcb
->chunks
.Flink
;
1077 while (le
!= &Vcb
->chunks
) {
1078 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1080 if (c
->space_changed
) {
1081 clean_space_cache_chunk(Vcb
, c
);
1082 c
->space_changed
= FALSE
;
1089 static BOOL
trees_consistent(device_extension
* Vcb
) {
1090 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
1093 le
= Vcb
->tree_cache
.Flink
;
1094 while (le
!= &Vcb
->tree_cache
) {
1095 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1098 if (tc2
->tree
->header
.num_items
== 0 && tc2
->tree
->parent
)
1101 if (tc2
->tree
->size
> maxsize
)
1104 if (!tc2
->tree
->has_new_address
)
1114 static NTSTATUS
add_parents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1118 le
= Vcb
->tree_cache
.Flink
;
1119 while (le
!= &Vcb
->tree_cache
) {
1120 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1123 if (tc2
->tree
->parent
)
1124 add_to_tree_cache(Vcb
, tc2
->tree
->parent
, TRUE
);
1125 else if (tc2
->tree
->root
!= Vcb
->chunk_root
&& tc2
->tree
->root
!= Vcb
->root_root
) {
1129 searchkey
.obj_id
= tc2
->tree
->root
->id
;
1130 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1131 searchkey
.offset
= 0xffffffffffffffff;
1133 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1134 if (!NT_SUCCESS(Status
)) {
1135 ERR("error - find_item returned %08x\n", Status
);
1139 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1140 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1141 free_traverse_ptr(&tp
);
1142 return STATUS_INTERNAL_ERROR
;
1145 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
1146 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1148 ERR("out of memory\n");
1149 return STATUS_INSUFFICIENT_RESOURCES
;
1152 if (tp
.item
->size
> 0)
1153 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
1155 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
1157 delete_tree_item(Vcb
, &tp
, rollback
);
1159 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1160 ERR("insert_tree_item failed\n");
1161 return STATUS_INTERNAL_ERROR
;
1164 add_to_tree_cache(Vcb
, tp
.tree
, TRUE
);
1167 free_traverse_ptr(&tp
);
1174 return STATUS_SUCCESS
;
1177 void print_trees(LIST_ENTRY
* tc
) {
1178 LIST_ENTRY
*le
, *le2
;
1182 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
1183 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1184 UINT32 num_items
= 0;
1186 le2
= tc2
->tree
->itemlist
.Flink
;
1187 while (le2
!= &tc2
->tree
->itemlist
) {
1188 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1190 firstitem
= td
->key
;
1196 ERR("tree: root %llx, first key %llx,%x,%llx, level %x, num_items %x / %x\n",
1197 tc2
->tree
->header
.tree_id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
, tc2
->tree
->header
.level
, num_items
, tc2
->tree
->header
.num_items
);
1203 static void add_parents_to_cache(device_extension
* Vcb
, tree
* t
) {
1211 add_to_tree_cache(Vcb
, t
, TRUE
);
1214 if (t
->root
== Vcb
->root_root
|| t
->root
== Vcb
->chunk_root
)
1217 searchkey
.obj_id
= t
->root
->id
;
1218 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1219 searchkey
.offset
= 0xffffffffffffffff;
1221 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1222 if (!NT_SUCCESS(Status
)) {
1223 ERR("error - find_item returned %08x\n", Status
);
1227 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1228 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1229 free_traverse_ptr(&tp
);
1233 add_to_tree_cache(Vcb
, tp
.tree
, TRUE
);
1235 free_traverse_ptr(&tp
);
1238 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, tree
* t
, chunk
* c
, UINT64 address
, LIST_ENTRY
* rollback
) {
1239 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1240 traverse_ptr insert_tp
;
1242 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1244 ERR("out of memory\n");
1248 eism
->ei
.refcount
= 1;
1249 eism
->ei
.generation
= Vcb
->superblock
.generation
;
1250 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1251 eism
->type
= TYPE_TREE_BLOCK_REF
;
1252 eism
->tbr
.offset
= t
->header
.tree_id
;
1254 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, t
->header
.level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, rollback
)) {
1255 ERR("insert_tree_item failed\n");
1260 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1262 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1263 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1265 free_traverse_ptr(&insert_tp
);
1267 t
->new_address
= address
;
1268 t
->has_new_address
= TRUE
;
1273 static BOOL
insert_tree_extent(device_extension
* Vcb
, tree
* t
, chunk
* c
, LIST_ENTRY
* rollback
) {
1275 EXTENT_ITEM_TREE2
* eit2
;
1276 traverse_ptr insert_tp
;
1278 TRACE("(%p, %p, %p, %p)\n", Vcb
, t
, c
, rollback
);
1280 if (!find_address_in_chunk(Vcb
, c
, Vcb
->superblock
.node_size
, &address
))
1283 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)
1284 return insert_tree_extent_skinny(Vcb
, t
, c
, address
, rollback
);
1286 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1288 ERR("out of memory\n");
1292 eit2
->eit
.extent_item
.refcount
= 1;
1293 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
1294 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1295 // eit2->eit.firstitem = wt->firstitem;
1296 eit2
->eit
.level
= t
->header
.level
;
1297 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1298 eit2
->tbr
.offset
= t
->header
.tree_id
;
1300 // #ifdef DEBUG_PARANOID
1301 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
1302 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
1303 // ERR("num_items = %u, level = %u, root = %x, delete = %u\n", wt->tree->header.num_items, wt->tree->header.level, (UINT32)wt->tree->root->id, wt->delete);
1308 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, rollback
)) {
1309 ERR("insert_tree_item failed\n");
1314 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1316 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1317 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1319 free_traverse_ptr(&insert_tp
);
1321 t
->new_address
= address
;
1322 t
->has_new_address
= TRUE
;
1327 static NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
1328 chunk
*origchunk
= NULL
, *c
;
1330 UINT64 flags
= t
->flags
;
1333 flags
= (t
->root
->id
== BTRFS_ROOT_CHUNK
? BLOCK_FLAG_SYSTEM
: BLOCK_FLAG_METADATA
) | BLOCK_FLAG_DUPLICATE
;
1335 // TRACE("flags = %x\n", (UINT32)wt->flags);
1337 // if (!chunk_test) { // TESTING
1338 // if ((c = alloc_chunk(Vcb, flags))) {
1339 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1340 // if (insert_tree_extent(Vcb, t, c)) {
1341 // chunk_test = TRUE;
1342 // return STATUS_SUCCESS;
1348 if (t
->has_address
) {
1349 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
1351 if (insert_tree_extent(Vcb
, t
, origchunk
, rollback
))
1352 return STATUS_SUCCESS
;
1355 le
= Vcb
->chunks
.Flink
;
1356 while (le
!= &Vcb
->chunks
) {
1357 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1359 // FIXME - make sure to avoid superblocks
1361 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1362 if (insert_tree_extent(Vcb
, t
, c
, rollback
))
1363 return STATUS_SUCCESS
;
1369 // allocate new chunk if necessary
1370 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
1371 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1372 if (insert_tree_extent(Vcb
, t
, c
, rollback
))
1373 return STATUS_SUCCESS
;
1377 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
1379 return STATUS_DISK_FULL
;
1382 static BOOL
reduce_tree_extent_skinny(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1386 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1389 searchkey
.obj_id
= address
;
1390 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
1391 searchkey
.offset
= 0xffffffffffffffff;
1393 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1394 if (!NT_SUCCESS(Status
)) {
1395 ERR("error - find_item returned %08x\n", Status
);
1399 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1400 TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1401 free_traverse_ptr(&tp
);
1405 if (tp
.item
->size
< sizeof(EXTENT_ITEM_SKINNY_METADATA
)) {
1406 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM_SKINNY_METADATA
));
1407 free_traverse_ptr(&tp
);
1411 delete_tree_item(Vcb
, &tp
, rollback
);
1413 eism
= (EXTENT_ITEM_SKINNY_METADATA
*)tp
.item
->data
;
1414 if (t
->header
.level
== 0 && eism
->ei
.flags
& EXTENT_ITEM_SHARED_BACKREFS
&& eism
->type
== TYPE_TREE_BLOCK_REF
) {
1415 // convert shared data extents
1417 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1418 while (le
!= &t
->itemlist
) {
1419 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1421 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1423 if (!td
->ignore
&& !td
->inserted
) {
1424 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1425 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1427 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1428 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1430 if (ed2
->address
!= 0) {
1431 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1432 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1441 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1444 c
= get_chunk_from_address(Vcb
, address
);
1447 decrease_chunk_usage(c
, Vcb
->superblock
.node_size
);
1449 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_DELETING
);
1451 ERR("could not find chunk for address %llx\n", address
);
1453 free_traverse_ptr(&tp
);
1459 // static void check_tree_num_items(tree* t) {
1463 // le2 = t->itemlist.Flink;
1465 // while (le2 != &t->itemlist) {
1466 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1469 // le2 = le2->Flink;
1472 // if (t->header.num_items != ni) {
1473 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
1476 // ERR("tree %p okay\n", t);
1480 // static void check_trees_num_items(LIST_ENTRY* tc) {
1481 // LIST_ENTRY* le = tc->Flink;
1482 // while (le != tc) {
1483 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
1485 // check_tree_num_items(tc2->tree);
1491 static void convert_old_tree_extent(device_extension
* Vcb
, tree_data
* td
, tree
* t
, LIST_ENTRY
* rollback
) {
1493 traverse_ptr tp
, tp2
, insert_tp
;
1494 EXTENT_REF_V0
* erv0
;
1497 TRACE("(%p, %p, %p)\n", Vcb
, td
, t
);
1499 searchkey
.obj_id
= td
->treeholder
.address
;
1500 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1501 searchkey
.offset
= 0xffffffffffffffff;
1503 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1504 if (!NT_SUCCESS(Status
)) {
1505 ERR("error - find_item returned %08x\n", Status
);
1509 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1510 TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey
.obj_id
);
1511 free_traverse_ptr(&tp
);
1515 searchkey
.obj_id
= td
->treeholder
.address
;
1516 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1517 searchkey
.offset
= Vcb
->superblock
.node_size
;
1519 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1520 if (!NT_SUCCESS(Status
)) {
1521 ERR("error - find_item returned %08x\n", Status
);
1522 free_traverse_ptr(&tp
);
1526 if (keycmp(&searchkey
, &tp2
.item
->key
)) {
1527 ERR("could not find %llx,%x,%llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1528 free_traverse_ptr(&tp2
);
1529 free_traverse_ptr(&tp
);
1533 if (tp
.item
->size
< sizeof(EXTENT_REF_V0
)) {
1534 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_REF_V0
));
1535 free_traverse_ptr(&tp2
);
1536 free_traverse_ptr(&tp
);
1540 erv0
= (EXTENT_REF_V0
*)tp
.item
->data
;
1542 delete_tree_item(Vcb
, &tp
, rollback
);
1543 delete_tree_item(Vcb
, &tp2
, rollback
);
1545 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1546 EXTENT_ITEM_SKINNY_METADATA
* eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1549 ERR("out of memory\n");
1550 free_traverse_ptr(&tp2
);
1551 free_traverse_ptr(&tp
);
1555 eism
->ei
.refcount
= 1;
1556 eism
->ei
.generation
= erv0
->gen
;
1557 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1558 eism
->type
= TYPE_TREE_BLOCK_REF
;
1559 eism
->tbr
.offset
= t
->header
.tree_id
;
1561 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, td
->treeholder
.address
, TYPE_METADATA_ITEM
, t
->header
.level
-1, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, rollback
)) {
1562 ERR("insert_tree_item failed\n");
1563 free_traverse_ptr(&tp2
);
1564 free_traverse_ptr(&tp
);
1568 EXTENT_ITEM_TREE2
* eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1571 ERR("out of memory\n");
1572 free_traverse_ptr(&tp2
);
1573 free_traverse_ptr(&tp
);
1577 eit2
->eit
.extent_item
.refcount
= 1;
1578 eit2
->eit
.extent_item
.generation
= erv0
->gen
;
1579 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1580 eit2
->eit
.firstitem
= td
->key
;
1581 eit2
->eit
.level
= t
->header
.level
- 1;
1582 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1583 eit2
->tbr
.offset
= t
->header
.tree_id
;
1585 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, td
->treeholder
.address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, rollback
)) {
1586 ERR("insert_tree_item failed\n");
1587 free_traverse_ptr(&tp2
);
1588 free_traverse_ptr(&tp
);
1593 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1594 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1595 add_parents_to_cache(Vcb
, tp
.tree
);
1596 add_parents_to_cache(Vcb
, tp2
.tree
);
1598 free_traverse_ptr(&insert_tp
);
1600 free_traverse_ptr(&tp2
);
1601 free_traverse_ptr(&tp
);
1604 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1608 EXTENT_ITEM_V0
* eiv0
;
1612 // FIXME - deal with refcounts > 1
1614 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
1616 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1617 if (reduce_tree_extent_skinny(Vcb
, address
, t
, rollback
)) {
1618 return STATUS_SUCCESS
;
1622 searchkey
.obj_id
= address
;
1623 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1624 searchkey
.offset
= Vcb
->superblock
.node_size
;
1626 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1627 if (!NT_SUCCESS(Status
)) {
1628 ERR("error - find_item returned %08x\n", Status
);
1632 if (keycmp(&tp
.item
->key
, &searchkey
)) {
1633 ERR("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1635 free_traverse_ptr(&tp
);
1636 return STATUS_INTERNAL_ERROR
;
1639 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1640 eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1642 if (eiv0
->refcount
> 1) {
1643 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0
->refcount
);
1644 free_traverse_ptr(&tp
);
1645 return STATUS_INTERNAL_ERROR
;
1648 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1649 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
1650 free_traverse_ptr(&tp
);
1651 return STATUS_INTERNAL_ERROR
;
1654 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1656 if (ei
->refcount
> 1) {
1657 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei
->refcount
);
1658 free_traverse_ptr(&tp
);
1659 return STATUS_INTERNAL_ERROR
;
1662 if (t
->header
.level
== 0 && ei
->flags
& EXTENT_ITEM_SHARED_BACKREFS
) {
1663 // convert shared data extents
1665 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1666 while (le
!= &t
->itemlist
) {
1667 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1669 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1671 if (!td
->ignore
&& !td
->inserted
) {
1672 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1673 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1675 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1676 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1678 if (ed2
->address
!= 0) {
1679 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1680 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1689 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1693 delete_tree_item(Vcb
, &tp
, rollback
);
1695 // if EXTENT_ITEM_V0, delete corresponding B4 item
1696 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1699 searchkey
.obj_id
= address
;
1700 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1701 searchkey
.offset
= 0xffffffffffffffff;
1703 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1704 if (!NT_SUCCESS(Status
)) {
1705 ERR("error - find_item returned %08x\n", Status
);
1706 free_traverse_ptr(&tp
);
1710 if (tp2
.item
->key
.obj_id
== searchkey
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey
.obj_type
) {
1711 delete_tree_item(Vcb
, &tp2
, rollback
);
1713 free_traverse_ptr(&tp2
);
1716 if (!(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1719 // when writing old internal trees, convert related extents
1721 le
= t
->itemlist
.Flink
;
1722 while (le
!= &t
->itemlist
) {
1723 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1725 // ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
1727 if (!td
->ignore
&& !td
->inserted
) {
1728 if (t
->header
.level
> 0) {
1729 convert_old_tree_extent(Vcb
, td
, t
, rollback
);
1730 } else if (td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
)) {
1731 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1733 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1734 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1736 if (ed2
->address
!= 0) {
1737 TRACE("trying to convert old data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1738 convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1748 c
= get_chunk_from_address(Vcb
, address
);
1751 decrease_chunk_usage(c
, tp
.item
->key
.offset
);
1753 add_to_space_list(c
, address
, tp
.item
->key
.offset
, SPACE_TYPE_DELETING
);
1755 ERR("could not find chunk for address %llx\n", address
);
1757 free_traverse_ptr(&tp
);
1759 return STATUS_SUCCESS
;
1762 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1766 TRACE("(%p)\n", Vcb
);
1768 le
= Vcb
->tree_cache
.Flink
;
1769 while (le
!= &Vcb
->tree_cache
) {
1770 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1772 if (tc2
->write
&& !tc2
->tree
->has_new_address
) {
1775 Status
= get_tree_new_address(Vcb
, tc2
->tree
, rollback
);
1776 if (!NT_SUCCESS(Status
)) {
1777 ERR("get_tree_new_address returned %08x\n", Status
);
1781 TRACE("allocated extent %llx\n", tc2
->tree
->new_address
);
1783 if (tc2
->tree
->has_address
) {
1784 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
1786 if (!NT_SUCCESS(Status
)) {
1787 ERR("reduce_tree_extent returned %08x\n", Status
);
1792 c
= get_chunk_from_address(Vcb
, tc2
->tree
->new_address
);
1795 increase_chunk_usage(c
, Vcb
->superblock
.node_size
);
1797 ERR("could not find chunk for address %llx\n", tc2
->tree
->new_address
);
1798 return STATUS_INTERNAL_ERROR
;
1805 return STATUS_SUCCESS
;
1808 static NTSTATUS
update_root_root(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1812 TRACE("(%p)\n", Vcb
);
1814 le
= Vcb
->tree_cache
.Flink
;
1815 while (le
!= &Vcb
->tree_cache
) {
1816 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1818 if (tc2
->write
&& !tc2
->tree
->parent
) {
1819 if (tc2
->tree
->root
!= Vcb
->root_root
&& tc2
->tree
->root
!= Vcb
->chunk_root
) {
1823 searchkey
.obj_id
= tc2
->tree
->root
->id
;
1824 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1825 searchkey
.offset
= 0xffffffffffffffff;
1827 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1828 if (!NT_SUCCESS(Status
)) {
1829 ERR("error - find_item returned %08x\n", Status
);
1833 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1834 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1835 free_traverse_ptr(&tp
);
1836 return STATUS_INTERNAL_ERROR
;
1839 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, tc2
->tree
->new_address
);
1841 tc2
->tree
->root
->root_item
.block_number
= tc2
->tree
->new_address
;
1842 tc2
->tree
->root
->root_item
.root_level
= tc2
->tree
->header
.level
;
1843 tc2
->tree
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1844 tc2
->tree
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1846 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
1847 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1850 ERR("out of memory\n");
1851 return STATUS_INSUFFICIENT_RESOURCES
;
1854 RtlCopyMemory(ri
, &tc2
->tree
->root
->root_item
, sizeof(ROOT_ITEM
));
1856 delete_tree_item(Vcb
, &tp
, rollback
);
1858 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1859 ERR("insert_tree_item failed\n");
1860 return STATUS_INTERNAL_ERROR
;
1863 RtlCopyMemory(tp
.item
->data
, &tc2
->tree
->root
->root_item
, sizeof(ROOT_ITEM
));
1865 free_traverse_ptr(&tp
);
1868 tc2
->tree
->root
->treeholder
.address
= tc2
->tree
->new_address
;
1874 return STATUS_SUCCESS
;
1877 enum write_tree_status
{
1878 WriteTreeStatus_Pending
,
1879 WriteTreeStatus_Success
,
1880 WriteTreeStatus_Error
,
1881 WriteTreeStatus_Cancelling
,
1882 WriteTreeStatus_Cancelled
1885 struct write_tree_context
;
1888 struct write_tree_context
* context
;
1892 IO_STATUS_BLOCK iosb
;
1893 enum write_tree_status status
;
1894 LIST_ENTRY list_entry
;
1895 } write_tree_stripe
;
1900 } write_tree_context
;
1902 static NTSTATUS STDCALL
write_tree_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1903 write_tree_stripe
* stripe
= conptr
;
1904 write_tree_context
* context
= (write_tree_context
*)stripe
->context
;
1908 if (stripe
->status
== WriteTreeStatus_Cancelling
) {
1909 stripe
->status
= WriteTreeStatus_Cancelled
;
1913 stripe
->iosb
= Irp
->IoStatus
;
1915 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
1916 stripe
->status
= WriteTreeStatus_Success
;
1918 le
= context
->stripes
.Flink
;
1920 stripe
->status
= WriteTreeStatus_Error
;
1922 while (le
!= &context
->stripes
) {
1923 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1925 if (s2
->status
== WriteTreeStatus_Pending
) {
1926 s2
->status
= WriteTreeStatus_Cancelling
;
1927 IoCancelIrp(s2
->Irp
);
1935 le
= context
->stripes
.Flink
;
1938 while (le
!= &context
->stripes
) {
1939 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1941 if (s2
->status
== WriteTreeStatus_Pending
|| s2
->status
== WriteTreeStatus_Cancelling
) {
1950 KeSetEvent(&context
->Event
, 0, FALSE
);
1952 return STATUS_MORE_PROCESSING_REQUIRED
;
1955 static NTSTATUS
write_tree(device_extension
* Vcb
, UINT64 addr
, UINT8
* data
, write_tree_context
* wtc
) {
1957 CHUNK_ITEM_STRIPE
* cis
;
1958 write_tree_stripe
* stripe
;
1961 c
= get_chunk_from_address(Vcb
, addr
);
1964 ERR("get_chunk_from_address failed\n");
1965 return STATUS_INTERNAL_ERROR
;
1968 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1970 // FIXME - make this work with RAID
1972 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1973 PIO_STACK_LOCATION IrpSp
;
1975 // FIXME - handle missing devices
1977 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_stripe
), ALLOC_TAG
);
1979 ERR("out of memory\n");
1980 return STATUS_INSUFFICIENT_RESOURCES
;
1983 stripe
->context
= (struct write_tree_context
*)wtc
;
1985 stripe
->device
= c
->devices
[i
];
1986 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
1987 stripe
->status
= WriteTreeStatus_Pending
;
1989 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
1992 ERR("IoAllocateIrp failed\n");
1993 return STATUS_INTERNAL_ERROR
;
1996 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
1997 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
1999 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
2000 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= data
;
2002 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
2003 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
2004 stripe
->Irp
->MdlAddress
= IoAllocateMdl(data
, Vcb
->superblock
.node_size
, FALSE
, FALSE
, NULL
);
2005 if (!stripe
->Irp
->MdlAddress
) {
2006 ERR("IoAllocateMdl failed\n");
2007 return STATUS_INTERNAL_ERROR
;
2010 MmProbeAndLockPages(stripe
->Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2012 stripe
->Irp
->UserBuffer
= data
;
2015 IrpSp
->Parameters
.Write
.Length
= Vcb
->superblock
.node_size
;
2016 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= addr
- c
->offset
+ cis
[i
].offset
;
2018 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
2020 IoSetCompletionRoutine(stripe
->Irp
, write_tree_completion
, stripe
, TRUE
, TRUE
, TRUE
);
2022 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
2025 return STATUS_SUCCESS
;
2028 static void free_stripes(write_tree_context
* wtc
) {
2029 LIST_ENTRY
*le
, *le2
, *nextle
;
2031 le
= wtc
->stripes
.Flink
;
2032 while (le
!= &wtc
->stripes
) {
2033 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2035 if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
2036 MmUnlockPages(stripe
->Irp
->MdlAddress
);
2037 IoFreeMdl(stripe
->Irp
->MdlAddress
);
2043 le
= wtc
->stripes
.Flink
;
2044 while (le
!= &wtc
->stripes
) {
2045 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2050 ExFreePool(stripe
->buf
);
2053 while (le2
!= &wtc
->stripes
) {
2054 write_tree_stripe
* s2
= CONTAINING_RECORD(le2
, write_tree_stripe
, list_entry
);
2056 if (s2
->buf
== stripe
->buf
)
2070 static NTSTATUS
write_trees(device_extension
* Vcb
) {
2076 write_tree_context
* wtc
;
2078 TRACE("(%p)\n", Vcb
);
2080 for (level
= 0; level
<= 255; level
++) {
2081 BOOL nothing_found
= TRUE
;
2083 TRACE("level = %u\n", level
);
2085 le
= Vcb
->tree_cache
.Flink
;
2086 while (le
!= &Vcb
->tree_cache
) {
2087 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2089 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
2090 KEY firstitem
, searchkey
;
2093 EXTENT_ITEM_TREE
* eit
;
2095 if (!tc2
->tree
->has_new_address
) {
2096 ERR("error - tried to write tree with no new address\n");
2100 le2
= tc2
->tree
->itemlist
.Flink
;
2101 while (le2
!= &tc2
->tree
->itemlist
) {
2102 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2104 firstitem
= td
->key
;
2110 if (tc2
->tree
->parent
) {
2111 tc2
->tree
->paritem
->key
= firstitem
;
2112 tc2
->tree
->paritem
->treeholder
.address
= tc2
->tree
->new_address
;
2113 tc2
->tree
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
2116 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
2117 searchkey
.obj_id
= tc2
->tree
->new_address
;
2118 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2119 searchkey
.offset
= Vcb
->superblock
.node_size
;
2121 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2122 if (!NT_SUCCESS(Status
)) {
2123 ERR("error - find_item returned %08x\n", Status
);
2127 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2128 // traverse_ptr next_tp;
2130 // tree_data* paritem;
2132 ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2133 free_traverse_ptr(&tp
);
2135 // searchkey.obj_id = 0;
2136 // searchkey.obj_type = 0;
2137 // searchkey.offset = 0;
2139 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
2143 // if (tp.tree->paritem != paritem) {
2144 // paritem = tp.tree->paritem;
2145 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
2148 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2150 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
2152 // free_traverse_ptr(&tp);
2157 // free_traverse_ptr(&tp);
2159 return STATUS_INTERNAL_ERROR
;
2162 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
2163 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM_TREE
));
2164 free_traverse_ptr(&tp
);
2165 return STATUS_INTERNAL_ERROR
;
2168 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
2169 eit
->firstitem
= firstitem
;
2171 free_traverse_ptr(&tp
);
2174 nothing_found
= FALSE
;
2184 TRACE("allocated tree extents\n");
2186 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_context
), ALLOC_TAG
);
2188 ERR("out of memory\n");
2189 return STATUS_INSUFFICIENT_RESOURCES
;
2192 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
2193 InitializeListHead(&wtc
->stripes
);
2195 le
= Vcb
->tree_cache
.Flink
;
2196 while (le
!= &Vcb
->tree_cache
) {
2197 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2198 #ifdef DEBUG_PARANOID
2199 UINT32 num_items
= 0, size
= 0;
2205 #ifdef DEBUG_PARANOID
2206 le2
= tc2
->tree
->itemlist
.Flink
;
2207 while (le2
!= &tc2
->tree
->itemlist
) {
2208 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2212 if (tc2
->tree
->header
.level
== 0)
2218 if (tc2
->tree
->header
.level
== 0)
2219 size
+= num_items
* sizeof(leaf_node
);
2221 size
+= num_items
* sizeof(internal_node
);
2223 if (num_items
!= tc2
->tree
->header
.num_items
) {
2224 ERR("tree %llx, level %x: num_items was %x, expected %x\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
, num_items
, tc2
->tree
->header
.num_items
);
2228 if (size
!= tc2
->tree
->size
) {
2229 ERR("tree %llx, level %x: size was %x, expected %x\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
, size
, tc2
->tree
->size
);
2233 if (tc2
->tree
->header
.num_items
== 0 && tc2
->tree
->parent
) {
2234 ERR("tree %llx, level %x: tried to write empty tree with parent\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
);
2238 if (tc2
->tree
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2239 ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
, tc2
->tree
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2244 ERR("tree %p\n", tc2
->tree
);
2245 le2
= tc2
->tree
->itemlist
.Flink
;
2246 while (le2
!= &tc2
->tree
->itemlist
) {
2247 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2249 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
2256 tc2
->tree
->header
.address
= tc2
->tree
->new_address
;
2257 tc2
->tree
->header
.generation
= Vcb
->superblock
.generation
;
2258 tc2
->tree
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
2259 tc2
->tree
->has_address
= TRUE
;
2261 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
2263 ERR("out of memory\n");
2264 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2268 body
= data
+ sizeof(tree_header
);
2270 RtlCopyMemory(data
, &tc2
->tree
->header
, sizeof(tree_header
));
2271 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2273 if (tc2
->tree
->header
.level
== 0) {
2274 leaf_node
* itemptr
= (leaf_node
*)body
;
2277 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
2279 le2
= tc2
->tree
->itemlist
.Flink
;
2280 while (le2
!= &tc2
->tree
->itemlist
) {
2281 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2283 dataptr
= dataptr
- td
->size
;
2285 itemptr
[i
].key
= td
->key
;
2286 itemptr
[i
].offset
= (UINT8
*)dataptr
- (UINT8
*)body
;
2287 itemptr
[i
].size
= td
->size
;
2291 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
2297 internal_node
* itemptr
= (internal_node
*)body
;
2301 le2
= tc2
->tree
->itemlist
.Flink
;
2302 while (le2
!= &tc2
->tree
->itemlist
) {
2303 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2305 itemptr
[i
].key
= td
->key
;
2306 itemptr
[i
].address
= td
->treeholder
.address
;
2307 itemptr
[i
].generation
= td
->treeholder
.generation
;
2315 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
2317 *((UINT32
*)data
) = crc32
;
2318 TRACE("setting crc32 to %08x\n", crc32
);
2320 Status
= write_tree(Vcb
, tc2
->tree
->new_address
, data
, wtc
);
2321 if (!NT_SUCCESS(Status
)) {
2322 ERR("write_tree returned %08x\n", Status
);
2330 Status
= STATUS_SUCCESS
;
2332 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
2333 // launch writes and wait
2334 le
= wtc
->stripes
.Flink
;
2335 while (le
!= &wtc
->stripes
) {
2336 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2338 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2343 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2345 le
= wtc
->stripes
.Flink
;
2346 while (le
!= &wtc
->stripes
) {
2347 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2349 if (!NT_SUCCESS(stripe
->iosb
.Status
)) {
2350 Status
= stripe
->iosb
.Status
;
2366 static NTSTATUS
write_superblocks(device_extension
* Vcb
) {
2371 TRACE("(%p)\n", Vcb
);
2373 le
= Vcb
->tree_cache
.Flink
;
2374 while (le
!= &Vcb
->tree_cache
) {
2375 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2377 if (tc2
->write
&& !tc2
->tree
->parent
) {
2378 if (tc2
->tree
->root
== Vcb
->root_root
) {
2379 Vcb
->superblock
.root_tree_addr
= tc2
->tree
->new_address
;
2380 Vcb
->superblock
.root_level
= tc2
->tree
->header
.level
;
2381 } else if (tc2
->tree
->root
== Vcb
->chunk_root
) {
2382 Vcb
->superblock
.chunk_tree_addr
= tc2
->tree
->new_address
;
2383 Vcb
->superblock
.chunk_root_generation
= tc2
->tree
->header
.generation
;
2384 Vcb
->superblock
.chunk_root_level
= tc2
->tree
->header
.level
;
2391 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2392 if (Vcb
->devices
[i
].devobj
) {
2393 Status
= write_superblock(Vcb
, &Vcb
->devices
[i
]);
2394 if (!NT_SUCCESS(Status
)) {
2395 ERR("write_superblock returned %08x\n", Status
);
2401 return STATUS_SUCCESS
;
2404 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
2405 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
2409 BLOCK_GROUP_ITEM
* bgi
;
2412 TRACE("(%p)\n", Vcb
);
2414 while (le
!= &Vcb
->chunks
) {
2415 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2417 if (c
->used
!= c
->oldused
) {
2418 searchkey
.obj_id
= c
->offset
;
2419 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2420 searchkey
.offset
= c
->chunk_item
->size
;
2422 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2423 if (!NT_SUCCESS(Status
)) {
2424 ERR("error - find_item returned %08x\n", Status
);
2428 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2429 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2431 free_traverse_ptr(&tp
);
2432 return STATUS_INTERNAL_ERROR
;
2435 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2436 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(BLOCK_GROUP_ITEM
));
2437 free_traverse_ptr(&tp
);
2438 return STATUS_INTERNAL_ERROR
;
2441 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2443 ERR("out of memory\n");
2444 free_traverse_ptr(&tp
);
2445 return STATUS_INSUFFICIENT_RESOURCES
;
2448 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2449 bgi
->used
= c
->used
;
2451 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2453 delete_tree_item(Vcb
, &tp
, rollback
);
2455 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, rollback
)) {
2456 ERR("insert_tree_item failed\n");
2458 return STATUS_INTERNAL_ERROR
;
2461 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2462 TRACE("chunk_item type = %llx\n", c
->chunk_item
->type
);
2464 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
2465 FIXME("RAID0 not yet supported\n");
2467 free_traverse_ptr(&tp
);
2468 return STATUS_INTERNAL_ERROR
;
2469 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
) {
2470 FIXME("RAID1 not yet supported\n");
2472 free_traverse_ptr(&tp
);
2473 return STATUS_INTERNAL_ERROR
;
2474 } else if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
) {
2475 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ (2 * (c
->used
- c
->oldused
));
2476 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
2477 FIXME("RAID10 not yet supported\n");
2479 free_traverse_ptr(&tp
);
2480 return STATUS_INTERNAL_ERROR
;
2481 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
2482 FIXME("RAID5 not yet supported\n");
2484 free_traverse_ptr(&tp
);
2485 return STATUS_INTERNAL_ERROR
;
2486 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2487 FIXME("RAID6 not yet supported\n");
2489 free_traverse_ptr(&tp
);
2490 return STATUS_INTERNAL_ERROR
;
2492 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ c
->used
- c
->oldused
;
2495 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2497 free_traverse_ptr(&tp
);
2499 c
->oldused
= c
->used
;
2505 return STATUS_SUCCESS
;
2508 static void get_first_item(tree
* t
, KEY
* key
) {
2511 le
= t
->itemlist
.Flink
;
2512 while (le
!= &t
->itemlist
) {
2513 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2520 static NTSTATUS STDCALL
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2523 tree_data
* oldlastitem
;
2525 // // tree_data *firsttd, *lasttd;
2526 // // LIST_ENTRY* le;
2527 // #ifdef DEBUG_PARANOID
2528 // KEY lastkey1, lastkey2;
2529 // traverse_ptr tp, next_tp;
2530 // ULONG numitems1, numitems2;
2533 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2535 // #ifdef DEBUG_PARANOID
2536 // lastkey1.obj_id = 0xffffffffffffffff;
2537 // lastkey1.obj_type = 0xff;
2538 // lastkey1.offset = 0xffffffffffffffff;
2540 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2541 // ERR("error - find_item failed\n");
2543 // lastkey1 = tp.item->key;
2545 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2546 // free_traverse_ptr(&tp);
2550 // free_traverse_ptr(&tp);
2554 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2556 ERR("out of memory\n");
2557 return STATUS_INSUFFICIENT_RESOURCES
;
2560 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2561 nt
->header
.address
= 0;
2562 nt
->header
.generation
= Vcb
->superblock
.generation
;
2563 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2564 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2567 nt
->has_address
= FALSE
;
2569 nt
->parent
= t
->parent
;
2571 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2572 nt
->new_address
= 0;
2573 nt
->has_new_address
= FALSE
;
2574 nt
->flags
= t
->flags
;
2575 InitializeListHead(&nt
->itemlist
);
2577 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2579 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2581 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2582 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2584 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2585 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2586 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2588 // // le = wt->tree->itemlist.Flink;
2589 // // while (le != &wt->tree->itemlist) {
2590 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2591 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2592 // // le = le->Flink;
2595 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2596 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2597 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2598 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2600 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2601 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2603 // // le = wt->tree->itemlist.Flink;
2604 // // while (le != &wt->tree->itemlist) {
2605 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2606 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2607 // // le = le->Flink;
2610 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2611 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2613 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2614 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2615 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2617 nt
->size
= t
->size
- size
;
2619 t
->header
.num_items
= numitems
;
2620 add_to_tree_cache(Vcb
, nt
, TRUE
);
2622 InterlockedIncrement(&Vcb
->open_trees
);
2623 #ifdef DEBUG_TREE_REFCOUNTS
2624 TRACE("created new split tree %p\n", nt
);
2626 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2629 // // td = wt->tree->items;
2631 // // if (!td->ignore) {
2632 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2634 // // td = td->next;
2637 // // oldlastitem->next = NULL;
2638 // // wt->tree->lastitem = oldlastitem;
2640 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2642 if (nt
->header
.level
> 0) {
2643 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2645 while (le
!= &nt
->itemlist
) {
2646 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2648 if (td2
->treeholder
.tree
) {
2649 td2
->treeholder
.tree
->parent
= nt
;
2650 increase_tree_rc(nt
);
2659 increase_tree_rc(nt
->parent
);
2661 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2663 ERR("out of memory\n");
2664 return STATUS_INSUFFICIENT_RESOURCES
;
2667 td
->key
= newfirstitem
->key
;
2669 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2672 td
->inserted
= TRUE
;
2673 td
->treeholder
.tree
= nt
;
2674 init_tree_holder(&td
->treeholder
);
2675 // td->treeholder.nonpaged->status = tree_holder_loaded;
2678 nt
->parent
->header
.num_items
++;
2679 nt
->parent
->size
+= sizeof(internal_node
);
2684 TRACE("adding new tree parent\n");
2686 if (nt
->header
.level
== 255) {
2687 ERR("cannot add parent to tree at level 255\n");
2688 return STATUS_INTERNAL_ERROR
;
2691 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2693 ERR("out of memory\n");
2694 return STATUS_INSUFFICIENT_RESOURCES
;
2697 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2698 pt
->header
.address
= 0;
2699 pt
->header
.num_items
= 2;
2700 pt
->header
.level
= nt
->header
.level
+ 1;
2701 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2704 pt
->has_address
= FALSE
;
2709 pt
->new_address
= 0;
2710 pt
->has_new_address
= FALSE
;
2711 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2712 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2713 pt
->flags
= t
->flags
;
2714 InitializeListHead(&pt
->itemlist
);
2716 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2718 InterlockedIncrement(&Vcb
->open_trees
);
2719 #ifdef DEBUG_TREE_REFCOUNTS
2720 TRACE("created new parent tree %p\n", pt
);
2722 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2724 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2726 ERR("out of memory\n");
2727 return STATUS_INSUFFICIENT_RESOURCES
;
2730 get_first_item(t
, &td
->key
);
2732 td
->inserted
= FALSE
;
2733 td
->treeholder
.address
= 0;
2734 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2735 td
->treeholder
.tree
= t
;
2736 init_tree_holder(&td
->treeholder
);
2737 // td->treeholder.nonpaged->status = tree_holder_loaded;
2738 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2741 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2743 ERR("out of memory\n");
2744 return STATUS_INSUFFICIENT_RESOURCES
;
2747 td
->key
= newfirstitem
->key
;
2749 td
->inserted
= FALSE
;
2750 td
->treeholder
.address
= 0;
2751 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2752 td
->treeholder
.tree
= nt
;
2753 init_tree_holder(&td
->treeholder
);
2754 // td->treeholder.nonpaged->status = tree_holder_loaded;
2755 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2758 add_to_tree_cache(Vcb
, pt
, TRUE
);
2760 t
->root
->treeholder
.tree
= pt
;
2766 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2768 // #ifdef DEBUG_PARANOID
2769 // lastkey2.obj_id = 0xffffffffffffffff;
2770 // lastkey2.obj_type = 0xff;
2771 // lastkey2.offset = 0xffffffffffffffff;
2773 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2774 // ERR("error - find_item failed\n");
2776 // lastkey2 = tp.item->key;
2779 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2780 // free_traverse_ptr(&tp);
2784 // free_traverse_ptr(&tp);
2787 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2788 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2789 // ERR("numitems1 = %u\n", numitems1);
2790 // ERR("numitems2 = %u\n", numitems2);
2793 return STATUS_SUCCESS
;
2796 static NTSTATUS STDCALL
split_tree(device_extension
* Vcb
, tree
* t
) {
2798 UINT32 size
, ds
, numitems
;
2803 // FIXME - naïve implementation: maximizes number of filled trees
2805 le
= t
->itemlist
.Flink
;
2806 while (le
!= &t
->itemlist
) {
2807 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2810 if (t
->header
.level
== 0)
2811 ds
= sizeof(leaf_node
) + td
->size
;
2813 ds
= sizeof(internal_node
);
2815 // FIXME - move back if previous item was deleted item with same key
2816 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
2817 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
2826 return STATUS_SUCCESS
;
2829 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
2831 tree_data
* nextparitem
= NULL
;
2833 tree
*next_tree
, *par
;
2836 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
2838 // FIXME - doesn't capture everything, as it doesn't ascend
2839 // FIXME - write proper function and put it in treefuncs.c
2840 le
= t
->paritem
->list_entry
.Flink
;
2841 while (le
!= &t
->parent
->itemlist
) {
2842 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2853 return STATUS_SUCCESS
;
2855 // FIXME - loop, and capture more than one tree if we can
2857 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
2858 // nextparitem = t->paritem;
2860 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2862 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
);
2863 if (!NT_SUCCESS(Status
)) {
2864 ERR("do_load_tree returned %08x\n", Status
);
2869 increase_tree_rc(t
->parent
);
2871 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2873 next_tree
= nextparitem
->treeholder
.tree
;
2875 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2876 // merge two trees into one
2878 t
->header
.num_items
+= next_tree
->header
.num_items
;
2879 t
->size
+= next_tree
->size
;
2881 if (next_tree
->header
.level
> 0) {
2882 le
= next_tree
->itemlist
.Flink
;
2884 while (le
!= &next_tree
->itemlist
) {
2885 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2887 if (td2
->treeholder
.tree
) {
2888 td2
->treeholder
.tree
->parent
= t
;
2889 increase_tree_rc(t
);
2890 free_tree(next_tree
);
2897 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
2898 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
2899 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
2900 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2903 // le = t->itemlist.Flink;
2904 // while (le != &t->itemlist) {
2905 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2906 // if (!td->ignore) {
2907 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2912 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
2914 next_tree
->header
.num_items
= 0;
2915 next_tree
->size
= 0;
2917 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
2918 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, rollback
);
2920 if (!NT_SUCCESS(Status
)) {
2921 ERR("reduce_tree_extent returned %08x\n", Status
);
2922 free_tree(next_tree
);
2925 } else if (next_tree
->has_address
) {
2926 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, rollback
);
2928 if (!NT_SUCCESS(Status
)) {
2929 ERR("reduce_tree_extent returned %08x\n", Status
);
2930 free_tree(next_tree
);
2935 if (!nextparitem
->ignore
) {
2936 nextparitem
->ignore
= TRUE
;
2937 next_tree
->parent
->header
.num_items
--;
2938 next_tree
->parent
->size
-= sizeof(internal_node
);
2941 par
= next_tree
->parent
;
2943 add_to_tree_cache(Vcb
, par
, TRUE
);
2947 RemoveEntryList(&nextparitem
->list_entry
);
2948 ExFreePool(next_tree
->paritem
);
2949 next_tree
->paritem
= NULL
;
2951 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2953 free_tree(next_tree
);
2955 // remove next_tree from tree cache
2956 le
= Vcb
->tree_cache
.Flink
;
2957 while (le
!= &Vcb
->tree_cache
) {
2958 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2960 if (tc2
->tree
== next_tree
) {
2961 free_tree(next_tree
);
2962 RemoveEntryList(le
);
2970 // rebalance by moving items from second tree into first
2971 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
2972 KEY firstitem
= {0, 0, 0};
2974 TRACE("attempting rebalance\n");
2976 le
= next_tree
->itemlist
.Flink
;
2977 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
2978 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2982 if (next_tree
->header
.level
== 0)
2983 size
= sizeof(leaf_node
) + td
->size
;
2985 size
= sizeof(internal_node
);
2989 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2990 RemoveEntryList(&td
->list_entry
);
2991 InsertTailList(&t
->itemlist
, &td
->list_entry
);
2993 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
) {
2994 td
->treeholder
.tree
->parent
= t
;
2995 increase_tree_rc(t
);
2996 free_tree(next_tree
);
3000 next_tree
->size
-= size
;
3002 next_tree
->header
.num_items
--;
3003 t
->header
.num_items
++;
3008 le
= next_tree
->itemlist
.Flink
;
3011 le
= next_tree
->itemlist
.Flink
;
3012 while (le
!= &next_tree
->itemlist
) {
3013 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3016 firstitem
= td
->key
;
3023 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
3025 // FIXME - once ascension is working, make this work with parent's parent, etc.
3026 if (next_tree
->paritem
)
3027 next_tree
->paritem
->key
= firstitem
;
3031 add_to_tree_cache(Vcb
, par
, TRUE
);
3035 free_tree(next_tree
);
3038 return STATUS_SUCCESS
;
3041 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, LIST_ENTRY
* rollback
) {
3046 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
3047 searchkey
.obj_id
= address
;
3048 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
3049 searchkey
.offset
= t
->header
.level
;
3051 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3052 if (!NT_SUCCESS(Status
)) {
3053 ERR("error - find_item returned %08x\n", Status
);
3057 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
3058 EXTENT_ITEM_SKINNY_METADATA
* eism
;
3060 if (tp
.item
->size
> 0) {
3061 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3064 ERR("out of memory\n");
3065 free_traverse_ptr(&tp
);
3066 return STATUS_INSUFFICIENT_RESOURCES
;
3069 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
3073 delete_tree_item(Vcb
, &tp
, rollback
);
3075 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, rollback
)) {
3076 ERR("insert_tree_item failed\n");
3078 free_traverse_ptr(&tp
);
3079 return STATUS_INTERNAL_ERROR
;
3082 free_traverse_ptr(&tp
);
3083 return STATUS_SUCCESS
;
3086 free_traverse_ptr(&tp
);
3089 searchkey
.obj_id
= address
;
3090 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3091 searchkey
.offset
= 0xffffffffffffffff;
3093 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3094 if (!NT_SUCCESS(Status
)) {
3095 ERR("error - find_item returned %08x\n", Status
);
3099 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3100 EXTENT_ITEM_TREE
* eit
;
3102 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
3103 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM_TREE
));
3104 free_traverse_ptr(&tp
);
3105 return STATUS_INTERNAL_ERROR
;
3108 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3111 ERR("out of memory\n");
3112 free_traverse_ptr(&tp
);
3113 return STATUS_INSUFFICIENT_RESOURCES
;
3116 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
3118 delete_tree_item(Vcb
, &tp
, rollback
);
3122 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, eit
, tp
.item
->size
, NULL
, rollback
)) {
3123 ERR("insert_tree_item failed\n");
3125 free_traverse_ptr(&tp
);
3126 return STATUS_INTERNAL_ERROR
;
3129 free_traverse_ptr(&tp
);
3131 return STATUS_SUCCESS
;
3134 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
3136 free_traverse_ptr(&tp
);
3138 return STATUS_INTERNAL_ERROR
;
3141 static NTSTATUS STDCALL
do_splits(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3142 // LIST_ENTRY *le, *le2;
3145 UINT8 level
, max_level
;
3147 BOOL empty
, done_deletions
= FALSE
;
3151 TRACE("(%p)\n", Vcb
);
3155 for (level
= 0; level
<= 255; level
++) {
3156 LIST_ENTRY
*le
, *nextle
;
3160 TRACE("doing level %u\n", level
);
3162 le
= Vcb
->tree_cache
.Flink
;
3164 while (le
!= &Vcb
->tree_cache
) {
3165 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3169 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
3172 if (tc2
->tree
->header
.num_items
== 0) {
3173 if (tc2
->tree
->parent
) {
3175 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
3177 done_deletions
= TRUE
;
3179 le2
= tc2
->tree
->itemlist
.Flink
;
3180 while (le2
!= &tc2
->tree
->itemlist
) {
3181 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3182 firstitem
= td
->key
;
3185 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
3186 tc2
->tree
->root
->id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
);
3188 tc2
->tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3190 if (tc2
->tree
->has_new_address
) { // delete associated EXTENT_ITEM
3191 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, rollback
);
3193 if (!NT_SUCCESS(Status
)) {
3194 ERR("reduce_tree_extent returned %08x\n", Status
);
3197 } else if (tc2
->tree
->has_address
) {
3198 Status
= reduce_tree_extent(Vcb
,tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
3200 if (!NT_SUCCESS(Status
)) {
3201 ERR("reduce_tree_extent returned %08x\n", Status
);
3206 if (!tc2
->tree
->paritem
->ignore
) {
3207 tc2
->tree
->paritem
->ignore
= TRUE
;
3208 tc2
->tree
->parent
->header
.num_items
--;
3209 tc2
->tree
->parent
->size
-= sizeof(internal_node
);
3212 RemoveEntryList(&tc2
->tree
->paritem
->list_entry
);
3213 ExFreePool(tc2
->tree
->paritem
);
3214 tc2
->tree
->paritem
= NULL
;
3216 free_tree(tc2
->tree
);
3218 RemoveEntryList(le
);
3220 } else if (tc2
->tree
->header
.level
!= 0) {
3221 if (tc2
->tree
->has_new_address
) {
3222 Status
= update_extent_level(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, 0, rollback
);
3224 if (!NT_SUCCESS(Status
)) {
3225 ERR("update_extent_level returned %08x\n", Status
);
3230 tc2
->tree
->header
.level
= 0;
3232 } else if (tc2
->tree
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3233 TRACE("splitting overlarge tree (%x > %x)\n", tc2
->tree
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3234 Status
= split_tree(Vcb
, tc2
->tree
);
3236 if (!NT_SUCCESS(Status
)) {
3237 ERR("split_tree returned %08x\n", Status
);
3249 TRACE("nothing found for level %u\n", level
);
3254 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
3256 for (level
= 0; level
<= max_level
; level
++) {
3259 le
= Vcb
->tree_cache
.Flink
;
3261 while (le
!= &Vcb
->tree_cache
) {
3262 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3264 if (tc2
->write
&& tc2
->tree
->header
.level
== level
&& tc2
->tree
->header
.num_items
> 0 && tc2
->tree
->parent
&& tc2
->tree
->size
< min_size
) {
3265 Status
= try_tree_amalgamate(Vcb
, tc2
->tree
, rollback
);
3266 if (!NT_SUCCESS(Status
)) {
3267 ERR("try_tree_amalgamate returned %08x\n", Status
);
3276 // simplify trees if top tree only has one entry
3278 if (done_deletions
) {
3279 for (level
= max_level
; level
> 0; level
--) {
3280 LIST_ENTRY
*le
, *nextle
;
3282 le
= Vcb
->tree_cache
.Flink
;
3283 while (le
!= &Vcb
->tree_cache
) {
3285 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3287 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
3288 if (!tc2
->tree
->parent
&& tc2
->tree
->header
.num_items
== 1) {
3289 LIST_ENTRY
* le2
= tc2
->tree
->itemlist
.Flink
;
3291 tree
* child_tree
= NULL
;
3293 while (le2
!= &tc2
->tree
->itemlist
) {
3294 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3300 TRACE("deleting top-level tree in root %llx with one item\n", tc2
->tree
->root
->id
);
3302 if (tc2
->tree
->has_new_address
) { // delete associated EXTENT_ITEM
3303 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, rollback
);
3305 if (!NT_SUCCESS(Status
)) {
3306 ERR("reduce_tree_extent returned %08x\n", Status
);
3309 } else if (tc2
->tree
->has_address
) {
3310 Status
= reduce_tree_extent(Vcb
,tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
3312 if (!NT_SUCCESS(Status
)) {
3313 ERR("reduce_tree_extent returned %08x\n", Status
);
3318 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3319 KEY searchkey
= {0,0,0};
3322 Status
= find_item(Vcb
, tc2
->tree
->root
, &tp
, &searchkey
, FALSE
);
3323 if (!NT_SUCCESS(Status
)) {
3324 ERR("error - find_item returned %08x\n", Status
);
3328 free_traverse_ptr(&tp
);
3331 child_tree
= td
->treeholder
.tree
;
3334 child_tree
->parent
= NULL
;
3335 child_tree
->paritem
= NULL
;
3336 free_tree(tc2
->tree
);
3339 tc2
->tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3341 free_tree(tc2
->tree
);
3344 child_tree
->root
->treeholder
.tree
= child_tree
;
3346 RemoveEntryList(le
);
3356 return STATUS_SUCCESS
;
3359 NTSTATUS STDCALL
do_write(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3363 TRACE("(%p)\n", Vcb
);
3365 // If only changing superblock, e.g. changing label, we still need to rewrite
3366 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
3367 if (Vcb
->write_trees
> 0) {
3371 searchkey
.obj_id
= 0;
3372 searchkey
.obj_type
= 0;
3373 searchkey
.offset
= 0;
3375 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3376 if (!NT_SUCCESS(Status
)) {
3377 ERR("error - find_item returned %08x\n", Status
);
3381 add_to_tree_cache(Vcb
, Vcb
->root_root
->treeholder
.tree
, TRUE
);
3383 free_traverse_ptr(&tp
);
3387 Status
= add_parents(Vcb
, rollback
);
3388 if (!NT_SUCCESS(Status
)) {
3389 ERR("add_parents returned %08x\n", Status
);
3393 Status
= do_splits(Vcb
, rollback
);
3394 if (!NT_SUCCESS(Status
)) {
3395 ERR("do_splits returned %08x\n", Status
);
3399 Status
= allocate_tree_extents(Vcb
, rollback
);
3400 if (!NT_SUCCESS(Status
)) {
3401 ERR("add_parents returned %08x\n", Status
);
3405 Status
= update_chunk_usage(Vcb
, rollback
);
3406 if (!NT_SUCCESS(Status
)) {
3407 ERR("update_chunk_usage returned %08x\n", Status
);
3410 } while (!trees_consistent(Vcb
));
3412 TRACE("trees consistent\n");
3414 Status
= update_root_root(Vcb
, rollback
);
3415 if (!NT_SUCCESS(Status
)) {
3416 ERR("update_root_root returned %08x\n", Status
);
3420 Status
= write_trees(Vcb
);
3421 if (!NT_SUCCESS(Status
)) {
3422 ERR("write_trees returned %08x\n", Status
);
3426 Status
= write_superblocks(Vcb
);
3427 if (!NT_SUCCESS(Status
)) {
3428 ERR("write_superblocks returned %08x\n", Status
);
3432 clean_space_cache(Vcb
);
3434 Vcb
->superblock
.generation
++;
3436 // print_trees(tc); // TESTING
3438 Status
= STATUS_SUCCESS
;
3440 le
= Vcb
->tree_cache
.Flink
;
3441 while (le
!= &Vcb
->tree_cache
) {
3442 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3449 Vcb
->write_trees
= 0;
3452 TRACE("do_write returning %08x\n", Status
);
3457 NTSTATUS
consider_write(device_extension
* Vcb
) {
3458 // FIXME - call do_write if Vcb->write_trees high
3460 LIST_ENTRY rollback
;
3461 NTSTATUS Status
= STATUS_SUCCESS
;
3463 InitializeListHead(&rollback
);
3465 if (Vcb
->write_trees
> 0)
3466 Status
= do_write(Vcb
, &rollback
);
3468 free_tree_cache(&Vcb
->tree_cache
);
3470 if (!NT_SUCCESS(Status
))
3471 do_rollback(Vcb
, &rollback
);
3473 clear_rollback(&rollback
);
3477 return STATUS_SUCCESS
;
3481 static __inline
void insert_into_ordered_list(LIST_ENTRY
* list
, ordered_list
* ins
) {
3482 LIST_ENTRY
* le
= list
->Flink
;
3485 while (le
!= list
) {
3486 ol
= (ordered_list
*)le
;
3488 if (ol
->key
> ins
->key
) {
3489 le
->Blink
->Flink
= &ins
->list_entry
;
3490 ins
->list_entry
.Blink
= le
->Blink
;
3491 le
->Blink
= &ins
->list_entry
;
3492 ins
->list_entry
.Flink
= le
;
3499 InsertTailList(list
, &ins
->list_entry
);
3502 static UINT64
get_extent_data_ref_hash(UINT64 root
, UINT64 objid
, UINT64 offset
) {
3503 UINT32 high_crc
= 0xffffffff, low_crc
= 0xffffffff;
3505 // FIXME - can we test this?
3507 // FIXME - make sure numbers here are little-endian
3508 high_crc
= calc_crc32c(high_crc
, (UINT8
*)&root
, sizeof(UINT64
));
3509 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&objid
, sizeof(UINT64
));
3510 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&offset
, sizeof(UINT64
));
3512 return ((UINT64
)high_crc
<< 31) ^ (UINT64
)low_crc
;
3515 NTSTATUS STDCALL
add_extent_ref(device_extension
* Vcb
, UINT64 address
, UINT64 size
, root
* subvol
, UINT64 inode
, UINT64 offset
, LIST_ENTRY
* rollback
) {
3519 UINT8
*siptr
, *type
;
3522 EXTENT_DATA_REF
* edr
;
3525 TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb
, address
, size
, subvol
->id
, inode
, offset
);
3527 searchkey
.obj_id
= address
;
3528 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3529 searchkey
.offset
= size
;
3531 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3532 if (!NT_SUCCESS(Status
)) {
3533 ERR("error - find_item returned %08x\n", Status
);
3537 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3540 len
= sizeof(EXTENT_ITEM
) + sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
3541 free_traverse_ptr(&tp
);
3543 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
3545 ERR("out of memory\n");
3546 return STATUS_INSUFFICIENT_RESOURCES
;
3550 ei
->generation
= Vcb
->superblock
.generation
;
3551 ei
->flags
= EXTENT_ITEM_DATA
;
3553 type
= (UINT8
*)&ei
[1];
3554 *type
= TYPE_EXTENT_DATA_REF
;
3556 edr
= (EXTENT_DATA_REF
*)&type
[1];
3557 edr
->root
= subvol
->id
;
3559 edr
->offset
= offset
;
3562 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
3563 ERR("error - failed to insert item\n");
3564 return STATUS_INTERNAL_ERROR
;
3567 // FIXME - update free space in superblock and CHUNK_ITEM
3569 return STATUS_SUCCESS
;
3572 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
3573 NTSTATUS Status
= convert_old_data_extent(Vcb
, address
, size
, rollback
);
3574 if (!NT_SUCCESS(Status
)) {
3575 ERR("convert_old_data_extent returned %08x\n", Status
);
3576 free_traverse_ptr(&tp
);
3580 free_traverse_ptr(&tp
);
3582 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3583 if (!NT_SUCCESS(Status
)) {
3584 ERR("error - find_item returned %08x\n", Status
);
3588 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3589 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3590 free_traverse_ptr(&tp
);
3591 return STATUS_SUCCESS
;
3595 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3597 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
3598 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
3599 free_traverse_ptr(&tp
);
3600 return STATUS_INTERNAL_ERROR
;
3603 if (extent_item_is_shared(ei
, tp
.item
->size
- sizeof(EXTENT_ITEM
))) {
3604 NTSTATUS Status
= convert_shared_data_extent(Vcb
, address
, size
, rollback
);
3605 if (!NT_SUCCESS(Status
)) {
3606 ERR("convert_shared_data_extent returned %08x\n", Status
);
3607 free_traverse_ptr(&tp
);
3611 free_traverse_ptr(&tp
);
3613 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3614 if (!NT_SUCCESS(Status
)) {
3615 ERR("error - find_item returned %08x\n", Status
);
3619 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3620 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3621 free_traverse_ptr(&tp
);
3622 return STATUS_SUCCESS
;
3625 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3628 if (ei
->flags
!= EXTENT_ITEM_DATA
) {
3629 ERR("error - flag was not EXTENT_ITEM_DATA\n");
3630 free_traverse_ptr(&tp
);
3631 return STATUS_INTERNAL_ERROR
;
3634 // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
3636 hash
= get_extent_data_ref_hash(subvol
->id
, inode
, offset
);
3638 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
3639 siptr
= (UINT8
*)&ei
[1];
3641 // FIXME - increase subitem refcount if there already?
3643 if (*siptr
== TYPE_EXTENT_DATA_REF
) {
3646 edr
= (EXTENT_DATA_REF
*)&siptr
[1];
3647 sihash
= get_extent_data_ref_hash(edr
->root
, edr
->objid
, edr
->offset
);
3652 siptr
+= sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
3654 if (len
> sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
)) {
3655 len
-= sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
);
3658 // FIXME - TYPE_TREE_BLOCK_REF 0xB0
3660 ERR("unrecognized extent subitem %x\n", *siptr
);
3661 free_traverse_ptr(&tp
);
3662 return STATUS_INTERNAL_ERROR
;
3666 len
= tp
.item
->size
+ sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
); // FIXME - die if too big
3667 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
3669 ERR("out of memory\n");
3670 return STATUS_INSUFFICIENT_RESOURCES
;
3673 RtlCopyMemory(ei
, tp
.item
->data
, siptr
- tp
.item
->data
);
3676 type
= (UINT8
*)ei
+ (siptr
- tp
.item
->data
);
3677 *type
= TYPE_EXTENT_DATA_REF
;
3679 edr
= (EXTENT_DATA_REF
*)&type
[1];
3680 edr
->root
= subvol
->id
;
3682 edr
->offset
= offset
;
3685 if (siptr
< tp
.item
->data
+ tp
.item
->size
)
3686 RtlCopyMemory(&edr
[1], siptr
, tp
.item
->data
+ tp
.item
->size
- siptr
);
3688 delete_tree_item(Vcb
, &tp
, rollback
);
3690 free_traverse_ptr(&tp
);
3692 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
3693 ERR("error - failed to insert item\n");
3695 return STATUS_INTERNAL_ERROR
;
3698 return STATUS_SUCCESS
;
3702 EXTENT_DATA_REF edr
;
3703 LIST_ENTRY list_entry
;
3706 static void add_data_ref(LIST_ENTRY
* data_refs
, UINT64 root
, UINT64 objid
, UINT64 offset
) {
3707 data_ref
* dr
= ExAllocatePoolWithTag(PagedPool
, sizeof(data_ref
), ALLOC_TAG
);
3710 ERR("out of memory\n");
3714 // FIXME - increase count if entry there already
3715 // FIXME - put in order?
3717 dr
->edr
.root
= root
;
3718 dr
->edr
.objid
= objid
;
3719 dr
->edr
.offset
= offset
;
3722 InsertTailList(data_refs
, &dr
->list_entry
);
3725 static void free_data_refs(LIST_ENTRY
* data_refs
) {
3726 while (!IsListEmpty(data_refs
)) {
3727 LIST_ENTRY
* le
= RemoveHeadList(data_refs
);
3728 data_ref
* dr
= CONTAINING_RECORD(le
, data_ref
, list_entry
);
3734 static NTSTATUS
convert_old_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
) {
3736 traverse_ptr tp
, next_tp
;
3738 LIST_ENTRY data_refs
;
3746 searchkey
.obj_id
= address
;
3747 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3748 searchkey
.offset
= size
;
3750 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3751 if (!NT_SUCCESS(Status
)) {
3752 ERR("error - find_item returned %08x\n", Status
);
3756 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3757 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3758 free_traverse_ptr(&tp
);
3759 return STATUS_SUCCESS
;
3762 if (tp
.item
->size
!= sizeof(EXTENT_ITEM_V0
)) {
3763 TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
3764 free_traverse_ptr(&tp
);
3765 return STATUS_SUCCESS
;
3768 delete_tree_item(Vcb
, &tp
, rollback
);
3770 free_traverse_ptr(&tp
);
3772 searchkey
.obj_id
= address
;
3773 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
3774 searchkey
.offset
= 0;
3776 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3777 if (!NT_SUCCESS(Status
)) {
3778 ERR("error - find_item returned %08x\n", Status
);
3782 InitializeListHead(&data_refs
);
3785 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
3787 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3790 // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
3792 Status
= load_tree(Vcb
, tp
.item
->key
.offset
, NULL
, &t
);
3794 if (!NT_SUCCESS(Status
)) {
3795 ERR("load tree for address %llx returned %08x\n", tp
.item
->key
.offset
, Status
);
3796 free_traverse_ptr(&tp
);
3797 free_data_refs(&data_refs
);
3801 if (t
->header
.level
== 0) {
3802 le
= t
->itemlist
.Flink
;
3803 while (le
!= &t
->itemlist
) {
3804 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3806 if (!td
->ignore
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
3807 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
3809 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
3810 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3812 if (ed2
->address
== address
)
3813 add_data_ref(&data_refs
, t
->header
.tree_id
, td
->key
.obj_id
, td
->key
.offset
);
3823 delete_tree_item(Vcb
, &tp
, rollback
);
3827 free_traverse_ptr(&tp
);
3830 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
3835 free_traverse_ptr(&tp
);
3837 if (IsListEmpty(&data_refs
)) {
3838 WARN("no data refs found\n");
3839 return STATUS_SUCCESS
;
3846 le
= data_refs
.Flink
;
3847 while (le
!= &data_refs
) {
3852 eisize
= sizeof(EXTENT_ITEM
) + ((sizeof(char) + sizeof(EXTENT_DATA_REF
)) * refcount
);
3853 ei
= ExAllocatePoolWithTag(PagedPool
, eisize
, ALLOC_TAG
);
3855 ERR("out of memory\n");
3856 return STATUS_INSUFFICIENT_RESOURCES
;
3859 ei
->refcount
= refcount
;
3860 ei
->generation
= Vcb
->superblock
.generation
;
3861 ei
->flags
= EXTENT_ITEM_DATA
;
3863 type
= (UINT8
*)&ei
[1];
3865 le
= data_refs
.Flink
;
3866 while (le
!= &data_refs
) {
3867 data_ref
* dr
= CONTAINING_RECORD(le
, data_ref
, list_entry
);
3869 type
[0] = TYPE_EXTENT_DATA_REF
;
3870 RtlCopyMemory(&type
[1], &dr
->edr
, sizeof(EXTENT_DATA_REF
));
3872 type
= &type
[1 + sizeof(EXTENT_DATA_REF
)];
3877 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, ei
, eisize
, NULL
, rollback
)) {
3878 ERR("error - failed to insert item\n");
3880 return STATUS_INTERNAL_ERROR
;
3883 free_data_refs(&data_refs
);
3885 return STATUS_SUCCESS
;
3892 LIST_ENTRY list_entry
;
3895 static void free_extent_refs(LIST_ENTRY
* extent_refs
) {
3896 while (!IsListEmpty(extent_refs
)) {
3897 LIST_ENTRY
* le
= RemoveHeadList(extent_refs
);
3898 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
3901 ExFreePool(er
->data
);
3907 static NTSTATUS
convert_shared_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
) {
3910 LIST_ENTRY extent_refs
;
3911 LIST_ENTRY
*le
, *next_le
;
3912 EXTENT_ITEM
*ei
, *newei
;
3918 searchkey
.obj_id
= address
;
3919 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3920 searchkey
.offset
= size
;
3922 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3923 if (!NT_SUCCESS(Status
)) {
3924 ERR("error - find_item returned %08x\n", Status
);
3928 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3929 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3930 free_traverse_ptr(&tp
);
3931 return STATUS_SUCCESS
;
3934 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
3935 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
3936 free_traverse_ptr(&tp
);
3937 return STATUS_INTERNAL_ERROR
;
3940 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3941 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
3943 InitializeListHead(&extent_refs
);
3945 siptr
= (UINT8
*)&ei
[1];
3948 extent_ref
* er
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
3950 ERR("out of memory\n");
3951 free_traverse_ptr(&tp
);
3952 return STATUS_INSUFFICIENT_RESOURCES
;
3957 er
->allocated
= FALSE
;
3959 InsertTailList(&extent_refs
, &er
->list_entry
);
3961 if (*siptr
== TYPE_TREE_BLOCK_REF
) {
3962 siptr
+= sizeof(TREE_BLOCK_REF
);
3963 len
-= sizeof(TREE_BLOCK_REF
) + 1;
3964 } else if (*siptr
== TYPE_EXTENT_DATA_REF
) {
3965 siptr
+= sizeof(EXTENT_DATA_REF
);
3966 len
-= sizeof(EXTENT_DATA_REF
) + 1;
3967 } else if (*siptr
== TYPE_SHARED_BLOCK_REF
) {
3968 siptr
+= sizeof(SHARED_BLOCK_REF
);
3969 len
-= sizeof(SHARED_BLOCK_REF
) + 1;
3970 } else if (*siptr
== TYPE_SHARED_DATA_REF
) {
3971 siptr
+= sizeof(SHARED_DATA_REF
);
3972 len
-= sizeof(SHARED_DATA_REF
) + 1;
3974 ERR("unrecognized extent subitem %x\n", *siptr
);
3975 free_traverse_ptr(&tp
);
3976 free_extent_refs(&extent_refs
);
3977 return STATUS_INTERNAL_ERROR
;
3981 le
= extent_refs
.Flink
;
3982 while (le
!= &extent_refs
) {
3983 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
3984 next_le
= le
->Flink
;
3986 if (er
->type
== TYPE_SHARED_DATA_REF
) {
3987 // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
3988 SHARED_DATA_REF
* sdr
= er
->data
;
3991 Status
= load_tree(Vcb
, sdr
->offset
, NULL
, &t
);
3992 if (!NT_SUCCESS(Status
)) {
3993 ERR("load_tree for address %llx returned %08x\n", sdr
->offset
, Status
);
3994 free_traverse_ptr(&tp
);
3995 free_data_refs(&extent_refs
);
3999 if (t
->header
.level
== 0) {
4000 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
4001 while (le2
!= &t
->itemlist
) {
4002 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
4004 if (!td
->ignore
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
4005 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
4007 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4008 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4010 if (ed2
->address
== address
) {
4012 EXTENT_DATA_REF
* edr
;
4014 er2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
4016 ERR("out of memory\n");
4017 free_traverse_ptr(&tp
);
4018 return STATUS_INSUFFICIENT_RESOURCES
;
4021 edr
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA_REF
), ALLOC_TAG
);
4023 ERR("out of memory\n");
4024 free_traverse_ptr(&tp
);
4026 return STATUS_INSUFFICIENT_RESOURCES
;
4029 edr
->root
= t
->header
.tree_id
;
4030 edr
->objid
= td
->key
.obj_id
;
4031 edr
->offset
= td
->key
.offset
;
4034 er2
->type
= TYPE_EXTENT_DATA_REF
;
4036 er2
->allocated
= TRUE
;
4038 InsertTailList(&extent_refs
, &er2
->list_entry
); // FIXME - list should be in order
4049 RemoveEntryList(&er
->list_entry
);
4052 ExFreePool(er
->data
);
4056 // FIXME - also do for SHARED_BLOCK_REF?
4061 if (IsListEmpty(&extent_refs
)) {
4062 WARN("no extent refs found\n");
4063 delete_tree_item(Vcb
, &tp
, rollback
);
4064 free_traverse_ptr(&tp
);
4065 return STATUS_SUCCESS
;
4070 le
= extent_refs
.Flink
;
4071 while (le
!= &extent_refs
) {
4072 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
4075 if (er
->type
== TYPE_TREE_BLOCK_REF
) {
4076 len
+= sizeof(TREE_BLOCK_REF
);
4077 } else if (er
->type
== TYPE_EXTENT_DATA_REF
) {
4078 len
+= sizeof(EXTENT_DATA_REF
);
4080 ERR("unexpected extent subitem %x\n", er
->type
);
4088 newei
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM
) + len
, ALLOC_TAG
);
4090 ERR("out of memory\n");
4091 free_traverse_ptr(&tp
);
4092 return STATUS_INSUFFICIENT_RESOURCES
;
4095 RtlCopyMemory(newei
, ei
, sizeof(EXTENT_ITEM
));
4096 newei
->refcount
= count
;
4098 siptr
= (UINT8
*)&newei
[1];
4099 le
= extent_refs
.Flink
;
4100 while (le
!= &extent_refs
) {
4101 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
4106 if (er
->type
== TYPE_TREE_BLOCK_REF
) {
4107 RtlCopyMemory(siptr
, er
->data
, sizeof(TREE_BLOCK_REF
));
4108 } else if (er
->type
== TYPE_EXTENT_DATA_REF
) {
4109 RtlCopyMemory(siptr
, er
->data
, sizeof(EXTENT_DATA_REF
));
4111 ERR("unexpected extent subitem %x\n", er
->type
);
4117 delete_tree_item(Vcb
, &tp
, rollback
);
4118 free_traverse_ptr(&tp
);
4120 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, newei
, sizeof(EXTENT_ITEM
) + len
, NULL
, rollback
)) {
4121 ERR("error - failed to insert item\n");
4123 free_extent_refs(&extent_refs
);
4124 return STATUS_INTERNAL_ERROR
;
4127 free_extent_refs(&extent_refs
);
4129 return STATUS_SUCCESS
;
4132 static BOOL
extent_item_is_shared(EXTENT_ITEM
* ei
, ULONG len
) {
4133 UINT8
* siptr
= (UINT8
*)&ei
[1];
4136 if (*siptr
== TYPE_TREE_BLOCK_REF
) {
4137 siptr
+= sizeof(TREE_BLOCK_REF
) + 1;
4138 len
-= sizeof(TREE_BLOCK_REF
) + 1;
4139 } else if (*siptr
== TYPE_EXTENT_DATA_REF
) {
4140 siptr
+= sizeof(EXTENT_DATA_REF
) + 1;
4141 len
-= sizeof(EXTENT_DATA_REF
) + 1;
4142 } else if (*siptr
== TYPE_SHARED_BLOCK_REF
) {
4144 } else if (*siptr
== TYPE_SHARED_DATA_REF
) {
4147 ERR("unrecognized extent subitem %x\n", *siptr
);
4155 NTSTATUS STDCALL
remove_extent_ref(device_extension
* Vcb
, UINT64 address
, UINT64 size
, root
* subvol
, UINT64 inode
, UINT64 offset
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4161 EXTENT_DATA_REF
* edr
;
4165 TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb
, address
, size
, subvol
->id
, inode
, offset
);
4167 searchkey
.obj_id
= address
;
4168 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
4169 searchkey
.offset
= size
;
4171 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4172 if (!NT_SUCCESS(Status
)) {
4173 ERR("error - find_item returned %08x\n", Status
);
4177 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4178 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4179 free_traverse_ptr(&tp
);
4180 return STATUS_SUCCESS
;
4183 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
4184 NTSTATUS Status
= convert_old_data_extent(Vcb
, address
, size
, rollback
);
4185 if (!NT_SUCCESS(Status
)) {
4186 ERR("convert_old_data_extent returned %08x\n", Status
);
4187 free_traverse_ptr(&tp
);
4191 free_traverse_ptr(&tp
);
4193 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4194 if (!NT_SUCCESS(Status
)) {
4195 ERR("error - find_item returned %08x\n", Status
);
4199 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4200 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4201 free_traverse_ptr(&tp
);
4202 return STATUS_SUCCESS
;
4206 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
4208 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
4209 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
4210 free_traverse_ptr(&tp
);
4211 return STATUS_INTERNAL_ERROR
;
4214 if (!(ei
->flags
& EXTENT_ITEM_DATA
)) {
4215 ERR("error - EXTENT_ITEM_DATA flag not set\n");
4216 free_traverse_ptr(&tp
);
4217 return STATUS_INTERNAL_ERROR
;
4220 // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
4222 if (extent_item_is_shared(ei
, tp
.item
->size
- sizeof(EXTENT_ITEM
))) {
4223 NTSTATUS Status
= convert_shared_data_extent(Vcb
, address
, size
, rollback
);
4224 if (!NT_SUCCESS(Status
)) {
4225 ERR("convert_shared_data_extent returned %08x\n", Status
);
4226 free_traverse_ptr(&tp
);
4230 free_traverse_ptr(&tp
);
4232 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4233 if (!NT_SUCCESS(Status
)) {
4234 ERR("error - find_item returned %08x\n", Status
);
4238 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4239 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4240 free_traverse_ptr(&tp
);
4241 return STATUS_SUCCESS
;
4244 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
4247 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
4248 siptr
= (UINT8
*)&ei
[1];
4252 if (*siptr
== TYPE_EXTENT_DATA_REF
) {
4253 edr
= (EXTENT_DATA_REF
*)&siptr
[1];
4255 if (edr
->root
== subvol
->id
&& edr
->objid
== inode
&& edr
->offset
== offset
) {
4260 siptr
+= sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
4262 if (len
> sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
)) {
4263 len
-= sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
);
4266 // // FIXME - TYPE_TREE_BLOCK_REF 0xB0
4268 ERR("unrecognized extent subitem %x\n", *siptr
);
4269 free_traverse_ptr(&tp
);
4270 return STATUS_INTERNAL_ERROR
;
4275 WARN("could not find extent data ref\n");
4276 free_traverse_ptr(&tp
);
4277 return STATUS_SUCCESS
;
4280 // FIXME - decrease subitem refcount if there already?
4282 len
= tp
.item
->size
- sizeof(UINT8
) - sizeof(EXTENT_DATA_REF
);
4284 delete_tree_item(Vcb
, &tp
, rollback
);
4286 if (len
== sizeof(EXTENT_ITEM
)) { // extent no longer needed
4290 if (changed_sector_list
) {
4291 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4293 ERR("out of memory\n");
4294 free_traverse_ptr(&tp
);
4295 return STATUS_INSUFFICIENT_RESOURCES
;
4298 sc
->ol
.key
= address
;
4299 sc
->checksums
= NULL
;
4300 sc
->length
= size
/ Vcb
->superblock
.sector_size
;
4304 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4308 le2
= Vcb
->chunks
.Flink
;
4309 while (le2
!= &Vcb
->chunks
) {
4310 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
4312 TRACE("chunk: %llx, %llx\n", c
->offset
, c
->chunk_item
->size
);
4314 if (address
>= c
->offset
&& address
+ size
< c
->offset
+ c
->chunk_item
->size
)
4319 if (le2
== &Vcb
->chunks
) c
= NULL
;
4322 decrease_chunk_usage(c
, size
);
4324 add_to_space_list(c
, address
, size
, SPACE_TYPE_DELETING
);
4327 free_traverse_ptr(&tp
);
4328 return STATUS_SUCCESS
;
4331 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
4333 ERR("out of memory\n");
4334 free_traverse_ptr(&tp
);
4335 return STATUS_INSUFFICIENT_RESOURCES
;
4338 RtlCopyMemory(ei
, tp
.item
->data
, siptr
- tp
.item
->data
);
4340 ei
->generation
= Vcb
->superblock
.generation
;
4342 if (tp
.item
->data
+ len
!= siptr
)
4343 RtlCopyMemory((UINT8
*)ei
+ (siptr
- tp
.item
->data
), siptr
+ sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
), tp
.item
->size
- (siptr
- tp
.item
->data
) - sizeof(UINT8
) - sizeof(EXTENT_DATA_REF
));
4345 free_traverse_ptr(&tp
);
4347 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
4348 ERR("error - failed to insert item\n");
4350 return STATUS_INTERNAL_ERROR
;
4353 return STATUS_SUCCESS
;
4356 static __inline BOOL
entry_in_ordered_list(LIST_ENTRY
* list
, UINT64 value
) {
4357 LIST_ENTRY
* le
= list
->Flink
;
4360 while (le
!= list
) {
4361 ol
= (ordered_list
*)le
;
4363 if (ol
->key
> value
)
4365 else if (ol
->key
== value
)
4374 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4376 traverse_ptr tp
, next_tp
;
4380 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, end_data
, changed_sector_list
);
4382 searchkey
.obj_id
= fcb
->inode
;
4383 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4384 searchkey
.offset
= start_data
;
4386 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4387 if (!NT_SUCCESS(Status
)) {
4388 ERR("error - find_item returned %08x\n", Status
);
4393 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
4394 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4397 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4398 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
4399 Status
= STATUS_INTERNAL_ERROR
;
4403 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4404 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
4405 Status
= STATUS_INTERNAL_ERROR
;
4409 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
4411 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
4413 if (tp
.item
->key
.offset
< end_data
&& tp
.item
->key
.offset
+ len
>= start_data
) {
4414 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4415 FIXME("FIXME - compression not supported at present\n");
4416 Status
= STATUS_NOT_SUPPORTED
;
4420 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4421 WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb
->subvol
->id
, fcb
->inode
, tp
.item
->key
.offset
, ed
->encryption
);
4422 Status
= STATUS_NOT_SUPPORTED
;
4426 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4427 WARN("other encodings not supported\n");
4428 Status
= STATUS_NOT_SUPPORTED
;
4432 if (ed
->type
== EXTENT_TYPE_INLINE
) {
4433 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4434 delete_tree_item(Vcb
, &tp
, rollback
);
4436 fcb
->inode_item
.st_blocks
-= len
;
4437 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4441 delete_tree_item(Vcb
, &tp
, rollback
);
4443 size
= len
- (end_data
- tp
.item
->key
.offset
);
4445 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4447 ERR("out of memory\n");
4448 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4452 ned
->generation
= Vcb
->superblock
.generation
;
4453 ned
->decoded_size
= size
;
4454 ned
->compression
= ed
->compression
;
4455 ned
->encryption
= ed
->encryption
;
4456 ned
->encoding
= ed
->encoding
;
4457 ned
->type
= ed
->type
;
4459 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4461 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4462 ERR("insert_tree_item failed\n");
4464 Status
= STATUS_INTERNAL_ERROR
;
4468 fcb
->inode_item
.st_blocks
-= end_data
- tp
.item
->key
.offset
;
4469 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4473 delete_tree_item(Vcb
, &tp
, rollback
);
4475 size
= start_data
- tp
.item
->key
.offset
;
4477 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4479 ERR("out of memory\n");
4480 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4484 ned
->generation
= Vcb
->superblock
.generation
;
4485 ned
->decoded_size
= size
;
4486 ned
->compression
= ed
->compression
;
4487 ned
->encryption
= ed
->encryption
;
4488 ned
->encoding
= ed
->encoding
;
4489 ned
->type
= ed
->type
;
4491 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4493 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4494 ERR("insert_tree_item failed\n");
4496 Status
= STATUS_INTERNAL_ERROR
;
4500 fcb
->inode_item
.st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4501 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4505 delete_tree_item(Vcb
, &tp
, rollback
);
4507 size
= start_data
- tp
.item
->key
.offset
;
4509 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4511 ERR("out of memory\n");
4512 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4516 ned
->generation
= Vcb
->superblock
.generation
;
4517 ned
->decoded_size
= size
;
4518 ned
->compression
= ed
->compression
;
4519 ned
->encryption
= ed
->encryption
;
4520 ned
->encoding
= ed
->encoding
;
4521 ned
->type
= ed
->type
;
4523 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4525 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4526 ERR("insert_tree_item failed\n");
4528 Status
= STATUS_INTERNAL_ERROR
;
4532 size
= tp
.item
->key
.offset
+ len
- end_data
;
4534 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4536 ERR("out of memory\n");
4537 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4541 ned
->generation
= Vcb
->superblock
.generation
;
4542 ned
->decoded_size
= size
;
4543 ned
->compression
= ed
->compression
;
4544 ned
->encryption
= ed
->encryption
;
4545 ned
->encoding
= ed
->encoding
;
4546 ned
->type
= ed
->type
;
4548 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4550 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4551 ERR("insert_tree_item failed\n");
4553 Status
= STATUS_INTERNAL_ERROR
;
4557 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
4559 } else if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4560 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4561 if (ed2
->address
!= 0) {
4562 Status
= remove_extent_ref(Vcb
, ed2
->address
, ed2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- ed2
->offset
, changed_sector_list
, rollback
);
4563 if (!NT_SUCCESS(Status
)) {
4564 ERR("remove_extent_ref returned %08x\n", Status
);
4568 fcb
->inode_item
.st_blocks
-= len
;
4571 delete_tree_item(Vcb
, &tp
, rollback
);
4572 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4576 if (ed2
->address
!= 0)
4577 fcb
->inode_item
.st_blocks
-= end_data
- tp
.item
->key
.offset
;
4579 delete_tree_item(Vcb
, &tp
, rollback
);
4581 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4583 ERR("out of memory\n");
4584 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4588 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4590 ned
->generation
= Vcb
->superblock
.generation
;
4591 ned
->decoded_size
= ed
->decoded_size
;
4592 ned
->compression
= ed
->compression
;
4593 ned
->encryption
= ed
->encryption
;
4594 ned
->encoding
= ed
->encoding
;
4595 ned
->type
= ed
->type
;
4596 ned2
->address
= ed2
->address
;
4597 ned2
->size
= ed2
->size
;
4598 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4599 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- tp
.item
->key
.offset
);
4601 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4602 ERR("insert_tree_item failed\n");
4604 Status
= STATUS_INTERNAL_ERROR
;
4607 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4611 if (ed2
->address
!= 0)
4612 fcb
->inode_item
.st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4614 delete_tree_item(Vcb
, &tp
, rollback
);
4616 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4618 ERR("out of memory\n");
4619 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4623 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4625 ned
->generation
= Vcb
->superblock
.generation
;
4626 ned
->decoded_size
= ed
->decoded_size
;
4627 ned
->compression
= ed
->compression
;
4628 ned
->encryption
= ed
->encryption
;
4629 ned
->encoding
= ed
->encoding
;
4630 ned
->type
= ed
->type
;
4631 ned2
->address
= ed2
->address
;
4632 ned2
->size
= ed2
->size
;
4633 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4634 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4636 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4637 ERR("insert_tree_item failed\n");
4639 Status
= STATUS_INTERNAL_ERROR
;
4642 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4646 if (ed2
->address
!= 0)
4647 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
4649 delete_tree_item(Vcb
, &tp
, rollback
);
4651 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4653 ERR("out of memory\n");
4654 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4658 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4660 ned
->generation
= Vcb
->superblock
.generation
;
4661 ned
->decoded_size
= ed
->decoded_size
;
4662 ned
->compression
= ed
->compression
;
4663 ned
->encryption
= ed
->encryption
;
4664 ned
->encoding
= ed
->encoding
;
4665 ned
->type
= ed
->type
;
4666 ned2
->address
= ed2
->address
;
4667 ned2
->size
= ed2
->size
;
4668 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4669 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4671 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4672 ERR("insert_tree_item failed\n");
4674 Status
= STATUS_INTERNAL_ERROR
;
4678 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4680 ERR("out of memory\n");
4681 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4685 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4687 ned
->generation
= Vcb
->superblock
.generation
;
4688 ned
->decoded_size
= ed
->decoded_size
;
4689 ned
->compression
= ed
->compression
;
4690 ned
->encryption
= ed
->encryption
;
4691 ned
->encoding
= ed
->encoding
;
4692 ned
->type
= ed
->type
;
4693 ned2
->address
= ed2
->address
;
4694 ned2
->size
= ed2
->size
;
4695 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4696 ned2
->num_bytes
= tp
.item
->key
.offset
+ len
- end_data
;
4698 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4699 ERR("insert_tree_item failed\n");
4701 Status
= STATUS_INTERNAL_ERROR
;
4709 free_traverse_ptr(&tp
);
4712 if (tp
.item
->key
.obj_id
> fcb
->inode
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= end_data
)
4717 // FIXME - do bitmap analysis of changed extents, and free what we can
4719 Status
= STATUS_SUCCESS
;
4722 free_traverse_ptr(&tp
);
4727 static BOOL
insert_extent_chunk(device_extension
* Vcb
, fcb
* fcb
, chunk
* c
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4730 EXTENT_ITEM_DATA_REF
* eidr
;
4733 ULONG edsize
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
4738 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, c
->offset
, start_data
, length
, data
, changed_sector_list
);
4740 if (!find_address_in_chunk(Vcb
, c
, length
, &address
))
4743 eidr
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_DATA_REF
), ALLOC_TAG
);
4745 ERR("out of memory\n");
4749 eidr
->ei
.refcount
= 1;
4750 eidr
->ei
.generation
= Vcb
->superblock
.generation
;
4751 eidr
->ei
.flags
= EXTENT_ITEM_DATA
;
4752 eidr
->type
= TYPE_EXTENT_DATA_REF
;
4753 eidr
->edr
.root
= fcb
->subvol
->id
;
4754 eidr
->edr
.objid
= fcb
->inode
;
4755 eidr
->edr
.offset
= start_data
;
4756 eidr
->edr
.count
= 1;
4758 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, length
, eidr
, sizeof(EXTENT_ITEM_DATA_REF
), &tp
, rollback
)) {
4759 ERR("insert_tree_item failed\n");
4764 tp
.tree
->header
.generation
= eidr
->ei
.generation
;
4766 free_traverse_ptr(&tp
);
4768 Status
= write_data(Vcb
, address
, data
, length
);
4769 if (!NT_SUCCESS(Status
)) {
4770 ERR("write_data returned %08x\n", Status
);
4774 if (changed_sector_list
) {
4775 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4777 ERR("out of memory\n");
4781 sc
->ol
.key
= address
;
4782 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4783 sc
->deleted
= FALSE
;
4785 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4786 if (!sc
->checksums
) {
4787 ERR("out of memory\n");
4792 for (i
= 0; i
< sc
->length
; i
++) {
4793 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4796 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4799 // add extent data to inode
4800 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
4802 ERR("out of memory\n");
4806 ed
->generation
= Vcb
->superblock
.generation
;
4807 ed
->decoded_size
= length
;
4808 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4809 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4810 ed
->encoding
= BTRFS_ENCODING_NONE
;
4811 ed
->type
= EXTENT_TYPE_REGULAR
;
4813 ed2
= (EXTENT_DATA2
*)ed
->data
;
4814 ed2
->address
= address
;
4817 ed2
->num_bytes
= length
;
4819 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start_data
, ed
, edsize
, NULL
, rollback
)) {
4820 ERR("insert_tree_item failed\n");
4825 increase_chunk_usage(c
, length
);
4826 add_to_space_list(c
, address
, length
, SPACE_TYPE_WRITING
);
4828 fcb
->inode_item
.st_blocks
+= length
;
4833 static BOOL
extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4834 LIST_ENTRY
* changed_sector_list
, traverse_ptr
* edtp
, traverse_ptr
* eitp
, LIST_ENTRY
* rollback
) {
4843 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
,
4844 length
, data
, changed_sector_list
, edtp
, eitp
);
4846 ed
= ExAllocatePoolWithTag(PagedPool
, edtp
->item
->size
, ALLOC_TAG
);
4848 ERR("out of memory\n");
4852 RtlCopyMemory(ed
, edtp
->item
->data
, edtp
->item
->size
);
4854 ed
->decoded_size
+= length
;
4855 ed2
= (EXTENT_DATA2
*)ed
->data
;
4856 ed2
->size
+= length
;
4857 ed2
->num_bytes
+= length
;
4859 delete_tree_item(Vcb
, edtp
, rollback
);
4861 if (!insert_tree_item(Vcb
, fcb
->subvol
, edtp
->item
->key
.obj_id
, edtp
->item
->key
.obj_type
, edtp
->item
->key
.offset
, ed
, edtp
->item
->size
, NULL
, rollback
)) {
4862 TRACE("insert_tree_item failed\n");
4868 ei
= ExAllocatePoolWithTag(PagedPool
, eitp
->item
->size
, ALLOC_TAG
);
4870 ERR("out of memory\n");
4875 RtlCopyMemory(ei
, eitp
->item
->data
, eitp
->item
->size
);
4877 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, eitp
->item
->key
.obj_id
, eitp
->item
->key
.obj_type
, eitp
->item
->key
.offset
+ length
, ei
, eitp
->item
->size
, NULL
, rollback
)) {
4878 ERR("insert_tree_item failed\n");
4884 delete_tree_item(Vcb
, eitp
, rollback
);
4886 Status
= write_data(Vcb
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, data
, length
);
4887 if (!NT_SUCCESS(Status
)) {
4888 ERR("write_data returned %08x\n", Status
);
4892 if (changed_sector_list
) {
4893 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4895 ERR("out of memory\n");
4899 sc
->ol
.key
= eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
;
4900 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4901 sc
->deleted
= FALSE
;
4903 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4904 if (!sc
->checksums
) {
4905 ERR("out of memory\n");
4910 for (i
= 0; i
< sc
->length
; i
++) {
4911 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4913 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4916 c
= get_chunk_from_address(Vcb
, eitp
->item
->key
.obj_id
);
4919 increase_chunk_usage(c
, length
);
4921 add_to_space_list(c
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, length
, SPACE_TYPE_WRITING
);
4924 fcb
->inode_item
.st_blocks
+= length
;
4929 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4930 LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4932 traverse_ptr tp
, tp2
;
4933 BOOL success
= FALSE
;
4942 searchkey
.obj_id
= fcb
->inode
;
4943 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4944 searchkey
.offset
= start_data
;
4946 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4947 if (!NT_SUCCESS(Status
)) {
4948 ERR("error - find_item returned %08x\n", Status
);
4952 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
) {
4953 WARN("previous EXTENT_DATA not found\n");
4957 ed
= (EXTENT_DATA
*)tp
.item
->data
;
4959 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4960 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
4964 if (ed
->type
!= EXTENT_TYPE_REGULAR
) {
4965 TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
4969 ed2
= (EXTENT_DATA2
*)ed
->data
;
4971 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4972 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
4976 if (tp
.item
->key
.offset
+ ed2
->num_bytes
!= start_data
) {
4977 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp
.item
->key
.offset
, ed2
->num_bytes
, start_data
);
4981 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4982 FIXME("FIXME: compression not yet supported\n");
4986 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4987 WARN("encryption not supported\n");
4991 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4992 WARN("other encodings not supported\n");
4996 if (ed2
->size
- ed2
->offset
!= ed2
->num_bytes
) {
4997 TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
5001 searchkey
.obj_id
= ed2
->address
;
5002 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5003 searchkey
.offset
= ed2
->size
;
5005 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5006 if (!NT_SUCCESS(Status
)) {
5007 ERR("error - find_item returned %08x\n", Status
);
5011 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5012 ERR("error - extent %llx,%llx not found in tree\n", ed2
->address
, ed2
->size
);
5017 if (tp2
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
5018 NTSTATUS Status
= convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
5019 if (!NT_SUCCESS(Status
)) {
5020 ERR("convert_old_data_extent returned %08x\n", Status
);
5024 free_traverse_ptr(&tp2
);
5026 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5027 if (!NT_SUCCESS(Status
)) {
5028 ERR("error - find_item returned %08x\n", Status
);
5032 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5033 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
5038 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
5040 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5041 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
5045 // FIXME - test this
5046 if (extent_item_is_shared(ei
, tp2
.item
->size
- sizeof(EXTENT_ITEM
))) {
5047 NTSTATUS Status
= convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
5048 if (!NT_SUCCESS(Status
)) {
5049 ERR("convert_shared_data_extent returned %08x\n", Status
);
5053 free_traverse_ptr(&tp2
);
5055 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5056 if (!NT_SUCCESS(Status
)) {
5057 ERR("error - find_item returned %08x\n", Status
);
5061 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5062 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
5066 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
5068 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5069 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
5074 if (ei
->refcount
!= 1) {
5075 TRACE("extent refcount was not 1\n");
5079 if (ei
->flags
!= EXTENT_ITEM_DATA
) {
5080 ERR("error - extent was not a data extent\n");
5084 c
= get_chunk_from_address(Vcb
, ed2
->address
);
5086 le
= c
->space
.Flink
;
5087 while (le
!= &c
->space
) {
5088 s
= CONTAINING_RECORD(le
, space
, list_entry
);
5090 if (s
->offset
== ed2
->address
+ ed2
->size
) {
5091 if (s
->type
== SPACE_TYPE_FREE
&& s
->size
>= length
) {
5092 success
= extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, &tp
, &tp2
, rollback
);
5095 } else if (s
->offset
> ed2
->address
+ ed2
->size
)
5102 free_traverse_ptr(&tp2
);
5105 free_traverse_ptr(&tp
);
5110 NTSTATUS
insert_sparse_extent(device_extension
* Vcb
, root
* r
, UINT64 inode
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
5114 TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb
, r
->id
, inode
, start
, length
);
5116 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5118 ERR("out of memory\n");
5119 return STATUS_INSUFFICIENT_RESOURCES
;
5122 ed
->generation
= Vcb
->superblock
.generation
;
5123 ed
->decoded_size
= length
;
5124 ed
->compression
= BTRFS_COMPRESSION_NONE
;
5125 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
5126 ed
->encoding
= BTRFS_ENCODING_NONE
;
5127 ed
->type
= EXTENT_TYPE_REGULAR
;
5129 ed2
= (EXTENT_DATA2
*)ed
->data
;
5133 ed2
->num_bytes
= length
;
5135 if (!insert_tree_item(Vcb
, r
, inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
5136 ERR("insert_tree_item failed\n");
5138 return STATUS_INTERNAL_ERROR
;
5141 return STATUS_SUCCESS
;
5144 // static void print_tree(tree* t) {
5145 // LIST_ENTRY* le = t->itemlist.Flink;
5146 // while (le != &t->itemlist) {
5147 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
5148 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
5153 static NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5154 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
5159 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
5161 // FIXME - split data up if not enough space for just one extent
5163 if (start_data
> 0 && try_extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, rollback
))
5164 return STATUS_SUCCESS
;
5166 // if there is a gap before start_data, plug it with a sparse extent
5167 if (start_data
> 0) {
5173 searchkey
.obj_id
= fcb
->inode
;
5174 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5175 searchkey
.offset
= start_data
;
5177 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5178 if (!NT_SUCCESS(Status
)) {
5179 ERR("error - find_item returned %08x\n", Status
);
5183 // if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
5184 // traverse_ptr next_tp;
5186 // ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
5187 // searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
5188 // print_tree(tp.tree);
5190 // if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
5192 // ERR("key = %llx,%x,%llx\n", next_tp.tree->paritem->key.obj_id, next_tp.tree->paritem->key.obj_type, next_tp.tree->paritem->key.offset);
5193 // print_tree(next_tp.tree);
5195 // free_traverse_ptr(&next_tp);
5197 // ERR("next item not found\n");
5200 // free_traverse_ptr(&tp);
5201 // return STATUS_INTERNAL_ERROR;
5204 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
&& tp
.item
->size
>= sizeof(EXTENT_DATA
)) {
5207 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5208 ed2
= (EXTENT_DATA2
*)ed
->data
;
5210 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
5214 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| !ed
|| tp
.item
->key
.offset
+ len
< start_data
) {
5215 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
5216 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, 0, start_data
, rollback
);
5218 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
5220 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
+ len
,
5221 start_data
- tp
.item
->key
.offset
- len
, rollback
);
5223 if (!NT_SUCCESS(Status
)) {
5224 ERR("insert_sparse_extent returned %08x\n", Status
);
5225 free_traverse_ptr(&tp
);
5230 free_traverse_ptr(&tp
);
5233 // FIXME - how do we know which RAID level to put this to?
5234 flags
= BLOCK_FLAG_DATA
; // SINGLE
5236 // if (!chunk_test) { // TESTING
5237 // if ((c = alloc_chunk(Vcb, flags, NULL))) {
5238 // ERR("chunk_item->type = %llx\n", c->chunk_item->type);
5239 // ERR("size = %llx\n", c->chunk_item->size);
5240 // ERR("used = %llx\n", c->used);
5242 // if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
5243 // if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
5244 // // chunk_test = TRUE;
5245 // ERR("SUCCESS\n");
5246 // return STATUS_SUCCESS;
5254 while (le
!= &Vcb
->chunks
) {
5255 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5257 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
5258 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, data
, changed_sector_list
, rollback
))
5259 return STATUS_SUCCESS
;
5265 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
5266 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
5267 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, data
, changed_sector_list
, rollback
))
5268 return STATUS_SUCCESS
;
5272 // FIXME - rebalance chunks if free space elsewhere?
5273 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
5275 return STATUS_DISK_FULL
;
5278 void update_checksum_tree(device_extension
* Vcb
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5279 LIST_ENTRY
* le
= changed_sector_list
->Flink
;
5281 traverse_ptr tp
, next_tp
;
5286 if (!Vcb
->checksum_root
) {
5287 ERR("no checksum root\n");
5291 while (le
!= changed_sector_list
) {
5292 UINT64 startaddr
, endaddr
;
5297 ULONG runlength
, index
;
5299 cs
= (changed_sector
*)le
;
5301 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5302 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5303 searchkey
.offset
= cs
->ol
.key
;
5305 // FIXME - create checksum_root if it doesn't exist at all
5307 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5308 if (!NT_SUCCESS(Status
)) { // tree is completely empty
5309 // FIXME - do proper check here that tree is empty
5311 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * cs
->length
, ALLOC_TAG
);
5313 ERR("out of memory\n");
5317 RtlCopyMemory(checksums
, cs
->checksums
, sizeof(UINT32
) * cs
->length
);
5319 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, cs
->ol
.key
, checksums
, sizeof(UINT32
) * cs
->length
, NULL
, rollback
)) {
5320 ERR("insert_tree_item failed\n");
5321 ExFreePool(checksums
);
5328 // FIXME - check entry is TYPE_EXTENT_CSUM?
5330 if (tp
.item
->key
.offset
< cs
->ol
.key
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= cs
->ol
.key
)
5331 startaddr
= tp
.item
->key
.offset
;
5333 startaddr
= cs
->ol
.key
;
5335 free_traverse_ptr(&tp
);
5337 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5338 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5339 searchkey
.offset
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5341 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5342 if (!NT_SUCCESS(Status
)) {
5343 ERR("error - find_item returned %08x\n", Status
);
5347 tplen
= tp
.item
->size
/ sizeof(UINT32
);
5349 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
))
5350 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
5352 endaddr
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5354 free_traverse_ptr(&tp
);
5356 TRACE("cs starts at %llx (%x sectors)\n", cs
->ol
.key
, cs
->length
);
5357 TRACE("startaddr = %llx\n", startaddr
);
5358 TRACE("endaddr = %llx\n", endaddr
);
5360 len
= (endaddr
- startaddr
) / Vcb
->superblock
.sector_size
;
5362 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
5364 ERR("out of memory\n");
5368 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
5370 ERR("out of memory\n");
5371 ExFreePool(checksums
);
5375 RtlInitializeBitMap(&bmp
, bmparr
, len
);
5376 RtlSetAllBits(&bmp
);
5378 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5379 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5380 searchkey
.offset
= cs
->ol
.key
;
5382 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5383 if (!NT_SUCCESS(Status
)) {
5384 ERR("error - find_item returned %08x\n", Status
);
5388 // set bit = free space, cleared bit = allocated sector
5390 // ERR("start loop\n");
5391 while (tp
.item
->key
.offset
< endaddr
) {
5392 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
5393 if (tp
.item
->key
.offset
>= startaddr
) {
5394 if (tp
.item
->size
> 0) {
5395 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, tp
.item
->size
);
5396 RtlClearBits(&bmp
, (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
, tp
.item
->size
/ sizeof(UINT32
));
5399 delete_tree_item(Vcb
, &tp
, rollback
);
5402 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
5403 free_traverse_ptr(&tp
);
5408 // ERR("end loop\n");
5410 free_traverse_ptr(&tp
);
5413 RtlSetBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5415 RtlCopyMemory(&checksums
[(cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
], cs
->checksums
, cs
->length
* sizeof(UINT32
));
5416 RtlClearBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5419 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
5421 while (runlength
!= 0) {
5425 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
5426 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
5430 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
5432 ERR("out of memory\n");
5434 ExFreePool(checksums
);
5438 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
5440 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, startaddr
+ (index
* Vcb
->superblock
.sector_size
), data
, sizeof(UINT32
) * rl
, NULL
, rollback
)) {
5441 ERR("insert_tree_item failed\n");
5444 ExFreePool(checksums
);
5450 } while (runlength
> 0);
5452 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
5456 ExFreePool(checksums
);
5463 while (!IsListEmpty(changed_sector_list
)) {
5464 le
= RemoveHeadList(changed_sector_list
);
5465 cs
= (changed_sector
*)le
;
5468 ExFreePool(cs
->checksums
);
5474 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, LIST_ENTRY
* rollback
) {
5475 LIST_ENTRY changed_sector_list
;
5477 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5480 InitializeListHead(&changed_sector_list
);
5482 // FIXME - convert into inline extent if short enough
5484 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
5485 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), nocsum
? NULL
: &changed_sector_list
, rollback
);
5486 if (!NT_SUCCESS(Status
)) {
5487 ERR("error - excise_extents failed\n");
5491 fcb
->inode_item
.st_size
= end
;
5492 TRACE("setting st_size to %llx\n", end
);
5494 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
5495 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
5496 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
5497 // FIXME - inform cache manager of this
5499 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
5502 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5504 return STATUS_SUCCESS
;
5507 NTSTATUS
extend_file(fcb
* fcb
, UINT64 end
, LIST_ENTRY
* rollback
) {
5508 UINT64 oldalloc
, newalloc
;
5514 TRACE("(%p, %x, %p)\n", fcb
, end
, rollback
);
5517 FIXME("FIXME - support streams here\n"); // FIXME
5518 return STATUS_NOT_IMPLEMENTED
;
5520 searchkey
.obj_id
= fcb
->inode
;
5521 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5522 searchkey
.offset
= 0xffffffffffffffff;
5524 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5525 if (!NT_SUCCESS(Status
)) {
5526 ERR("error - find_item returned %08x\n", Status
);
5531 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
5532 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
5533 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5535 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5536 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
5537 free_traverse_ptr(&tp
);
5538 return STATUS_INTERNAL_ERROR
;
5541 oldalloc
= tp
.item
->key
.offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
5542 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
5544 if (cur_inline
&& end
> fcb
->Vcb
->max_inline
) {
5545 LIST_ENTRY changed_sector_list
;
5546 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5547 UINT64 origlength
, length
;
5550 TRACE("giving inline file proper extents\n");
5552 origlength
= ed
->decoded_size
;
5557 InitializeListHead(&changed_sector_list
);
5559 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5561 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
5563 data
= ExAllocatePoolWithTag(PagedPool
, length
, ALLOC_TAG
);
5565 ERR("could not allocate %llx bytes for data\n", length
);
5566 free_traverse_ptr(&tp
);
5567 return STATUS_INSUFFICIENT_RESOURCES
;
5570 if (length
> origlength
)
5571 RtlZeroMemory(data
+ origlength
, length
- origlength
);
5573 RtlCopyMemory(data
, ed
->data
, origlength
);
5575 fcb
->inode_item
.st_blocks
-= origlength
;
5577 Status
= insert_extent(fcb
->Vcb
, fcb
, tp
.item
->key
.offset
, length
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
5578 if (!NT_SUCCESS(Status
)) {
5579 ERR("insert_extent returned %08x\n", Status
);
5580 free_traverse_ptr(&tp
);
5585 oldalloc
= tp
.item
->key
.offset
+ length
;
5590 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5596 if (end
> oldalloc
) {
5597 edsize
= sizeof(EXTENT_DATA
) - 1 + end
- tp
.item
->key
.offset
;
5598 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5601 ERR("out of memory\n");
5602 free_traverse_ptr(&tp
);
5603 return STATUS_INSUFFICIENT_RESOURCES
;
5606 RtlZeroMemory(ed
, edsize
);
5607 RtlCopyMemory(ed
, tp
.item
->data
, tp
.item
->size
);
5609 ed
->decoded_size
= end
- tp
.item
->key
.offset
;
5611 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5613 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, ed
, edsize
, NULL
, rollback
)) {
5614 ERR("error - failed to insert item\n");
5616 free_traverse_ptr(&tp
);
5617 return STATUS_INTERNAL_ERROR
;
5621 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
5623 fcb
->inode_item
.st_size
= end
;
5624 TRACE("setting st_size to %llx\n", end
);
5626 fcb
->inode_item
.st_blocks
= end
;
5628 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5630 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5632 if (newalloc
> oldalloc
) {
5633 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, oldalloc
, newalloc
- oldalloc
, rollback
);
5635 if (!NT_SUCCESS(Status
)) {
5636 ERR("insert_sparse_extent returned %08x\n", Status
);
5637 free_traverse_ptr(&tp
);
5642 fcb
->inode_item
.st_size
= end
;
5643 TRACE("setting st_size to %llx\n", end
);
5645 TRACE("newalloc = %llx\n", newalloc
);
5647 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5648 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5651 if (end
> fcb
->Vcb
->max_inline
) {
5652 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5654 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, 0, newalloc
, rollback
);
5656 if (!NT_SUCCESS(Status
)) {
5657 ERR("insert_sparse_extent returned %08x\n", Status
);
5658 free_traverse_ptr(&tp
);
5662 fcb
->inode_item
.st_size
= end
;
5663 TRACE("setting st_size to %llx\n", end
);
5665 TRACE("newalloc = %llx\n", newalloc
);
5667 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5668 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5673 edsize
= sizeof(EXTENT_DATA
) - 1 + end
;
5674 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5677 ERR("out of memory\n");
5678 free_traverse_ptr(&tp
);
5679 return STATUS_INSUFFICIENT_RESOURCES
;
5682 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
5683 ed
->decoded_size
= end
;
5684 ed
->compression
= BTRFS_COMPRESSION_NONE
;
5685 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
5686 ed
->encoding
= BTRFS_ENCODING_NONE
;
5687 ed
->type
= EXTENT_TYPE_INLINE
;
5689 RtlZeroMemory(ed
->data
, end
);
5691 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed
, edsize
, NULL
, rollback
)) {
5692 ERR("error - failed to insert item\n");
5694 free_traverse_ptr(&tp
);
5695 return STATUS_INTERNAL_ERROR
;
5698 fcb
->inode_item
.st_size
= end
;
5699 TRACE("setting st_size to %llx\n", end
);
5701 fcb
->inode_item
.st_blocks
= end
;
5703 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5707 free_traverse_ptr(&tp
);
5710 return STATUS_SUCCESS
;
5713 static UINT64
get_extent_item_refcount(device_extension
* Vcb
, UINT64 address
) {
5720 searchkey
.obj_id
= address
;
5721 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5722 searchkey
.offset
= 0xffffffffffffffff;
5724 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
5725 if (!NT_SUCCESS(Status
)) {
5726 ERR("error - find_item returned %08x\n", Status
);
5730 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5731 ERR("error - could not find EXTENT_ITEM for %llx\n", address
);
5732 free_traverse_ptr(&tp
);
5736 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5737 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
5738 free_traverse_ptr(&tp
);
5742 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
5745 free_traverse_ptr(&tp
);
5750 static NTSTATUS
do_nocow_write(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5752 traverse_ptr tp
, next_tp
;
5757 UINT64 size
, new_start
, new_end
, last_write
= 0;
5759 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
5761 searchkey
.obj_id
= fcb
->inode
;
5762 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5763 searchkey
.offset
= start_data
;
5765 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5766 if (!NT_SUCCESS(Status
)) {
5767 ERR("error - find_item returned %08x\n", Status
);
5771 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
> start_data
) {
5772 ERR("previous EXTENT_DATA not found (found %llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5773 Status
= STATUS_INTERNAL_ERROR
;
5778 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5779 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
5780 Status
= STATUS_INTERNAL_ERROR
;
5784 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5786 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5787 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
5788 Status
= STATUS_INTERNAL_ERROR
;
5792 eds
= (EXTENT_DATA2
*)&ed
->data
[0];
5794 b
= find_next_item(Vcb
, &tp
, &next_tp
, TRUE
);
5797 case EXTENT_TYPE_REGULAR
:
5799 UINT64 rc
= get_extent_item_refcount(Vcb
, eds
->address
);
5802 ERR("get_extent_item_refcount failed\n");
5803 Status
= STATUS_INTERNAL_ERROR
;
5811 case EXTENT_TYPE_INLINE
:
5815 case EXTENT_TYPE_PREALLOC
:
5816 FIXME("FIXME - handle prealloc extents\n"); // FIXME
5817 Status
= STATUS_NOT_SUPPORTED
;
5821 ERR("error - unknown extent type %x\n", ed
->type
);
5822 Status
= STATUS_NOT_SUPPORTED
;
5826 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
5827 FIXME("FIXME: compression not yet supported\n");
5828 Status
= STATUS_NOT_SUPPORTED
;
5832 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
5833 WARN("encryption not supported\n");
5834 Status
= STATUS_INTERNAL_ERROR
;
5838 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
5839 WARN("other encodings not supported\n");
5840 Status
= STATUS_INTERNAL_ERROR
;
5844 size
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: eds
->num_bytes
;
5846 TRACE("extent: start = %llx, length = %llx\n", tp
.item
->key
.offset
, size
);
5848 new_start
= tp
.item
->key
.offset
< start_data
? start_data
: tp
.item
->key
.offset
;
5849 new_end
= tp
.item
->key
.offset
+ size
> start_data
+ length
? (start_data
+ length
) : (tp
.item
->key
.offset
+ size
);
5851 TRACE("new_start = %llx\n", new_start
);
5852 TRACE("new_end = %llx\n", new_end
);
5855 TRACE("doing COW write\n");
5857 Status
= excise_extents(Vcb
, fcb
, new_start
, new_start
+ new_end
, changed_sector_list
, rollback
);
5859 if (!NT_SUCCESS(Status
)) {
5860 ERR("error - excise_extents returned %08x\n", Status
);
5864 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
5866 if (!NT_SUCCESS(Status
)) {
5867 ERR("error - insert_extent returned %08x\n", Status
);
5873 writeaddr
= eds
->address
+ eds
->offset
+ new_start
- tp
.item
->key
.offset
;
5874 TRACE("doing non-COW write to %llx\n", writeaddr
);
5876 Status
= write_data(Vcb
, writeaddr
, (UINT8
*)data
+ new_start
- start_data
, new_end
- new_start
);
5878 if (!NT_SUCCESS(Status
)) {
5879 ERR("error - write_data returned %08x\n", Status
);
5883 if (changed_sector_list
) {
5887 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
5889 ERR("out of memory\n");
5890 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5894 sc
->ol
.key
= writeaddr
;
5895 sc
->length
= (new_end
- new_start
) / Vcb
->superblock
.sector_size
;
5896 sc
->deleted
= FALSE
;
5898 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
5899 if (!sc
->checksums
) {
5900 ERR("out of memory\n");
5902 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5906 for (i
= 0; i
< sc
->length
; i
++) {
5907 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ new_start
- start_data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
5910 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
5914 last_write
= new_end
;
5917 free_traverse_ptr(&tp
);
5920 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
+ length
)
5925 if (last_write
< start_data
+ length
) {
5926 new_start
= last_write
;
5927 new_end
= start_data
+ length
;
5929 TRACE("new_start = %llx\n", new_start
);
5930 TRACE("new_end = %llx\n", new_end
);
5932 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
5934 if (!NT_SUCCESS(Status
)) {
5935 ERR("error - insert_extent returned %08x\n", Status
);
5940 Status
= STATUS_SUCCESS
;
5943 free_traverse_ptr(&tp
);
5948 #ifdef DEBUG_PARANOID
5949 static void print_loaded_trees(tree
* t
, int spaces
) {
5954 for (i
= 0; i
< spaces
; i
++) {
5960 ERR("%s(not loaded)\n", pref
);
5964 le
= t
->itemlist
.Flink
;
5965 while (le
!= &t
->itemlist
) {
5966 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
5968 ERR("%s%llx,%x,%llx ignore=%s\n", pref
, td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->ignore
? "TRUE" : "FALSE");
5970 if (t
->header
.level
> 0) {
5971 print_loaded_trees(td
->treeholder
.tree
, spaces
+1);
5978 static void check_extents_consistent(device_extension
* Vcb
, fcb
* fcb
) {
5980 traverse_ptr tp
, next_tp
;
5981 UINT64 length
, oldlength
, lastoff
, alloc
;
5986 if (fcb
->ads
|| fcb
->inode_item
.st_size
== 0 || fcb
->deleted
)
5989 TRACE("inode = %llx, subvol = %llx\n", fcb
->inode
, fcb
->subvol
->id
);
5991 searchkey
.obj_id
= fcb
->inode
;
5992 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5993 searchkey
.offset
= 0;
5995 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5996 if (!NT_SUCCESS(Status
)) {
5997 ERR("error - find_item returned %08x\n", Status
);
6001 if (keycmp(&searchkey
, &tp
.item
->key
)) {
6002 ERR("could not find EXTENT_DATA at offset 0\n");
6006 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6007 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
6011 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6012 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6014 length
= oldlength
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6015 lastoff
= tp
.item
->key
.offset
;
6017 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6020 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6024 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
6025 if (next_tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| next_tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6026 free_traverse_ptr(&next_tp
);
6030 free_traverse_ptr(&tp
);
6033 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6034 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA
));
6038 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6039 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6041 length
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6043 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6045 if (tp
.item
->key
.offset
!= lastoff
+ oldlength
) {
6046 ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb
->subvol
->id
, fcb
->inode
, tp
.item
->key
.offset
, lastoff
+ oldlength
);
6050 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6055 lastoff
= tp
.item
->key
.offset
;
6058 if (alloc
!= fcb
->inode_item
.st_blocks
) {
6059 ERR("allocation size was %llx, expected %llx\n", alloc
, fcb
->inode_item
.st_blocks
);
6063 // if (fcb->inode_item.st_blocks != lastoff + oldlength) {
6064 // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
6068 free_traverse_ptr(&tp
);
6073 free_traverse_ptr(&tp
);
6076 if (fcb
->subvol
->treeholder
.tree
)
6077 print_loaded_trees(fcb
->subvol
->treeholder
.tree
, 0);
6082 // static void check_extent_tree_consistent(device_extension* Vcb) {
6084 // traverse_ptr tp, next_tp;
6086 // BOOL b, inconsistency;
6088 // searchkey.obj_id = 0;
6089 // searchkey.obj_type = 0;
6090 // searchkey.offset = 0;
6092 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6093 // ERR("error - could not find any entries in extent_root\n");
6098 // inconsistency = FALSE;
6101 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6102 // // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6104 // if (tp.item->key.obj_id < lastaddr) {
6105 // // ERR("inconsistency!\n");
6107 // inconsistency = TRUE;
6110 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6113 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6115 // free_traverse_ptr(&tp);
6120 // free_traverse_ptr(&tp);
6122 // if (!inconsistency)
6125 // ERR("Inconsistency detected:\n");
6127 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6128 // ERR("error - could not find any entries in extent_root\n");
6133 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6134 // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6136 // if (tp.item->key.obj_id < lastaddr) {
6137 // ERR("inconsistency!\n");
6140 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6143 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6145 // free_traverse_ptr(&tp);
6150 // free_traverse_ptr(&tp);
6156 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOL paging_io
, BOOL no_cache
, LIST_ENTRY
* rollback
) {
6157 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6158 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6162 UINT64 newlength
, start_data
, end_data
;
6166 LIST_ENTRY changed_sector_list
;
6167 INODE_ITEM
*ii
, *origii
;
6168 BOOL changed_length
= FALSE
, nocsum
, nocow
/*, lazy_writer = FALSE, write_eof = FALSE*/;
6173 BOOL paging_lock
= FALSE
;
6175 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
6178 WARN("returning success for zero-length write\n");
6179 return STATUS_SUCCESS
;
6183 ERR("error - FileObject was NULL\n");
6184 return STATUS_ACCESS_DENIED
;
6187 fcb
= FileObject
->FsContext
;
6189 if (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
6190 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
);
6191 return STATUS_ACCESS_DENIED
;
6194 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1) {
6195 offset
= fcb
->Header
.FileSize
;
6196 // write_eof = TRUE;
6199 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
6201 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
6202 IO_STATUS_BLOCK iosb
;
6204 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
6206 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
6208 if (!NT_SUCCESS(iosb
.Status
)) {
6209 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6210 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
6216 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
6220 ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, TRUE
);
6224 nocsum
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
6225 nocow
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
;
6227 newlength
= fcb
->ads
? fcb
->adssize
: fcb
->inode_item
.st_size
;
6232 TRACE("newlength = %llx\n", newlength
);
6234 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
6235 // ERR("lazy writer on the TV\n");
6236 // lazy_writer = TRUE;
6239 if (offset
.QuadPart
+ *length
> newlength
) {
6241 if (offset
.QuadPart
>= newlength
) {
6242 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, offset
.QuadPart
, *length
);
6243 TRACE("filename %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
6244 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
6245 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
6246 Status
= STATUS_SUCCESS
;
6250 *length
= newlength
- offset
.QuadPart
;
6252 newlength
= offset
.QuadPart
+ *length
;
6253 changed_length
= TRUE
;
6255 TRACE("extending length to %llx\n", newlength
);
6259 make_inline
= fcb
->ads
? FALSE
: newlength
<= fcb
->Vcb
->max_inline
;
6261 if (changed_length
) {
6262 if (newlength
> fcb
->Header
.AllocationSize
.QuadPart
) {
6263 Status
= extend_file(fcb
, newlength
, rollback
);
6264 if (!NT_SUCCESS(Status
)) {
6265 ERR("extend_file returned %08x\n", Status
);
6268 } else if (fcb
->ads
)
6269 fcb
->adssize
= newlength
;
6271 fcb
->inode_item
.st_size
= newlength
;
6273 fcb
->Header
.FileSize
.QuadPart
= newlength
;
6274 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6276 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
6277 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
6278 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
6284 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
6287 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6288 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6289 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6291 if (!FileObject
->PrivateCacheMap
) {
6292 TRACE("calling CcInitializeCacheMap...\n");
6293 CcInitializeCacheMap(FileObject
, &ccfs
, FALSE
, cache_callbacks
, FileObject
);
6295 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
6297 CcSetFileSizes(FileObject
, &ccfs
);
6301 // FIXME - uncomment this when async is working
6302 // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
6305 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
);
6306 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
6307 TRACE("CcCopyWrite failed.\n");
6309 IoMarkIrpPending(Irp
);
6310 Status
= STATUS_PENDING
;
6313 TRACE("CcCopyWrite finished\n");
6315 Status
= STATUS_SUCCESS
;
6324 if (!get_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, &data
, &datalen
)) {
6325 ERR("get_xattr failed\n");
6326 Status
= STATUS_INTERNAL_ERROR
;
6330 if (changed_length
) {
6331 // find maximum length of xattr
6332 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
6334 searchkey
.obj_id
= fcb
->inode
;
6335 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
6336 searchkey
.offset
= fcb
->adshash
;
6338 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6339 if (!NT_SUCCESS(Status
)) {
6340 ERR("error - find_item returned %08x\n", Status
);
6344 if (keycmp(&tp
.item
->key
, &searchkey
)) {
6345 ERR("error - could not find key for xattr\n");
6346 free_traverse_ptr(&tp
);
6347 Status
= STATUS_INTERNAL_ERROR
;
6351 if (tp
.item
->size
< datalen
) {
6352 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, datalen
);
6353 free_traverse_ptr(&tp
);
6354 Status
= STATUS_INTERNAL_ERROR
;
6358 maxlen
-= tp
.item
->size
- datalen
; // subtract XATTR_ITEM overhead
6360 free_traverse_ptr(&tp
);
6362 if (newlength
> maxlen
) {
6363 ERR("error - xattr too long (%llu > %u)\n", newlength
, maxlen
);
6364 Status
= STATUS_DISK_FULL
;
6368 fcb
->adssize
= newlength
;
6370 data2
= ExAllocatePoolWithTag(PagedPool
, newlength
, ALLOC_TAG
);
6372 ERR("out of memory\n");
6373 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6377 RtlCopyMemory(data2
, data
, datalen
);
6379 if (offset
.QuadPart
> datalen
)
6380 RtlZeroMemory(&data2
[datalen
], offset
.QuadPart
- datalen
);
6385 RtlCopyMemory(&data2
[offset
.QuadPart
], buf
, *length
);
6387 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, data2
, newlength
, rollback
);
6388 if (!NT_SUCCESS(Status
)) {
6389 ERR("set_xattr returned %08x\n", Status
);
6393 if (data
) ExFreePool(data
);
6394 if (data2
!= data
) ExFreePool(data2
);
6396 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6400 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
6401 bufhead
= sizeof(EXTENT_DATA
) - 1;
6403 start_data
= offset
.QuadPart
& ~(fcb
->Vcb
->superblock
.sector_size
- 1);
6404 end_data
= sector_align(offset
.QuadPart
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
6408 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6409 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
6411 data
= ExAllocatePoolWithTag(PagedPool
, end_data
- start_data
+ bufhead
, ALLOC_TAG
);
6413 ERR("out of memory\n");
6414 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6418 RtlZeroMemory(data
+ bufhead
, end_data
- start_data
);
6420 TRACE("start_data = %llx\n", start_data
);
6421 TRACE("end_data = %llx\n", end_data
);
6423 if (offset
.QuadPart
> start_data
|| offset
.QuadPart
+ *length
< end_data
) {
6424 if (changed_length
) {
6425 if (fcb
->inode_item
.st_size
> start_data
)
6426 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
);
6428 Status
= STATUS_SUCCESS
;
6430 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
);
6432 if (!NT_SUCCESS(Status
)) {
6433 ERR("read_file returned %08x\n", Status
);
6439 RtlCopyMemory(data
+ bufhead
+ offset
.QuadPart
- start_data
, buf
, *length
);
6442 InitializeListHead(&changed_sector_list
);
6444 if (make_inline
|| !nocow
) {
6445 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6446 if (!NT_SUCCESS(Status
)) {
6447 ERR("error - excise_extents returned %08x\n", Status
);
6453 Status
= insert_extent(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6455 if (!NT_SUCCESS(Status
)) {
6456 ERR("error - insert_extent returned %08x\n", Status
);
6463 ed2
= (EXTENT_DATA
*)data
;
6464 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
6465 ed2
->decoded_size
= newlength
;
6466 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
6467 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
6468 ed2
->encoding
= BTRFS_ENCODING_NONE
;
6469 ed2
->type
= EXTENT_TYPE_INLINE
;
6471 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed2
, sizeof(EXTENT_DATA
) - 1 + newlength
, NULL
, rollback
);
6473 fcb
->inode_item
.st_blocks
+= newlength
;
6476 Status
= do_nocow_write(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6478 if (!NT_SUCCESS(Status
)) {
6479 ERR("error - do_nocow_write returned %08x\n", Status
);
6488 KeQuerySystemTime(&time
);
6489 win_time_to_unix(time
, &now
);
6491 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
6494 // if (!FileObject->PrivateCacheMap) {
6495 // CC_FILE_SIZES ccfs;
6497 // ccfs.AllocationSize = fcb->Header.AllocationSize;
6498 // ccfs.FileSize = fcb->Header.FileSize;
6499 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
6501 // TRACE("calling CcInitializeCacheMap...\n");
6502 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
6504 // changed_length = FALSE;
6509 origii
= &fcb
->par
->inode_item
;
6511 origii
= &fcb
->inode_item
;
6513 origii
->transid
= Vcb
->superblock
.generation
;
6515 origii
->st_ctime
= now
;
6518 TRACE("setting st_size to %llx\n", newlength
);
6519 origii
->st_size
= newlength
;
6520 origii
->st_mtime
= now
;
6523 searchkey
.obj_id
= fcb
->inode
;
6524 searchkey
.obj_type
= TYPE_INODE_ITEM
;
6525 searchkey
.offset
= 0;
6527 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6528 if (!NT_SUCCESS(Status
)) {
6529 ERR("error - find_item returned %08x\n", Status
);
6533 if (!keycmp(&tp
.item
->key
, &searchkey
))
6534 delete_tree_item(Vcb
, &tp
, rollback
);
6536 WARN("couldn't find existing INODE_ITEM\n");
6538 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
6540 ERR("out of memory\n");
6541 free_traverse_ptr(&tp
);
6542 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6546 RtlCopyMemory(ii
, origii
, sizeof(INODE_ITEM
));
6547 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
6549 free_traverse_ptr(&tp
);
6551 // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
6554 update_checksum_tree(Vcb
, &changed_sector_list
, rollback
);
6556 if (changed_length
) {
6559 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6560 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6561 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6563 CcSetFileSizes(FileObject
, &ccfs
);
6566 // FIXME - make sure this still called if STATUS_PENDING and async
6568 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
6569 // ERR("CcCopyWrite failed.\n");
6573 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
6574 fcb
->subvol
->root_item
.ctime
= now
;
6576 Status
= STATUS_SUCCESS
;
6579 if (FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
6580 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6581 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
6582 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6586 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6591 NTSTATUS
write_file(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
6592 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6593 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
6596 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
6597 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6598 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
6599 BOOL locked
= FALSE
;
6600 // LARGE_INTEGER freq, time1, time2;
6601 LIST_ENTRY rollback
;
6603 InitializeListHead(&rollback
);
6606 return STATUS_MEDIA_WRITE_PROTECTED
;
6608 if (fcb
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
6609 return STATUS_ACCESS_DENIED
;
6611 // time1 = KeQueryPerformanceCounter(&freq);
6615 Irp
->IoStatus
.Information
= 0;
6617 switch (IrpSp
->MinorFunction
) {
6618 case IRP_MN_COMPLETE
:
6619 FIXME("unsupported - IRP_MN_COMPLETE\n");
6622 case IRP_MN_COMPLETE_MDL
:
6623 FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
6626 case IRP_MN_COMPLETE_MDL_DPC
:
6627 FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
6630 case IRP_MN_COMPRESSED
:
6631 FIXME("unsupported - IRP_MN_COMPRESSED\n");
6635 FIXME("unsupported - IRP_MN_DPC\n");
6639 FIXME("unsupported - IRP_MN_MDL\n");
6642 case IRP_MN_MDL_DPC
:
6643 FIXME("unsupported - IRP_MN_MDL_DPC\n");
6647 TRACE("IRP_MN_NORMAL\n");
6651 WARN("unknown minor function %x\n", IrpSp
->MinorFunction
);
6655 TRACE("offset = %llx\n", offset
.QuadPart
);
6656 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
6658 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
6659 buf
= map_user_buffer(Irp
);
6661 if (Irp
->MdlAddress
&& !buf
) {
6662 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
6663 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6667 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
6669 TRACE("buf = %p\n", buf
);
6671 acquire_tree_lock(Vcb
, TRUE
);
6674 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
6675 WARN("tried to write to locked region\n");
6676 Status
= STATUS_FILE_LOCK_CONFLICT
;
6680 // ERR("Irp->Flags = %x\n", Irp->Flags);
6681 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
, &rollback
);
6682 if (!NT_SUCCESS(Status
)) {
6683 if (Status
!= STATUS_PENDING
)
6684 ERR("write_file2 returned %08x\n", Status
);
6688 Status
= consider_write(Vcb
);
6690 if (NT_SUCCESS(Status
)) {
6691 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
6693 #ifdef DEBUG_PARANOID
6694 check_extents_consistent(Vcb
, FileObject
->FsContext
); // TESTING
6696 // check_extent_tree_consistent(Vcb);
6702 if (NT_SUCCESS(Status
))
6703 clear_rollback(&rollback
);
6705 do_rollback(Vcb
, &rollback
);
6707 release_tree_lock(Vcb
, TRUE
);
6710 // time2 = KeQueryPerformanceCounter(NULL);
6712 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);