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
266 if (s
->offset
+ s
->size
== offset
+ size
) {
267 insbef
= s
->list_entry
.Flink
;
268 RemoveEntryList(&s
->list_entry
);
273 RemoveEntryList(&s
->list_entry
);
275 } else if (s
->offset
< offset
&& s
->offset
+ s
->size
> offset
+ size
) { // split in two
276 s3
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
278 ERR("out of memory\n");
282 s3
->offset
= offset
+ size
;
283 s3
->size
= s
->size
- size
- offset
+ s
->offset
;
285 InsertHeadList(&s
->list_entry
, &s3
->list_entry
);
286 insbef
= &s3
->list_entry
;
288 s
->size
= offset
- s
->offset
;
290 } else if (s
->offset
+ s
->size
> offset
&& s
->offset
+ s
->size
<= offset
+ size
) { // truncate before
291 s
->size
= offset
- s
->offset
;
292 } else if (s
->offset
< offset
+ size
&& s
->offset
+ s
->size
> offset
+ size
) { // truncate after
293 s
->size
-= s
->offset
- offset
+ size
;
294 s
->offset
= offset
+ size
;
303 s2
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
305 ERR("out of memory\n");
312 InsertTailList(insbef
, &s2
->list_entry
);
314 // merge entries if same type
316 if (s2
->list_entry
.Blink
!= &c
->space
) {
317 s
= CONTAINING_RECORD(s2
->list_entry
.Blink
, space
, list_entry
);
319 if (s
->type
== type
) {
322 RemoveEntryList(&s2
->list_entry
);
329 if (s2
->list_entry
.Flink
!= &c
->space
) {
330 s
= CONTAINING_RECORD(s2
->list_entry
.Flink
, space
, list_entry
);
332 if (s
->type
== type
) {
335 RemoveEntryList(&s
->list_entry
);
341 while (le
!= &c
->space
) {
342 s
= CONTAINING_RECORD(le
, space
, list_entry
);
344 TRACE("%llx,%llx,%x\n", s
->offset
, s
->size
, s
->type
);
349 #ifdef DEBUG_PARANOID
351 lastaddr
= c
->offset
;
353 while (le
!= &c
->space
) {
354 s
= CONTAINING_RECORD(le
, space
, list_entry
);
356 if (s
->offset
!= lastaddr
) {
357 ERR("inconsistency detected!\n");
361 lastaddr
= s
->offset
+ s
->size
;
366 if (lastaddr
!= c
->offset
+ c
->chunk_item
->size
) {
367 ERR("inconsistency detected - space doesn't run all the way to end of chunk\n");
373 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
377 le2
= Vcb
->chunks
.Flink
;
378 while (le2
!= &Vcb
->chunks
) {
379 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
381 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
383 if (address
>= c
->offset
&& address
< c
->offset
+ c
->chunk_item
->size
)
397 static void add_provisional_disk_hole(device_extension
* Vcb
, stripe
* s
, UINT64 max_stripe_size
) {
398 // LIST_ENTRY* le = s->device->disk_holes.Flink;
401 // ERR("old holes:\n");
402 // while (le != &s->device->disk_holes) {
403 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
405 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
410 if (s
->dh
->size
<= max_stripe_size
) {
411 s
->dh
->provisional
= TRUE
;
413 disk_hole
* newdh
= ExAllocatePoolWithTag(PagedPool
, sizeof(disk_hole
), ALLOC_TAG
);
415 ERR("out of memory\n");
419 newdh
->address
= s
->dh
->address
+ max_stripe_size
;
420 newdh
->size
= s
->dh
->size
- max_stripe_size
;
421 newdh
->provisional
= FALSE
;
422 InsertTailList(&s
->device
->disk_holes
, &newdh
->listentry
);
424 s
->dh
->size
= max_stripe_size
;
425 s
->dh
->provisional
= TRUE
;
428 // ERR("new holes:\n");
429 // le = s->device->disk_holes.Flink;
430 // while (le != &s->device->disk_holes) {
431 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
433 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
439 static UINT64
find_new_chunk_address(device_extension
* Vcb
, UINT64 size
) {
441 traverse_ptr tp
, next_tp
;
446 searchkey
.obj_id
= 0x100;
447 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
448 searchkey
.offset
= 0;
450 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
451 if (!NT_SUCCESS(Status
)) {
452 ERR("error - find_item returned %08x\n", Status
);
453 return 0xffffffffffffffff;
459 if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
460 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
461 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
));
463 CHUNK_ITEM
* ci
= (CHUNK_ITEM
*)tp
.item
->data
;
465 if (tp
.item
->key
.offset
>= lastaddr
+ size
) {
466 free_traverse_ptr(&tp
);
470 lastaddr
= tp
.item
->key
.offset
+ ci
->size
;
474 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
476 free_traverse_ptr(&tp
);
479 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
484 free_traverse_ptr(&tp
);
489 static BOOL
increase_dev_item_used(device_extension
* Vcb
, device
* device
, UINT64 size
, LIST_ENTRY
* rollback
) {
495 searchkey
.obj_id
= 1;
496 searchkey
.obj_type
= TYPE_DEV_ITEM
;
497 searchkey
.offset
= device
->devitem
.dev_id
;
499 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
500 if (!NT_SUCCESS(Status
)) {
501 ERR("error - find_item returned %08x\n", Status
);
505 if (keycmp(&tp
.item
->key
, &searchkey
)) {
506 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
507 free_traverse_ptr(&tp
);
511 delete_tree_item(Vcb
, &tp
, rollback
);
513 free_traverse_ptr(&tp
);
515 device
->devitem
.bytes_used
+= size
;
517 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
519 ERR("out of memory\n");
523 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
525 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, rollback
)) {
526 ERR("insert_tree_item failed\n");
533 static void reset_disk_holes(device
* device
, BOOL commit
) {
534 LIST_ENTRY
* le
= device
->disk_holes
.Flink
;
537 // ERR("old holes:\n");
538 // while (le != &device->disk_holes) {
539 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
541 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
546 le
= device
->disk_holes
.Flink
;
547 while (le
!= &device
->disk_holes
) {
548 LIST_ENTRY
* le2
= le
->Flink
;
550 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
552 if (dh
->provisional
) {
557 dh
->provisional
= FALSE
;
565 le
= device
->disk_holes
.Flink
;
566 while (le
!= &device
->disk_holes
) {
567 LIST_ENTRY
* le2
= le
->Flink
;
569 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
571 while (le2
!= &device
->disk_holes
) {
572 disk_hole
* dh2
= CONTAINING_RECORD(le2
, disk_hole
, listentry
);
574 if (dh2
->address
== dh
->address
+ dh
->size
) {
575 LIST_ENTRY
* le3
= le2
->Flink
;
576 dh
->size
+= dh2
->size
;
578 RemoveEntryList(le2
);
590 // ERR("new holes:\n");
591 // le = device->disk_holes.Flink;
592 // while (le != &device->disk_holes) {
593 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
595 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
601 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, ULONG size
) {
606 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
607 ERR("error - bootstrap is full\n");
608 return STATUS_INTERNAL_ERROR
;
611 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
613 ERR("out of memory\n");
614 return STATUS_INSUFFICIENT_RESOURCES
;
617 sc
->key
.obj_id
= obj_id
;
618 sc
->key
.obj_type
= obj_type
;
619 sc
->key
.offset
= offset
;
621 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
623 ERR("out of memory\n");
625 return STATUS_INSUFFICIENT_RESOURCES
;
628 RtlCopyMemory(sc
->data
, data
, sc
->size
);
630 le
= Vcb
->sys_chunks
.Flink
;
631 while (le
!= &Vcb
->sys_chunks
) {
632 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
634 if (keycmp(&sc2
->key
, &sc
->key
) == 1)
639 InsertTailList(le
, &sc
->list_entry
);
641 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
644 le
= Vcb
->sys_chunks
.Flink
;
645 while (le
!= &Vcb
->sys_chunks
) {
646 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
648 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
650 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
653 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
659 return STATUS_SUCCESS
;
662 static chunk
* alloc_chunk(device_extension
* Vcb
, UINT64 flags
, LIST_ENTRY
* rollback
) {
663 UINT64 max_stripe_size
, max_chunk_size
, stripe_size
;
664 UINT64 total_size
= 0, i
, j
, logaddr
;
670 CHUNK_ITEM_STRIPE
* cis
;
673 BOOL success
= FALSE
;
674 BLOCK_GROUP_ITEM
* bgi
;
676 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
677 total_size
+= Vcb
->devices
[i
].devitem
.num_bytes
;
679 TRACE("total_size = %llx\n", total_size
);
681 if (flags
& BLOCK_FLAG_DATA
) {
682 max_stripe_size
= 0x40000000; // 1 GB
683 max_chunk_size
= 10 * max_stripe_size
;
684 } else if (flags
& BLOCK_FLAG_METADATA
) {
685 if (total_size
> 0xC80000000) // 50 GB
686 max_stripe_size
= 0x40000000; // 1 GB
688 max_stripe_size
= 0x10000000; // 256 MB
690 max_chunk_size
= max_stripe_size
;
691 } else if (flags
& BLOCK_FLAG_SYSTEM
) {
692 max_stripe_size
= 0x2000000; // 32 MB
693 max_chunk_size
= 2 * max_stripe_size
;
696 // FIXME - make sure whole number of sectors?
697 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
699 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
701 if (flags
& BLOCK_FLAG_DUPLICATE
) {
703 } else if (flags
& BLOCK_FLAG_RAID0
) {
704 FIXME("RAID0 not yet supported\n");
706 } else if (flags
& BLOCK_FLAG_RAID1
) {
707 FIXME("RAID1 not yet supported\n");
709 } else if (flags
& BLOCK_FLAG_RAID10
) {
710 FIXME("RAID10 not yet supported\n");
712 } else if (flags
& BLOCK_FLAG_RAID5
) {
713 FIXME("RAID5 not yet supported\n");
715 } else if (flags
& BLOCK_FLAG_RAID6
) {
716 FIXME("RAID6 not yet supported\n");
722 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * num_stripes
, ALLOC_TAG
);
724 ERR("out of memory\n");
728 for (i
= 0; i
< num_stripes
; i
++) {
729 stripes
[i
].dh
= NULL
;
731 for (j
= 0; j
< Vcb
->superblock
.num_devices
; j
++) {
732 LIST_ENTRY
* le
= Vcb
->devices
[j
].disk_holes
.Flink
;
734 while (le
!= &Vcb
->devices
[j
].disk_holes
) {
735 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
737 if (!dh
->provisional
) {
738 if (!stripes
[i
].dh
|| dh
->size
> stripes
[i
].dh
->size
) {
740 stripes
[i
].device
= &Vcb
->devices
[j
];
742 if (stripes
[i
].dh
->size
>= max_stripe_size
)
750 if (stripes
[i
].dh
&& stripes
[i
].dh
->size
>= max_stripe_size
)
755 TRACE("good DH: device %llx, address %llx, size %llx\n", stripes
[i
].device
->devitem
.dev_id
, stripes
[i
].dh
->address
, stripes
[i
].dh
->size
);
757 TRACE("good DH not found\n");
761 add_provisional_disk_hole(Vcb
, &stripes
[i
], max_stripe_size
);
764 stripe_size
= min(stripes
[0].dh
->size
, max_stripe_size
);
765 for (i
= 1; i
< num_stripes
; i
++) {
766 stripe_size
= min(stripe_size
, stripes
[1].dh
->size
);
768 // FIXME - make sure stripe_size aligned properly
769 // FIXME - obey max_chunk_size
771 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
773 ERR("out of memory\n");
777 // add CHUNK_ITEM to tree 3
779 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
780 ci
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
782 ERR("out of memory\n");
786 ci
->size
= stripe_size
; // FIXME for RAID
787 ci
->root_id
= Vcb
->extent_root
->id
;
788 ci
->stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
790 ci
->opt_io_alignment
= ci
->stripe_length
;
791 ci
->opt_io_width
= ci
->stripe_length
;
792 ci
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
793 ci
->num_stripes
= num_stripes
;
796 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
798 ERR("out of memory\n");
803 for (i
= 0; i
< num_stripes
; i
++) {
805 cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
809 cis
->dev_id
= stripes
[i
].device
->devitem
.dev_id
;
810 cis
->offset
= stripes
[i
].dh
->address
;
811 cis
->dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
813 c
->devices
[i
] = stripes
[i
].device
;
816 logaddr
= find_new_chunk_address(Vcb
, ci
->size
);
817 if (logaddr
== 0xffffffffffffffff) {
818 ERR("find_new_chunk_address failed\n");
823 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
, NULL
, rollback
)) {
824 ERR("insert_tree_item failed\n");
829 if (flags
& BLOCK_FLAG_SYSTEM
) {
830 NTSTATUS Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
);
831 if (!NT_SUCCESS(Status
)) {
832 ERR("add_to_bootstrap returned %08x\n", Status
);
837 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
839 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
840 if (!c
->chunk_item
) {
841 ERR("out of memory\n");
845 RtlCopyMemory(c
->chunk_item
, ci
, cisize
);
848 c
->used
= c
->oldused
= 0;
849 c
->space_changed
= FALSE
;
850 InitializeListHead(&c
->space
);
852 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
854 ERR("out of memory\n");
858 s
->offset
= c
->offset
;
859 s
->size
= c
->chunk_item
->size
;
860 s
->type
= SPACE_TYPE_FREE
;
861 InsertTailList(&c
->space
, &s
->list_entry
);
863 protect_superblocks(Vcb
, c
);
865 // add BLOCK_GROUP_ITEM to tree 2
867 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
869 ERR("out of memory\n");
874 bgi
->chunk_tree
= 0x100;
877 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, logaddr
, TYPE_BLOCK_GROUP_ITEM
, ci
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, rollback
)) {
878 ERR("insert_tree_item failed\n");
883 // add DEV_EXTENTs to tree 4
885 for (i
= 0; i
< num_stripes
; i
++) {
888 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
890 ERR("out of memory\n");
894 de
->chunktree
= Vcb
->chunk_root
->id
;
896 de
->address
= logaddr
;
897 de
->length
= ci
->size
;
898 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
900 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
)) {
901 ERR("insert_tree_item failed\n");
906 if (!increase_dev_item_used(Vcb
, stripes
[i
].device
, ci
->size
, rollback
)) {
907 ERR("increase_dev_item_used failed\n");
912 for (i
= 0; i
< num_stripes
; i
++) {
914 for (j
= 0; j
< i
; j
++) {
915 if (stripes
[j
].device
== stripes
[i
].device
)
920 reset_disk_holes(stripes
[i
].device
, TRUE
);
929 for (i
= 0; i
< num_stripes
; i
++) {
931 for (j
= 0; j
< i
; j
++) {
932 if (stripes
[j
].device
== stripes
[i
].device
)
937 reset_disk_holes(stripes
[i
].device
, FALSE
);
940 if (c
) ExFreePool(c
);
941 if (s
) ExFreePool(s
);
943 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
945 return success
? c
: NULL
;
948 static void decrease_chunk_usage(chunk
* c
, UINT64 delta
) {
951 TRACE("decreasing size of chunk %llx by %llx\n", c
->offset
, delta
);
954 static void increase_chunk_usage(chunk
* c
, UINT64 delta
) {
957 TRACE("increasing size of chunk %llx by %llx\n", c
->offset
, delta
);
960 static NTSTATUS STDCALL
write_data(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
) {
967 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
969 // FIXME - use version cached in Vcb
971 searchkey
.obj_id
= 0x100; // fixed?
972 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
973 searchkey
.offset
= address
;
975 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
976 if (!NT_SUCCESS(Status
)) {
977 ERR("error - find_item returned %08x\n", Status
);
981 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
982 ERR("error - unexpected item in chunk tree\n");
983 Status
= STATUS_INTERNAL_ERROR
;
987 if (tp
.item
->size
< sizeof(CHUNK_ITEM2
)) {
988 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
));
989 Status
= STATUS_INTERNAL_ERROR
;
993 ci
= (CHUNK_ITEM2
*)tp
.item
->data
;
995 if (tp
.item
->key
.offset
> address
|| tp
.item
->key
.offset
+ ci
->ci
.size
< address
) {
996 ERR("error - address %llx was out of chunk bounds\n", address
);
997 Status
= STATUS_INTERNAL_ERROR
;
1001 // FIXME - only do this for chunks marked DUPLICATE?
1002 // FIXME - for multiple writes, if PENDING do waits at the end
1003 // FIXME - work with RAID
1004 for (i
= 0; i
< ci
->ci
.num_stripes
; i
++) {
1005 Status
= write_data_phys(Vcb
->devices
[0].devobj
, address
- tp
.item
->key
.offset
+ ci
->stripes
[i
].offset
, data
, length
);
1006 if (!NT_SUCCESS(Status
)) {
1007 ERR("error - write_data_phys failed\n");
1013 free_traverse_ptr(&tp
);
1018 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
1019 LIST_ENTRY
*le
, *nextle
;
1023 // le = c->space.Flink;
1024 // while (le != &c->space) {
1025 // s = CONTAINING_RECORD(le, space, list_entry);
1027 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1032 le
= c
->space
.Flink
;
1033 while (le
!= &c
->space
) {
1034 s
= CONTAINING_RECORD(le
, space
, list_entry
);
1037 if (s
->type
== SPACE_TYPE_DELETING
)
1038 s
->type
= SPACE_TYPE_FREE
;
1039 else if (s
->type
== SPACE_TYPE_WRITING
)
1040 s
->type
= SPACE_TYPE_USED
;
1042 if (le
->Blink
!= &c
->space
) {
1043 s2
= CONTAINING_RECORD(le
->Blink
, space
, list_entry
);
1045 if (s2
->type
== s
->type
) { // do merge
1046 s2
->size
+= s
->size
;
1048 RemoveEntryList(&s
->list_entry
);
1056 // le = c->space.Flink;
1057 // while (le != &c->space) {
1058 // s = CONTAINING_RECORD(le, space, list_entry);
1060 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1066 static void clean_space_cache(device_extension
* Vcb
) {
1070 TRACE("(%p)\n", Vcb
);
1072 le
= Vcb
->chunks
.Flink
;
1073 while (le
!= &Vcb
->chunks
) {
1074 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1076 if (c
->space_changed
) {
1077 clean_space_cache_chunk(Vcb
, c
);
1078 c
->space_changed
= FALSE
;
1085 static BOOL
trees_consistent(device_extension
* Vcb
) {
1086 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
1089 le
= Vcb
->tree_cache
.Flink
;
1090 while (le
!= &Vcb
->tree_cache
) {
1091 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1094 if (tc2
->tree
->header
.num_items
== 0 && tc2
->tree
->parent
)
1097 if (tc2
->tree
->size
> maxsize
)
1100 if (!tc2
->tree
->has_new_address
)
1110 static NTSTATUS
add_parents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1114 le
= Vcb
->tree_cache
.Flink
;
1115 while (le
!= &Vcb
->tree_cache
) {
1116 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1119 if (tc2
->tree
->parent
)
1120 add_to_tree_cache(Vcb
, tc2
->tree
->parent
, TRUE
);
1121 else if (tc2
->tree
->root
!= Vcb
->chunk_root
&& tc2
->tree
->root
!= Vcb
->root_root
) {
1125 searchkey
.obj_id
= tc2
->tree
->root
->id
;
1126 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1127 searchkey
.offset
= 0xffffffffffffffff;
1129 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1130 if (!NT_SUCCESS(Status
)) {
1131 ERR("error - find_item returned %08x\n", Status
);
1135 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1136 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1137 free_traverse_ptr(&tp
);
1138 return STATUS_INTERNAL_ERROR
;
1141 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
1142 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1144 ERR("out of memory\n");
1145 return STATUS_INSUFFICIENT_RESOURCES
;
1148 if (tp
.item
->size
> 0)
1149 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
1151 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
1153 delete_tree_item(Vcb
, &tp
, rollback
);
1155 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1156 ERR("insert_tree_item failed\n");
1157 return STATUS_INTERNAL_ERROR
;
1160 add_to_tree_cache(Vcb
, tp
.tree
, TRUE
);
1163 free_traverse_ptr(&tp
);
1170 return STATUS_SUCCESS
;
1173 void print_trees(LIST_ENTRY
* tc
) {
1174 LIST_ENTRY
*le
, *le2
;
1178 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
1179 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1180 UINT32 num_items
= 0;
1182 le2
= tc2
->tree
->itemlist
.Flink
;
1183 while (le2
!= &tc2
->tree
->itemlist
) {
1184 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1186 firstitem
= td
->key
;
1192 ERR("tree: root %llx, first key %llx,%x,%llx, level %x, num_items %x / %x\n",
1193 tc2
->tree
->header
.tree_id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
, tc2
->tree
->header
.level
, num_items
, tc2
->tree
->header
.num_items
);
1199 static void add_parents_to_cache(device_extension
* Vcb
, tree
* t
) {
1207 add_to_tree_cache(Vcb
, t
, TRUE
);
1210 if (t
->root
== Vcb
->root_root
|| t
->root
== Vcb
->chunk_root
)
1213 searchkey
.obj_id
= t
->root
->id
;
1214 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1215 searchkey
.offset
= 0xffffffffffffffff;
1217 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1218 if (!NT_SUCCESS(Status
)) {
1219 ERR("error - find_item returned %08x\n", Status
);
1223 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1224 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1225 free_traverse_ptr(&tp
);
1229 add_to_tree_cache(Vcb
, tp
.tree
, TRUE
);
1231 free_traverse_ptr(&tp
);
1234 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, tree
* t
, chunk
* c
, UINT64 address
, LIST_ENTRY
* rollback
) {
1235 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1236 traverse_ptr insert_tp
;
1238 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1240 ERR("out of memory\n");
1244 eism
->ei
.refcount
= 1;
1245 eism
->ei
.generation
= Vcb
->superblock
.generation
;
1246 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1247 eism
->type
= TYPE_TREE_BLOCK_REF
;
1248 eism
->tbr
.offset
= t
->header
.tree_id
;
1250 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, t
->header
.level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, rollback
)) {
1251 ERR("insert_tree_item failed\n");
1256 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1258 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1259 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1261 free_traverse_ptr(&insert_tp
);
1263 t
->new_address
= address
;
1264 t
->has_new_address
= TRUE
;
1269 static BOOL
insert_tree_extent(device_extension
* Vcb
, tree
* t
, chunk
* c
, LIST_ENTRY
* rollback
) {
1271 EXTENT_ITEM_TREE2
* eit2
;
1272 traverse_ptr insert_tp
;
1274 TRACE("(%p, %p, %p, %p)\n", Vcb
, t
, c
, rollback
);
1276 if (!find_address_in_chunk(Vcb
, c
, Vcb
->superblock
.node_size
, &address
))
1279 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)
1280 return insert_tree_extent_skinny(Vcb
, t
, c
, address
, rollback
);
1282 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1284 ERR("out of memory\n");
1288 eit2
->eit
.extent_item
.refcount
= 1;
1289 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
1290 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1291 // eit2->eit.firstitem = wt->firstitem;
1292 eit2
->eit
.level
= t
->header
.level
;
1293 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1294 eit2
->tbr
.offset
= t
->header
.tree_id
;
1296 // #ifdef DEBUG_PARANOID
1297 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
1298 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
1299 // 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);
1304 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, rollback
)) {
1305 ERR("insert_tree_item failed\n");
1310 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1312 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1313 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1315 free_traverse_ptr(&insert_tp
);
1317 t
->new_address
= address
;
1318 t
->has_new_address
= TRUE
;
1323 static NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
1324 chunk
*origchunk
= NULL
, *c
;
1326 UINT64 flags
= t
->flags
;
1329 flags
= (t
->root
->id
== BTRFS_ROOT_CHUNK
? BLOCK_FLAG_SYSTEM
: BLOCK_FLAG_METADATA
) | BLOCK_FLAG_DUPLICATE
;
1331 // TRACE("flags = %x\n", (UINT32)wt->flags);
1333 // if (!chunk_test) { // TESTING
1334 // if ((c = alloc_chunk(Vcb, flags))) {
1335 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1336 // if (insert_tree_extent(Vcb, t, c)) {
1337 // chunk_test = TRUE;
1338 // return STATUS_SUCCESS;
1344 if (t
->has_address
) {
1345 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
1347 if (insert_tree_extent(Vcb
, t
, origchunk
, rollback
))
1348 return STATUS_SUCCESS
;
1351 le
= Vcb
->chunks
.Flink
;
1352 while (le
!= &Vcb
->chunks
) {
1353 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1355 // FIXME - make sure to avoid superblocks
1357 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1358 if (insert_tree_extent(Vcb
, t
, c
, rollback
))
1359 return STATUS_SUCCESS
;
1365 // allocate new chunk if necessary
1366 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
1367 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1368 if (insert_tree_extent(Vcb
, t
, c
, rollback
))
1369 return STATUS_SUCCESS
;
1373 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
1375 return STATUS_DISK_FULL
;
1378 static BOOL
reduce_tree_extent_skinny(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1382 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1385 searchkey
.obj_id
= address
;
1386 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
1387 searchkey
.offset
= 0xffffffffffffffff;
1389 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1390 if (!NT_SUCCESS(Status
)) {
1391 ERR("error - find_item returned %08x\n", Status
);
1395 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1396 TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1397 free_traverse_ptr(&tp
);
1401 if (tp
.item
->size
< sizeof(EXTENT_ITEM_SKINNY_METADATA
)) {
1402 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
));
1403 free_traverse_ptr(&tp
);
1407 delete_tree_item(Vcb
, &tp
, rollback
);
1409 eism
= (EXTENT_ITEM_SKINNY_METADATA
*)tp
.item
->data
;
1410 if (t
->header
.level
== 0 && eism
->ei
.flags
& EXTENT_ITEM_SHARED_BACKREFS
&& eism
->type
== TYPE_TREE_BLOCK_REF
) {
1411 // convert shared data extents
1413 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1414 while (le
!= &t
->itemlist
) {
1415 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1417 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1419 if (!td
->ignore
&& !td
->inserted
) {
1420 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1421 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1423 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1424 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1426 if (ed2
->address
!= 0) {
1427 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1428 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1437 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1440 c
= get_chunk_from_address(Vcb
, address
);
1443 decrease_chunk_usage(c
, Vcb
->superblock
.node_size
);
1445 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_DELETING
);
1447 ERR("could not find chunk for address %llx\n", address
);
1449 free_traverse_ptr(&tp
);
1455 // static void check_tree_num_items(tree* t) {
1459 // le2 = t->itemlist.Flink;
1461 // while (le2 != &t->itemlist) {
1462 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1465 // le2 = le2->Flink;
1468 // if (t->header.num_items != ni) {
1469 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
1472 // ERR("tree %p okay\n", t);
1476 // static void check_trees_num_items(LIST_ENTRY* tc) {
1477 // LIST_ENTRY* le = tc->Flink;
1478 // while (le != tc) {
1479 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
1481 // check_tree_num_items(tc2->tree);
1487 static void convert_old_tree_extent(device_extension
* Vcb
, tree_data
* td
, tree
* t
, LIST_ENTRY
* rollback
) {
1489 traverse_ptr tp
, tp2
, insert_tp
;
1490 EXTENT_REF_V0
* erv0
;
1493 TRACE("(%p, %p, %p)\n", Vcb
, td
, t
);
1495 searchkey
.obj_id
= td
->treeholder
.address
;
1496 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1497 searchkey
.offset
= 0xffffffffffffffff;
1499 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1500 if (!NT_SUCCESS(Status
)) {
1501 ERR("error - find_item returned %08x\n", Status
);
1505 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1506 TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey
.obj_id
);
1507 free_traverse_ptr(&tp
);
1511 searchkey
.obj_id
= td
->treeholder
.address
;
1512 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1513 searchkey
.offset
= Vcb
->superblock
.node_size
;
1515 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1516 if (!NT_SUCCESS(Status
)) {
1517 ERR("error - find_item returned %08x\n", Status
);
1518 free_traverse_ptr(&tp
);
1522 if (keycmp(&searchkey
, &tp2
.item
->key
)) {
1523 ERR("could not find %llx,%x,%llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1524 free_traverse_ptr(&tp2
);
1525 free_traverse_ptr(&tp
);
1529 if (tp
.item
->size
< sizeof(EXTENT_REF_V0
)) {
1530 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
));
1531 free_traverse_ptr(&tp2
);
1532 free_traverse_ptr(&tp
);
1536 erv0
= (EXTENT_REF_V0
*)tp
.item
->data
;
1538 delete_tree_item(Vcb
, &tp
, rollback
);
1539 delete_tree_item(Vcb
, &tp2
, rollback
);
1541 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1542 EXTENT_ITEM_SKINNY_METADATA
* eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1545 ERR("out of memory\n");
1546 free_traverse_ptr(&tp2
);
1547 free_traverse_ptr(&tp
);
1551 eism
->ei
.refcount
= 1;
1552 eism
->ei
.generation
= erv0
->gen
;
1553 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1554 eism
->type
= TYPE_TREE_BLOCK_REF
;
1555 eism
->tbr
.offset
= t
->header
.tree_id
;
1557 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
)) {
1558 ERR("insert_tree_item failed\n");
1559 free_traverse_ptr(&tp2
);
1560 free_traverse_ptr(&tp
);
1564 EXTENT_ITEM_TREE2
* eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1567 ERR("out of memory\n");
1568 free_traverse_ptr(&tp2
);
1569 free_traverse_ptr(&tp
);
1573 eit2
->eit
.extent_item
.refcount
= 1;
1574 eit2
->eit
.extent_item
.generation
= erv0
->gen
;
1575 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1576 eit2
->eit
.firstitem
= td
->key
;
1577 eit2
->eit
.level
= t
->header
.level
- 1;
1578 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1579 eit2
->tbr
.offset
= t
->header
.tree_id
;
1581 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
)) {
1582 ERR("insert_tree_item failed\n");
1583 free_traverse_ptr(&tp2
);
1584 free_traverse_ptr(&tp
);
1589 // add_to_tree_cache(tc, insert_tp.tree, TRUE);
1590 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1591 add_parents_to_cache(Vcb
, tp
.tree
);
1592 add_parents_to_cache(Vcb
, tp2
.tree
);
1594 free_traverse_ptr(&insert_tp
);
1596 free_traverse_ptr(&tp2
);
1597 free_traverse_ptr(&tp
);
1600 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1604 EXTENT_ITEM_V0
* eiv0
;
1608 // FIXME - deal with refcounts > 1
1610 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
1612 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1613 if (reduce_tree_extent_skinny(Vcb
, address
, t
, rollback
)) {
1614 return STATUS_SUCCESS
;
1618 searchkey
.obj_id
= address
;
1619 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1620 searchkey
.offset
= Vcb
->superblock
.node_size
;
1622 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1623 if (!NT_SUCCESS(Status
)) {
1624 ERR("error - find_item returned %08x\n", Status
);
1628 if (keycmp(&tp
.item
->key
, &searchkey
)) {
1629 ERR("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1631 free_traverse_ptr(&tp
);
1632 return STATUS_INTERNAL_ERROR
;
1635 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1636 eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1638 if (eiv0
->refcount
> 1) {
1639 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0
->refcount
);
1640 free_traverse_ptr(&tp
);
1641 return STATUS_INTERNAL_ERROR
;
1644 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1645 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
));
1646 free_traverse_ptr(&tp
);
1647 return STATUS_INTERNAL_ERROR
;
1650 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1652 if (ei
->refcount
> 1) {
1653 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei
->refcount
);
1654 free_traverse_ptr(&tp
);
1655 return STATUS_INTERNAL_ERROR
;
1658 if (t
->header
.level
== 0 && ei
->flags
& EXTENT_ITEM_SHARED_BACKREFS
) {
1659 // convert shared data extents
1661 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1662 while (le
!= &t
->itemlist
) {
1663 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1665 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1667 if (!td
->ignore
&& !td
->inserted
) {
1668 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1669 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1671 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1672 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1674 if (ed2
->address
!= 0) {
1675 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1676 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1685 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1689 delete_tree_item(Vcb
, &tp
, rollback
);
1691 // if EXTENT_ITEM_V0, delete corresponding B4 item
1692 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1695 searchkey
.obj_id
= address
;
1696 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1697 searchkey
.offset
= 0xffffffffffffffff;
1699 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1700 if (!NT_SUCCESS(Status
)) {
1701 ERR("error - find_item returned %08x\n", Status
);
1702 free_traverse_ptr(&tp
);
1706 if (tp2
.item
->key
.obj_id
== searchkey
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey
.obj_type
) {
1707 delete_tree_item(Vcb
, &tp2
, rollback
);
1709 free_traverse_ptr(&tp2
);
1712 if (!(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1715 // when writing old internal trees, convert related extents
1717 le
= t
->itemlist
.Flink
;
1718 while (le
!= &t
->itemlist
) {
1719 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1721 // ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
1723 if (!td
->ignore
&& !td
->inserted
) {
1724 if (t
->header
.level
> 0) {
1725 convert_old_tree_extent(Vcb
, td
, t
, rollback
);
1726 } else if (td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
)) {
1727 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1729 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1730 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1732 if (ed2
->address
!= 0) {
1733 TRACE("trying to convert old data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1734 convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1744 c
= get_chunk_from_address(Vcb
, address
);
1747 decrease_chunk_usage(c
, tp
.item
->key
.offset
);
1749 add_to_space_list(c
, address
, tp
.item
->key
.offset
, SPACE_TYPE_DELETING
);
1751 ERR("could not find chunk for address %llx\n", address
);
1753 free_traverse_ptr(&tp
);
1755 return STATUS_SUCCESS
;
1758 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1762 TRACE("(%p)\n", Vcb
);
1764 le
= Vcb
->tree_cache
.Flink
;
1765 while (le
!= &Vcb
->tree_cache
) {
1766 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1768 if (tc2
->write
&& !tc2
->tree
->has_new_address
) {
1771 Status
= get_tree_new_address(Vcb
, tc2
->tree
, rollback
);
1772 if (!NT_SUCCESS(Status
)) {
1773 ERR("get_tree_new_address returned %08x\n", Status
);
1777 TRACE("allocated extent %llx\n", tc2
->tree
->new_address
);
1779 if (tc2
->tree
->has_address
) {
1780 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
1782 if (!NT_SUCCESS(Status
)) {
1783 ERR("reduce_tree_extent returned %08x\n", Status
);
1788 c
= get_chunk_from_address(Vcb
, tc2
->tree
->new_address
);
1791 increase_chunk_usage(c
, Vcb
->superblock
.node_size
);
1793 ERR("could not find chunk for address %llx\n", tc2
->tree
->new_address
);
1794 return STATUS_INTERNAL_ERROR
;
1801 return STATUS_SUCCESS
;
1804 static NTSTATUS
update_root_root(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1808 TRACE("(%p)\n", Vcb
);
1810 le
= Vcb
->tree_cache
.Flink
;
1811 while (le
!= &Vcb
->tree_cache
) {
1812 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
1814 if (tc2
->write
&& !tc2
->tree
->parent
) {
1815 if (tc2
->tree
->root
!= Vcb
->root_root
&& tc2
->tree
->root
!= Vcb
->chunk_root
) {
1819 searchkey
.obj_id
= tc2
->tree
->root
->id
;
1820 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1821 searchkey
.offset
= 0xffffffffffffffff;
1823 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1824 if (!NT_SUCCESS(Status
)) {
1825 ERR("error - find_item returned %08x\n", Status
);
1829 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1830 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1831 free_traverse_ptr(&tp
);
1832 return STATUS_INTERNAL_ERROR
;
1835 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, tc2
->tree
->new_address
);
1837 tc2
->tree
->root
->root_item
.block_number
= tc2
->tree
->new_address
;
1838 tc2
->tree
->root
->root_item
.root_level
= tc2
->tree
->header
.level
;
1839 tc2
->tree
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1840 tc2
->tree
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1842 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
1843 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1846 ERR("out of memory\n");
1847 return STATUS_INSUFFICIENT_RESOURCES
;
1850 RtlCopyMemory(ri
, &tc2
->tree
->root
->root_item
, sizeof(ROOT_ITEM
));
1852 delete_tree_item(Vcb
, &tp
, rollback
);
1854 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1855 ERR("insert_tree_item failed\n");
1856 return STATUS_INTERNAL_ERROR
;
1859 RtlCopyMemory(tp
.item
->data
, &tc2
->tree
->root
->root_item
, sizeof(ROOT_ITEM
));
1861 free_traverse_ptr(&tp
);
1864 tc2
->tree
->root
->treeholder
.address
= tc2
->tree
->new_address
;
1870 return STATUS_SUCCESS
;
1873 enum write_tree_status
{
1874 WriteTreeStatus_Pending
,
1875 WriteTreeStatus_Success
,
1876 WriteTreeStatus_Error
,
1877 WriteTreeStatus_Cancelling
,
1878 WriteTreeStatus_Cancelled
1881 struct write_tree_context
;
1884 struct write_tree_context
* context
;
1888 IO_STATUS_BLOCK iosb
;
1889 enum write_tree_status status
;
1890 LIST_ENTRY list_entry
;
1891 } write_tree_stripe
;
1896 } write_tree_context
;
1898 static NTSTATUS STDCALL
write_tree_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1899 write_tree_stripe
* stripe
= conptr
;
1900 write_tree_context
* context
= (write_tree_context
*)stripe
->context
;
1904 if (stripe
->status
== WriteTreeStatus_Cancelling
) {
1905 stripe
->status
= WriteTreeStatus_Cancelled
;
1909 stripe
->iosb
= Irp
->IoStatus
;
1911 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
1912 stripe
->status
= WriteTreeStatus_Success
;
1914 le
= context
->stripes
.Flink
;
1916 stripe
->status
= WriteTreeStatus_Error
;
1918 while (le
!= &context
->stripes
) {
1919 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1921 if (s2
->status
== WriteTreeStatus_Pending
) {
1922 s2
->status
= WriteTreeStatus_Cancelling
;
1923 IoCancelIrp(s2
->Irp
);
1931 le
= context
->stripes
.Flink
;
1934 while (le
!= &context
->stripes
) {
1935 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1937 if (s2
->status
== WriteTreeStatus_Pending
|| s2
->status
== WriteTreeStatus_Cancelling
) {
1946 KeSetEvent(&context
->Event
, 0, FALSE
);
1948 return STATUS_MORE_PROCESSING_REQUIRED
;
1951 static NTSTATUS
write_tree(device_extension
* Vcb
, UINT64 addr
, UINT8
* data
, write_tree_context
* wtc
) {
1953 CHUNK_ITEM_STRIPE
* cis
;
1954 write_tree_stripe
* stripe
;
1957 c
= get_chunk_from_address(Vcb
, addr
);
1960 ERR("get_chunk_from_address failed\n");
1961 return STATUS_INTERNAL_ERROR
;
1964 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1966 // FIXME - make this work with RAID
1968 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1969 PIO_STACK_LOCATION IrpSp
;
1971 // FIXME - handle missing devices
1973 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_stripe
), ALLOC_TAG
);
1975 ERR("out of memory\n");
1976 return STATUS_INSUFFICIENT_RESOURCES
;
1979 stripe
->context
= (struct write_tree_context
*)wtc
;
1981 stripe
->device
= c
->devices
[i
];
1982 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
1983 stripe
->status
= WriteTreeStatus_Pending
;
1985 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
1988 ERR("IoAllocateIrp failed\n");
1989 return STATUS_INTERNAL_ERROR
;
1992 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
1993 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
1995 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
1996 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= data
;
1998 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
1999 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
2000 stripe
->Irp
->MdlAddress
= IoAllocateMdl(data
, Vcb
->superblock
.node_size
, FALSE
, FALSE
, NULL
);
2001 if (!stripe
->Irp
->MdlAddress
) {
2002 ERR("IoAllocateMdl failed\n");
2003 return STATUS_INTERNAL_ERROR
;
2006 MmProbeAndLockPages(stripe
->Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2008 stripe
->Irp
->UserBuffer
= data
;
2011 IrpSp
->Parameters
.Write
.Length
= Vcb
->superblock
.node_size
;
2012 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= addr
- c
->offset
+ cis
[i
].offset
;
2014 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
2016 IoSetCompletionRoutine(stripe
->Irp
, write_tree_completion
, stripe
, TRUE
, TRUE
, TRUE
);
2018 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
2021 return STATUS_SUCCESS
;
2024 static void free_stripes(write_tree_context
* wtc
) {
2025 LIST_ENTRY
*le
, *le2
, *nextle
;
2027 le
= wtc
->stripes
.Flink
;
2028 while (le
!= &wtc
->stripes
) {
2029 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2031 if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
2032 MmUnlockPages(stripe
->Irp
->MdlAddress
);
2033 IoFreeMdl(stripe
->Irp
->MdlAddress
);
2039 le
= wtc
->stripes
.Flink
;
2040 while (le
!= &wtc
->stripes
) {
2041 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2046 ExFreePool(stripe
->buf
);
2049 while (le2
!= &wtc
->stripes
) {
2050 write_tree_stripe
* s2
= CONTAINING_RECORD(le2
, write_tree_stripe
, list_entry
);
2052 if (s2
->buf
== stripe
->buf
)
2066 static NTSTATUS
write_trees(device_extension
* Vcb
) {
2072 write_tree_context
* wtc
;
2074 TRACE("(%p)\n", Vcb
);
2076 for (level
= 0; level
<= 255; level
++) {
2077 BOOL nothing_found
= TRUE
;
2079 TRACE("level = %u\n", level
);
2081 le
= Vcb
->tree_cache
.Flink
;
2082 while (le
!= &Vcb
->tree_cache
) {
2083 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2085 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
2086 KEY firstitem
, searchkey
;
2089 EXTENT_ITEM_TREE
* eit
;
2091 if (!tc2
->tree
->has_new_address
) {
2092 ERR("error - tried to write tree with no new address\n");
2096 le2
= tc2
->tree
->itemlist
.Flink
;
2097 while (le2
!= &tc2
->tree
->itemlist
) {
2098 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2100 firstitem
= td
->key
;
2106 if (tc2
->tree
->parent
) {
2107 tc2
->tree
->paritem
->key
= firstitem
;
2108 tc2
->tree
->paritem
->treeholder
.address
= tc2
->tree
->new_address
;
2109 tc2
->tree
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
2112 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
2113 searchkey
.obj_id
= tc2
->tree
->new_address
;
2114 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2115 searchkey
.offset
= Vcb
->superblock
.node_size
;
2117 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2118 if (!NT_SUCCESS(Status
)) {
2119 ERR("error - find_item returned %08x\n", Status
);
2123 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2124 // traverse_ptr next_tp;
2126 // tree_data* paritem;
2128 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
);
2129 free_traverse_ptr(&tp
);
2131 // searchkey.obj_id = 0;
2132 // searchkey.obj_type = 0;
2133 // searchkey.offset = 0;
2135 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
2139 // if (tp.tree->paritem != paritem) {
2140 // paritem = tp.tree->paritem;
2141 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
2144 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2146 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
2148 // free_traverse_ptr(&tp);
2153 // free_traverse_ptr(&tp);
2155 return STATUS_INTERNAL_ERROR
;
2158 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
2159 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
));
2160 free_traverse_ptr(&tp
);
2161 return STATUS_INTERNAL_ERROR
;
2164 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
2165 eit
->firstitem
= firstitem
;
2167 free_traverse_ptr(&tp
);
2170 nothing_found
= FALSE
;
2180 TRACE("allocated tree extents\n");
2182 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_context
), ALLOC_TAG
);
2184 ERR("out of memory\n");
2185 return STATUS_INSUFFICIENT_RESOURCES
;
2188 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
2189 InitializeListHead(&wtc
->stripes
);
2191 le
= Vcb
->tree_cache
.Flink
;
2192 while (le
!= &Vcb
->tree_cache
) {
2193 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2194 #ifdef DEBUG_PARANOID
2195 UINT32 num_items
= 0, size
= 0;
2201 #ifdef DEBUG_PARANOID
2202 le2
= tc2
->tree
->itemlist
.Flink
;
2203 while (le2
!= &tc2
->tree
->itemlist
) {
2204 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2208 if (tc2
->tree
->header
.level
== 0)
2214 if (tc2
->tree
->header
.level
== 0)
2215 size
+= num_items
* sizeof(leaf_node
);
2217 size
+= num_items
* sizeof(internal_node
);
2219 if (num_items
!= tc2
->tree
->header
.num_items
) {
2220 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
);
2224 if (size
!= tc2
->tree
->size
) {
2225 ERR("tree %llx, level %x: size was %x, expected %x\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
, size
, tc2
->tree
->size
);
2229 if (tc2
->tree
->header
.num_items
== 0 && tc2
->tree
->parent
) {
2230 ERR("tree %llx, level %x: tried to write empty tree with parent\n", tc2
->tree
->root
->id
, tc2
->tree
->header
.level
);
2234 if (tc2
->tree
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2235 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
));
2240 ERR("tree %p\n", tc2
->tree
);
2241 le2
= tc2
->tree
->itemlist
.Flink
;
2242 while (le2
!= &tc2
->tree
->itemlist
) {
2243 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2245 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
2252 tc2
->tree
->header
.address
= tc2
->tree
->new_address
;
2253 tc2
->tree
->header
.generation
= Vcb
->superblock
.generation
;
2254 tc2
->tree
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
2255 tc2
->tree
->has_address
= TRUE
;
2257 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
2259 ERR("out of memory\n");
2260 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2264 body
= data
+ sizeof(tree_header
);
2266 RtlCopyMemory(data
, &tc2
->tree
->header
, sizeof(tree_header
));
2267 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2269 if (tc2
->tree
->header
.level
== 0) {
2270 leaf_node
* itemptr
= (leaf_node
*)body
;
2273 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
2275 le2
= tc2
->tree
->itemlist
.Flink
;
2276 while (le2
!= &tc2
->tree
->itemlist
) {
2277 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2279 dataptr
= dataptr
- td
->size
;
2281 itemptr
[i
].key
= td
->key
;
2282 itemptr
[i
].offset
= (UINT8
*)dataptr
- (UINT8
*)body
;
2283 itemptr
[i
].size
= td
->size
;
2287 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
2293 internal_node
* itemptr
= (internal_node
*)body
;
2297 le2
= tc2
->tree
->itemlist
.Flink
;
2298 while (le2
!= &tc2
->tree
->itemlist
) {
2299 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2301 itemptr
[i
].key
= td
->key
;
2302 itemptr
[i
].address
= td
->treeholder
.address
;
2303 itemptr
[i
].generation
= td
->treeholder
.generation
;
2311 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
2313 *((UINT32
*)data
) = crc32
;
2314 TRACE("setting crc32 to %08x\n", crc32
);
2316 Status
= write_tree(Vcb
, tc2
->tree
->new_address
, data
, wtc
);
2317 if (!NT_SUCCESS(Status
)) {
2318 ERR("write_tree returned %08x\n", Status
);
2326 Status
= STATUS_SUCCESS
;
2328 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
2329 // launch writes and wait
2330 le
= wtc
->stripes
.Flink
;
2331 while (le
!= &wtc
->stripes
) {
2332 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2334 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2339 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2341 le
= wtc
->stripes
.Flink
;
2342 while (le
!= &wtc
->stripes
) {
2343 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2345 if (!NT_SUCCESS(stripe
->iosb
.Status
)) {
2346 Status
= stripe
->iosb
.Status
;
2362 static NTSTATUS
write_superblocks(device_extension
* Vcb
) {
2367 TRACE("(%p)\n", Vcb
);
2369 le
= Vcb
->tree_cache
.Flink
;
2370 while (le
!= &Vcb
->tree_cache
) {
2371 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2373 if (tc2
->write
&& !tc2
->tree
->parent
) {
2374 if (tc2
->tree
->root
== Vcb
->root_root
) {
2375 Vcb
->superblock
.root_tree_addr
= tc2
->tree
->new_address
;
2376 Vcb
->superblock
.root_level
= tc2
->tree
->header
.level
;
2377 } else if (tc2
->tree
->root
== Vcb
->chunk_root
) {
2378 Vcb
->superblock
.chunk_tree_addr
= tc2
->tree
->new_address
;
2379 Vcb
->superblock
.chunk_root_generation
= tc2
->tree
->header
.generation
;
2380 Vcb
->superblock
.chunk_root_level
= tc2
->tree
->header
.level
;
2387 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2388 if (Vcb
->devices
[i
].devobj
) {
2389 Status
= write_superblock(Vcb
, &Vcb
->devices
[i
]);
2390 if (!NT_SUCCESS(Status
)) {
2391 ERR("write_superblock returned %08x\n", Status
);
2397 return STATUS_SUCCESS
;
2400 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
2401 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
2405 BLOCK_GROUP_ITEM
* bgi
;
2408 TRACE("(%p)\n", Vcb
);
2410 while (le
!= &Vcb
->chunks
) {
2411 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2413 if (c
->used
!= c
->oldused
) {
2414 searchkey
.obj_id
= c
->offset
;
2415 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2416 searchkey
.offset
= c
->chunk_item
->size
;
2418 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2419 if (!NT_SUCCESS(Status
)) {
2420 ERR("error - find_item returned %08x\n", Status
);
2424 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2425 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2427 free_traverse_ptr(&tp
);
2428 return STATUS_INTERNAL_ERROR
;
2431 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2432 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
));
2433 free_traverse_ptr(&tp
);
2434 return STATUS_INTERNAL_ERROR
;
2437 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2439 ERR("out of memory\n");
2440 free_traverse_ptr(&tp
);
2441 return STATUS_INSUFFICIENT_RESOURCES
;
2444 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2445 bgi
->used
= c
->used
;
2447 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2449 delete_tree_item(Vcb
, &tp
, rollback
);
2451 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, rollback
)) {
2452 ERR("insert_tree_item failed\n");
2454 return STATUS_INTERNAL_ERROR
;
2457 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2458 TRACE("chunk_item type = %llx\n", c
->chunk_item
->type
);
2460 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
2461 FIXME("RAID0 not yet supported\n");
2463 free_traverse_ptr(&tp
);
2464 return STATUS_INTERNAL_ERROR
;
2465 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
) {
2466 FIXME("RAID1 not yet supported\n");
2468 free_traverse_ptr(&tp
);
2469 return STATUS_INTERNAL_ERROR
;
2470 } else if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
) {
2471 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ (2 * (c
->used
- c
->oldused
));
2472 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
2473 FIXME("RAID10 not yet supported\n");
2475 free_traverse_ptr(&tp
);
2476 return STATUS_INTERNAL_ERROR
;
2477 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
2478 FIXME("RAID5 not yet supported\n");
2480 free_traverse_ptr(&tp
);
2481 return STATUS_INTERNAL_ERROR
;
2482 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2483 FIXME("RAID6 not yet supported\n");
2485 free_traverse_ptr(&tp
);
2486 return STATUS_INTERNAL_ERROR
;
2488 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ c
->used
- c
->oldused
;
2491 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2493 free_traverse_ptr(&tp
);
2495 c
->oldused
= c
->used
;
2501 return STATUS_SUCCESS
;
2504 static void get_first_item(tree
* t
, KEY
* key
) {
2507 le
= t
->itemlist
.Flink
;
2508 while (le
!= &t
->itemlist
) {
2509 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2516 static NTSTATUS STDCALL
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2519 tree_data
* oldlastitem
;
2521 // // tree_data *firsttd, *lasttd;
2522 // // LIST_ENTRY* le;
2523 // #ifdef DEBUG_PARANOID
2524 // KEY lastkey1, lastkey2;
2525 // traverse_ptr tp, next_tp;
2526 // ULONG numitems1, numitems2;
2529 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2531 // #ifdef DEBUG_PARANOID
2532 // lastkey1.obj_id = 0xffffffffffffffff;
2533 // lastkey1.obj_type = 0xff;
2534 // lastkey1.offset = 0xffffffffffffffff;
2536 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2537 // ERR("error - find_item failed\n");
2539 // lastkey1 = tp.item->key;
2541 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2542 // free_traverse_ptr(&tp);
2546 // free_traverse_ptr(&tp);
2550 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2552 ERR("out of memory\n");
2553 return STATUS_INSUFFICIENT_RESOURCES
;
2556 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2557 nt
->header
.address
= 0;
2558 nt
->header
.generation
= Vcb
->superblock
.generation
;
2559 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2560 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2563 nt
->has_address
= FALSE
;
2565 nt
->parent
= t
->parent
;
2567 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2568 nt
->new_address
= 0;
2569 nt
->has_new_address
= FALSE
;
2570 nt
->flags
= t
->flags
;
2571 InitializeListHead(&nt
->itemlist
);
2573 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2575 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2577 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2578 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2580 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2581 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2582 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2584 // // le = wt->tree->itemlist.Flink;
2585 // // while (le != &wt->tree->itemlist) {
2586 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2587 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2588 // // le = le->Flink;
2591 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2592 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2593 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2594 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2596 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2597 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2599 // // le = wt->tree->itemlist.Flink;
2600 // // while (le != &wt->tree->itemlist) {
2601 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2602 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2603 // // le = le->Flink;
2606 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2607 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2609 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2610 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2611 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2613 nt
->size
= t
->size
- size
;
2615 t
->header
.num_items
= numitems
;
2616 add_to_tree_cache(Vcb
, nt
, TRUE
);
2618 InterlockedIncrement(&Vcb
->open_trees
);
2619 #ifdef DEBUG_TREE_REFCOUNTS
2620 TRACE("created new split tree %p\n", nt
);
2622 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2625 // // td = wt->tree->items;
2627 // // if (!td->ignore) {
2628 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2630 // // td = td->next;
2633 // // oldlastitem->next = NULL;
2634 // // wt->tree->lastitem = oldlastitem;
2636 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2638 if (nt
->header
.level
> 0) {
2639 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2641 while (le
!= &nt
->itemlist
) {
2642 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2644 if (td2
->treeholder
.tree
) {
2645 td2
->treeholder
.tree
->parent
= nt
;
2646 increase_tree_rc(nt
);
2655 increase_tree_rc(nt
->parent
);
2657 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2659 ERR("out of memory\n");
2660 return STATUS_INSUFFICIENT_RESOURCES
;
2663 td
->key
= newfirstitem
->key
;
2665 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2668 td
->inserted
= TRUE
;
2669 td
->treeholder
.tree
= nt
;
2670 init_tree_holder(&td
->treeholder
);
2671 // td->treeholder.nonpaged->status = tree_holder_loaded;
2674 nt
->parent
->header
.num_items
++;
2675 nt
->parent
->size
+= sizeof(internal_node
);
2680 TRACE("adding new tree parent\n");
2682 if (nt
->header
.level
== 255) {
2683 ERR("cannot add parent to tree at level 255\n");
2684 return STATUS_INTERNAL_ERROR
;
2687 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2689 ERR("out of memory\n");
2690 return STATUS_INSUFFICIENT_RESOURCES
;
2693 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2694 pt
->header
.address
= 0;
2695 pt
->header
.num_items
= 2;
2696 pt
->header
.level
= nt
->header
.level
+ 1;
2697 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2700 pt
->has_address
= FALSE
;
2705 pt
->new_address
= 0;
2706 pt
->has_new_address
= FALSE
;
2707 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2708 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2709 pt
->flags
= t
->flags
;
2710 InitializeListHead(&pt
->itemlist
);
2712 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2714 InterlockedIncrement(&Vcb
->open_trees
);
2715 #ifdef DEBUG_TREE_REFCOUNTS
2716 TRACE("created new parent tree %p\n", pt
);
2718 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2720 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2722 ERR("out of memory\n");
2723 return STATUS_INSUFFICIENT_RESOURCES
;
2726 get_first_item(t
, &td
->key
);
2728 td
->inserted
= FALSE
;
2729 td
->treeholder
.address
= 0;
2730 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2731 td
->treeholder
.tree
= t
;
2732 init_tree_holder(&td
->treeholder
);
2733 // td->treeholder.nonpaged->status = tree_holder_loaded;
2734 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2737 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2739 ERR("out of memory\n");
2740 return STATUS_INSUFFICIENT_RESOURCES
;
2743 td
->key
= newfirstitem
->key
;
2745 td
->inserted
= FALSE
;
2746 td
->treeholder
.address
= 0;
2747 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2748 td
->treeholder
.tree
= nt
;
2749 init_tree_holder(&td
->treeholder
);
2750 // td->treeholder.nonpaged->status = tree_holder_loaded;
2751 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2754 add_to_tree_cache(Vcb
, pt
, TRUE
);
2756 t
->root
->treeholder
.tree
= pt
;
2762 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2764 // #ifdef DEBUG_PARANOID
2765 // lastkey2.obj_id = 0xffffffffffffffff;
2766 // lastkey2.obj_type = 0xff;
2767 // lastkey2.offset = 0xffffffffffffffff;
2769 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2770 // ERR("error - find_item failed\n");
2772 // lastkey2 = tp.item->key;
2775 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2776 // free_traverse_ptr(&tp);
2780 // free_traverse_ptr(&tp);
2783 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2784 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2785 // ERR("numitems1 = %u\n", numitems1);
2786 // ERR("numitems2 = %u\n", numitems2);
2789 return STATUS_SUCCESS
;
2792 static NTSTATUS STDCALL
split_tree(device_extension
* Vcb
, tree
* t
) {
2794 UINT32 size
, ds
, numitems
;
2799 // FIXME - naïve implementation: maximizes number of filled trees
2801 le
= t
->itemlist
.Flink
;
2802 while (le
!= &t
->itemlist
) {
2803 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2806 if (t
->header
.level
== 0)
2807 ds
= sizeof(leaf_node
) + td
->size
;
2809 ds
= sizeof(internal_node
);
2811 // FIXME - move back if previous item was deleted item with same key
2812 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
2813 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
2822 return STATUS_SUCCESS
;
2825 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
2827 tree_data
* nextparitem
= NULL
;
2829 tree
*next_tree
, *par
;
2832 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
2834 // FIXME - doesn't capture everything, as it doesn't ascend
2835 // FIXME - write proper function and put it in treefuncs.c
2836 le
= t
->paritem
->list_entry
.Flink
;
2837 while (le
!= &t
->parent
->itemlist
) {
2838 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2849 return STATUS_SUCCESS
;
2851 // FIXME - loop, and capture more than one tree if we can
2853 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
2854 // nextparitem = t->paritem;
2856 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2858 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
);
2859 if (!NT_SUCCESS(Status
)) {
2860 ERR("do_load_tree returned %08x\n", Status
);
2865 increase_tree_rc(t
->parent
);
2867 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2869 next_tree
= nextparitem
->treeholder
.tree
;
2871 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2872 // merge two trees into one
2874 t
->header
.num_items
+= next_tree
->header
.num_items
;
2875 t
->size
+= next_tree
->size
;
2877 if (next_tree
->header
.level
> 0) {
2878 le
= next_tree
->itemlist
.Flink
;
2880 while (le
!= &next_tree
->itemlist
) {
2881 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2883 if (td2
->treeholder
.tree
) {
2884 td2
->treeholder
.tree
->parent
= t
;
2885 increase_tree_rc(t
);
2886 free_tree(next_tree
);
2893 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
2894 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
2895 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
2896 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2899 // le = t->itemlist.Flink;
2900 // while (le != &t->itemlist) {
2901 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2902 // if (!td->ignore) {
2903 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2908 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
2910 next_tree
->header
.num_items
= 0;
2911 next_tree
->size
= 0;
2913 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
2914 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, rollback
);
2916 if (!NT_SUCCESS(Status
)) {
2917 ERR("reduce_tree_extent returned %08x\n", Status
);
2918 free_tree(next_tree
);
2921 } else if (next_tree
->has_address
) {
2922 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, rollback
);
2924 if (!NT_SUCCESS(Status
)) {
2925 ERR("reduce_tree_extent returned %08x\n", Status
);
2926 free_tree(next_tree
);
2931 if (!nextparitem
->ignore
) {
2932 nextparitem
->ignore
= TRUE
;
2933 next_tree
->parent
->header
.num_items
--;
2934 next_tree
->parent
->size
-= sizeof(internal_node
);
2937 par
= next_tree
->parent
;
2939 add_to_tree_cache(Vcb
, par
, TRUE
);
2943 RemoveEntryList(&nextparitem
->list_entry
);
2944 ExFreePool(next_tree
->paritem
);
2945 next_tree
->paritem
= NULL
;
2947 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2949 free_tree(next_tree
);
2951 // remove next_tree from tree cache
2952 le
= Vcb
->tree_cache
.Flink
;
2953 while (le
!= &Vcb
->tree_cache
) {
2954 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
2956 if (tc2
->tree
== next_tree
) {
2957 free_tree(next_tree
);
2958 RemoveEntryList(le
);
2966 // rebalance by moving items from second tree into first
2967 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
2968 KEY firstitem
= {0, 0, 0};
2970 TRACE("attempting rebalance\n");
2972 le
= next_tree
->itemlist
.Flink
;
2973 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
2974 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2978 if (next_tree
->header
.level
== 0)
2979 size
= sizeof(leaf_node
) + td
->size
;
2981 size
= sizeof(internal_node
);
2985 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2986 RemoveEntryList(&td
->list_entry
);
2987 InsertTailList(&t
->itemlist
, &td
->list_entry
);
2989 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
) {
2990 td
->treeholder
.tree
->parent
= t
;
2991 increase_tree_rc(t
);
2992 free_tree(next_tree
);
2996 next_tree
->size
-= size
;
2998 next_tree
->header
.num_items
--;
2999 t
->header
.num_items
++;
3004 le
= next_tree
->itemlist
.Flink
;
3007 le
= next_tree
->itemlist
.Flink
;
3008 while (le
!= &next_tree
->itemlist
) {
3009 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3012 firstitem
= td
->key
;
3019 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
3021 // FIXME - once ascension is working, make this work with parent's parent, etc.
3022 if (next_tree
->paritem
)
3023 next_tree
->paritem
->key
= firstitem
;
3027 add_to_tree_cache(Vcb
, par
, TRUE
);
3031 free_tree(next_tree
);
3034 return STATUS_SUCCESS
;
3037 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, LIST_ENTRY
* rollback
) {
3042 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
3043 searchkey
.obj_id
= address
;
3044 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
3045 searchkey
.offset
= t
->header
.level
;
3047 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3048 if (!NT_SUCCESS(Status
)) {
3049 ERR("error - find_item returned %08x\n", Status
);
3053 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
3054 EXTENT_ITEM_SKINNY_METADATA
* eism
;
3056 if (tp
.item
->size
> 0) {
3057 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3060 ERR("out of memory\n");
3061 free_traverse_ptr(&tp
);
3062 return STATUS_INSUFFICIENT_RESOURCES
;
3065 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
3069 delete_tree_item(Vcb
, &tp
, rollback
);
3071 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, rollback
)) {
3072 ERR("insert_tree_item failed\n");
3074 free_traverse_ptr(&tp
);
3075 return STATUS_INTERNAL_ERROR
;
3078 free_traverse_ptr(&tp
);
3079 return STATUS_SUCCESS
;
3082 free_traverse_ptr(&tp
);
3085 searchkey
.obj_id
= address
;
3086 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3087 searchkey
.offset
= 0xffffffffffffffff;
3089 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3090 if (!NT_SUCCESS(Status
)) {
3091 ERR("error - find_item returned %08x\n", Status
);
3095 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3096 EXTENT_ITEM_TREE
* eit
;
3098 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
3099 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
));
3100 free_traverse_ptr(&tp
);
3101 return STATUS_INTERNAL_ERROR
;
3104 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3107 ERR("out of memory\n");
3108 free_traverse_ptr(&tp
);
3109 return STATUS_INSUFFICIENT_RESOURCES
;
3112 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
3114 delete_tree_item(Vcb
, &tp
, rollback
);
3118 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
)) {
3119 ERR("insert_tree_item failed\n");
3121 free_traverse_ptr(&tp
);
3122 return STATUS_INTERNAL_ERROR
;
3125 free_traverse_ptr(&tp
);
3127 return STATUS_SUCCESS
;
3130 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
3132 free_traverse_ptr(&tp
);
3134 return STATUS_INTERNAL_ERROR
;
3137 static NTSTATUS STDCALL
do_splits(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3138 // LIST_ENTRY *le, *le2;
3141 UINT8 level
, max_level
;
3143 BOOL empty
, done_deletions
= FALSE
;
3147 TRACE("(%p)\n", Vcb
);
3151 for (level
= 0; level
<= 255; level
++) {
3152 LIST_ENTRY
*le
, *nextle
;
3156 TRACE("doing level %u\n", level
);
3158 le
= Vcb
->tree_cache
.Flink
;
3160 while (le
!= &Vcb
->tree_cache
) {
3161 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3165 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
3168 if (tc2
->tree
->header
.num_items
== 0) {
3169 if (tc2
->tree
->parent
) {
3171 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
3173 done_deletions
= TRUE
;
3175 le2
= tc2
->tree
->itemlist
.Flink
;
3176 while (le2
!= &tc2
->tree
->itemlist
) {
3177 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3178 firstitem
= td
->key
;
3181 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
3182 tc2
->tree
->root
->id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
);
3184 tc2
->tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3186 if (tc2
->tree
->has_new_address
) { // delete associated EXTENT_ITEM
3187 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, rollback
);
3189 if (!NT_SUCCESS(Status
)) {
3190 ERR("reduce_tree_extent returned %08x\n", Status
);
3193 } else if (tc2
->tree
->has_address
) {
3194 Status
= reduce_tree_extent(Vcb
,tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
3196 if (!NT_SUCCESS(Status
)) {
3197 ERR("reduce_tree_extent returned %08x\n", Status
);
3202 if (!tc2
->tree
->paritem
->ignore
) {
3203 tc2
->tree
->paritem
->ignore
= TRUE
;
3204 tc2
->tree
->parent
->header
.num_items
--;
3205 tc2
->tree
->parent
->size
-= sizeof(internal_node
);
3208 RemoveEntryList(&tc2
->tree
->paritem
->list_entry
);
3209 ExFreePool(tc2
->tree
->paritem
);
3210 tc2
->tree
->paritem
= NULL
;
3212 free_tree(tc2
->tree
);
3214 RemoveEntryList(le
);
3216 } else if (tc2
->tree
->header
.level
!= 0) {
3217 if (tc2
->tree
->has_new_address
) {
3218 Status
= update_extent_level(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, 0, rollback
);
3220 if (!NT_SUCCESS(Status
)) {
3221 ERR("update_extent_level returned %08x\n", Status
);
3226 tc2
->tree
->header
.level
= 0;
3228 } else if (tc2
->tree
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3229 TRACE("splitting overlarge tree (%x > %x)\n", tc2
->tree
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3230 Status
= split_tree(Vcb
, tc2
->tree
);
3232 if (!NT_SUCCESS(Status
)) {
3233 ERR("split_tree returned %08x\n", Status
);
3245 TRACE("nothing found for level %u\n", level
);
3250 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
3252 for (level
= 0; level
<= max_level
; level
++) {
3255 le
= Vcb
->tree_cache
.Flink
;
3257 while (le
!= &Vcb
->tree_cache
) {
3258 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3260 if (tc2
->write
&& tc2
->tree
->header
.level
== level
&& tc2
->tree
->header
.num_items
> 0 && tc2
->tree
->parent
&& tc2
->tree
->size
< min_size
) {
3261 Status
= try_tree_amalgamate(Vcb
, tc2
->tree
, rollback
);
3262 if (!NT_SUCCESS(Status
)) {
3263 ERR("try_tree_amalgamate returned %08x\n", Status
);
3272 // simplify trees if top tree only has one entry
3274 if (done_deletions
) {
3275 for (level
= max_level
; level
> 0; level
--) {
3276 LIST_ENTRY
*le
, *nextle
;
3278 le
= Vcb
->tree_cache
.Flink
;
3279 while (le
!= &Vcb
->tree_cache
) {
3281 tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3283 if (tc2
->write
&& tc2
->tree
->header
.level
== level
) {
3284 if (!tc2
->tree
->parent
&& tc2
->tree
->header
.num_items
== 1) {
3285 LIST_ENTRY
* le2
= tc2
->tree
->itemlist
.Flink
;
3287 tree
* child_tree
= NULL
;
3289 while (le2
!= &tc2
->tree
->itemlist
) {
3290 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3296 TRACE("deleting top-level tree in root %llx with one item\n", tc2
->tree
->root
->id
);
3298 if (tc2
->tree
->has_new_address
) { // delete associated EXTENT_ITEM
3299 Status
= reduce_tree_extent(Vcb
, tc2
->tree
->new_address
, tc2
->tree
, rollback
);
3301 if (!NT_SUCCESS(Status
)) {
3302 ERR("reduce_tree_extent returned %08x\n", Status
);
3305 } else if (tc2
->tree
->has_address
) {
3306 Status
= reduce_tree_extent(Vcb
,tc2
->tree
->header
.address
, tc2
->tree
, rollback
);
3308 if (!NT_SUCCESS(Status
)) {
3309 ERR("reduce_tree_extent returned %08x\n", Status
);
3314 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3315 KEY searchkey
= {0,0,0};
3318 Status
= find_item(Vcb
, tc2
->tree
->root
, &tp
, &searchkey
, FALSE
);
3319 if (!NT_SUCCESS(Status
)) {
3320 ERR("error - find_item returned %08x\n", Status
);
3324 free_traverse_ptr(&tp
);
3327 child_tree
= td
->treeholder
.tree
;
3330 child_tree
->parent
= NULL
;
3331 child_tree
->paritem
= NULL
;
3332 free_tree(tc2
->tree
);
3335 tc2
->tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3337 free_tree(tc2
->tree
);
3340 child_tree
->root
->treeholder
.tree
= child_tree
;
3342 RemoveEntryList(le
);
3352 return STATUS_SUCCESS
;
3355 NTSTATUS STDCALL
do_write(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3359 TRACE("(%p)\n", Vcb
);
3361 // If only changing superblock, e.g. changing label, we still need to rewrite
3362 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
3363 if (Vcb
->write_trees
> 0) {
3367 searchkey
.obj_id
= 0;
3368 searchkey
.obj_type
= 0;
3369 searchkey
.offset
= 0;
3371 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3372 if (!NT_SUCCESS(Status
)) {
3373 ERR("error - find_item returned %08x\n", Status
);
3377 add_to_tree_cache(Vcb
, Vcb
->root_root
->treeholder
.tree
, TRUE
);
3379 free_traverse_ptr(&tp
);
3383 Status
= add_parents(Vcb
, rollback
);
3384 if (!NT_SUCCESS(Status
)) {
3385 ERR("add_parents returned %08x\n", Status
);
3389 Status
= do_splits(Vcb
, rollback
);
3390 if (!NT_SUCCESS(Status
)) {
3391 ERR("do_splits returned %08x\n", Status
);
3395 Status
= allocate_tree_extents(Vcb
, rollback
);
3396 if (!NT_SUCCESS(Status
)) {
3397 ERR("add_parents returned %08x\n", Status
);
3401 Status
= update_chunk_usage(Vcb
, rollback
);
3402 if (!NT_SUCCESS(Status
)) {
3403 ERR("update_chunk_usage returned %08x\n", Status
);
3406 } while (!trees_consistent(Vcb
));
3408 TRACE("trees consistent\n");
3410 Status
= update_root_root(Vcb
, rollback
);
3411 if (!NT_SUCCESS(Status
)) {
3412 ERR("update_root_root returned %08x\n", Status
);
3416 Status
= write_trees(Vcb
);
3417 if (!NT_SUCCESS(Status
)) {
3418 ERR("write_trees returned %08x\n", Status
);
3422 Status
= write_superblocks(Vcb
);
3423 if (!NT_SUCCESS(Status
)) {
3424 ERR("write_superblocks returned %08x\n", Status
);
3428 clean_space_cache(Vcb
);
3430 Vcb
->superblock
.generation
++;
3432 // print_trees(tc); // TESTING
3434 Status
= STATUS_SUCCESS
;
3436 le
= Vcb
->tree_cache
.Flink
;
3437 while (le
!= &Vcb
->tree_cache
) {
3438 tree_cache
* tc2
= CONTAINING_RECORD(le
, tree_cache
, list_entry
);
3445 Vcb
->write_trees
= 0;
3448 TRACE("do_write returning %08x\n", Status
);
3453 NTSTATUS
consider_write(device_extension
* Vcb
) {
3454 // FIXME - call do_write if Vcb->write_trees high
3456 LIST_ENTRY rollback
;
3457 NTSTATUS Status
= STATUS_SUCCESS
;
3459 InitializeListHead(&rollback
);
3461 if (Vcb
->write_trees
> 0)
3462 Status
= do_write(Vcb
, &rollback
);
3464 free_tree_cache(&Vcb
->tree_cache
);
3466 if (!NT_SUCCESS(Status
))
3467 do_rollback(Vcb
, &rollback
);
3469 clear_rollback(&rollback
);
3473 return STATUS_SUCCESS
;
3477 static __inline
void insert_into_ordered_list(LIST_ENTRY
* list
, ordered_list
* ins
) {
3478 LIST_ENTRY
* le
= list
->Flink
;
3481 while (le
!= list
) {
3482 ol
= (ordered_list
*)le
;
3484 if (ol
->key
> ins
->key
) {
3485 le
->Blink
->Flink
= &ins
->list_entry
;
3486 ins
->list_entry
.Blink
= le
->Blink
;
3487 le
->Blink
= &ins
->list_entry
;
3488 ins
->list_entry
.Flink
= le
;
3495 InsertTailList(list
, &ins
->list_entry
);
3498 static UINT64
get_extent_data_ref_hash(UINT64 root
, UINT64 objid
, UINT64 offset
) {
3499 UINT32 high_crc
= 0xffffffff, low_crc
= 0xffffffff;
3501 // FIXME - can we test this?
3503 // FIXME - make sure numbers here are little-endian
3504 high_crc
= calc_crc32c(high_crc
, (UINT8
*)&root
, sizeof(UINT64
));
3505 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&objid
, sizeof(UINT64
));
3506 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&offset
, sizeof(UINT64
));
3508 return ((UINT64
)high_crc
<< 31) ^ (UINT64
)low_crc
;
3511 NTSTATUS STDCALL
add_extent_ref(device_extension
* Vcb
, UINT64 address
, UINT64 size
, root
* subvol
, UINT64 inode
, UINT64 offset
, LIST_ENTRY
* rollback
) {
3515 UINT8
*siptr
, *type
;
3518 EXTENT_DATA_REF
* edr
;
3521 TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb
, address
, size
, subvol
->id
, inode
, offset
);
3523 searchkey
.obj_id
= address
;
3524 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3525 searchkey
.offset
= size
;
3527 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3528 if (!NT_SUCCESS(Status
)) {
3529 ERR("error - find_item returned %08x\n", Status
);
3533 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3536 len
= sizeof(EXTENT_ITEM
) + sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
3537 free_traverse_ptr(&tp
);
3539 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
3541 ERR("out of memory\n");
3542 return STATUS_INSUFFICIENT_RESOURCES
;
3546 ei
->generation
= Vcb
->superblock
.generation
;
3547 ei
->flags
= EXTENT_ITEM_DATA
;
3549 type
= (UINT8
*)&ei
[1];
3550 *type
= TYPE_EXTENT_DATA_REF
;
3552 edr
= (EXTENT_DATA_REF
*)&type
[1];
3553 edr
->root
= subvol
->id
;
3555 edr
->offset
= offset
;
3558 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
3559 ERR("error - failed to insert item\n");
3560 return STATUS_INTERNAL_ERROR
;
3563 // FIXME - update free space in superblock and CHUNK_ITEM
3565 return STATUS_SUCCESS
;
3568 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
3569 NTSTATUS Status
= convert_old_data_extent(Vcb
, address
, size
, rollback
);
3570 if (!NT_SUCCESS(Status
)) {
3571 ERR("convert_old_data_extent returned %08x\n", Status
);
3572 free_traverse_ptr(&tp
);
3576 free_traverse_ptr(&tp
);
3578 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3579 if (!NT_SUCCESS(Status
)) {
3580 ERR("error - find_item returned %08x\n", Status
);
3584 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3585 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3586 free_traverse_ptr(&tp
);
3587 return STATUS_SUCCESS
;
3591 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3593 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
3594 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
));
3595 free_traverse_ptr(&tp
);
3596 return STATUS_INTERNAL_ERROR
;
3599 if (extent_item_is_shared(ei
, tp
.item
->size
- sizeof(EXTENT_ITEM
))) {
3600 NTSTATUS Status
= convert_shared_data_extent(Vcb
, address
, size
, rollback
);
3601 if (!NT_SUCCESS(Status
)) {
3602 ERR("convert_shared_data_extent returned %08x\n", Status
);
3603 free_traverse_ptr(&tp
);
3607 free_traverse_ptr(&tp
);
3609 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3610 if (!NT_SUCCESS(Status
)) {
3611 ERR("error - find_item returned %08x\n", Status
);
3615 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3616 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3617 free_traverse_ptr(&tp
);
3618 return STATUS_SUCCESS
;
3621 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3624 if (ei
->flags
!= EXTENT_ITEM_DATA
) {
3625 ERR("error - flag was not EXTENT_ITEM_DATA\n");
3626 free_traverse_ptr(&tp
);
3627 return STATUS_INTERNAL_ERROR
;
3630 // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
3632 hash
= get_extent_data_ref_hash(subvol
->id
, inode
, offset
);
3634 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
3635 siptr
= (UINT8
*)&ei
[1];
3637 // FIXME - increase subitem refcount if there already?
3639 if (*siptr
== TYPE_EXTENT_DATA_REF
) {
3642 edr
= (EXTENT_DATA_REF
*)&siptr
[1];
3643 sihash
= get_extent_data_ref_hash(edr
->root
, edr
->objid
, edr
->offset
);
3648 siptr
+= sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
3650 if (len
> sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
)) {
3651 len
-= sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
);
3654 // FIXME - TYPE_TREE_BLOCK_REF 0xB0
3656 ERR("unrecognized extent subitem %x\n", *siptr
);
3657 free_traverse_ptr(&tp
);
3658 return STATUS_INTERNAL_ERROR
;
3662 len
= tp
.item
->size
+ sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
); // FIXME - die if too big
3663 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
3665 ERR("out of memory\n");
3666 return STATUS_INSUFFICIENT_RESOURCES
;
3669 RtlCopyMemory(ei
, tp
.item
->data
, siptr
- tp
.item
->data
);
3672 type
= (UINT8
*)ei
+ (siptr
- tp
.item
->data
);
3673 *type
= TYPE_EXTENT_DATA_REF
;
3675 edr
= (EXTENT_DATA_REF
*)&type
[1];
3676 edr
->root
= subvol
->id
;
3678 edr
->offset
= offset
;
3681 if (siptr
< tp
.item
->data
+ tp
.item
->size
)
3682 RtlCopyMemory(&edr
[1], siptr
, tp
.item
->data
+ tp
.item
->size
- siptr
);
3684 delete_tree_item(Vcb
, &tp
, rollback
);
3686 free_traverse_ptr(&tp
);
3688 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
3689 ERR("error - failed to insert item\n");
3691 return STATUS_INTERNAL_ERROR
;
3694 return STATUS_SUCCESS
;
3698 EXTENT_DATA_REF edr
;
3699 LIST_ENTRY list_entry
;
3702 static void add_data_ref(LIST_ENTRY
* data_refs
, UINT64 root
, UINT64 objid
, UINT64 offset
) {
3703 data_ref
* dr
= ExAllocatePoolWithTag(PagedPool
, sizeof(data_ref
), ALLOC_TAG
);
3706 ERR("out of memory\n");
3710 // FIXME - increase count if entry there already
3711 // FIXME - put in order?
3713 dr
->edr
.root
= root
;
3714 dr
->edr
.objid
= objid
;
3715 dr
->edr
.offset
= offset
;
3718 InsertTailList(data_refs
, &dr
->list_entry
);
3721 static void free_data_refs(LIST_ENTRY
* data_refs
) {
3722 while (!IsListEmpty(data_refs
)) {
3723 LIST_ENTRY
* le
= RemoveHeadList(data_refs
);
3724 data_ref
* dr
= CONTAINING_RECORD(le
, data_ref
, list_entry
);
3730 static NTSTATUS
convert_old_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
) {
3732 traverse_ptr tp
, next_tp
;
3734 LIST_ENTRY data_refs
;
3742 searchkey
.obj_id
= address
;
3743 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3744 searchkey
.offset
= size
;
3746 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3747 if (!NT_SUCCESS(Status
)) {
3748 ERR("error - find_item returned %08x\n", Status
);
3752 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3753 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3754 free_traverse_ptr(&tp
);
3755 return STATUS_SUCCESS
;
3758 if (tp
.item
->size
!= sizeof(EXTENT_ITEM_V0
)) {
3759 TRACE("extent does not appear to be old - returning STATUS_SUCCESS\n");
3760 free_traverse_ptr(&tp
);
3761 return STATUS_SUCCESS
;
3764 delete_tree_item(Vcb
, &tp
, rollback
);
3766 free_traverse_ptr(&tp
);
3768 searchkey
.obj_id
= address
;
3769 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
3770 searchkey
.offset
= 0;
3772 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3773 if (!NT_SUCCESS(Status
)) {
3774 ERR("error - find_item returned %08x\n", Status
);
3778 InitializeListHead(&data_refs
);
3781 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
3783 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3786 // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
3788 Status
= load_tree(Vcb
, tp
.item
->key
.offset
, NULL
, &t
);
3790 if (!NT_SUCCESS(Status
)) {
3791 ERR("load tree for address %llx returned %08x\n", tp
.item
->key
.offset
, Status
);
3792 free_traverse_ptr(&tp
);
3793 free_data_refs(&data_refs
);
3797 if (t
->header
.level
== 0) {
3798 le
= t
->itemlist
.Flink
;
3799 while (le
!= &t
->itemlist
) {
3800 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3802 if (!td
->ignore
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
3803 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
3805 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
3806 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3808 if (ed2
->address
== address
)
3809 add_data_ref(&data_refs
, t
->header
.tree_id
, td
->key
.obj_id
, td
->key
.offset
);
3819 delete_tree_item(Vcb
, &tp
, rollback
);
3823 free_traverse_ptr(&tp
);
3826 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
3831 free_traverse_ptr(&tp
);
3833 if (IsListEmpty(&data_refs
)) {
3834 WARN("no data refs found\n");
3835 return STATUS_SUCCESS
;
3842 le
= data_refs
.Flink
;
3843 while (le
!= &data_refs
) {
3848 eisize
= sizeof(EXTENT_ITEM
) + ((sizeof(char) + sizeof(EXTENT_DATA_REF
)) * refcount
);
3849 ei
= ExAllocatePoolWithTag(PagedPool
, eisize
, ALLOC_TAG
);
3851 ERR("out of memory\n");
3852 return STATUS_INSUFFICIENT_RESOURCES
;
3855 ei
->refcount
= refcount
;
3856 ei
->generation
= Vcb
->superblock
.generation
;
3857 ei
->flags
= EXTENT_ITEM_DATA
;
3859 type
= (UINT8
*)&ei
[1];
3861 le
= data_refs
.Flink
;
3862 while (le
!= &data_refs
) {
3863 data_ref
* dr
= CONTAINING_RECORD(le
, data_ref
, list_entry
);
3865 type
[0] = TYPE_EXTENT_DATA_REF
;
3866 RtlCopyMemory(&type
[1], &dr
->edr
, sizeof(EXTENT_DATA_REF
));
3868 type
= &type
[1 + sizeof(EXTENT_DATA_REF
)];
3873 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, ei
, eisize
, NULL
, rollback
)) {
3874 ERR("error - failed to insert item\n");
3876 return STATUS_INTERNAL_ERROR
;
3879 free_data_refs(&data_refs
);
3881 return STATUS_SUCCESS
;
3888 LIST_ENTRY list_entry
;
3891 static void free_extent_refs(LIST_ENTRY
* extent_refs
) {
3892 while (!IsListEmpty(extent_refs
)) {
3893 LIST_ENTRY
* le
= RemoveHeadList(extent_refs
);
3894 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
3897 ExFreePool(er
->data
);
3903 static NTSTATUS
convert_shared_data_extent(device_extension
* Vcb
, UINT64 address
, UINT64 size
, LIST_ENTRY
* rollback
) {
3906 LIST_ENTRY extent_refs
;
3907 LIST_ENTRY
*le
, *next_le
;
3908 EXTENT_ITEM
*ei
, *newei
;
3914 searchkey
.obj_id
= address
;
3915 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3916 searchkey
.offset
= size
;
3918 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3919 if (!NT_SUCCESS(Status
)) {
3920 ERR("error - find_item returned %08x\n", Status
);
3924 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3925 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
3926 free_traverse_ptr(&tp
);
3927 return STATUS_SUCCESS
;
3930 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
3931 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
));
3932 free_traverse_ptr(&tp
);
3933 return STATUS_INTERNAL_ERROR
;
3936 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3937 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
3939 InitializeListHead(&extent_refs
);
3941 siptr
= (UINT8
*)&ei
[1];
3944 extent_ref
* er
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
3946 ERR("out of memory\n");
3947 free_traverse_ptr(&tp
);
3948 return STATUS_INSUFFICIENT_RESOURCES
;
3953 er
->allocated
= FALSE
;
3955 InsertTailList(&extent_refs
, &er
->list_entry
);
3957 if (*siptr
== TYPE_TREE_BLOCK_REF
) {
3958 siptr
+= sizeof(TREE_BLOCK_REF
);
3959 len
-= sizeof(TREE_BLOCK_REF
) + 1;
3960 } else if (*siptr
== TYPE_EXTENT_DATA_REF
) {
3961 siptr
+= sizeof(EXTENT_DATA_REF
);
3962 len
-= sizeof(EXTENT_DATA_REF
) + 1;
3963 } else if (*siptr
== TYPE_SHARED_BLOCK_REF
) {
3964 siptr
+= sizeof(SHARED_BLOCK_REF
);
3965 len
-= sizeof(SHARED_BLOCK_REF
) + 1;
3966 } else if (*siptr
== TYPE_SHARED_DATA_REF
) {
3967 siptr
+= sizeof(SHARED_DATA_REF
);
3968 len
-= sizeof(SHARED_DATA_REF
) + 1;
3970 ERR("unrecognized extent subitem %x\n", *siptr
);
3971 free_traverse_ptr(&tp
);
3972 free_extent_refs(&extent_refs
);
3973 return STATUS_INTERNAL_ERROR
;
3977 le
= extent_refs
.Flink
;
3978 while (le
!= &extent_refs
) {
3979 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
3980 next_le
= le
->Flink
;
3982 if (er
->type
== TYPE_SHARED_DATA_REF
) {
3983 // normally we'd need to acquire load_tree_lock here, but we're protected by the write tree lock
3984 SHARED_DATA_REF
* sdr
= er
->data
;
3987 Status
= load_tree(Vcb
, sdr
->offset
, NULL
, &t
);
3988 if (!NT_SUCCESS(Status
)) {
3989 ERR("load_tree for address %llx returned %08x\n", sdr
->offset
, Status
);
3990 free_traverse_ptr(&tp
);
3991 free_data_refs(&extent_refs
);
3995 if (t
->header
.level
== 0) {
3996 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
3997 while (le2
!= &t
->itemlist
) {
3998 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
4000 if (!td
->ignore
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
4001 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
4003 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4004 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4006 if (ed2
->address
== address
) {
4008 EXTENT_DATA_REF
* edr
;
4010 er2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
4012 ERR("out of memory\n");
4013 free_traverse_ptr(&tp
);
4014 return STATUS_INSUFFICIENT_RESOURCES
;
4017 edr
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA_REF
), ALLOC_TAG
);
4019 ERR("out of memory\n");
4020 free_traverse_ptr(&tp
);
4022 return STATUS_INSUFFICIENT_RESOURCES
;
4025 edr
->root
= t
->header
.tree_id
;
4026 edr
->objid
= td
->key
.obj_id
;
4027 edr
->offset
= td
->key
.offset
;
4030 er2
->type
= TYPE_EXTENT_DATA_REF
;
4032 er2
->allocated
= TRUE
;
4034 InsertTailList(&extent_refs
, &er2
->list_entry
); // FIXME - list should be in order
4045 RemoveEntryList(&er
->list_entry
);
4048 ExFreePool(er
->data
);
4052 // FIXME - also do for SHARED_BLOCK_REF?
4057 if (IsListEmpty(&extent_refs
)) {
4058 WARN("no extent refs found\n");
4059 delete_tree_item(Vcb
, &tp
, rollback
);
4060 free_traverse_ptr(&tp
);
4061 return STATUS_SUCCESS
;
4066 le
= extent_refs
.Flink
;
4067 while (le
!= &extent_refs
) {
4068 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
4071 if (er
->type
== TYPE_TREE_BLOCK_REF
) {
4072 len
+= sizeof(TREE_BLOCK_REF
);
4073 } else if (er
->type
== TYPE_EXTENT_DATA_REF
) {
4074 len
+= sizeof(EXTENT_DATA_REF
);
4076 ERR("unexpected extent subitem %x\n", er
->type
);
4084 newei
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM
) + len
, ALLOC_TAG
);
4086 ERR("out of memory\n");
4087 free_traverse_ptr(&tp
);
4088 return STATUS_INSUFFICIENT_RESOURCES
;
4091 RtlCopyMemory(newei
, ei
, sizeof(EXTENT_ITEM
));
4092 newei
->refcount
= count
;
4094 siptr
= (UINT8
*)&newei
[1];
4095 le
= extent_refs
.Flink
;
4096 while (le
!= &extent_refs
) {
4097 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
4102 if (er
->type
== TYPE_TREE_BLOCK_REF
) {
4103 RtlCopyMemory(siptr
, er
->data
, sizeof(TREE_BLOCK_REF
));
4104 } else if (er
->type
== TYPE_EXTENT_DATA_REF
) {
4105 RtlCopyMemory(siptr
, er
->data
, sizeof(EXTENT_DATA_REF
));
4107 ERR("unexpected extent subitem %x\n", er
->type
);
4113 delete_tree_item(Vcb
, &tp
, rollback
);
4114 free_traverse_ptr(&tp
);
4116 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, newei
, sizeof(EXTENT_ITEM
) + len
, NULL
, rollback
)) {
4117 ERR("error - failed to insert item\n");
4119 free_extent_refs(&extent_refs
);
4120 return STATUS_INTERNAL_ERROR
;
4123 free_extent_refs(&extent_refs
);
4125 return STATUS_SUCCESS
;
4128 static BOOL
extent_item_is_shared(EXTENT_ITEM
* ei
, ULONG len
) {
4129 UINT8
* siptr
= (UINT8
*)&ei
[1];
4132 if (*siptr
== TYPE_TREE_BLOCK_REF
) {
4133 siptr
+= sizeof(TREE_BLOCK_REF
) + 1;
4134 len
-= sizeof(TREE_BLOCK_REF
) + 1;
4135 } else if (*siptr
== TYPE_EXTENT_DATA_REF
) {
4136 siptr
+= sizeof(EXTENT_DATA_REF
) + 1;
4137 len
-= sizeof(EXTENT_DATA_REF
) + 1;
4138 } else if (*siptr
== TYPE_SHARED_BLOCK_REF
) {
4140 } else if (*siptr
== TYPE_SHARED_DATA_REF
) {
4143 ERR("unrecognized extent subitem %x\n", *siptr
);
4151 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
) {
4157 EXTENT_DATA_REF
* edr
;
4161 TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb
, address
, size
, subvol
->id
, inode
, offset
);
4163 searchkey
.obj_id
= address
;
4164 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
4165 searchkey
.offset
= size
;
4167 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4168 if (!NT_SUCCESS(Status
)) {
4169 ERR("error - find_item returned %08x\n", Status
);
4173 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4174 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4175 free_traverse_ptr(&tp
);
4176 return STATUS_SUCCESS
;
4179 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
4180 NTSTATUS Status
= convert_old_data_extent(Vcb
, address
, size
, rollback
);
4181 if (!NT_SUCCESS(Status
)) {
4182 ERR("convert_old_data_extent returned %08x\n", Status
);
4183 free_traverse_ptr(&tp
);
4187 free_traverse_ptr(&tp
);
4189 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4190 if (!NT_SUCCESS(Status
)) {
4191 ERR("error - find_item returned %08x\n", Status
);
4195 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4196 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4197 free_traverse_ptr(&tp
);
4198 return STATUS_SUCCESS
;
4202 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
4204 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
4205 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
));
4206 free_traverse_ptr(&tp
);
4207 return STATUS_INTERNAL_ERROR
;
4210 if (!(ei
->flags
& EXTENT_ITEM_DATA
)) {
4211 ERR("error - EXTENT_ITEM_DATA flag not set\n");
4212 free_traverse_ptr(&tp
);
4213 return STATUS_INTERNAL_ERROR
;
4216 // FIXME - is ei->refcount definitely the number of items, or is it the sum of the subitem refcounts?
4218 if (extent_item_is_shared(ei
, tp
.item
->size
- sizeof(EXTENT_ITEM
))) {
4219 NTSTATUS Status
= convert_shared_data_extent(Vcb
, address
, size
, rollback
);
4220 if (!NT_SUCCESS(Status
)) {
4221 ERR("convert_shared_data_extent returned %08x\n", Status
);
4222 free_traverse_ptr(&tp
);
4226 free_traverse_ptr(&tp
);
4228 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
4229 if (!NT_SUCCESS(Status
)) {
4230 ERR("error - find_item returned %08x\n", Status
);
4234 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4235 WARN("extent item not found for address %llx, size %llx\n", address
, size
);
4236 free_traverse_ptr(&tp
);
4237 return STATUS_SUCCESS
;
4240 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
4243 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
4244 siptr
= (UINT8
*)&ei
[1];
4248 if (*siptr
== TYPE_EXTENT_DATA_REF
) {
4249 edr
= (EXTENT_DATA_REF
*)&siptr
[1];
4251 if (edr
->root
== subvol
->id
&& edr
->objid
== inode
&& edr
->offset
== offset
) {
4256 siptr
+= sizeof(UINT8
) + sizeof(EXTENT_DATA_REF
);
4258 if (len
> sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
)) {
4259 len
-= sizeof(EXTENT_DATA_REF
) + sizeof(UINT8
);
4262 // // FIXME - TYPE_TREE_BLOCK_REF 0xB0
4264 ERR("unrecognized extent subitem %x\n", *siptr
);
4265 free_traverse_ptr(&tp
);
4266 return STATUS_INTERNAL_ERROR
;
4271 WARN("could not find extent data ref\n");
4272 free_traverse_ptr(&tp
);
4273 return STATUS_SUCCESS
;
4276 // FIXME - decrease subitem refcount if there already?
4278 len
= tp
.item
->size
- sizeof(UINT8
) - sizeof(EXTENT_DATA_REF
);
4280 delete_tree_item(Vcb
, &tp
, rollback
);
4282 if (len
== sizeof(EXTENT_ITEM
)) { // extent no longer needed
4286 if (changed_sector_list
) {
4287 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4289 ERR("out of memory\n");
4290 free_traverse_ptr(&tp
);
4291 return STATUS_INSUFFICIENT_RESOURCES
;
4294 sc
->ol
.key
= address
;
4295 sc
->checksums
= NULL
;
4296 sc
->length
= size
/ Vcb
->superblock
.sector_size
;
4300 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4304 le2
= Vcb
->chunks
.Flink
;
4305 while (le2
!= &Vcb
->chunks
) {
4306 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
4308 TRACE("chunk: %llx, %llx\n", c
->offset
, c
->chunk_item
->size
);
4310 if (address
>= c
->offset
&& address
+ size
< c
->offset
+ c
->chunk_item
->size
)
4315 if (le2
== &Vcb
->chunks
) c
= NULL
;
4318 decrease_chunk_usage(c
, size
);
4320 add_to_space_list(c
, address
, size
, SPACE_TYPE_DELETING
);
4323 free_traverse_ptr(&tp
);
4324 return STATUS_SUCCESS
;
4327 ei
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
4329 ERR("out of memory\n");
4330 free_traverse_ptr(&tp
);
4331 return STATUS_INSUFFICIENT_RESOURCES
;
4334 RtlCopyMemory(ei
, tp
.item
->data
, siptr
- tp
.item
->data
);
4336 ei
->generation
= Vcb
->superblock
.generation
;
4338 if (tp
.item
->data
+ len
!= siptr
)
4339 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
));
4341 free_traverse_ptr(&tp
);
4343 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ei
, len
, NULL
, rollback
)) {
4344 ERR("error - failed to insert item\n");
4346 return STATUS_INTERNAL_ERROR
;
4349 return STATUS_SUCCESS
;
4352 static __inline BOOL
entry_in_ordered_list(LIST_ENTRY
* list
, UINT64 value
) {
4353 LIST_ENTRY
* le
= list
->Flink
;
4356 while (le
!= list
) {
4357 ol
= (ordered_list
*)le
;
4359 if (ol
->key
> value
)
4361 else if (ol
->key
== value
)
4370 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4372 traverse_ptr tp
, next_tp
;
4376 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, end_data
, changed_sector_list
);
4378 searchkey
.obj_id
= fcb
->inode
;
4379 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4380 searchkey
.offset
= start_data
;
4382 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4383 if (!NT_SUCCESS(Status
)) {
4384 ERR("error - find_item returned %08x\n", Status
);
4389 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
4390 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4393 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4394 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
));
4395 Status
= STATUS_INTERNAL_ERROR
;
4399 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4400 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
));
4401 Status
= STATUS_INTERNAL_ERROR
;
4405 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
4407 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
4409 if (tp
.item
->key
.offset
< end_data
&& tp
.item
->key
.offset
+ len
>= start_data
) {
4410 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4411 FIXME("FIXME - compression not supported at present\n");
4412 Status
= STATUS_NOT_SUPPORTED
;
4416 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4417 WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", fcb
->subvol
->id
, fcb
->inode
, tp
.item
->key
.offset
, ed
->encryption
);
4418 Status
= STATUS_NOT_SUPPORTED
;
4422 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4423 WARN("other encodings not supported\n");
4424 Status
= STATUS_NOT_SUPPORTED
;
4428 if (ed
->type
== EXTENT_TYPE_INLINE
) {
4429 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4430 delete_tree_item(Vcb
, &tp
, rollback
);
4432 fcb
->inode_item
.st_blocks
-= len
;
4433 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4437 delete_tree_item(Vcb
, &tp
, rollback
);
4439 size
= len
- (end_data
- tp
.item
->key
.offset
);
4441 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4443 ERR("out of memory\n");
4444 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4448 ned
->generation
= Vcb
->superblock
.generation
;
4449 ned
->decoded_size
= size
;
4450 ned
->compression
= ed
->compression
;
4451 ned
->encryption
= ed
->encryption
;
4452 ned
->encoding
= ed
->encoding
;
4453 ned
->type
= ed
->type
;
4455 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4457 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4458 ERR("insert_tree_item failed\n");
4460 Status
= STATUS_INTERNAL_ERROR
;
4464 fcb
->inode_item
.st_blocks
-= end_data
- tp
.item
->key
.offset
;
4465 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4469 delete_tree_item(Vcb
, &tp
, rollback
);
4471 size
= start_data
- tp
.item
->key
.offset
;
4473 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4475 ERR("out of memory\n");
4476 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4480 ned
->generation
= Vcb
->superblock
.generation
;
4481 ned
->decoded_size
= size
;
4482 ned
->compression
= ed
->compression
;
4483 ned
->encryption
= ed
->encryption
;
4484 ned
->encoding
= ed
->encoding
;
4485 ned
->type
= ed
->type
;
4487 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4489 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4490 ERR("insert_tree_item failed\n");
4492 Status
= STATUS_INTERNAL_ERROR
;
4496 fcb
->inode_item
.st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4497 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4501 delete_tree_item(Vcb
, &tp
, rollback
);
4503 size
= start_data
- tp
.item
->key
.offset
;
4505 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4507 ERR("out of memory\n");
4508 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4512 ned
->generation
= Vcb
->superblock
.generation
;
4513 ned
->decoded_size
= size
;
4514 ned
->compression
= ed
->compression
;
4515 ned
->encryption
= ed
->encryption
;
4516 ned
->encoding
= ed
->encoding
;
4517 ned
->type
= ed
->type
;
4519 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4521 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4522 ERR("insert_tree_item failed\n");
4524 Status
= STATUS_INTERNAL_ERROR
;
4528 size
= tp
.item
->key
.offset
+ len
- end_data
;
4530 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4532 ERR("out of memory\n");
4533 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4537 ned
->generation
= Vcb
->superblock
.generation
;
4538 ned
->decoded_size
= size
;
4539 ned
->compression
= ed
->compression
;
4540 ned
->encryption
= ed
->encryption
;
4541 ned
->encoding
= ed
->encoding
;
4542 ned
->type
= ed
->type
;
4544 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4546 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4547 ERR("insert_tree_item failed\n");
4549 Status
= STATUS_INTERNAL_ERROR
;
4553 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
4555 } else if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4556 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4557 if (ed2
->address
!= 0) {
4558 Status
= remove_extent_ref(Vcb
, ed2
->address
, ed2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- ed2
->offset
, changed_sector_list
, rollback
);
4559 if (!NT_SUCCESS(Status
)) {
4560 ERR("remove_extent_ref returned %08x\n", Status
);
4564 fcb
->inode_item
.st_blocks
-= len
;
4567 delete_tree_item(Vcb
, &tp
, rollback
);
4568 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4572 if (ed2
->address
!= 0)
4573 fcb
->inode_item
.st_blocks
-= end_data
- tp
.item
->key
.offset
;
4575 delete_tree_item(Vcb
, &tp
, rollback
);
4577 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4579 ERR("out of memory\n");
4580 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4584 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4586 ned
->generation
= Vcb
->superblock
.generation
;
4587 ned
->decoded_size
= ed
->decoded_size
;
4588 ned
->compression
= ed
->compression
;
4589 ned
->encryption
= ed
->encryption
;
4590 ned
->encoding
= ed
->encoding
;
4591 ned
->type
= ed
->type
;
4592 ned2
->address
= ed2
->address
;
4593 ned2
->size
= ed2
->size
;
4594 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4595 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- tp
.item
->key
.offset
);
4597 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4598 ERR("insert_tree_item failed\n");
4600 Status
= STATUS_INTERNAL_ERROR
;
4603 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4607 if (ed2
->address
!= 0)
4608 fcb
->inode_item
.st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4610 delete_tree_item(Vcb
, &tp
, rollback
);
4612 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4614 ERR("out of memory\n");
4615 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4619 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4621 ned
->generation
= Vcb
->superblock
.generation
;
4622 ned
->decoded_size
= ed
->decoded_size
;
4623 ned
->compression
= ed
->compression
;
4624 ned
->encryption
= ed
->encryption
;
4625 ned
->encoding
= ed
->encoding
;
4626 ned
->type
= ed
->type
;
4627 ned2
->address
= ed2
->address
;
4628 ned2
->size
= ed2
->size
;
4629 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4630 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4632 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
)) {
4633 ERR("insert_tree_item failed\n");
4635 Status
= STATUS_INTERNAL_ERROR
;
4638 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4642 if (ed2
->address
!= 0)
4643 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
4645 delete_tree_item(Vcb
, &tp
, rollback
);
4647 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4649 ERR("out of memory\n");
4650 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4654 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4656 ned
->generation
= Vcb
->superblock
.generation
;
4657 ned
->decoded_size
= ed
->decoded_size
;
4658 ned
->compression
= ed
->compression
;
4659 ned
->encryption
= ed
->encryption
;
4660 ned
->encoding
= ed
->encoding
;
4661 ned
->type
= ed
->type
;
4662 ned2
->address
= ed2
->address
;
4663 ned2
->size
= ed2
->size
;
4664 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4665 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4667 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
)) {
4668 ERR("insert_tree_item failed\n");
4670 Status
= STATUS_INTERNAL_ERROR
;
4674 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4676 ERR("out of memory\n");
4677 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4681 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4683 ned
->generation
= Vcb
->superblock
.generation
;
4684 ned
->decoded_size
= ed
->decoded_size
;
4685 ned
->compression
= ed
->compression
;
4686 ned
->encryption
= ed
->encryption
;
4687 ned
->encoding
= ed
->encoding
;
4688 ned
->type
= ed
->type
;
4689 ned2
->address
= ed2
->address
;
4690 ned2
->size
= ed2
->size
;
4691 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4692 ned2
->num_bytes
= tp
.item
->key
.offset
+ len
- end_data
;
4694 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4695 ERR("insert_tree_item failed\n");
4697 Status
= STATUS_INTERNAL_ERROR
;
4705 free_traverse_ptr(&tp
);
4708 if (tp
.item
->key
.obj_id
> fcb
->inode
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= end_data
)
4713 // FIXME - do bitmap analysis of changed extents, and free what we can
4715 Status
= STATUS_SUCCESS
;
4718 free_traverse_ptr(&tp
);
4723 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
) {
4726 EXTENT_ITEM_DATA_REF
* eidr
;
4729 ULONG edsize
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
4734 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
);
4736 if (!find_address_in_chunk(Vcb
, c
, length
, &address
))
4739 eidr
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_DATA_REF
), ALLOC_TAG
);
4741 ERR("out of memory\n");
4745 eidr
->ei
.refcount
= 1;
4746 eidr
->ei
.generation
= Vcb
->superblock
.generation
;
4747 eidr
->ei
.flags
= EXTENT_ITEM_DATA
;
4748 eidr
->type
= TYPE_EXTENT_DATA_REF
;
4749 eidr
->edr
.root
= fcb
->subvol
->id
;
4750 eidr
->edr
.objid
= fcb
->inode
;
4751 eidr
->edr
.offset
= start_data
;
4752 eidr
->edr
.count
= 1;
4754 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, length
, eidr
, sizeof(EXTENT_ITEM_DATA_REF
), &tp
, rollback
)) {
4755 ERR("insert_tree_item failed\n");
4760 tp
.tree
->header
.generation
= eidr
->ei
.generation
;
4762 free_traverse_ptr(&tp
);
4764 Status
= write_data(Vcb
, address
, data
, length
);
4765 if (!NT_SUCCESS(Status
)) {
4766 ERR("write_data returned %08x\n", Status
);
4770 if (changed_sector_list
) {
4771 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4773 ERR("out of memory\n");
4777 sc
->ol
.key
= address
;
4778 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4779 sc
->deleted
= FALSE
;
4781 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4782 if (!sc
->checksums
) {
4783 ERR("out of memory\n");
4788 for (i
= 0; i
< sc
->length
; i
++) {
4789 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4792 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4795 // add extent data to inode
4796 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
4798 ERR("out of memory\n");
4802 ed
->generation
= Vcb
->superblock
.generation
;
4803 ed
->decoded_size
= length
;
4804 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4805 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4806 ed
->encoding
= BTRFS_ENCODING_NONE
;
4807 ed
->type
= EXTENT_TYPE_REGULAR
;
4809 ed2
= (EXTENT_DATA2
*)ed
->data
;
4810 ed2
->address
= address
;
4813 ed2
->num_bytes
= length
;
4815 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start_data
, ed
, edsize
, NULL
, rollback
)) {
4816 ERR("insert_tree_item failed\n");
4821 increase_chunk_usage(c
, length
);
4822 add_to_space_list(c
, address
, length
, SPACE_TYPE_WRITING
);
4824 fcb
->inode_item
.st_blocks
+= length
;
4829 static BOOL
extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4830 LIST_ENTRY
* changed_sector_list
, traverse_ptr
* edtp
, traverse_ptr
* eitp
, LIST_ENTRY
* rollback
) {
4839 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
,
4840 length
, data
, changed_sector_list
, edtp
, eitp
);
4842 ed
= ExAllocatePoolWithTag(PagedPool
, edtp
->item
->size
, ALLOC_TAG
);
4844 ERR("out of memory\n");
4848 RtlCopyMemory(ed
, edtp
->item
->data
, edtp
->item
->size
);
4850 ed
->decoded_size
+= length
;
4851 ed2
= (EXTENT_DATA2
*)ed
->data
;
4852 ed2
->size
+= length
;
4853 ed2
->num_bytes
+= length
;
4855 delete_tree_item(Vcb
, edtp
, rollback
);
4857 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
)) {
4858 TRACE("insert_tree_item failed\n");
4864 ei
= ExAllocatePoolWithTag(PagedPool
, eitp
->item
->size
, ALLOC_TAG
);
4866 ERR("out of memory\n");
4871 RtlCopyMemory(ei
, eitp
->item
->data
, eitp
->item
->size
);
4873 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
)) {
4874 ERR("insert_tree_item failed\n");
4880 delete_tree_item(Vcb
, eitp
, rollback
);
4882 Status
= write_data(Vcb
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, data
, length
);
4883 if (!NT_SUCCESS(Status
)) {
4884 ERR("write_data returned %08x\n", Status
);
4888 if (changed_sector_list
) {
4889 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4891 ERR("out of memory\n");
4895 sc
->ol
.key
= eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
;
4896 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4897 sc
->deleted
= FALSE
;
4899 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4900 if (!sc
->checksums
) {
4901 ERR("out of memory\n");
4906 for (i
= 0; i
< sc
->length
; i
++) {
4907 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4909 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4912 c
= get_chunk_from_address(Vcb
, eitp
->item
->key
.obj_id
);
4915 increase_chunk_usage(c
, length
);
4917 add_to_space_list(c
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, length
, SPACE_TYPE_WRITING
);
4920 fcb
->inode_item
.st_blocks
+= length
;
4925 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4926 LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4928 traverse_ptr tp
, tp2
;
4929 BOOL success
= FALSE
;
4938 searchkey
.obj_id
= fcb
->inode
;
4939 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4940 searchkey
.offset
= start_data
;
4942 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4943 if (!NT_SUCCESS(Status
)) {
4944 ERR("error - find_item returned %08x\n", Status
);
4948 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
) {
4949 WARN("previous EXTENT_DATA not found\n");
4953 ed
= (EXTENT_DATA
*)tp
.item
->data
;
4955 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4956 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
));
4960 if (ed
->type
!= EXTENT_TYPE_REGULAR
) {
4961 TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
4965 ed2
= (EXTENT_DATA2
*)ed
->data
;
4967 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4968 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
));
4972 if (tp
.item
->key
.offset
+ ed2
->num_bytes
!= start_data
) {
4973 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp
.item
->key
.offset
, ed2
->num_bytes
, start_data
);
4977 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4978 FIXME("FIXME: compression not yet supported\n");
4982 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4983 WARN("encryption not supported\n");
4987 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4988 WARN("other encodings not supported\n");
4992 if (ed2
->size
- ed2
->offset
!= ed2
->num_bytes
) {
4993 TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
4997 searchkey
.obj_id
= ed2
->address
;
4998 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
4999 searchkey
.offset
= ed2
->size
;
5001 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5002 if (!NT_SUCCESS(Status
)) {
5003 ERR("error - find_item returned %08x\n", Status
);
5007 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5008 ERR("error - extent %llx,%llx not found in tree\n", ed2
->address
, ed2
->size
);
5013 if (tp2
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
5014 NTSTATUS Status
= convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
5015 if (!NT_SUCCESS(Status
)) {
5016 ERR("convert_old_data_extent returned %08x\n", Status
);
5020 free_traverse_ptr(&tp2
);
5022 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5023 if (!NT_SUCCESS(Status
)) {
5024 ERR("error - find_item returned %08x\n", Status
);
5028 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5029 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
5034 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
5036 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5037 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
));
5041 // FIXME - test this
5042 if (extent_item_is_shared(ei
, tp2
.item
->size
- sizeof(EXTENT_ITEM
))) {
5043 NTSTATUS Status
= convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
5044 if (!NT_SUCCESS(Status
)) {
5045 ERR("convert_shared_data_extent returned %08x\n", Status
);
5049 free_traverse_ptr(&tp2
);
5051 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
5052 if (!NT_SUCCESS(Status
)) {
5053 ERR("error - find_item returned %08x\n", Status
);
5057 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
5058 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
5062 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
5064 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5065 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
));
5070 if (ei
->refcount
!= 1) {
5071 TRACE("extent refcount was not 1\n");
5075 if (ei
->flags
!= EXTENT_ITEM_DATA
) {
5076 ERR("error - extent was not a data extent\n");
5080 c
= get_chunk_from_address(Vcb
, ed2
->address
);
5082 le
= c
->space
.Flink
;
5083 while (le
!= &c
->space
) {
5084 s
= CONTAINING_RECORD(le
, space
, list_entry
);
5086 if (s
->offset
== ed2
->address
+ ed2
->size
) {
5087 if (s
->type
== SPACE_TYPE_FREE
&& s
->size
>= length
) {
5088 success
= extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, &tp
, &tp2
, rollback
);
5091 } else if (s
->offset
> ed2
->address
+ ed2
->size
)
5098 free_traverse_ptr(&tp2
);
5101 free_traverse_ptr(&tp
);
5106 NTSTATUS
insert_sparse_extent(device_extension
* Vcb
, root
* r
, UINT64 inode
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
5110 TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb
, r
->id
, inode
, start
, length
);
5112 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5114 ERR("out of memory\n");
5115 return STATUS_INSUFFICIENT_RESOURCES
;
5118 ed
->generation
= Vcb
->superblock
.generation
;
5119 ed
->decoded_size
= length
;
5120 ed
->compression
= BTRFS_COMPRESSION_NONE
;
5121 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
5122 ed
->encoding
= BTRFS_ENCODING_NONE
;
5123 ed
->type
= EXTENT_TYPE_REGULAR
;
5125 ed2
= (EXTENT_DATA2
*)ed
->data
;
5129 ed2
->num_bytes
= length
;
5131 if (!insert_tree_item(Vcb
, r
, inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
5132 ERR("insert_tree_item failed\n");
5134 return STATUS_INTERNAL_ERROR
;
5137 return STATUS_SUCCESS
;
5140 // static void print_tree(tree* t) {
5141 // LIST_ENTRY* le = t->itemlist.Flink;
5142 // while (le != &t->itemlist) {
5143 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
5144 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
5149 static NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5150 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
5155 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
5157 // FIXME - split data up if not enough space for just one extent
5159 if (start_data
> 0 && try_extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, rollback
))
5160 return STATUS_SUCCESS
;
5162 // if there is a gap before start_data, plug it with a sparse extent
5163 if (start_data
> 0) {
5169 searchkey
.obj_id
= fcb
->inode
;
5170 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5171 searchkey
.offset
= start_data
;
5173 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5174 if (!NT_SUCCESS(Status
)) {
5175 ERR("error - find_item returned %08x\n", Status
);
5179 // if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
5180 // traverse_ptr next_tp;
5182 // ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
5183 // searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
5184 // print_tree(tp.tree);
5186 // if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
5188 // 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);
5189 // print_tree(next_tp.tree);
5191 // free_traverse_ptr(&next_tp);
5193 // ERR("next item not found\n");
5196 // free_traverse_ptr(&tp);
5197 // return STATUS_INTERNAL_ERROR;
5200 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
&& tp
.item
->size
>= sizeof(EXTENT_DATA
)) {
5203 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5204 ed2
= (EXTENT_DATA2
*)ed
->data
;
5206 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
5210 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| !ed
|| tp
.item
->key
.offset
+ len
< start_data
) {
5211 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
5212 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, 0, start_data
, rollback
);
5214 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
));
5216 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
+ len
,
5217 start_data
- tp
.item
->key
.offset
- len
, rollback
);
5219 if (!NT_SUCCESS(Status
)) {
5220 ERR("insert_sparse_extent returned %08x\n", Status
);
5221 free_traverse_ptr(&tp
);
5226 free_traverse_ptr(&tp
);
5229 // FIXME - how do we know which RAID level to put this to?
5230 flags
= BLOCK_FLAG_DATA
; // SINGLE
5232 // if (!chunk_test) { // TESTING
5233 // if ((c = alloc_chunk(Vcb, flags, NULL))) {
5234 // ERR("chunk_item->type = %llx\n", c->chunk_item->type);
5235 // ERR("size = %llx\n", c->chunk_item->size);
5236 // ERR("used = %llx\n", c->used);
5238 // if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
5239 // if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
5240 // // chunk_test = TRUE;
5241 // ERR("SUCCESS\n");
5242 // return STATUS_SUCCESS;
5250 while (le
!= &Vcb
->chunks
) {
5251 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5253 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
5254 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, data
, changed_sector_list
, rollback
))
5255 return STATUS_SUCCESS
;
5261 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
5262 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
5263 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, data
, changed_sector_list
, rollback
))
5264 return STATUS_SUCCESS
;
5268 // FIXME - rebalance chunks if free space elsewhere?
5269 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
5271 return STATUS_DISK_FULL
;
5274 void update_checksum_tree(device_extension
* Vcb
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5275 LIST_ENTRY
* le
= changed_sector_list
->Flink
;
5277 traverse_ptr tp
, next_tp
;
5282 if (!Vcb
->checksum_root
) {
5283 ERR("no checksum root\n");
5287 while (le
!= changed_sector_list
) {
5288 UINT64 startaddr
, endaddr
;
5293 ULONG runlength
, index
;
5295 cs
= (changed_sector
*)le
;
5297 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5298 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5299 searchkey
.offset
= cs
->ol
.key
;
5301 // FIXME - create checksum_root if it doesn't exist at all
5303 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5304 if (!NT_SUCCESS(Status
)) { // tree is completely empty
5305 // FIXME - do proper check here that tree is empty
5307 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * cs
->length
, ALLOC_TAG
);
5309 ERR("out of memory\n");
5313 RtlCopyMemory(checksums
, cs
->checksums
, sizeof(UINT32
) * cs
->length
);
5315 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, cs
->ol
.key
, checksums
, sizeof(UINT32
) * cs
->length
, NULL
, rollback
)) {
5316 ERR("insert_tree_item failed\n");
5317 ExFreePool(checksums
);
5324 // FIXME - check entry is TYPE_EXTENT_CSUM?
5326 if (tp
.item
->key
.offset
< cs
->ol
.key
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= cs
->ol
.key
)
5327 startaddr
= tp
.item
->key
.offset
;
5329 startaddr
= cs
->ol
.key
;
5331 free_traverse_ptr(&tp
);
5333 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5334 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5335 searchkey
.offset
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5337 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5338 if (!NT_SUCCESS(Status
)) {
5339 ERR("error - find_item returned %08x\n", Status
);
5343 tplen
= tp
.item
->size
/ sizeof(UINT32
);
5345 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
))
5346 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
5348 endaddr
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5350 free_traverse_ptr(&tp
);
5352 TRACE("cs starts at %llx (%x sectors)\n", cs
->ol
.key
, cs
->length
);
5353 TRACE("startaddr = %llx\n", startaddr
);
5354 TRACE("endaddr = %llx\n", endaddr
);
5356 len
= (endaddr
- startaddr
) / Vcb
->superblock
.sector_size
;
5358 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
5360 ERR("out of memory\n");
5364 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
5366 ERR("out of memory\n");
5367 ExFreePool(checksums
);
5371 RtlInitializeBitMap(&bmp
, bmparr
, len
);
5372 RtlSetAllBits(&bmp
);
5374 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5375 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5376 searchkey
.offset
= cs
->ol
.key
;
5378 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5379 if (!NT_SUCCESS(Status
)) {
5380 ERR("error - find_item returned %08x\n", Status
);
5384 // set bit = free space, cleared bit = allocated sector
5386 // ERR("start loop\n");
5387 while (tp
.item
->key
.offset
< endaddr
) {
5388 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
5389 if (tp
.item
->key
.offset
>= startaddr
) {
5390 if (tp
.item
->size
> 0) {
5391 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, tp
.item
->size
);
5392 RtlClearBits(&bmp
, (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
, tp
.item
->size
/ sizeof(UINT32
));
5395 delete_tree_item(Vcb
, &tp
, rollback
);
5398 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
5399 free_traverse_ptr(&tp
);
5404 // ERR("end loop\n");
5406 free_traverse_ptr(&tp
);
5409 RtlSetBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5411 RtlCopyMemory(&checksums
[(cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
], cs
->checksums
, cs
->length
* sizeof(UINT32
));
5412 RtlClearBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5415 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
5417 while (runlength
!= 0) {
5421 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
5422 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
5426 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
5428 ERR("out of memory\n");
5430 ExFreePool(checksums
);
5434 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
5436 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
)) {
5437 ERR("insert_tree_item failed\n");
5440 ExFreePool(checksums
);
5446 } while (runlength
> 0);
5448 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
5452 ExFreePool(checksums
);
5459 while (!IsListEmpty(changed_sector_list
)) {
5460 le
= RemoveHeadList(changed_sector_list
);
5461 cs
= (changed_sector
*)le
;
5464 ExFreePool(cs
->checksums
);
5470 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, LIST_ENTRY
* rollback
) {
5471 LIST_ENTRY changed_sector_list
;
5473 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5476 InitializeListHead(&changed_sector_list
);
5478 // FIXME - convert into inline extent if short enough
5480 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
5481 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), nocsum
? NULL
: &changed_sector_list
, rollback
);
5482 if (!NT_SUCCESS(Status
)) {
5483 ERR("error - excise_extents failed\n");
5487 fcb
->inode_item
.st_size
= end
;
5488 TRACE("setting st_size to %llx\n", end
);
5490 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
5491 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
5492 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
5493 // FIXME - inform cache manager of this
5495 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
5498 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5500 return STATUS_SUCCESS
;
5503 NTSTATUS
extend_file(fcb
* fcb
, UINT64 end
, LIST_ENTRY
* rollback
) {
5504 UINT64 oldalloc
, newalloc
;
5510 TRACE("(%p, %x, %p)\n", fcb
, end
, rollback
);
5513 FIXME("FIXME - support streams here\n"); // FIXME
5514 return STATUS_NOT_IMPLEMENTED
;
5516 searchkey
.obj_id
= fcb
->inode
;
5517 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5518 searchkey
.offset
= 0xffffffffffffffff;
5520 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5521 if (!NT_SUCCESS(Status
)) {
5522 ERR("error - find_item returned %08x\n", Status
);
5527 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
5528 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
5529 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5531 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5532 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
));
5533 free_traverse_ptr(&tp
);
5534 return STATUS_INTERNAL_ERROR
;
5537 oldalloc
= tp
.item
->key
.offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
5538 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
5540 if (cur_inline
&& end
> fcb
->Vcb
->max_inline
) {
5541 LIST_ENTRY changed_sector_list
;
5542 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5543 UINT64 origlength
, length
;
5546 TRACE("giving inline file proper extents\n");
5548 origlength
= ed
->decoded_size
;
5553 InitializeListHead(&changed_sector_list
);
5555 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5557 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
5559 data
= ExAllocatePoolWithTag(PagedPool
, length
, ALLOC_TAG
);
5561 ERR("could not allocate %llx bytes for data\n", length
);
5562 free_traverse_ptr(&tp
);
5563 return STATUS_INSUFFICIENT_RESOURCES
;
5566 if (length
> origlength
)
5567 RtlZeroMemory(data
+ origlength
, length
- origlength
);
5569 RtlCopyMemory(data
, ed
->data
, origlength
);
5571 fcb
->inode_item
.st_blocks
-= origlength
;
5573 Status
= insert_extent(fcb
->Vcb
, fcb
, tp
.item
->key
.offset
, length
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
5574 if (!NT_SUCCESS(Status
)) {
5575 ERR("insert_extent returned %08x\n", Status
);
5576 free_traverse_ptr(&tp
);
5581 oldalloc
= tp
.item
->key
.offset
+ length
;
5586 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5592 if (end
> oldalloc
) {
5593 edsize
= sizeof(EXTENT_DATA
) - 1 + end
- tp
.item
->key
.offset
;
5594 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5597 ERR("out of memory\n");
5598 free_traverse_ptr(&tp
);
5599 return STATUS_INSUFFICIENT_RESOURCES
;
5602 RtlZeroMemory(ed
, edsize
);
5603 RtlCopyMemory(ed
, tp
.item
->data
, tp
.item
->size
);
5605 ed
->decoded_size
= end
- tp
.item
->key
.offset
;
5607 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5609 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
)) {
5610 ERR("error - failed to insert item\n");
5612 free_traverse_ptr(&tp
);
5613 return STATUS_INTERNAL_ERROR
;
5617 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
5619 fcb
->inode_item
.st_size
= end
;
5620 TRACE("setting st_size to %llx\n", end
);
5622 fcb
->inode_item
.st_blocks
= end
;
5624 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5626 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5628 if (newalloc
> oldalloc
) {
5629 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, oldalloc
, newalloc
- oldalloc
, rollback
);
5631 if (!NT_SUCCESS(Status
)) {
5632 ERR("insert_sparse_extent returned %08x\n", Status
);
5633 free_traverse_ptr(&tp
);
5638 fcb
->inode_item
.st_size
= end
;
5639 TRACE("setting st_size to %llx\n", end
);
5641 TRACE("newalloc = %llx\n", newalloc
);
5643 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5644 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5647 if (end
> fcb
->Vcb
->max_inline
) {
5648 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5650 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, 0, newalloc
, rollback
);
5652 if (!NT_SUCCESS(Status
)) {
5653 ERR("insert_sparse_extent returned %08x\n", Status
);
5654 free_traverse_ptr(&tp
);
5658 fcb
->inode_item
.st_size
= end
;
5659 TRACE("setting st_size to %llx\n", end
);
5661 TRACE("newalloc = %llx\n", newalloc
);
5663 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5664 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5669 edsize
= sizeof(EXTENT_DATA
) - 1 + end
;
5670 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5673 ERR("out of memory\n");
5674 free_traverse_ptr(&tp
);
5675 return STATUS_INSUFFICIENT_RESOURCES
;
5678 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
5679 ed
->decoded_size
= end
;
5680 ed
->compression
= BTRFS_COMPRESSION_NONE
;
5681 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
5682 ed
->encoding
= BTRFS_ENCODING_NONE
;
5683 ed
->type
= EXTENT_TYPE_INLINE
;
5685 RtlZeroMemory(ed
->data
, end
);
5687 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed
, edsize
, NULL
, rollback
)) {
5688 ERR("error - failed to insert item\n");
5690 free_traverse_ptr(&tp
);
5691 return STATUS_INTERNAL_ERROR
;
5694 fcb
->inode_item
.st_size
= end
;
5695 TRACE("setting st_size to %llx\n", end
);
5697 fcb
->inode_item
.st_blocks
= end
;
5699 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5703 free_traverse_ptr(&tp
);
5706 return STATUS_SUCCESS
;
5709 static UINT64
get_extent_item_refcount(device_extension
* Vcb
, UINT64 address
) {
5716 searchkey
.obj_id
= address
;
5717 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5718 searchkey
.offset
= 0xffffffffffffffff;
5720 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
5721 if (!NT_SUCCESS(Status
)) {
5722 ERR("error - find_item returned %08x\n", Status
);
5726 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5727 ERR("error - could not find EXTENT_ITEM for %llx\n", address
);
5728 free_traverse_ptr(&tp
);
5732 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5733 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
));
5734 free_traverse_ptr(&tp
);
5738 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
5741 free_traverse_ptr(&tp
);
5746 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
) {
5748 traverse_ptr tp
, next_tp
;
5753 UINT64 size
, new_start
, new_end
, last_write
= 0;
5755 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
5757 searchkey
.obj_id
= fcb
->inode
;
5758 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5759 searchkey
.offset
= start_data
;
5761 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5762 if (!NT_SUCCESS(Status
)) {
5763 ERR("error - find_item returned %08x\n", Status
);
5767 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
> start_data
) {
5768 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
);
5769 Status
= STATUS_INTERNAL_ERROR
;
5774 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5775 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
));
5776 Status
= STATUS_INTERNAL_ERROR
;
5780 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5782 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5783 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
));
5784 Status
= STATUS_INTERNAL_ERROR
;
5788 eds
= (EXTENT_DATA2
*)&ed
->data
[0];
5790 b
= find_next_item(Vcb
, &tp
, &next_tp
, TRUE
);
5793 case EXTENT_TYPE_REGULAR
:
5795 UINT64 rc
= get_extent_item_refcount(Vcb
, eds
->address
);
5798 ERR("get_extent_item_refcount failed\n");
5799 Status
= STATUS_INTERNAL_ERROR
;
5807 case EXTENT_TYPE_INLINE
:
5811 case EXTENT_TYPE_PREALLOC
:
5812 FIXME("FIXME - handle prealloc extents\n"); // FIXME
5813 Status
= STATUS_NOT_SUPPORTED
;
5817 ERR("error - unknown extent type %x\n", ed
->type
);
5818 Status
= STATUS_NOT_SUPPORTED
;
5822 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
5823 FIXME("FIXME: compression not yet supported\n");
5824 Status
= STATUS_NOT_SUPPORTED
;
5828 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
5829 WARN("encryption not supported\n");
5830 Status
= STATUS_INTERNAL_ERROR
;
5834 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
5835 WARN("other encodings not supported\n");
5836 Status
= STATUS_INTERNAL_ERROR
;
5840 size
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: eds
->num_bytes
;
5842 TRACE("extent: start = %llx, length = %llx\n", tp
.item
->key
.offset
, size
);
5844 new_start
= tp
.item
->key
.offset
< start_data
? start_data
: tp
.item
->key
.offset
;
5845 new_end
= tp
.item
->key
.offset
+ size
> start_data
+ length
? (start_data
+ length
) : (tp
.item
->key
.offset
+ size
);
5847 TRACE("new_start = %llx\n", new_start
);
5848 TRACE("new_end = %llx\n", new_end
);
5851 TRACE("doing COW write\n");
5853 Status
= excise_extents(Vcb
, fcb
, new_start
, new_start
+ new_end
, changed_sector_list
, rollback
);
5855 if (!NT_SUCCESS(Status
)) {
5856 ERR("error - excise_extents returned %08x\n", Status
);
5860 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
5862 if (!NT_SUCCESS(Status
)) {
5863 ERR("error - insert_extent returned %08x\n", Status
);
5869 writeaddr
= eds
->address
+ eds
->offset
+ new_start
- tp
.item
->key
.offset
;
5870 TRACE("doing non-COW write to %llx\n", writeaddr
);
5872 Status
= write_data(Vcb
, writeaddr
, (UINT8
*)data
+ new_start
- start_data
, new_end
- new_start
);
5874 if (!NT_SUCCESS(Status
)) {
5875 ERR("error - write_data returned %08x\n", Status
);
5879 if (changed_sector_list
) {
5883 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
5885 ERR("out of memory\n");
5886 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5890 sc
->ol
.key
= writeaddr
;
5891 sc
->length
= (new_end
- new_start
) / Vcb
->superblock
.sector_size
;
5892 sc
->deleted
= FALSE
;
5894 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
5895 if (!sc
->checksums
) {
5896 ERR("out of memory\n");
5898 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5902 for (i
= 0; i
< sc
->length
; i
++) {
5903 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ new_start
- start_data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
5906 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
5910 last_write
= new_end
;
5913 free_traverse_ptr(&tp
);
5916 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
+ length
)
5921 if (last_write
< start_data
+ length
) {
5922 new_start
= last_write
;
5923 new_end
= start_data
+ length
;
5925 TRACE("new_start = %llx\n", new_start
);
5926 TRACE("new_end = %llx\n", new_end
);
5928 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
5930 if (!NT_SUCCESS(Status
)) {
5931 ERR("error - insert_extent returned %08x\n", Status
);
5936 Status
= STATUS_SUCCESS
;
5939 free_traverse_ptr(&tp
);
5944 #ifdef DEBUG_PARANOID
5945 static void print_loaded_trees(tree
* t
, int spaces
) {
5950 for (i
= 0; i
< spaces
; i
++) {
5956 ERR("%s(not loaded)\n", pref
);
5960 le
= t
->itemlist
.Flink
;
5961 while (le
!= &t
->itemlist
) {
5962 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
5964 ERR("%s%llx,%x,%llx ignore=%s\n", pref
, td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->ignore
? "TRUE" : "FALSE");
5966 if (t
->header
.level
> 0) {
5967 print_loaded_trees(td
->treeholder
.tree
, spaces
+1);
5974 static void check_extents_consistent(device_extension
* Vcb
, fcb
* fcb
) {
5976 traverse_ptr tp
, next_tp
;
5977 UINT64 length
, oldlength
, lastoff
, alloc
;
5982 if (fcb
->ads
|| fcb
->inode_item
.st_size
== 0 || fcb
->deleted
)
5985 TRACE("inode = %llx, subvol = %llx\n", fcb
->inode
, fcb
->subvol
->id
);
5987 searchkey
.obj_id
= fcb
->inode
;
5988 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5989 searchkey
.offset
= 0;
5991 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5992 if (!NT_SUCCESS(Status
)) {
5993 ERR("error - find_item returned %08x\n", Status
);
5997 if (keycmp(&searchkey
, &tp
.item
->key
)) {
5998 ERR("could not find EXTENT_DATA at offset 0\n");
6002 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6003 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
));
6007 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6008 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6010 length
= oldlength
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6011 lastoff
= tp
.item
->key
.offset
;
6013 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6016 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6020 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
6021 if (next_tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| next_tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6022 free_traverse_ptr(&next_tp
);
6026 free_traverse_ptr(&tp
);
6029 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6030 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
));
6034 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6035 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6037 length
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6039 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6041 if (tp
.item
->key
.offset
!= lastoff
+ oldlength
) {
6042 ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb
->subvol
->id
, fcb
->inode
, tp
.item
->key
.offset
, lastoff
+ oldlength
);
6046 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6051 lastoff
= tp
.item
->key
.offset
;
6054 if (alloc
!= fcb
->inode_item
.st_blocks
) {
6055 ERR("allocation size was %llx, expected %llx\n", alloc
, fcb
->inode_item
.st_blocks
);
6059 // if (fcb->inode_item.st_blocks != lastoff + oldlength) {
6060 // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
6064 free_traverse_ptr(&tp
);
6069 free_traverse_ptr(&tp
);
6072 if (fcb
->subvol
->treeholder
.tree
)
6073 print_loaded_trees(fcb
->subvol
->treeholder
.tree
, 0);
6078 // static void check_extent_tree_consistent(device_extension* Vcb) {
6080 // traverse_ptr tp, next_tp;
6082 // BOOL b, inconsistency;
6084 // searchkey.obj_id = 0;
6085 // searchkey.obj_type = 0;
6086 // searchkey.offset = 0;
6088 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6089 // ERR("error - could not find any entries in extent_root\n");
6094 // inconsistency = FALSE;
6097 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6098 // // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6100 // if (tp.item->key.obj_id < lastaddr) {
6101 // // ERR("inconsistency!\n");
6103 // inconsistency = TRUE;
6106 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6109 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6111 // free_traverse_ptr(&tp);
6116 // free_traverse_ptr(&tp);
6118 // if (!inconsistency)
6121 // ERR("Inconsistency detected:\n");
6123 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6124 // ERR("error - could not find any entries in extent_root\n");
6129 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6130 // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6132 // if (tp.item->key.obj_id < lastaddr) {
6133 // ERR("inconsistency!\n");
6136 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6139 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6141 // free_traverse_ptr(&tp);
6146 // free_traverse_ptr(&tp);
6152 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOL paging_io
, BOOL no_cache
, LIST_ENTRY
* rollback
) {
6153 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6154 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6158 UINT64 newlength
, start_data
, end_data
;
6162 LIST_ENTRY changed_sector_list
;
6163 INODE_ITEM
*ii
, *origii
;
6164 BOOL changed_length
= FALSE
, nocsum
, nocow
/*, lazy_writer = FALSE, write_eof = FALSE*/;
6169 BOOL paging_lock
= FALSE
;
6171 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
6174 WARN("returning success for zero-length write\n");
6175 return STATUS_SUCCESS
;
6179 ERR("error - FileObject was NULL\n");
6180 return STATUS_ACCESS_DENIED
;
6183 fcb
= FileObject
->FsContext
;
6185 if (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
6186 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
);
6187 return STATUS_ACCESS_DENIED
;
6190 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1) {
6191 offset
= fcb
->Header
.FileSize
;
6192 // write_eof = TRUE;
6195 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
6197 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
6198 IO_STATUS_BLOCK iosb
;
6200 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
6202 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
6204 if (!NT_SUCCESS(iosb
.Status
)) {
6205 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6206 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
6212 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
6216 ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, TRUE
);
6220 nocsum
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
6221 nocow
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
;
6223 newlength
= fcb
->ads
? fcb
->adssize
: fcb
->inode_item
.st_size
;
6228 TRACE("newlength = %llx\n", newlength
);
6230 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
6231 // ERR("lazy writer on the TV\n");
6232 // lazy_writer = TRUE;
6235 if (offset
.QuadPart
+ *length
> newlength
) {
6237 if (offset
.QuadPart
>= newlength
) {
6238 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, offset
.QuadPart
, *length
);
6239 TRACE("filename %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
6240 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
6241 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
6242 Status
= STATUS_SUCCESS
;
6246 *length
= newlength
- offset
.QuadPart
;
6248 newlength
= offset
.QuadPart
+ *length
;
6249 changed_length
= TRUE
;
6251 TRACE("extending length to %llx\n", newlength
);
6255 make_inline
= fcb
->ads
? FALSE
: newlength
<= fcb
->Vcb
->max_inline
;
6257 if (changed_length
) {
6258 if (newlength
> fcb
->Header
.AllocationSize
.QuadPart
) {
6259 Status
= extend_file(fcb
, newlength
, rollback
);
6260 if (!NT_SUCCESS(Status
)) {
6261 ERR("extend_file returned %08x\n", Status
);
6264 } else if (fcb
->ads
)
6265 fcb
->adssize
= newlength
;
6267 fcb
->inode_item
.st_size
= newlength
;
6269 fcb
->Header
.FileSize
.QuadPart
= newlength
;
6270 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6272 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
6273 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
6274 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
6280 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
6283 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6284 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6285 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6287 if (!FileObject
->PrivateCacheMap
) {
6288 TRACE("calling CcInitializeCacheMap...\n");
6289 CcInitializeCacheMap(FileObject
, &ccfs
, FALSE
, cache_callbacks
, FileObject
);
6291 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
6293 CcSetFileSizes(FileObject
, &ccfs
);
6297 // FIXME - uncomment this when async is working
6298 // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
6301 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
);
6302 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
6303 TRACE("CcCopyWrite failed.\n");
6305 IoMarkIrpPending(Irp
);
6306 Status
= STATUS_PENDING
;
6309 TRACE("CcCopyWrite finished\n");
6311 Status
= STATUS_SUCCESS
;
6320 if (!get_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, &data
, &datalen
)) {
6321 ERR("get_xattr failed\n");
6322 Status
= STATUS_INTERNAL_ERROR
;
6326 if (changed_length
) {
6327 // find maximum length of xattr
6328 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
6330 searchkey
.obj_id
= fcb
->inode
;
6331 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
6332 searchkey
.offset
= fcb
->adshash
;
6334 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6335 if (!NT_SUCCESS(Status
)) {
6336 ERR("error - find_item returned %08x\n", Status
);
6340 if (keycmp(&tp
.item
->key
, &searchkey
)) {
6341 ERR("error - could not find key for xattr\n");
6342 free_traverse_ptr(&tp
);
6343 Status
= STATUS_INTERNAL_ERROR
;
6347 if (tp
.item
->size
< datalen
) {
6348 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
);
6349 free_traverse_ptr(&tp
);
6350 Status
= STATUS_INTERNAL_ERROR
;
6354 maxlen
-= tp
.item
->size
- datalen
; // subtract XATTR_ITEM overhead
6356 free_traverse_ptr(&tp
);
6358 if (newlength
> maxlen
) {
6359 ERR("error - xattr too long (%llu > %u)\n", newlength
, maxlen
);
6360 Status
= STATUS_DISK_FULL
;
6364 fcb
->adssize
= newlength
;
6366 data2
= ExAllocatePoolWithTag(PagedPool
, newlength
, ALLOC_TAG
);
6368 ERR("out of memory\n");
6369 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6373 RtlCopyMemory(data2
, data
, datalen
);
6375 if (offset
.QuadPart
> datalen
)
6376 RtlZeroMemory(&data2
[datalen
], offset
.QuadPart
- datalen
);
6381 RtlCopyMemory(&data2
[offset
.QuadPart
], buf
, *length
);
6383 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, data2
, newlength
, rollback
);
6384 if (!NT_SUCCESS(Status
)) {
6385 ERR("set_xattr returned %08x\n", Status
);
6389 if (data
) ExFreePool(data
);
6390 if (data2
!= data
) ExFreePool(data2
);
6392 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6396 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
6397 bufhead
= sizeof(EXTENT_DATA
) - 1;
6399 start_data
= offset
.QuadPart
& ~(fcb
->Vcb
->superblock
.sector_size
- 1);
6400 end_data
= sector_align(offset
.QuadPart
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
6404 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6405 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
6407 data
= ExAllocatePoolWithTag(PagedPool
, end_data
- start_data
+ bufhead
, ALLOC_TAG
);
6409 ERR("out of memory\n");
6410 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6414 RtlZeroMemory(data
+ bufhead
, end_data
- start_data
);
6416 TRACE("start_data = %llx\n", start_data
);
6417 TRACE("end_data = %llx\n", end_data
);
6419 if (offset
.QuadPart
> start_data
|| offset
.QuadPart
+ *length
< end_data
) {
6420 if (changed_length
) {
6421 if (fcb
->inode_item
.st_size
> start_data
)
6422 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
);
6424 Status
= STATUS_SUCCESS
;
6426 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
);
6428 if (!NT_SUCCESS(Status
)) {
6429 ERR("read_file returned %08x\n", Status
);
6435 RtlCopyMemory(data
+ bufhead
+ offset
.QuadPart
- start_data
, buf
, *length
);
6438 InitializeListHead(&changed_sector_list
);
6440 if (make_inline
|| !nocow
) {
6441 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6442 if (!NT_SUCCESS(Status
)) {
6443 ERR("error - excise_extents returned %08x\n", Status
);
6449 Status
= insert_extent(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6451 if (!NT_SUCCESS(Status
)) {
6452 ERR("error - insert_extent returned %08x\n", Status
);
6459 ed2
= (EXTENT_DATA
*)data
;
6460 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
6461 ed2
->decoded_size
= newlength
;
6462 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
6463 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
6464 ed2
->encoding
= BTRFS_ENCODING_NONE
;
6465 ed2
->type
= EXTENT_TYPE_INLINE
;
6467 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed2
, sizeof(EXTENT_DATA
) - 1 + newlength
, NULL
, rollback
);
6469 fcb
->inode_item
.st_blocks
+= newlength
;
6472 Status
= do_nocow_write(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6474 if (!NT_SUCCESS(Status
)) {
6475 ERR("error - do_nocow_write returned %08x\n", Status
);
6484 KeQuerySystemTime(&time
);
6485 win_time_to_unix(time
, &now
);
6487 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
6490 // if (!FileObject->PrivateCacheMap) {
6491 // CC_FILE_SIZES ccfs;
6493 // ccfs.AllocationSize = fcb->Header.AllocationSize;
6494 // ccfs.FileSize = fcb->Header.FileSize;
6495 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
6497 // TRACE("calling CcInitializeCacheMap...\n");
6498 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
6500 // changed_length = FALSE;
6505 origii
= &fcb
->par
->inode_item
;
6507 origii
= &fcb
->inode_item
;
6509 origii
->transid
= Vcb
->superblock
.generation
;
6511 origii
->st_ctime
= now
;
6514 TRACE("setting st_size to %llx\n", newlength
);
6515 origii
->st_size
= newlength
;
6516 origii
->st_mtime
= now
;
6519 searchkey
.obj_id
= fcb
->inode
;
6520 searchkey
.obj_type
= TYPE_INODE_ITEM
;
6521 searchkey
.offset
= 0;
6523 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6524 if (!NT_SUCCESS(Status
)) {
6525 ERR("error - find_item returned %08x\n", Status
);
6529 if (!keycmp(&tp
.item
->key
, &searchkey
))
6530 delete_tree_item(Vcb
, &tp
, rollback
);
6532 WARN("couldn't find existing INODE_ITEM\n");
6534 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
6536 ERR("out of memory\n");
6537 free_traverse_ptr(&tp
);
6538 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6542 RtlCopyMemory(ii
, origii
, sizeof(INODE_ITEM
));
6543 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
6545 free_traverse_ptr(&tp
);
6547 // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
6550 update_checksum_tree(Vcb
, &changed_sector_list
, rollback
);
6552 if (changed_length
) {
6555 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6556 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6557 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6559 CcSetFileSizes(FileObject
, &ccfs
);
6562 // FIXME - make sure this still called if STATUS_PENDING and async
6564 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
6565 // ERR("CcCopyWrite failed.\n");
6569 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
6570 fcb
->subvol
->root_item
.ctime
= now
;
6572 Status
= STATUS_SUCCESS
;
6575 if (FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
6576 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6577 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
6578 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6582 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6587 NTSTATUS
write_file(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
6588 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6589 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
6592 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
6593 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6594 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
6595 BOOL locked
= FALSE
;
6596 // LARGE_INTEGER freq, time1, time2;
6597 LIST_ENTRY rollback
;
6599 InitializeListHead(&rollback
);
6602 return STATUS_MEDIA_WRITE_PROTECTED
;
6604 if (fcb
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
6605 return STATUS_ACCESS_DENIED
;
6607 // time1 = KeQueryPerformanceCounter(&freq);
6611 Irp
->IoStatus
.Information
= 0;
6613 switch (IrpSp
->MinorFunction
) {
6614 case IRP_MN_COMPLETE
:
6615 FIXME("unsupported - IRP_MN_COMPLETE\n");
6618 case IRP_MN_COMPLETE_MDL
:
6619 FIXME("unsupported - IRP_MN_COMPLETE_MDL\n");
6622 case IRP_MN_COMPLETE_MDL_DPC
:
6623 FIXME("unsupported - IRP_MN_COMPLETE_MDL_DPC\n");
6626 case IRP_MN_COMPRESSED
:
6627 FIXME("unsupported - IRP_MN_COMPRESSED\n");
6631 FIXME("unsupported - IRP_MN_DPC\n");
6635 FIXME("unsupported - IRP_MN_MDL\n");
6638 case IRP_MN_MDL_DPC
:
6639 FIXME("unsupported - IRP_MN_MDL_DPC\n");
6643 TRACE("IRP_MN_NORMAL\n");
6647 WARN("unknown minor function %x\n", IrpSp
->MinorFunction
);
6651 TRACE("offset = %llx\n", offset
.QuadPart
);
6652 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
6654 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
6655 buf
= map_user_buffer(Irp
);
6657 if (Irp
->MdlAddress
&& !buf
) {
6658 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
6659 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6663 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
6665 TRACE("buf = %p\n", buf
);
6667 acquire_tree_lock(Vcb
, TRUE
);
6670 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
6671 WARN("tried to write to locked region\n");
6672 Status
= STATUS_FILE_LOCK_CONFLICT
;
6676 // ERR("Irp->Flags = %x\n", Irp->Flags);
6677 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
, &rollback
);
6678 if (!NT_SUCCESS(Status
)) {
6679 if (Status
!= STATUS_PENDING
)
6680 ERR("write_file2 returned %08x\n", Status
);
6684 Status
= consider_write(Vcb
);
6686 if (NT_SUCCESS(Status
)) {
6687 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
6689 #ifdef DEBUG_PARANOID
6690 check_extents_consistent(Vcb
, FileObject
->FsContext
); // TESTING
6692 // check_extent_tree_consistent(Vcb);
6698 if (NT_SUCCESS(Status
))
6699 clear_rollback(&rollback
);
6701 do_rollback(Vcb
, &rollback
);
6703 release_tree_lock(Vcb
, TRUE
);
6706 // time2 = KeQueryPerformanceCounter(NULL);
6708 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);