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];
53 static BOOL
extent_item_is_shared(EXTENT_ITEM
* ei
, ULONG len
);
54 static BOOL
is_file_prealloc(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
);
56 static NTSTATUS STDCALL
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
57 write_context
* context
= conptr
;
59 context
->iosb
= Irp
->IoStatus
;
60 KeSetEvent(&context
->Event
, 0, FALSE
);
62 // return STATUS_SUCCESS;
63 return STATUS_MORE_PROCESSING_REQUIRED
;
66 static NTSTATUS STDCALL
write_data_phys(PDEVICE_OBJECT device
, UINT64 address
, void* data
, UINT32 length
) {
70 PIO_STACK_LOCATION IrpSp
;
71 write_context
* context
= NULL
;
73 TRACE("(%p, %llx, %p, %x)\n", device
, address
, data
, length
);
75 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_context
), ALLOC_TAG
);
77 ERR("out of memory\n");
78 return STATUS_INSUFFICIENT_RESOURCES
;
81 RtlZeroMemory(context
, sizeof(write_context
));
83 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
85 offset
.QuadPart
= address
;
87 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
89 Irp
= IoAllocateIrp(device
->StackSize
, FALSE
);
92 ERR("IoAllocateIrp failed\n");
93 Status
= STATUS_INTERNAL_ERROR
;
97 IrpSp
= IoGetNextIrpStackLocation(Irp
);
98 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
100 if (device
->Flags
& DO_BUFFERED_IO
) {
101 Irp
->AssociatedIrp
.SystemBuffer
= data
;
103 Irp
->Flags
= IRP_BUFFERED_IO
;
104 } else if (device
->Flags
& DO_DIRECT_IO
) {
105 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
106 if (!Irp
->MdlAddress
) {
107 DbgPrint("IoAllocateMdl failed\n");
111 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
113 Irp
->UserBuffer
= data
;
116 IrpSp
->Parameters
.Write
.Length
= length
;
117 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
119 Irp
->UserIosb
= &context
->iosb
;
121 Irp
->UserEvent
= &context
->Event
;
123 IoSetCompletionRoutine(Irp
, write_completion
, context
, TRUE
, TRUE
, TRUE
);
125 // FIXME - support multiple devices
126 Status
= IoCallDriver(device
, Irp
);
128 if (Status
== STATUS_PENDING
) {
129 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
130 Status
= context
->iosb
.Status
;
133 if (!NT_SUCCESS(Status
)) {
134 ERR("IoCallDriver returned %08x\n", Status
);
137 if (device
->Flags
& DO_DIRECT_IO
) {
138 MmUnlockPages(Irp
->MdlAddress
);
139 IoFreeMdl(Irp
->MdlAddress
);
152 static NTSTATUS STDCALL
write_superblock(device_extension
* Vcb
, device
* device
) {
158 Status
= STATUS_INTERNAL_ERROR
;
161 // FIXME - work with RAID
163 // FIXME - only write one superblock if on SSD (?)
164 while (superblock_addrs
[i
] > 0 && Vcb
->length
>= superblock_addrs
[i
] + sizeof(superblock
)) {
165 TRACE("writing superblock %u\n", i
);
167 Vcb
->superblock
.sb_phys_addr
= superblock_addrs
[i
];
168 RtlCopyMemory(&Vcb
->superblock
.dev_item
, &device
->devitem
, sizeof(DEV_ITEM
));
170 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
172 TRACE("crc32 is %08x\n", crc32
);
173 RtlCopyMemory(&Vcb
->superblock
.checksum
, &crc32
, sizeof(UINT32
));
175 Status
= write_data_phys(device
->devobj
, superblock_addrs
[i
], &Vcb
->superblock
, sizeof(superblock
));
177 if (!NT_SUCCESS(Status
))
186 static BOOL
find_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64 length
, UINT64
* address
) {
188 space
*s
, *bestfit
= NULL
;
190 TRACE("(%p, %llx, %llx, %p)\n", Vcb
, c
->offset
, length
, address
);
193 while (le
!= &c
->space
) {
194 s
= CONTAINING_RECORD(le
, space
, list_entry
);
196 if (s
->type
== SPACE_TYPE_FREE
) {
197 if (s
->size
== length
) {
198 *address
= s
->offset
;
199 TRACE("returning exact fit at %llx\n", s
->offset
);
201 } else if (s
->size
> length
&& (!bestfit
|| bestfit
->size
> s
->size
)) {
210 TRACE("returning best fit at %llx\n", bestfit
->offset
);
211 *address
= bestfit
->offset
;
218 void add_to_space_list(chunk
* c
, UINT64 offset
, UINT64 size
, UINT8 type
) {
219 LIST_ENTRY
*le
= c
->space
.Flink
, *nextle
, *insbef
;
221 #ifdef DEBUG_PARANOID
225 TRACE("(%p, %llx, %llx, %x)\n", c
, offset
, size
, type
);
227 #ifdef DEBUG_PARANOID
230 while (le
!= &c
->space
) {
231 s
= CONTAINING_RECORD(le
, space
, list_entry
);
233 TRACE("%llx,%llx,%x\n", s
->offset
, s
->size
, s
->type
);
239 c
->space_changed
= TRUE
;
243 while (le
!= &c
->space
) {
244 s
= CONTAINING_RECORD(le
, space
, list_entry
);
247 if (s
->offset
>= offset
+ size
) {
252 if (s
->offset
>= offset
&& s
->offset
+ s
->size
<= offset
+ size
) { // delete entirely
254 RemoveEntryList(&s
->list_entry
);
257 if (s
->offset
+ s
->size
== offset
+ size
) {
258 insbef
= s
->list_entry
.Flink
;
259 RemoveEntryList(&s
->list_entry
);
264 RemoveEntryList(&s
->list_entry
);
266 } else if (s
->offset
< offset
&& s
->offset
+ s
->size
> offset
+ size
) { // split in two
267 s3
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
269 ERR("out of memory\n");
273 s3
->offset
= offset
+ size
;
274 s3
->size
= s
->size
- size
- offset
+ s
->offset
;
276 InsertHeadList(&s
->list_entry
, &s3
->list_entry
);
277 insbef
= &s3
->list_entry
;
279 s
->size
= offset
- s
->offset
;
281 } else if (s
->offset
+ s
->size
> offset
&& s
->offset
+ s
->size
<= offset
+ size
) { // truncate before
282 s
->size
= offset
- s
->offset
;
283 } else if (s
->offset
< offset
+ size
&& s
->offset
+ s
->size
> offset
+ size
) { // truncate after
284 s
->size
-= offset
+ size
- s
->offset
;
285 s
->offset
= offset
+ size
;
294 s2
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
296 ERR("out of memory\n");
303 InsertTailList(insbef
, &s2
->list_entry
);
305 // merge entries if same type
307 if (s2
->list_entry
.Blink
!= &c
->space
) {
308 s
= CONTAINING_RECORD(s2
->list_entry
.Blink
, space
, list_entry
);
310 if (s
->type
== type
) {
313 RemoveEntryList(&s2
->list_entry
);
320 if (s2
->list_entry
.Flink
!= &c
->space
) {
321 s
= CONTAINING_RECORD(s2
->list_entry
.Flink
, space
, list_entry
);
323 if (s
->type
== type
) {
326 RemoveEntryList(&s
->list_entry
);
332 while (le
!= &c
->space
) {
333 s
= CONTAINING_RECORD(le
, space
, list_entry
);
335 TRACE("%llx,%llx,%x\n", s
->offset
, s
->size
, s
->type
);
340 #ifdef DEBUG_PARANOID
342 lastaddr
= c
->offset
;
344 while (le
!= &c
->space
) {
345 s
= CONTAINING_RECORD(le
, space
, list_entry
);
347 if (s
->offset
!= lastaddr
) {
348 ERR("inconsistency detected!\n");
352 lastaddr
= s
->offset
+ s
->size
;
357 if (lastaddr
!= c
->offset
+ c
->chunk_item
->size
) {
358 ERR("inconsistency detected - space doesn't run all the way to end of chunk\n");
364 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
368 le2
= Vcb
->chunks
.Flink
;
369 while (le2
!= &Vcb
->chunks
) {
370 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
372 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
374 if (address
>= c
->offset
&& address
< c
->offset
+ c
->chunk_item
->size
)
388 static void add_provisional_disk_hole(device_extension
* Vcb
, stripe
* s
, UINT64 max_stripe_size
) {
389 // LIST_ENTRY* le = s->device->disk_holes.Flink;
392 // ERR("old holes:\n");
393 // while (le != &s->device->disk_holes) {
394 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
396 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
401 if (s
->dh
->size
<= max_stripe_size
) {
402 s
->dh
->provisional
= TRUE
;
404 disk_hole
* newdh
= ExAllocatePoolWithTag(PagedPool
, sizeof(disk_hole
), ALLOC_TAG
);
406 ERR("out of memory\n");
410 newdh
->address
= s
->dh
->address
+ max_stripe_size
;
411 newdh
->size
= s
->dh
->size
- max_stripe_size
;
412 newdh
->provisional
= FALSE
;
413 InsertTailList(&s
->device
->disk_holes
, &newdh
->listentry
);
415 s
->dh
->size
= max_stripe_size
;
416 s
->dh
->provisional
= TRUE
;
419 // ERR("new holes:\n");
420 // le = s->device->disk_holes.Flink;
421 // while (le != &s->device->disk_holes) {
422 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
424 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
430 static UINT64
find_new_chunk_address(device_extension
* Vcb
, UINT64 size
) {
432 traverse_ptr tp
, next_tp
;
437 searchkey
.obj_id
= 0x100;
438 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
439 searchkey
.offset
= 0;
441 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
442 if (!NT_SUCCESS(Status
)) {
443 ERR("error - find_item returned %08x\n", Status
);
444 return 0xffffffffffffffff;
450 if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
451 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
452 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
));
454 CHUNK_ITEM
* ci
= (CHUNK_ITEM
*)tp
.item
->data
;
456 if (tp
.item
->key
.offset
>= lastaddr
+ size
)
459 lastaddr
= tp
.item
->key
.offset
+ ci
->size
;
463 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
467 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
475 static BOOL
increase_dev_item_used(device_extension
* Vcb
, device
* device
, UINT64 size
, LIST_ENTRY
* rollback
) {
481 searchkey
.obj_id
= 1;
482 searchkey
.obj_type
= TYPE_DEV_ITEM
;
483 searchkey
.offset
= device
->devitem
.dev_id
;
485 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
486 if (!NT_SUCCESS(Status
)) {
487 ERR("error - find_item returned %08x\n", Status
);
491 if (keycmp(&tp
.item
->key
, &searchkey
)) {
492 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
496 delete_tree_item(Vcb
, &tp
, rollback
);
498 device
->devitem
.bytes_used
+= size
;
500 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
502 ERR("out of memory\n");
506 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
508 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, rollback
)) {
509 ERR("insert_tree_item failed\n");
516 static void reset_disk_holes(device
* device
, BOOL commit
) {
517 LIST_ENTRY
* le
= device
->disk_holes
.Flink
;
520 // ERR("old holes:\n");
521 // while (le != &device->disk_holes) {
522 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
524 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
529 le
= device
->disk_holes
.Flink
;
530 while (le
!= &device
->disk_holes
) {
531 LIST_ENTRY
* le2
= le
->Flink
;
533 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
535 if (dh
->provisional
) {
540 dh
->provisional
= FALSE
;
548 le
= device
->disk_holes
.Flink
;
549 while (le
!= &device
->disk_holes
) {
550 LIST_ENTRY
* le2
= le
->Flink
;
552 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
554 while (le2
!= &device
->disk_holes
) {
555 disk_hole
* dh2
= CONTAINING_RECORD(le2
, disk_hole
, listentry
);
557 if (dh2
->address
== dh
->address
+ dh
->size
) {
558 LIST_ENTRY
* le3
= le2
->Flink
;
559 dh
->size
+= dh2
->size
;
561 RemoveEntryList(le2
);
573 // ERR("new holes:\n");
574 // le = device->disk_holes.Flink;
575 // while (le != &device->disk_holes) {
576 // dh = CONTAINING_RECORD(le, disk_hole, listentry);
578 // ERR("address %llx, size %llx, provisional %u\n", dh->address, dh->size, dh->provisional);
584 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, ULONG size
) {
589 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
590 ERR("error - bootstrap is full\n");
591 return STATUS_INTERNAL_ERROR
;
594 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
596 ERR("out of memory\n");
597 return STATUS_INSUFFICIENT_RESOURCES
;
600 sc
->key
.obj_id
= obj_id
;
601 sc
->key
.obj_type
= obj_type
;
602 sc
->key
.offset
= offset
;
604 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
606 ERR("out of memory\n");
608 return STATUS_INSUFFICIENT_RESOURCES
;
611 RtlCopyMemory(sc
->data
, data
, sc
->size
);
613 le
= Vcb
->sys_chunks
.Flink
;
614 while (le
!= &Vcb
->sys_chunks
) {
615 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
617 if (keycmp(&sc2
->key
, &sc
->key
) == 1)
622 InsertTailList(le
, &sc
->list_entry
);
624 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
627 le
= Vcb
->sys_chunks
.Flink
;
628 while (le
!= &Vcb
->sys_chunks
) {
629 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
631 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
633 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
636 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
642 return STATUS_SUCCESS
;
645 chunk
* alloc_chunk(device_extension
* Vcb
, UINT64 flags
, LIST_ENTRY
* rollback
) {
646 UINT64 max_stripe_size
, max_chunk_size
, stripe_size
;
647 UINT64 total_size
= 0, i
, j
, logaddr
;
653 CHUNK_ITEM_STRIPE
* cis
;
656 BOOL success
= FALSE
;
657 BLOCK_GROUP_ITEM
* bgi
;
659 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
660 total_size
+= Vcb
->devices
[i
].devitem
.num_bytes
;
662 TRACE("total_size = %llx\n", total_size
);
664 if (flags
& BLOCK_FLAG_DATA
) {
665 max_stripe_size
= 0x40000000; // 1 GB
666 max_chunk_size
= 10 * max_stripe_size
;
667 } else if (flags
& BLOCK_FLAG_METADATA
) {
668 if (total_size
> 0xC80000000) // 50 GB
669 max_stripe_size
= 0x40000000; // 1 GB
671 max_stripe_size
= 0x10000000; // 256 MB
673 max_chunk_size
= max_stripe_size
;
674 } else if (flags
& BLOCK_FLAG_SYSTEM
) {
675 max_stripe_size
= 0x2000000; // 32 MB
676 max_chunk_size
= 2 * max_stripe_size
;
679 // FIXME - make sure whole number of sectors?
680 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
682 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
684 if (flags
& BLOCK_FLAG_DUPLICATE
) {
686 } else if (flags
& BLOCK_FLAG_RAID0
) {
687 FIXME("RAID0 not yet supported\n");
689 } else if (flags
& BLOCK_FLAG_RAID1
) {
690 FIXME("RAID1 not yet supported\n");
692 } else if (flags
& BLOCK_FLAG_RAID10
) {
693 FIXME("RAID10 not yet supported\n");
695 } else if (flags
& BLOCK_FLAG_RAID5
) {
696 FIXME("RAID5 not yet supported\n");
698 } else if (flags
& BLOCK_FLAG_RAID6
) {
699 FIXME("RAID6 not yet supported\n");
705 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * num_stripes
, ALLOC_TAG
);
707 ERR("out of memory\n");
711 for (i
= 0; i
< num_stripes
; i
++) {
712 stripes
[i
].dh
= NULL
;
714 for (j
= 0; j
< Vcb
->superblock
.num_devices
; j
++) {
715 LIST_ENTRY
* le
= Vcb
->devices
[j
].disk_holes
.Flink
;
717 while (le
!= &Vcb
->devices
[j
].disk_holes
) {
718 dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
720 if (!dh
->provisional
) {
721 if (!stripes
[i
].dh
|| dh
->size
> stripes
[i
].dh
->size
) {
723 stripes
[i
].device
= &Vcb
->devices
[j
];
725 if (stripes
[i
].dh
->size
>= max_stripe_size
)
733 if (stripes
[i
].dh
&& stripes
[i
].dh
->size
>= max_stripe_size
)
738 TRACE("good DH: device %llx, address %llx, size %llx\n", stripes
[i
].device
->devitem
.dev_id
, stripes
[i
].dh
->address
, stripes
[i
].dh
->size
);
740 TRACE("good DH not found\n");
744 add_provisional_disk_hole(Vcb
, &stripes
[i
], max_stripe_size
);
747 stripe_size
= min(stripes
[0].dh
->size
, max_stripe_size
);
748 for (i
= 1; i
< num_stripes
; i
++) {
749 stripe_size
= min(stripe_size
, stripes
[1].dh
->size
);
751 // FIXME - make sure stripe_size aligned properly
752 // FIXME - obey max_chunk_size
754 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
756 ERR("out of memory\n");
760 // add CHUNK_ITEM to tree 3
762 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
763 ci
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
765 ERR("out of memory\n");
769 ci
->size
= stripe_size
; // FIXME for RAID
770 ci
->root_id
= Vcb
->extent_root
->id
;
771 ci
->stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
773 ci
->opt_io_alignment
= ci
->stripe_length
;
774 ci
->opt_io_width
= ci
->stripe_length
;
775 ci
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
776 ci
->num_stripes
= num_stripes
;
779 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
781 ERR("out of memory\n");
786 for (i
= 0; i
< num_stripes
; i
++) {
788 cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
792 cis
->dev_id
= stripes
[i
].device
->devitem
.dev_id
;
793 cis
->offset
= stripes
[i
].dh
->address
;
794 cis
->dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
796 c
->devices
[i
] = stripes
[i
].device
;
799 logaddr
= find_new_chunk_address(Vcb
, ci
->size
);
800 if (logaddr
== 0xffffffffffffffff) {
801 ERR("find_new_chunk_address failed\n");
806 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
, NULL
, rollback
)) {
807 ERR("insert_tree_item failed\n");
812 if (flags
& BLOCK_FLAG_SYSTEM
) {
813 NTSTATUS Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, logaddr
, ci
, cisize
);
814 if (!NT_SUCCESS(Status
)) {
815 ERR("add_to_bootstrap returned %08x\n", Status
);
820 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
822 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, cisize
, ALLOC_TAG
);
823 if (!c
->chunk_item
) {
824 ERR("out of memory\n");
828 RtlCopyMemory(c
->chunk_item
, ci
, cisize
);
831 c
->used
= c
->oldused
= 0;
832 c
->space_changed
= FALSE
;
835 InitializeListHead(&c
->space
);
837 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
839 ERR("out of memory\n");
843 s
->offset
= c
->offset
;
844 s
->size
= c
->chunk_item
->size
;
845 s
->type
= SPACE_TYPE_FREE
;
846 InsertTailList(&c
->space
, &s
->list_entry
);
848 protect_superblocks(Vcb
, c
);
850 // add BLOCK_GROUP_ITEM to tree 2
852 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
854 ERR("out of memory\n");
859 bgi
->chunk_tree
= 0x100;
862 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, logaddr
, TYPE_BLOCK_GROUP_ITEM
, ci
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, rollback
)) {
863 ERR("insert_tree_item failed\n");
868 // add DEV_EXTENTs to tree 4
870 for (i
= 0; i
< num_stripes
; i
++) {
873 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
875 ERR("out of memory\n");
879 de
->chunktree
= Vcb
->chunk_root
->id
;
881 de
->address
= logaddr
;
882 de
->length
= ci
->size
;
883 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
885 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
)) {
886 ERR("insert_tree_item failed\n");
891 if (!increase_dev_item_used(Vcb
, stripes
[i
].device
, ci
->size
, rollback
)) {
892 ERR("increase_dev_item_used failed\n");
897 for (i
= 0; i
< num_stripes
; i
++) {
899 for (j
= 0; j
< i
; j
++) {
900 if (stripes
[j
].device
== stripes
[i
].device
)
905 reset_disk_holes(stripes
[i
].device
, TRUE
);
914 for (i
= 0; i
< num_stripes
; i
++) {
916 for (j
= 0; j
< i
; j
++) {
917 if (stripes
[j
].device
== stripes
[i
].device
)
922 reset_disk_holes(stripes
[i
].device
, FALSE
);
925 if (c
) ExFreePool(c
);
926 if (s
) ExFreePool(s
);
928 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
930 return success
? c
: NULL
;
933 NTSTATUS STDCALL
write_data(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
) {
940 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
942 // FIXME - use version cached in Vcb
944 searchkey
.obj_id
= 0x100; // fixed?
945 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
946 searchkey
.offset
= address
;
948 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
949 if (!NT_SUCCESS(Status
)) {
950 ERR("error - find_item returned %08x\n", Status
);
954 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
955 ERR("error - unexpected item in chunk tree\n");
956 Status
= STATUS_INTERNAL_ERROR
;
960 if (tp
.item
->size
< sizeof(CHUNK_ITEM2
)) {
961 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
));
962 Status
= STATUS_INTERNAL_ERROR
;
966 ci
= (CHUNK_ITEM2
*)tp
.item
->data
;
968 if (tp
.item
->key
.offset
> address
|| tp
.item
->key
.offset
+ ci
->ci
.size
< address
) {
969 ERR("error - address %llx was out of chunk bounds\n", address
);
970 Status
= STATUS_INTERNAL_ERROR
;
974 // FIXME - only do this for chunks marked DUPLICATE?
975 // FIXME - for multiple writes, if PENDING do waits at the end
976 // FIXME - work with RAID
977 for (i
= 0; i
< ci
->ci
.num_stripes
; i
++) {
978 Status
= write_data_phys(Vcb
->devices
[0].devobj
, address
- tp
.item
->key
.offset
+ ci
->stripes
[i
].offset
, data
, length
);
979 if (!NT_SUCCESS(Status
)) {
980 ERR("error - write_data_phys failed\n");
990 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
991 LIST_ENTRY
*le
, *nextle
;
995 // le = c->space.Flink;
996 // while (le != &c->space) {
997 // s = CONTAINING_RECORD(le, space, list_entry);
999 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1004 le
= c
->space
.Flink
;
1005 while (le
!= &c
->space
) {
1006 s
= CONTAINING_RECORD(le
, space
, list_entry
);
1009 if (s
->type
== SPACE_TYPE_DELETING
)
1010 s
->type
= SPACE_TYPE_FREE
;
1011 else if (s
->type
== SPACE_TYPE_WRITING
)
1012 s
->type
= SPACE_TYPE_USED
;
1014 if (le
->Blink
!= &c
->space
) {
1015 s2
= CONTAINING_RECORD(le
->Blink
, space
, list_entry
);
1017 if (s2
->type
== s
->type
) { // do merge
1018 s2
->size
+= s
->size
;
1020 RemoveEntryList(&s
->list_entry
);
1028 // le = c->space.Flink;
1029 // while (le != &c->space) {
1030 // s = CONTAINING_RECORD(le, space, list_entry);
1032 // TRACE("%x,%x,%x\n", (UINT32)s->offset, (UINT32)s->size, s->type);
1038 static void clean_space_cache(device_extension
* Vcb
) {
1042 TRACE("(%p)\n", Vcb
);
1044 le
= Vcb
->chunks
.Flink
;
1045 while (le
!= &Vcb
->chunks
) {
1046 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1048 if (c
->space_changed
) {
1049 clean_space_cache_chunk(Vcb
, c
);
1050 c
->space_changed
= FALSE
;
1057 static BOOL
trees_consistent(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1058 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
1061 le
= Vcb
->trees
.Flink
;
1062 while (le
!= &Vcb
->trees
) {
1063 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1066 if (t
->header
.num_items
== 0 && t
->parent
)
1069 if (t
->size
> maxsize
)
1072 if (!t
->has_new_address
)
1082 static NTSTATUS
add_parents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1086 le
= Vcb
->trees
.Flink
;
1087 while (le
!= &Vcb
->trees
) {
1088 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1092 if (!t
->parent
->write
) {
1093 t
->parent
->write
= TRUE
;
1096 } else if (t
->root
!= Vcb
->chunk_root
&& t
->root
!= Vcb
->root_root
) {
1100 searchkey
.obj_id
= t
->root
->id
;
1101 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1102 searchkey
.offset
= 0xffffffffffffffff;
1104 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1105 if (!NT_SUCCESS(Status
)) {
1106 ERR("error - find_item returned %08x\n", Status
);
1110 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1111 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1112 return STATUS_INTERNAL_ERROR
;
1115 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
1116 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1118 ERR("out of memory\n");
1119 return STATUS_INSUFFICIENT_RESOURCES
;
1122 if (tp
.item
->size
> 0)
1123 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
1125 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
1127 delete_tree_item(Vcb
, &tp
, rollback
);
1129 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1130 ERR("insert_tree_item failed\n");
1131 return STATUS_INTERNAL_ERROR
;
1134 if (!tp
.tree
->write
) {
1135 tp
.tree
->write
= TRUE
;
1145 return STATUS_SUCCESS
;
1148 static void add_parents_to_cache(device_extension
* Vcb
, tree
* t
) {
1162 if (t
->root
== Vcb
->root_root
|| t
->root
== Vcb
->chunk_root
)
1165 searchkey
.obj_id
= t
->root
->id
;
1166 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1167 searchkey
.offset
= 0xffffffffffffffff;
1169 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1170 if (!NT_SUCCESS(Status
)) {
1171 ERR("error - find_item returned %08x\n", Status
);
1175 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1176 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1180 if (!tp
.tree
->write
) {
1181 tp
.tree
->write
= TRUE
;
1186 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64 address
, LIST_ENTRY
* rollback
) {
1187 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1188 traverse_ptr insert_tp
;
1190 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1192 ERR("out of memory\n");
1196 eism
->ei
.refcount
= 1;
1197 eism
->ei
.generation
= Vcb
->superblock
.generation
;
1198 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1199 eism
->type
= TYPE_TREE_BLOCK_REF
;
1200 eism
->tbr
.offset
= root_id
;
1202 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, rollback
)) {
1203 ERR("insert_tree_item failed\n");
1208 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1210 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1215 static BOOL
insert_tree_extent(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64
* new_address
, LIST_ENTRY
* rollback
) {
1217 EXTENT_ITEM_TREE2
* eit2
;
1218 traverse_ptr insert_tp
;
1220 TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb
, level
, root_id
, c
, new_address
, rollback
);
1222 if (!find_address_in_chunk(Vcb
, c
, Vcb
->superblock
.node_size
, &address
))
1225 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1226 BOOL b
= insert_tree_extent_skinny(Vcb
, level
, root_id
, c
, address
, rollback
);
1229 *new_address
= address
;
1234 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1236 ERR("out of memory\n");
1240 eit2
->eit
.extent_item
.refcount
= 1;
1241 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
1242 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1243 // eit2->eit.firstitem = wt->firstitem;
1244 eit2
->eit
.level
= level
;
1245 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1246 eit2
->tbr
.offset
= root_id
;
1248 // #ifdef DEBUG_PARANOID
1249 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
1250 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
1251 // 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);
1256 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, rollback
)) {
1257 ERR("insert_tree_item failed\n");
1262 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_WRITING
);
1264 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1266 *new_address
= address
;
1271 NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
1272 chunk
*origchunk
= NULL
, *c
;
1274 UINT64 flags
= t
->flags
, addr
;
1277 flags
= (t
->root
->id
== BTRFS_ROOT_CHUNK
? BLOCK_FLAG_SYSTEM
: BLOCK_FLAG_METADATA
) | BLOCK_FLAG_DUPLICATE
;
1279 // TRACE("flags = %x\n", (UINT32)wt->flags);
1281 // if (!chunk_test) { // TESTING
1282 // if ((c = alloc_chunk(Vcb, flags))) {
1283 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1284 // if (insert_tree_extent(Vcb, t, c)) {
1285 // chunk_test = TRUE;
1286 // return STATUS_SUCCESS;
1292 if (t
->has_address
) {
1293 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
1295 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, origchunk
, &addr
, rollback
)) {
1296 t
->new_address
= addr
;
1297 t
->has_new_address
= TRUE
;
1298 return STATUS_SUCCESS
;
1302 le
= Vcb
->chunks
.Flink
;
1303 while (le
!= &Vcb
->chunks
) {
1304 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1306 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1307 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, c
, &addr
, rollback
)) {
1308 t
->new_address
= addr
;
1309 t
->has_new_address
= TRUE
;
1310 return STATUS_SUCCESS
;
1317 // allocate new chunk if necessary
1318 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
1319 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1320 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, c
, &addr
, rollback
)) {
1321 t
->new_address
= addr
;
1322 t
->has_new_address
= TRUE
;
1323 return STATUS_SUCCESS
;
1328 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
1330 return STATUS_DISK_FULL
;
1333 static BOOL
reduce_tree_extent_skinny(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1337 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1340 searchkey
.obj_id
= address
;
1341 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
1342 searchkey
.offset
= 0xffffffffffffffff;
1344 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1345 if (!NT_SUCCESS(Status
)) {
1346 ERR("error - find_item returned %08x\n", Status
);
1350 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1351 TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1355 if (tp
.item
->size
< sizeof(EXTENT_ITEM_SKINNY_METADATA
)) {
1356 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
));
1360 delete_tree_item(Vcb
, &tp
, rollback
);
1362 eism
= (EXTENT_ITEM_SKINNY_METADATA
*)tp
.item
->data
;
1363 if (t
&& t
->header
.level
== 0 && eism
->ei
.flags
& EXTENT_ITEM_SHARED_BACKREFS
&& eism
->type
== TYPE_TREE_BLOCK_REF
) {
1364 // convert shared data extents
1366 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1367 while (le
!= &t
->itemlist
) {
1368 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1370 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1372 if (!td
->ignore
&& !td
->inserted
) {
1373 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1374 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1376 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1377 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1379 if (ed2
->address
!= 0) {
1380 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1381 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1390 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1393 c
= get_chunk_from_address(Vcb
, address
);
1396 decrease_chunk_usage(c
, Vcb
->superblock
.node_size
);
1398 add_to_space_list(c
, address
, Vcb
->superblock
.node_size
, SPACE_TYPE_DELETING
);
1400 ERR("could not find chunk for address %llx\n", address
);
1406 // static void check_tree_num_items(tree* t) {
1410 // le2 = t->itemlist.Flink;
1412 // while (le2 != &t->itemlist) {
1413 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1416 // le2 = le2->Flink;
1419 // if (t->header.num_items != ni) {
1420 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
1423 // ERR("tree %p okay\n", t);
1427 // static void check_trees_num_items(LIST_ENTRY* tc) {
1428 // LIST_ENTRY* le = tc->Flink;
1429 // while (le != tc) {
1430 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
1432 // check_tree_num_items(tc2->tree);
1438 static void convert_old_tree_extent(device_extension
* Vcb
, tree_data
* td
, tree
* t
, LIST_ENTRY
* rollback
) {
1440 traverse_ptr tp
, tp2
, insert_tp
;
1441 EXTENT_REF_V0
* erv0
;
1444 TRACE("(%p, %p, %p)\n", Vcb
, td
, t
);
1446 searchkey
.obj_id
= td
->treeholder
.address
;
1447 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1448 searchkey
.offset
= 0xffffffffffffffff;
1450 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1451 if (!NT_SUCCESS(Status
)) {
1452 ERR("error - find_item returned %08x\n", Status
);
1456 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1457 TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey
.obj_id
);
1461 searchkey
.obj_id
= td
->treeholder
.address
;
1462 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1463 searchkey
.offset
= Vcb
->superblock
.node_size
;
1465 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1466 if (!NT_SUCCESS(Status
)) {
1467 ERR("error - find_item returned %08x\n", Status
);
1471 if (keycmp(&searchkey
, &tp2
.item
->key
)) {
1472 ERR("could not find %llx,%x,%llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1476 if (tp
.item
->size
< sizeof(EXTENT_REF_V0
)) {
1477 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
));
1481 erv0
= (EXTENT_REF_V0
*)tp
.item
->data
;
1483 delete_tree_item(Vcb
, &tp
, rollback
);
1484 delete_tree_item(Vcb
, &tp2
, rollback
);
1486 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1487 EXTENT_ITEM_SKINNY_METADATA
* eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1490 ERR("out of memory\n");
1494 eism
->ei
.refcount
= 1;
1495 eism
->ei
.generation
= erv0
->gen
;
1496 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1497 eism
->type
= TYPE_TREE_BLOCK_REF
;
1498 eism
->tbr
.offset
= t
->header
.tree_id
;
1500 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
)) {
1501 ERR("insert_tree_item failed\n");
1505 EXTENT_ITEM_TREE2
* eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1508 ERR("out of memory\n");
1512 eit2
->eit
.extent_item
.refcount
= 1;
1513 eit2
->eit
.extent_item
.generation
= erv0
->gen
;
1514 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1515 eit2
->eit
.firstitem
= td
->key
;
1516 eit2
->eit
.level
= t
->header
.level
- 1;
1517 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1518 eit2
->tbr
.offset
= t
->header
.tree_id
;
1520 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
)) {
1521 ERR("insert_tree_item failed\n");
1526 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1527 add_parents_to_cache(Vcb
, tp
.tree
);
1528 add_parents_to_cache(Vcb
, tp2
.tree
);
1531 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, LIST_ENTRY
* rollback
) {
1535 EXTENT_ITEM_V0
* eiv0
;
1539 // FIXME - deal with refcounts > 1
1541 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
1543 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1544 if (reduce_tree_extent_skinny(Vcb
, address
, t
, rollback
)) {
1545 return STATUS_SUCCESS
;
1549 searchkey
.obj_id
= address
;
1550 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1551 searchkey
.offset
= Vcb
->superblock
.node_size
;
1553 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
1554 if (!NT_SUCCESS(Status
)) {
1555 ERR("error - find_item returned %08x\n", Status
);
1559 if (keycmp(&tp
.item
->key
, &searchkey
)) {
1560 ERR("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1562 return STATUS_INTERNAL_ERROR
;
1565 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1566 eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1568 if (eiv0
->refcount
> 1) {
1569 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0
->refcount
);
1570 return STATUS_INTERNAL_ERROR
;
1573 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1574 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
));
1575 return STATUS_INTERNAL_ERROR
;
1578 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1580 if (ei
->refcount
> 1) {
1581 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei
->refcount
);
1582 return STATUS_INTERNAL_ERROR
;
1585 if (t
&& t
->header
.level
== 0 && ei
->flags
& EXTENT_ITEM_SHARED_BACKREFS
) {
1586 // convert shared data extents
1588 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
1589 while (le
!= &t
->itemlist
) {
1590 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1592 TRACE("%llx,%x,%llx\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1594 if (!td
->ignore
&& !td
->inserted
) {
1595 if (td
->key
.obj_type
== TYPE_EXTENT_DATA
) {
1596 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1598 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1599 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1601 if (ed2
->address
!= 0) {
1602 TRACE("trying to convert shared data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1603 convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1612 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1616 delete_tree_item(Vcb
, &tp
, rollback
);
1618 // if EXTENT_ITEM_V0, delete corresponding B4 item
1619 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1622 searchkey
.obj_id
= address
;
1623 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1624 searchkey
.offset
= 0xffffffffffffffff;
1626 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
1627 if (!NT_SUCCESS(Status
)) {
1628 ERR("error - find_item returned %08x\n", Status
);
1632 if (tp2
.item
->key
.obj_id
== searchkey
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey
.obj_type
) {
1633 delete_tree_item(Vcb
, &tp2
, rollback
);
1637 if (t
&& !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1640 // when writing old internal trees, convert related extents
1642 le
= t
->itemlist
.Flink
;
1643 while (le
!= &t
->itemlist
) {
1644 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1646 // ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
1648 if (!td
->ignore
&& !td
->inserted
) {
1649 if (t
->header
.level
> 0) {
1650 convert_old_tree_extent(Vcb
, td
, t
, rollback
);
1651 } else if (td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
)) {
1652 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1654 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1655 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1657 if (ed2
->address
!= 0) {
1658 TRACE("trying to convert old data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1659 convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
1669 c
= get_chunk_from_address(Vcb
, address
);
1672 decrease_chunk_usage(c
, tp
.item
->key
.offset
);
1674 add_to_space_list(c
, address
, tp
.item
->key
.offset
, SPACE_TYPE_DELETING
);
1676 ERR("could not find chunk for address %llx\n", address
);
1678 return STATUS_SUCCESS
;
1681 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1685 TRACE("(%p)\n", Vcb
);
1687 le
= Vcb
->trees
.Flink
;
1688 while (le
!= &Vcb
->trees
) {
1689 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1691 if (t
->write
&& !t
->has_new_address
) {
1694 Status
= get_tree_new_address(Vcb
, t
, rollback
);
1695 if (!NT_SUCCESS(Status
)) {
1696 ERR("get_tree_new_address returned %08x\n", Status
);
1700 TRACE("allocated extent %llx\n", t
->new_address
);
1702 if (t
->has_address
) {
1703 Status
= reduce_tree_extent(Vcb
, t
->header
.address
, t
, rollback
);
1705 if (!NT_SUCCESS(Status
)) {
1706 ERR("reduce_tree_extent returned %08x\n", Status
);
1711 c
= get_chunk_from_address(Vcb
, t
->new_address
);
1714 increase_chunk_usage(c
, Vcb
->superblock
.node_size
);
1716 ERR("could not find chunk for address %llx\n", t
->new_address
);
1717 return STATUS_INTERNAL_ERROR
;
1724 return STATUS_SUCCESS
;
1727 static NTSTATUS
update_root_root(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1731 TRACE("(%p)\n", Vcb
);
1733 le
= Vcb
->trees
.Flink
;
1734 while (le
!= &Vcb
->trees
) {
1735 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1737 if (t
->write
&& !t
->parent
) {
1738 if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
1742 searchkey
.obj_id
= t
->root
->id
;
1743 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1744 searchkey
.offset
= 0xffffffffffffffff;
1746 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
1747 if (!NT_SUCCESS(Status
)) {
1748 ERR("error - find_item returned %08x\n", Status
);
1752 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1753 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1754 return STATUS_INTERNAL_ERROR
;
1757 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, t
->new_address
);
1759 t
->root
->root_item
.block_number
= t
->new_address
;
1760 t
->root
->root_item
.root_level
= t
->header
.level
;
1761 t
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1762 t
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1764 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
1765 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1768 ERR("out of memory\n");
1769 return STATUS_INSUFFICIENT_RESOURCES
;
1772 RtlCopyMemory(ri
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1774 delete_tree_item(Vcb
, &tp
, rollback
);
1776 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, rollback
)) {
1777 ERR("insert_tree_item failed\n");
1778 return STATUS_INTERNAL_ERROR
;
1781 RtlCopyMemory(tp
.item
->data
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1784 t
->root
->treeholder
.address
= t
->new_address
;
1790 Status
= update_chunk_caches(Vcb
, rollback
);
1791 if (!NT_SUCCESS(Status
)) {
1792 ERR("update_chunk_caches returned %08x\n", Status
);
1796 return STATUS_SUCCESS
;
1799 static NTSTATUS STDCALL
write_tree_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1800 write_tree_stripe
* stripe
= conptr
;
1801 write_tree_context
* context
= (write_tree_context
*)stripe
->context
;
1805 if (stripe
->status
== WriteTreeStatus_Cancelling
) {
1806 stripe
->status
= WriteTreeStatus_Cancelled
;
1810 stripe
->iosb
= Irp
->IoStatus
;
1812 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
1813 stripe
->status
= WriteTreeStatus_Success
;
1815 le
= context
->stripes
.Flink
;
1817 stripe
->status
= WriteTreeStatus_Error
;
1819 while (le
!= &context
->stripes
) {
1820 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1822 if (s2
->status
== WriteTreeStatus_Pending
) {
1823 s2
->status
= WriteTreeStatus_Cancelling
;
1824 IoCancelIrp(s2
->Irp
);
1832 le
= context
->stripes
.Flink
;
1835 while (le
!= &context
->stripes
) {
1836 write_tree_stripe
* s2
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1838 if (s2
->status
== WriteTreeStatus_Pending
|| s2
->status
== WriteTreeStatus_Cancelling
) {
1847 KeSetEvent(&context
->Event
, 0, FALSE
);
1849 return STATUS_MORE_PROCESSING_REQUIRED
;
1852 NTSTATUS
write_tree(device_extension
* Vcb
, UINT64 addr
, UINT8
* data
, write_tree_context
* wtc
) {
1854 CHUNK_ITEM_STRIPE
* cis
;
1855 write_tree_stripe
* stripe
;
1858 c
= get_chunk_from_address(Vcb
, addr
);
1861 ERR("get_chunk_from_address failed\n");
1862 return STATUS_INTERNAL_ERROR
;
1865 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
1867 // FIXME - make this work with RAID
1869 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
1870 PIO_STACK_LOCATION IrpSp
;
1872 // FIXME - handle missing devices
1874 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_stripe
), ALLOC_TAG
);
1876 ERR("out of memory\n");
1877 return STATUS_INSUFFICIENT_RESOURCES
;
1880 stripe
->context
= (struct write_tree_context
*)wtc
;
1882 stripe
->device
= c
->devices
[i
];
1883 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
1884 stripe
->status
= WriteTreeStatus_Pending
;
1886 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
1889 ERR("IoAllocateIrp failed\n");
1890 return STATUS_INTERNAL_ERROR
;
1893 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
1894 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
1896 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
1897 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= data
;
1899 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
1900 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1901 stripe
->Irp
->MdlAddress
= IoAllocateMdl(data
, Vcb
->superblock
.node_size
, FALSE
, FALSE
, NULL
);
1902 if (!stripe
->Irp
->MdlAddress
) {
1903 ERR("IoAllocateMdl failed\n");
1904 return STATUS_INTERNAL_ERROR
;
1907 MmProbeAndLockPages(stripe
->Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
1909 stripe
->Irp
->UserBuffer
= data
;
1912 IrpSp
->Parameters
.Write
.Length
= Vcb
->superblock
.node_size
;
1913 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= addr
- c
->offset
+ cis
[i
].offset
;
1915 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
1917 IoSetCompletionRoutine(stripe
->Irp
, write_tree_completion
, stripe
, TRUE
, TRUE
, TRUE
);
1919 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
1922 return STATUS_SUCCESS
;
1925 void free_write_tree_stripes(write_tree_context
* wtc
) {
1926 LIST_ENTRY
*le
, *le2
, *nextle
;
1928 le
= wtc
->stripes
.Flink
;
1929 while (le
!= &wtc
->stripes
) {
1930 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1932 if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1933 MmUnlockPages(stripe
->Irp
->MdlAddress
);
1934 IoFreeMdl(stripe
->Irp
->MdlAddress
);
1940 le
= wtc
->stripes
.Flink
;
1941 while (le
!= &wtc
->stripes
) {
1942 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
1947 ExFreePool(stripe
->buf
);
1950 while (le2
!= &wtc
->stripes
) {
1951 write_tree_stripe
* s2
= CONTAINING_RECORD(le2
, write_tree_stripe
, list_entry
);
1953 if (s2
->buf
== stripe
->buf
)
1967 static NTSTATUS
write_trees(device_extension
* Vcb
) {
1973 write_tree_context
* wtc
;
1975 TRACE("(%p)\n", Vcb
);
1977 for (level
= 0; level
<= 255; level
++) {
1978 BOOL nothing_found
= TRUE
;
1980 TRACE("level = %u\n", level
);
1982 le
= Vcb
->trees
.Flink
;
1983 while (le
!= &Vcb
->trees
) {
1984 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1986 if (t
->write
&& t
->header
.level
== level
) {
1987 KEY firstitem
, searchkey
;
1990 EXTENT_ITEM_TREE
* eit
;
1992 if (!t
->has_new_address
) {
1993 ERR("error - tried to write tree with no new address\n");
1997 le2
= t
->itemlist
.Flink
;
1998 while (le2
!= &t
->itemlist
) {
1999 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2001 firstitem
= td
->key
;
2008 t
->paritem
->key
= firstitem
;
2009 t
->paritem
->treeholder
.address
= t
->new_address
;
2010 t
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
2013 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
2014 searchkey
.obj_id
= t
->new_address
;
2015 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2016 searchkey
.offset
= Vcb
->superblock
.node_size
;
2018 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2019 if (!NT_SUCCESS(Status
)) {
2020 ERR("error - find_item returned %08x\n", Status
);
2024 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2025 // traverse_ptr next_tp;
2027 // tree_data* paritem;
2029 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
);
2031 // searchkey.obj_id = 0;
2032 // searchkey.obj_type = 0;
2033 // searchkey.offset = 0;
2035 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
2039 // if (tp.tree->paritem != paritem) {
2040 // paritem = tp.tree->paritem;
2041 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
2044 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
2046 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
2048 // free_traverse_ptr(&tp);
2053 // free_traverse_ptr(&tp);
2055 return STATUS_INTERNAL_ERROR
;
2058 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
2059 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
));
2060 return STATUS_INTERNAL_ERROR
;
2063 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
2064 eit
->firstitem
= firstitem
;
2067 nothing_found
= FALSE
;
2077 TRACE("allocated tree extents\n");
2079 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_tree_context
), ALLOC_TAG
);
2081 ERR("out of memory\n");
2082 return STATUS_INSUFFICIENT_RESOURCES
;
2085 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
2086 InitializeListHead(&wtc
->stripes
);
2088 le
= Vcb
->trees
.Flink
;
2089 while (le
!= &Vcb
->trees
) {
2090 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2091 #ifdef DEBUG_PARANOID
2092 UINT32 num_items
= 0, size
= 0;
2098 #ifdef DEBUG_PARANOID
2099 le2
= t
->itemlist
.Flink
;
2100 while (le2
!= &t
->itemlist
) {
2101 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2105 if (t
->header
.level
== 0)
2111 if (t
->header
.level
== 0)
2112 size
+= num_items
* sizeof(leaf_node
);
2114 size
+= num_items
* sizeof(internal_node
);
2116 if (num_items
!= t
->header
.num_items
) {
2117 ERR("tree %llx, level %x: num_items was %x, expected %x\n", t
->root
->id
, t
->header
.level
, num_items
, t
->header
.num_items
);
2121 if (size
!= t
->size
) {
2122 ERR("tree %llx, level %x: size was %x, expected %x\n", t
->root
->id
, t
->header
.level
, size
, t
->size
);
2126 if (t
->header
.num_items
== 0 && t
->parent
) {
2127 ERR("tree %llx, level %x: tried to write empty tree with parent\n", t
->root
->id
, t
->header
.level
);
2131 if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2132 ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", t
->root
->id
, t
->header
.level
, t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2137 ERR("tree %p\n", t
);
2138 le2
= t
->itemlist
.Flink
;
2139 while (le2
!= &t
->itemlist
) {
2140 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2142 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
2149 t
->header
.address
= t
->new_address
;
2150 t
->header
.generation
= Vcb
->superblock
.generation
;
2151 t
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
2152 t
->has_address
= TRUE
;
2154 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
2156 ERR("out of memory\n");
2157 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2161 body
= data
+ sizeof(tree_header
);
2163 RtlCopyMemory(data
, &t
->header
, sizeof(tree_header
));
2164 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2166 if (t
->header
.level
== 0) {
2167 leaf_node
* itemptr
= (leaf_node
*)body
;
2170 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
2172 le2
= t
->itemlist
.Flink
;
2173 while (le2
!= &t
->itemlist
) {
2174 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2176 dataptr
= dataptr
- td
->size
;
2178 itemptr
[i
].key
= td
->key
;
2179 itemptr
[i
].offset
= (UINT8
*)dataptr
- (UINT8
*)body
;
2180 itemptr
[i
].size
= td
->size
;
2184 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
2190 internal_node
* itemptr
= (internal_node
*)body
;
2194 le2
= t
->itemlist
.Flink
;
2195 while (le2
!= &t
->itemlist
) {
2196 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2198 itemptr
[i
].key
= td
->key
;
2199 itemptr
[i
].address
= td
->treeholder
.address
;
2200 itemptr
[i
].generation
= td
->treeholder
.generation
;
2208 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
2210 *((UINT32
*)data
) = crc32
;
2211 TRACE("setting crc32 to %08x\n", crc32
);
2213 Status
= write_tree(Vcb
, t
->new_address
, data
, wtc
);
2214 if (!NT_SUCCESS(Status
)) {
2215 ERR("write_tree returned %08x\n", Status
);
2223 Status
= STATUS_SUCCESS
;
2225 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
2226 // launch writes and wait
2227 le
= wtc
->stripes
.Flink
;
2228 while (le
!= &wtc
->stripes
) {
2229 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2231 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2236 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2238 le
= wtc
->stripes
.Flink
;
2239 while (le
!= &wtc
->stripes
) {
2240 write_tree_stripe
* stripe
= CONTAINING_RECORD(le
, write_tree_stripe
, list_entry
);
2242 if (!NT_SUCCESS(stripe
->iosb
.Status
)) {
2243 Status
= stripe
->iosb
.Status
;
2250 free_write_tree_stripes(wtc
);
2259 static void update_backup_superblock(device_extension
* Vcb
, superblock_backup
* sb
) {
2263 RtlZeroMemory(sb
, sizeof(superblock_backup
));
2265 sb
->root_tree_addr
= Vcb
->superblock
.root_tree_addr
;
2266 sb
->root_tree_generation
= Vcb
->superblock
.generation
;
2267 sb
->root_level
= Vcb
->superblock
.root_level
;
2269 sb
->chunk_tree_addr
= Vcb
->superblock
.chunk_tree_addr
;
2270 sb
->chunk_tree_generation
= Vcb
->superblock
.chunk_root_generation
;
2271 sb
->chunk_root_level
= Vcb
->superblock
.chunk_root_level
;
2273 searchkey
.obj_id
= BTRFS_ROOT_EXTENT
;
2274 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
2275 searchkey
.offset
= 0xffffffffffffffff;
2277 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
))) {
2278 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2279 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2281 sb
->extent_tree_addr
= ri
->block_number
;
2282 sb
->extent_tree_generation
= ri
->generation
;
2283 sb
->extent_root_level
= ri
->root_level
;
2287 searchkey
.obj_id
= BTRFS_ROOT_FSTREE
;
2289 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
))) {
2290 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2291 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2293 sb
->fs_tree_addr
= ri
->block_number
;
2294 sb
->fs_tree_generation
= ri
->generation
;
2295 sb
->fs_root_level
= ri
->root_level
;
2299 searchkey
.obj_id
= BTRFS_ROOT_DEVTREE
;
2301 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
))) {
2302 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2303 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2305 sb
->dev_root_addr
= ri
->block_number
;
2306 sb
->dev_root_generation
= ri
->generation
;
2307 sb
->dev_root_level
= ri
->root_level
;
2311 searchkey
.obj_id
= BTRFS_ROOT_CHECKSUM
;
2313 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
))) {
2314 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2315 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2317 sb
->csum_root_addr
= ri
->block_number
;
2318 sb
->csum_root_generation
= ri
->generation
;
2319 sb
->csum_root_level
= ri
->root_level
;
2323 sb
->total_bytes
= Vcb
->superblock
.total_bytes
;
2324 sb
->bytes_used
= Vcb
->superblock
.bytes_used
;
2325 sb
->num_devices
= Vcb
->superblock
.num_devices
;
2328 static NTSTATUS
write_superblocks(device_extension
* Vcb
) {
2333 TRACE("(%p)\n", Vcb
);
2335 le
= Vcb
->trees
.Flink
;
2336 while (le
!= &Vcb
->trees
) {
2337 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2339 if (t
->write
&& !t
->parent
) {
2340 if (t
->root
== Vcb
->root_root
) {
2341 Vcb
->superblock
.root_tree_addr
= t
->new_address
;
2342 Vcb
->superblock
.root_level
= t
->header
.level
;
2343 } else if (t
->root
== Vcb
->chunk_root
) {
2344 Vcb
->superblock
.chunk_tree_addr
= t
->new_address
;
2345 Vcb
->superblock
.chunk_root_generation
= t
->header
.generation
;
2346 Vcb
->superblock
.chunk_root_level
= t
->header
.level
;
2353 for (i
= 0; i
< BTRFS_NUM_BACKUP_ROOTS
- 1; i
++) {
2354 RtlCopyMemory(&Vcb
->superblock
.backup
[i
], &Vcb
->superblock
.backup
[i
+1], sizeof(superblock_backup
));
2357 update_backup_superblock(Vcb
, &Vcb
->superblock
.backup
[BTRFS_NUM_BACKUP_ROOTS
- 1]);
2359 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2360 if (Vcb
->devices
[i
].devobj
) {
2361 Status
= write_superblock(Vcb
, &Vcb
->devices
[i
]);
2362 if (!NT_SUCCESS(Status
)) {
2363 ERR("write_superblock returned %08x\n", Status
);
2369 return STATUS_SUCCESS
;
2372 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
2373 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
2377 BLOCK_GROUP_ITEM
* bgi
;
2380 TRACE("(%p)\n", Vcb
);
2382 while (le
!= &Vcb
->chunks
) {
2383 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2385 if (c
->used
!= c
->oldused
) {
2386 searchkey
.obj_id
= c
->offset
;
2387 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2388 searchkey
.offset
= c
->chunk_item
->size
;
2390 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2391 if (!NT_SUCCESS(Status
)) {
2392 ERR("error - find_item returned %08x\n", Status
);
2396 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2397 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2399 return STATUS_INTERNAL_ERROR
;
2402 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2403 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
));
2404 return STATUS_INTERNAL_ERROR
;
2407 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2409 ERR("out of memory\n");
2410 return STATUS_INSUFFICIENT_RESOURCES
;
2413 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2414 bgi
->used
= c
->used
;
2416 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2418 delete_tree_item(Vcb
, &tp
, rollback
);
2420 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, rollback
)) {
2421 ERR("insert_tree_item failed\n");
2423 return STATUS_INTERNAL_ERROR
;
2426 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2427 TRACE("chunk_item type = %llx\n", c
->chunk_item
->type
);
2429 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
2430 FIXME("RAID0 not yet supported\n");
2432 return STATUS_INTERNAL_ERROR
;
2433 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
) {
2434 FIXME("RAID1 not yet supported\n");
2436 return STATUS_INTERNAL_ERROR
;
2437 } else if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
) {
2438 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ (2 * (c
->used
- c
->oldused
));
2439 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
2440 FIXME("RAID10 not yet supported\n");
2442 return STATUS_INTERNAL_ERROR
;
2443 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
2444 FIXME("RAID5 not yet supported\n");
2446 return STATUS_INTERNAL_ERROR
;
2447 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2448 FIXME("RAID6 not yet supported\n");
2450 return STATUS_INTERNAL_ERROR
;
2452 Vcb
->superblock
.bytes_used
= Vcb
->superblock
.bytes_used
+ c
->used
- c
->oldused
;
2455 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2457 c
->oldused
= c
->used
;
2463 return STATUS_SUCCESS
;
2466 static void get_first_item(tree
* t
, KEY
* key
) {
2469 le
= t
->itemlist
.Flink
;
2470 while (le
!= &t
->itemlist
) {
2471 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2478 static NTSTATUS STDCALL
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2481 tree_data
* oldlastitem
;
2483 // // tree_data *firsttd, *lasttd;
2484 // // LIST_ENTRY* le;
2485 // #ifdef DEBUG_PARANOID
2486 // KEY lastkey1, lastkey2;
2487 // traverse_ptr tp, next_tp;
2488 // ULONG numitems1, numitems2;
2491 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2493 // #ifdef DEBUG_PARANOID
2494 // lastkey1.obj_id = 0xffffffffffffffff;
2495 // lastkey1.obj_type = 0xff;
2496 // lastkey1.offset = 0xffffffffffffffff;
2498 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2499 // ERR("error - find_item failed\n");
2501 // lastkey1 = tp.item->key;
2503 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2504 // free_traverse_ptr(&tp);
2508 // free_traverse_ptr(&tp);
2512 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2514 ERR("out of memory\n");
2515 return STATUS_INSUFFICIENT_RESOURCES
;
2518 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2519 nt
->header
.address
= 0;
2520 nt
->header
.generation
= Vcb
->superblock
.generation
;
2521 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2522 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2524 nt
->has_address
= FALSE
;
2526 nt
->parent
= t
->parent
;
2528 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2529 nt
->new_address
= 0;
2530 nt
->has_new_address
= FALSE
;
2531 nt
->flags
= t
->flags
;
2532 InitializeListHead(&nt
->itemlist
);
2534 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2536 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2538 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2539 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2541 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2542 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2543 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2545 // // le = wt->tree->itemlist.Flink;
2546 // // while (le != &wt->tree->itemlist) {
2547 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2548 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2549 // // le = le->Flink;
2552 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2553 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2554 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2555 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2557 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2558 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2560 // // le = wt->tree->itemlist.Flink;
2561 // // while (le != &wt->tree->itemlist) {
2562 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2563 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2564 // // le = le->Flink;
2567 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2568 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2570 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2571 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2572 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2574 nt
->size
= t
->size
- size
;
2576 t
->header
.num_items
= numitems
;
2580 InterlockedIncrement(&Vcb
->open_trees
);
2581 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2584 // // td = wt->tree->items;
2586 // // if (!td->ignore) {
2587 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2589 // // td = td->next;
2592 // // oldlastitem->next = NULL;
2593 // // wt->tree->lastitem = oldlastitem;
2595 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2597 if (nt
->header
.level
> 0) {
2598 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2600 while (le
!= &nt
->itemlist
) {
2601 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2603 if (td2
->treeholder
.tree
)
2604 td2
->treeholder
.tree
->parent
= nt
;
2611 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2613 ERR("out of memory\n");
2614 return STATUS_INSUFFICIENT_RESOURCES
;
2617 td
->key
= newfirstitem
->key
;
2619 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2622 td
->inserted
= TRUE
;
2623 td
->treeholder
.tree
= nt
;
2624 init_tree_holder(&td
->treeholder
);
2625 // td->treeholder.nonpaged->status = tree_holder_loaded;
2628 nt
->parent
->header
.num_items
++;
2629 nt
->parent
->size
+= sizeof(internal_node
);
2634 TRACE("adding new tree parent\n");
2636 if (nt
->header
.level
== 255) {
2637 ERR("cannot add parent to tree at level 255\n");
2638 return STATUS_INTERNAL_ERROR
;
2641 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2643 ERR("out of memory\n");
2644 return STATUS_INSUFFICIENT_RESOURCES
;
2647 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2648 pt
->header
.address
= 0;
2649 pt
->header
.num_items
= 2;
2650 pt
->header
.level
= nt
->header
.level
+ 1;
2651 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2653 pt
->has_address
= FALSE
;
2658 pt
->new_address
= 0;
2659 pt
->has_new_address
= FALSE
;
2660 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2661 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2662 pt
->flags
= t
->flags
;
2663 InitializeListHead(&pt
->itemlist
);
2665 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2667 InterlockedIncrement(&Vcb
->open_trees
);
2668 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2670 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2672 ERR("out of memory\n");
2673 return STATUS_INSUFFICIENT_RESOURCES
;
2676 get_first_item(t
, &td
->key
);
2678 td
->inserted
= FALSE
;
2679 td
->treeholder
.address
= 0;
2680 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2681 td
->treeholder
.tree
= t
;
2682 init_tree_holder(&td
->treeholder
);
2683 // td->treeholder.nonpaged->status = tree_holder_loaded;
2684 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2687 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2689 ERR("out of memory\n");
2690 return STATUS_INSUFFICIENT_RESOURCES
;
2693 td
->key
= newfirstitem
->key
;
2695 td
->inserted
= FALSE
;
2696 td
->treeholder
.address
= 0;
2697 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2698 td
->treeholder
.tree
= nt
;
2699 init_tree_holder(&td
->treeholder
);
2700 // td->treeholder.nonpaged->status = tree_holder_loaded;
2701 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2707 t
->root
->treeholder
.tree
= pt
;
2713 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2715 // #ifdef DEBUG_PARANOID
2716 // lastkey2.obj_id = 0xffffffffffffffff;
2717 // lastkey2.obj_type = 0xff;
2718 // lastkey2.offset = 0xffffffffffffffff;
2720 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2721 // ERR("error - find_item failed\n");
2723 // lastkey2 = tp.item->key;
2726 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2727 // free_traverse_ptr(&tp);
2731 // free_traverse_ptr(&tp);
2734 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2735 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2736 // ERR("numitems1 = %u\n", numitems1);
2737 // ERR("numitems2 = %u\n", numitems2);
2740 return STATUS_SUCCESS
;
2743 static NTSTATUS STDCALL
split_tree(device_extension
* Vcb
, tree
* t
) {
2745 UINT32 size
, ds
, numitems
;
2750 // FIXME - naïve implementation: maximizes number of filled trees
2752 le
= t
->itemlist
.Flink
;
2753 while (le
!= &t
->itemlist
) {
2754 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2757 if (t
->header
.level
== 0)
2758 ds
= sizeof(leaf_node
) + td
->size
;
2760 ds
= sizeof(internal_node
);
2762 // FIXME - move back if previous item was deleted item with same key
2763 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
2764 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
2773 return STATUS_SUCCESS
;
2776 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, LIST_ENTRY
* rollback
) {
2778 tree_data
* nextparitem
= NULL
;
2780 tree
*next_tree
, *par
;
2783 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
2785 // FIXME - doesn't capture everything, as it doesn't ascend
2786 // FIXME - write proper function and put it in treefuncs.c
2787 le
= t
->paritem
->list_entry
.Flink
;
2788 while (le
!= &t
->parent
->itemlist
) {
2789 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2800 return STATUS_SUCCESS
;
2802 // FIXME - loop, and capture more than one tree if we can
2804 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
2805 // nextparitem = t->paritem;
2807 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2809 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
);
2810 if (!NT_SUCCESS(Status
)) {
2811 ERR("do_load_tree returned %08x\n", Status
);
2815 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2817 next_tree
= nextparitem
->treeholder
.tree
;
2819 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2820 // merge two trees into one
2822 t
->header
.num_items
+= next_tree
->header
.num_items
;
2823 t
->size
+= next_tree
->size
;
2825 if (next_tree
->header
.level
> 0) {
2826 le
= next_tree
->itemlist
.Flink
;
2828 while (le
!= &next_tree
->itemlist
) {
2829 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2831 if (td2
->treeholder
.tree
)
2832 td2
->treeholder
.tree
->parent
= t
;
2838 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
2839 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
2840 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
2841 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2844 // le = t->itemlist.Flink;
2845 // while (le != &t->itemlist) {
2846 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2847 // if (!td->ignore) {
2848 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2853 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
2855 next_tree
->header
.num_items
= 0;
2856 next_tree
->size
= 0;
2858 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
2859 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, rollback
);
2861 if (!NT_SUCCESS(Status
)) {
2862 ERR("reduce_tree_extent returned %08x\n", Status
);
2865 } else if (next_tree
->has_address
) {
2866 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, rollback
);
2868 if (!NT_SUCCESS(Status
)) {
2869 ERR("reduce_tree_extent returned %08x\n", Status
);
2874 if (!nextparitem
->ignore
) {
2875 nextparitem
->ignore
= TRUE
;
2876 next_tree
->parent
->header
.num_items
--;
2877 next_tree
->parent
->size
-= sizeof(internal_node
);
2880 par
= next_tree
->parent
;
2889 RemoveEntryList(&nextparitem
->list_entry
);
2890 ExFreePool(next_tree
->paritem
);
2891 next_tree
->paritem
= NULL
;
2893 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2895 free_tree(next_tree
);
2897 // rebalance by moving items from second tree into first
2898 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
2899 KEY firstitem
= {0, 0, 0};
2901 TRACE("attempting rebalance\n");
2903 le
= next_tree
->itemlist
.Flink
;
2904 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
2905 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2909 if (next_tree
->header
.level
== 0)
2910 size
= sizeof(leaf_node
) + td
->size
;
2912 size
= sizeof(internal_node
);
2916 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2917 RemoveEntryList(&td
->list_entry
);
2918 InsertTailList(&t
->itemlist
, &td
->list_entry
);
2920 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
)
2921 td
->treeholder
.tree
->parent
= t
;
2924 next_tree
->size
-= size
;
2926 next_tree
->header
.num_items
--;
2927 t
->header
.num_items
++;
2932 le
= next_tree
->itemlist
.Flink
;
2935 le
= next_tree
->itemlist
.Flink
;
2936 while (le
!= &next_tree
->itemlist
) {
2937 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2940 firstitem
= td
->key
;
2947 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
2949 // FIXME - once ascension is working, make this work with parent's parent, etc.
2950 if (next_tree
->paritem
)
2951 next_tree
->paritem
->key
= firstitem
;
2963 return STATUS_SUCCESS
;
2966 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, LIST_ENTRY
* rollback
) {
2971 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
2972 searchkey
.obj_id
= address
;
2973 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
2974 searchkey
.offset
= t
->header
.level
;
2976 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2977 if (!NT_SUCCESS(Status
)) {
2978 ERR("error - find_item returned %08x\n", Status
);
2982 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
2983 EXTENT_ITEM_SKINNY_METADATA
* eism
;
2985 if (tp
.item
->size
> 0) {
2986 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2989 ERR("out of memory\n");
2990 return STATUS_INSUFFICIENT_RESOURCES
;
2993 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
2997 delete_tree_item(Vcb
, &tp
, rollback
);
2999 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, rollback
)) {
3000 ERR("insert_tree_item failed\n");
3002 return STATUS_INTERNAL_ERROR
;
3005 return STATUS_SUCCESS
;
3009 searchkey
.obj_id
= address
;
3010 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3011 searchkey
.offset
= 0xffffffffffffffff;
3013 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3014 if (!NT_SUCCESS(Status
)) {
3015 ERR("error - find_item returned %08x\n", Status
);
3019 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3020 EXTENT_ITEM_TREE
* eit
;
3022 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
3023 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
));
3024 return STATUS_INTERNAL_ERROR
;
3027 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3030 ERR("out of memory\n");
3031 return STATUS_INSUFFICIENT_RESOURCES
;
3034 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
3036 delete_tree_item(Vcb
, &tp
, rollback
);
3040 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
)) {
3041 ERR("insert_tree_item failed\n");
3043 return STATUS_INTERNAL_ERROR
;
3046 return STATUS_SUCCESS
;
3049 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
3051 return STATUS_INTERNAL_ERROR
;
3054 static NTSTATUS STDCALL
do_splits(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3055 // LIST_ENTRY *le, *le2;
3058 UINT8 level
, max_level
;
3060 BOOL empty
, done_deletions
= FALSE
;
3064 TRACE("(%p)\n", Vcb
);
3068 for (level
= 0; level
<= 255; level
++) {
3069 LIST_ENTRY
*le
, *nextle
;
3073 TRACE("doing level %u\n", level
);
3075 le
= Vcb
->trees
.Flink
;
3077 while (le
!= &Vcb
->trees
) {
3078 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3082 if (t
->write
&& t
->header
.level
== level
) {
3085 if (t
->header
.num_items
== 0) {
3088 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
3093 done_deletions
= TRUE
;
3095 le2
= t
->itemlist
.Flink
;
3096 while (le2
!= &t
->itemlist
) {
3097 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3098 firstitem
= td
->key
;
3102 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
3103 t
->root
->id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
);
3105 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3107 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3108 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, rollback
);
3110 if (!NT_SUCCESS(Status
)) {
3111 ERR("reduce_tree_extent returned %08x\n", Status
);
3114 } else if (t
->has_address
) {
3115 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, rollback
);
3117 if (!NT_SUCCESS(Status
)) {
3118 ERR("reduce_tree_extent returned %08x\n", Status
);
3123 if (!t
->paritem
->ignore
) {
3124 t
->paritem
->ignore
= TRUE
;
3125 t
->parent
->header
.num_items
--;
3126 t
->parent
->size
-= sizeof(internal_node
);
3129 RemoveEntryList(&t
->paritem
->list_entry
);
3130 ExFreePool(t
->paritem
);
3134 } else if (t
->header
.level
!= 0) {
3135 if (t
->has_new_address
) {
3136 Status
= update_extent_level(Vcb
, t
->new_address
, t
, 0, rollback
);
3138 if (!NT_SUCCESS(Status
)) {
3139 ERR("update_extent_level returned %08x\n", Status
);
3144 t
->header
.level
= 0;
3146 } else if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3147 TRACE("splitting overlarge tree (%x > %x)\n", t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3148 Status
= split_tree(Vcb
, t
);
3150 if (!NT_SUCCESS(Status
)) {
3151 ERR("split_tree returned %08x\n", Status
);
3163 TRACE("nothing found for level %u\n", level
);
3168 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
3170 for (level
= 0; level
<= max_level
; level
++) {
3173 le
= Vcb
->trees
.Flink
;
3175 while (le
!= &Vcb
->trees
) {
3176 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3178 if (t
->write
&& t
->header
.level
== level
&& t
->header
.num_items
> 0 && t
->parent
&& t
->size
< min_size
) {
3179 Status
= try_tree_amalgamate(Vcb
, t
, rollback
);
3180 if (!NT_SUCCESS(Status
)) {
3181 ERR("try_tree_amalgamate returned %08x\n", Status
);
3190 // simplify trees if top tree only has one entry
3192 if (done_deletions
) {
3193 for (level
= max_level
; level
> 0; level
--) {
3194 LIST_ENTRY
*le
, *nextle
;
3196 le
= Vcb
->trees
.Flink
;
3197 while (le
!= &Vcb
->trees
) {
3199 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3201 if (t
->write
&& t
->header
.level
== level
) {
3202 if (!t
->parent
&& t
->header
.num_items
== 1) {
3203 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
3205 tree
* child_tree
= NULL
;
3207 while (le2
!= &t
->itemlist
) {
3208 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3214 TRACE("deleting top-level tree in root %llx with one item\n", t
->root
->id
);
3216 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3217 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, rollback
);
3219 if (!NT_SUCCESS(Status
)) {
3220 ERR("reduce_tree_extent returned %08x\n", Status
);
3223 } else if (t
->has_address
) {
3224 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, rollback
);
3226 if (!NT_SUCCESS(Status
)) {
3227 ERR("reduce_tree_extent returned %08x\n", Status
);
3232 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3233 KEY searchkey
= {0,0,0};
3236 Status
= find_item(Vcb
, t
->root
, &tp
, &searchkey
, FALSE
);
3237 if (!NT_SUCCESS(Status
)) {
3238 ERR("error - find_item returned %08x\n", Status
);
3243 child_tree
= td
->treeholder
.tree
;
3246 child_tree
->parent
= NULL
;
3247 child_tree
->paritem
= NULL
;
3250 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3255 child_tree
->root
->treeholder
.tree
= child_tree
;
3264 return STATUS_SUCCESS
;
3267 static NTSTATUS
remove_root_extents(device_extension
* Vcb
, root
* r
, tree_holder
* th
, UINT8 level
, LIST_ENTRY
* rollback
) {
3272 Status
= load_tree(Vcb
, th
->address
, r
, &th
->tree
);
3274 if (!NT_SUCCESS(Status
)) {
3275 ERR("load_tree(%llx) returned %08x\n", th
->address
, Status
);
3280 if (th
->tree
->header
.level
> 0) {
3281 LIST_ENTRY
* le
= th
->tree
->itemlist
.Flink
;
3283 while (le
!= &th
->tree
->itemlist
) {
3284 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3287 Status
= remove_root_extents(Vcb
, r
, &td
->treeholder
, th
->tree
->header
.level
- 1, rollback
);
3289 if (!NT_SUCCESS(Status
)) {
3290 ERR("remove_root_extents returned %08x\n", Status
);
3300 if (!th
->tree
|| th
->tree
->has_address
) {
3301 Status
= reduce_tree_extent(Vcb
, th
->address
, NULL
, rollback
);
3303 if (!NT_SUCCESS(Status
)) {
3304 ERR("reduce_tree_extent(%llx) returned %08x\n", th
->address
, Status
);
3309 return STATUS_SUCCESS
;
3312 static NTSTATUS
drop_root(device_extension
* Vcb
, root
* r
, LIST_ENTRY
* rollback
) {
3317 Status
= remove_root_extents(Vcb
, r
, &r
->treeholder
, r
->root_item
.root_level
, rollback
);
3318 if (!NT_SUCCESS(Status
)) {
3319 ERR("remove_root_extents returned %08x\n", Status
);
3323 // remove entry in uuid root (tree 9)
3324 if (Vcb
->uuid_root
) {
3325 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
.uuid
[0], sizeof(UINT64
));
3326 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
3327 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
3329 if (searchkey
.obj_id
!= 0 || searchkey
.offset
!= 0) {
3330 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
);
3331 if (!NT_SUCCESS(Status
)) {
3332 WARN("find_item returned %08x\n", Status
);
3334 if (!keycmp(&tp
.item
->key
, &searchkey
))
3335 delete_tree_item(Vcb
, &tp
, rollback
);
3337 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3344 searchkey
.obj_id
= r
->id
;
3345 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
3346 searchkey
.offset
= 0xffffffffffffffff;
3348 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3349 if (!NT_SUCCESS(Status
)) {
3350 ERR("find_item returned %08x\n", Status
);
3354 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
3355 delete_tree_item(Vcb
, &tp
, rollback
);
3357 WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3359 // delete items in tree cache
3361 free_trees_root(Vcb
, r
);
3363 return STATUS_SUCCESS
;
3366 static NTSTATUS
drop_roots(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3367 LIST_ENTRY
*le
= Vcb
->drop_roots
.Flink
, *le2
;
3370 while (le
!= &Vcb
->drop_roots
) {
3371 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3375 Status
= drop_root(Vcb
, r
, rollback
);
3376 if (!NT_SUCCESS(Status
)) {
3377 ERR("drop_root(%llx) returned %08x\n", r
->id
, Status
);
3384 return STATUS_SUCCESS
;
3387 NTSTATUS STDCALL
do_write(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
3392 TRACE("(%p)\n", Vcb
);
3394 if (!IsListEmpty(&Vcb
->drop_roots
)) {
3395 Status
= drop_roots(Vcb
, rollback
);
3397 if (!NT_SUCCESS(Status
)) {
3398 ERR("drop_roots returned %08x\n", Status
);
3403 // If only changing superblock, e.g. changing label, we still need to rewrite
3404 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
3405 if (Vcb
->write_trees
== 0) {
3409 searchkey
.obj_id
= 0;
3410 searchkey
.obj_type
= 0;
3411 searchkey
.offset
= 0;
3413 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3414 if (!NT_SUCCESS(Status
)) {
3415 ERR("error - find_item returned %08x\n", Status
);
3419 if (!Vcb
->root_root
->treeholder
.tree
->write
) {
3420 Vcb
->root_root
->treeholder
.tree
->write
= TRUE
;
3426 Status
= add_parents(Vcb
, rollback
);
3427 if (!NT_SUCCESS(Status
)) {
3428 ERR("add_parents returned %08x\n", Status
);
3432 Status
= do_splits(Vcb
, rollback
);
3433 if (!NT_SUCCESS(Status
)) {
3434 ERR("do_splits returned %08x\n", Status
);
3438 Status
= allocate_tree_extents(Vcb
, rollback
);
3439 if (!NT_SUCCESS(Status
)) {
3440 ERR("add_parents returned %08x\n", Status
);
3444 Status
= update_chunk_usage(Vcb
, rollback
);
3445 if (!NT_SUCCESS(Status
)) {
3446 ERR("update_chunk_usage returned %08x\n", Status
);
3450 Status
= allocate_cache(Vcb
, &cache_changed
, rollback
);
3451 if (!NT_SUCCESS(Status
)) {
3452 ERR("allocate_cache returned %08x\n", Status
);
3455 } while (cache_changed
|| !trees_consistent(Vcb
, rollback
));
3457 TRACE("trees consistent\n");
3459 Status
= update_root_root(Vcb
, rollback
);
3460 if (!NT_SUCCESS(Status
)) {
3461 ERR("update_root_root returned %08x\n", Status
);
3465 Status
= write_trees(Vcb
);
3466 if (!NT_SUCCESS(Status
)) {
3467 ERR("write_trees returned %08x\n", Status
);
3471 Vcb
->superblock
.cache_generation
= Vcb
->superblock
.generation
;
3473 Status
= write_superblocks(Vcb
);
3474 if (!NT_SUCCESS(Status
)) {
3475 ERR("write_superblocks returned %08x\n", Status
);
3479 clean_space_cache(Vcb
);
3481 Vcb
->superblock
.generation
++;
3483 Status
= STATUS_SUCCESS
;
3485 le
= Vcb
->trees
.Flink
;
3486 while (le
!= &Vcb
->trees
) {
3487 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3494 Vcb
->write_trees
= 0;
3496 while (!IsListEmpty(&Vcb
->drop_roots
)) {
3497 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->drop_roots
);
3498 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3500 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
3501 ExFreePool(r
->nonpaged
);
3506 TRACE("do_write returning %08x\n", Status
);
3511 NTSTATUS
consider_write(device_extension
* Vcb
) {
3512 // FIXME - call do_write if Vcb->write_trees high
3514 LIST_ENTRY rollback
;
3515 NTSTATUS Status
= STATUS_SUCCESS
;
3517 InitializeListHead(&rollback
);
3519 if (Vcb
->write_trees
> 0)
3520 Status
= do_write(Vcb
, &rollback
);
3522 free_tree_cache(&Vcb
->tree_cache
);
3524 if (!NT_SUCCESS(Status
))
3525 do_rollback(Vcb
, &rollback
);
3527 clear_rollback(&rollback
);
3531 return STATUS_SUCCESS
;
3535 // NTSTATUS STDCALL add_extent_ref(device_extension* Vcb, UINT64 address, UINT64 size, root* subvol, UINT64 inode, UINT64 offset, LIST_ENTRY* rollback) {
3539 // UINT8 *siptr, *type;
3542 // EXTENT_DATA_REF* edr;
3545 // TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
3547 // searchkey.obj_id = address;
3548 // searchkey.obj_type = TYPE_EXTENT_ITEM;
3549 // searchkey.offset = size;
3551 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3552 // if (!NT_SUCCESS(Status)) {
3553 // ERR("error - find_item returned %08x\n", Status);
3557 // if (keycmp(&tp.item->key, &searchkey)) {
3558 // // create new entry
3560 // len = sizeof(EXTENT_ITEM) + sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
3562 // ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
3564 // ERR("out of memory\n");
3565 // return STATUS_INSUFFICIENT_RESOURCES;
3568 // ei->refcount = 1;
3569 // ei->generation = Vcb->superblock.generation;
3570 // ei->flags = EXTENT_ITEM_DATA;
3572 // type = (UINT8*)&ei[1];
3573 // *type = TYPE_EXTENT_DATA_REF;
3575 // edr = (EXTENT_DATA_REF*)&type[1];
3576 // edr->root = subvol->id;
3577 // edr->objid = inode;
3578 // edr->offset = offset;
3581 // if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
3582 // ERR("error - failed to insert item\n");
3583 // return STATUS_INTERNAL_ERROR;
3586 // // FIXME - update free space in superblock and CHUNK_ITEM
3588 // return STATUS_SUCCESS;
3591 // if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
3592 // NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
3593 // if (!NT_SUCCESS(Status)) {
3594 // ERR("convert_old_data_extent returned %08x\n", Status);
3598 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3599 // if (!NT_SUCCESS(Status)) {
3600 // ERR("error - find_item returned %08x\n", Status);
3604 // if (keycmp(&tp.item->key, &searchkey)) {
3605 // WARN("extent item not found for address %llx, size %llx\n", address, size);
3606 // return STATUS_SUCCESS;
3610 // ei = (EXTENT_ITEM*)tp.item->data;
3612 // if (tp.item->size < sizeof(EXTENT_ITEM)) {
3613 // 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));
3614 // return STATUS_INTERNAL_ERROR;
3617 // if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
3618 // NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
3619 // if (!NT_SUCCESS(Status)) {
3620 // ERR("convert_shared_data_extent returned %08x\n", Status);
3624 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3625 // if (!NT_SUCCESS(Status)) {
3626 // ERR("error - find_item returned %08x\n", Status);
3630 // if (keycmp(&tp.item->key, &searchkey)) {
3631 // WARN("extent item not found for address %llx, size %llx\n", address, size);
3632 // return STATUS_SUCCESS;
3635 // ei = (EXTENT_ITEM*)tp.item->data;
3638 // if (ei->flags != EXTENT_ITEM_DATA) {
3639 // ERR("error - flag was not EXTENT_ITEM_DATA\n");
3640 // return STATUS_INTERNAL_ERROR;
3643 // hash = get_extent_data_ref_hash(subvol->id, inode, offset);
3645 // len = tp.item->size - sizeof(EXTENT_ITEM);
3646 // siptr = (UINT8*)&ei[1];
3649 // if (*siptr == TYPE_EXTENT_DATA_REF) {
3652 // edr = (EXTENT_DATA_REF*)&siptr[1];
3654 // // already exists - increase refcount
3655 // if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
3656 // ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
3659 // ERR("out of memory\n");
3660 // return STATUS_INSUFFICIENT_RESOURCES;
3663 // RtlCopyMemory(ei, tp.item->data, tp.item->size);
3665 // edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data));
3669 // delete_tree_item(Vcb, &tp, rollback);
3671 // if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) {
3672 // ERR("error - failed to insert item\n");
3674 // return STATUS_INTERNAL_ERROR;
3677 // return STATUS_SUCCESS;
3680 // sihash = get_extent_data_ref_hash(edr->root, edr->objid, edr->offset);
3682 // if (sihash >= hash)
3685 // siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
3687 // if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
3688 // len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
3691 // // FIXME - TYPE_TREE_BLOCK_REF 0xB0
3693 // ERR("unrecognized extent subitem %x\n", *siptr);
3694 // return STATUS_INTERNAL_ERROR;
3696 // } while (len > 0);
3698 // len = tp.item->size + sizeof(UINT8) + sizeof(EXTENT_DATA_REF); // FIXME - die if too big
3699 // ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
3701 // ERR("out of memory\n");
3702 // return STATUS_INSUFFICIENT_RESOURCES;
3705 // RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
3708 // type = (UINT8*)ei + (siptr - tp.item->data);
3709 // *type = TYPE_EXTENT_DATA_REF;
3711 // edr = (EXTENT_DATA_REF*)&type[1];
3712 // edr->root = subvol->id;
3713 // edr->objid = inode;
3714 // edr->offset = offset;
3717 // if (siptr < tp.item->data + tp.item->size)
3718 // RtlCopyMemory(&edr[1], siptr, tp.item->data + tp.item->size - siptr);
3720 // delete_tree_item(Vcb, &tp, rollback);
3722 // if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
3723 // ERR("error - failed to insert item\n");
3725 // return STATUS_INTERNAL_ERROR;
3728 // return STATUS_SUCCESS;
3731 static BOOL
extent_item_is_shared(EXTENT_ITEM
* ei
, ULONG len
) {
3732 UINT8
* siptr
= (UINT8
*)&ei
[1];
3735 if (*siptr
== TYPE_TREE_BLOCK_REF
) {
3736 siptr
+= sizeof(TREE_BLOCK_REF
) + 1;
3737 len
-= sizeof(TREE_BLOCK_REF
) + 1;
3738 } else if (*siptr
== TYPE_EXTENT_DATA_REF
) {
3739 siptr
+= sizeof(EXTENT_DATA_REF
) + 1;
3740 len
-= sizeof(EXTENT_DATA_REF
) + 1;
3741 } else if (*siptr
== TYPE_SHARED_BLOCK_REF
) {
3743 } else if (*siptr
== TYPE_SHARED_DATA_REF
) {
3746 ERR("unrecognized extent subitem %x\n", *siptr
);
3754 // static 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) {
3760 // EXTENT_DATA_REF* edr;
3764 // TRACE("(%p, %llx, %llx, %llx, %llx, %llx)\n", Vcb, address, size, subvol->id, inode, offset);
3766 // searchkey.obj_id = address;
3767 // searchkey.obj_type = TYPE_EXTENT_ITEM;
3768 // searchkey.offset = size;
3770 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3771 // if (!NT_SUCCESS(Status)) {
3772 // ERR("error - find_item returned %08x\n", Status);
3776 // if (keycmp(&tp.item->key, &searchkey)) {
3777 // WARN("extent item not found for address %llx, size %llx\n", address, size);
3778 // return STATUS_SUCCESS;
3781 // if (tp.item->size == sizeof(EXTENT_ITEM_V0)) { // old extent ref, convert
3782 // NTSTATUS Status = convert_old_data_extent(Vcb, address, size, rollback);
3783 // if (!NT_SUCCESS(Status)) {
3784 // ERR("convert_old_data_extent returned %08x\n", Status);
3788 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3789 // if (!NT_SUCCESS(Status)) {
3790 // ERR("error - find_item returned %08x\n", Status);
3794 // if (keycmp(&tp.item->key, &searchkey)) {
3795 // WARN("extent item not found for address %llx, size %llx\n", address, size);
3796 // return STATUS_SUCCESS;
3800 // ei = (EXTENT_ITEM*)tp.item->data;
3802 // if (tp.item->size < sizeof(EXTENT_ITEM)) {
3803 // 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));
3804 // return STATUS_INTERNAL_ERROR;
3807 // if (!(ei->flags & EXTENT_ITEM_DATA)) {
3808 // ERR("error - EXTENT_ITEM_DATA flag not set\n");
3809 // return STATUS_INTERNAL_ERROR;
3812 // if (extent_item_is_shared(ei, tp.item->size - sizeof(EXTENT_ITEM))) {
3813 // NTSTATUS Status = convert_shared_data_extent(Vcb, address, size, rollback);
3814 // if (!NT_SUCCESS(Status)) {
3815 // ERR("convert_shared_data_extent returned %08x\n", Status);
3819 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
3820 // if (!NT_SUCCESS(Status)) {
3821 // ERR("error - find_item returned %08x\n", Status);
3825 // if (keycmp(&tp.item->key, &searchkey)) {
3826 // WARN("extent item not found for address %llx, size %llx\n", address, size);
3827 // return STATUS_SUCCESS;
3830 // ei = (EXTENT_ITEM*)tp.item->data;
3833 // len = tp.item->size - sizeof(EXTENT_ITEM);
3834 // siptr = (UINT8*)&ei[1];
3838 // if (*siptr == TYPE_EXTENT_DATA_REF) {
3839 // edr = (EXTENT_DATA_REF*)&siptr[1];
3841 // if (edr->root == subvol->id && edr->objid == inode && edr->offset == offset) {
3842 // if (edr->count > 1) {
3843 // ei = ExAllocatePoolWithTag(PagedPool, tp.item->size, ALLOC_TAG);
3846 // ERR("out of memory\n");
3847 // return STATUS_INSUFFICIENT_RESOURCES;
3850 // RtlCopyMemory(ei, tp.item->data, tp.item->size);
3852 // edr = (EXTENT_DATA_REF*)((UINT8*)ei + ((UINT8*)edr - tp.item->data));
3856 // delete_tree_item(Vcb, &tp, rollback);
3858 // if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, tp.item->size, NULL, rollback)) {
3859 // ERR("error - failed to insert item\n");
3861 // return STATUS_INTERNAL_ERROR;
3864 // return STATUS_SUCCESS;
3871 // siptr += sizeof(UINT8) + sizeof(EXTENT_DATA_REF);
3873 // if (len > sizeof(EXTENT_DATA_REF) + sizeof(UINT8)) {
3874 // len -= sizeof(EXTENT_DATA_REF) + sizeof(UINT8);
3877 // // // FIXME - TYPE_TREE_BLOCK_REF 0xB0
3879 // ERR("unrecognized extent subitem %x\n", *siptr);
3880 // return STATUS_INTERNAL_ERROR;
3882 // } while (len > 0);
3885 // WARN("could not find extent data ref\n");
3886 // return STATUS_SUCCESS;
3889 // // FIXME - decrease subitem refcount if there already?
3891 // len = tp.item->size - sizeof(UINT8) - sizeof(EXTENT_DATA_REF);
3893 // delete_tree_item(Vcb, &tp, rollback);
3895 // if (len == sizeof(EXTENT_ITEM)) { // extent no longer needed
3899 // if (changed_sector_list) {
3900 // changed_sector* sc = ExAllocatePoolWithTag(PagedPool, sizeof(changed_sector), ALLOC_TAG);
3902 // ERR("out of memory\n");
3903 // return STATUS_INSUFFICIENT_RESOURCES;
3906 // sc->ol.key = address;
3907 // sc->checksums = NULL;
3908 // sc->length = size / Vcb->superblock.sector_size;
3910 // sc->deleted = TRUE;
3912 // insert_into_ordered_list(changed_sector_list, &sc->ol);
3916 // le2 = Vcb->chunks.Flink;
3917 // while (le2 != &Vcb->chunks) {
3918 // c = CONTAINING_RECORD(le2, chunk, list_entry);
3920 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
3922 // if (address >= c->offset && address + size < c->offset + c->chunk_item->size)
3925 // le2 = le2->Flink;
3927 // if (le2 == &Vcb->chunks) c = NULL;
3930 // decrease_chunk_usage(c, size);
3932 // add_to_space_list(c, address, size, SPACE_TYPE_DELETING);
3935 // return STATUS_SUCCESS;
3938 // ei = ExAllocatePoolWithTag(PagedPool, len, ALLOC_TAG);
3940 // ERR("out of memory\n");
3941 // return STATUS_INSUFFICIENT_RESOURCES;
3944 // RtlCopyMemory(ei, tp.item->data, siptr - tp.item->data);
3946 // ei->generation = Vcb->superblock.generation;
3948 // if (tp.item->data + len != siptr)
3949 // 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));
3951 // if (!insert_tree_item(Vcb, Vcb->extent_root, searchkey.obj_id, searchkey.obj_type, searchkey.offset, ei, len, NULL, rollback)) {
3952 // ERR("error - failed to insert item\n");
3954 // return STATUS_INTERNAL_ERROR;
3957 // return STATUS_SUCCESS;
3960 static __inline BOOL
entry_in_ordered_list(LIST_ENTRY
* list
, UINT64 value
) {
3961 LIST_ENTRY
* le
= list
->Flink
;
3964 while (le
!= list
) {
3965 ol
= (ordered_list
*)le
;
3967 if (ol
->key
> value
)
3969 else if (ol
->key
== value
)
3978 static BOOL
is_file_prealloc_inode(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 start_data
, UINT64 end_data
) {
3981 traverse_ptr tp
, next_tp
;
3984 searchkey
.obj_id
= inode
;
3985 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
3986 searchkey
.offset
= start_data
;
3988 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
3989 if (!NT_SUCCESS(Status
)) {
3990 ERR("error - find_item returned %08x\n", Status
);
3994 if (tp
.item
->key
.obj_id
!= inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
3998 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
3999 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4002 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4003 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
));
4007 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4008 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
));
4012 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
4014 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
4016 if (tp
.item
->key
.offset
< end_data
&& tp
.item
->key
.offset
+ len
>= start_data
&& ed
->type
== EXTENT_TYPE_PREALLOC
)
4022 if (tp
.item
->key
.obj_id
> inode
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= end_data
)
4030 NTSTATUS
excise_extents_inode(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, INODE_ITEM
* ii
, UINT64 start_data
, UINT64 end_data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4032 traverse_ptr tp
, next_tp
;
4034 BOOL b
, deleted_prealloc
= FALSE
;
4036 searchkey
.obj_id
= inode
;
4037 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4038 searchkey
.offset
= start_data
;
4040 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
4041 if (!NT_SUCCESS(Status
)) {
4042 ERR("error - find_item returned %08x\n", Status
);
4047 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
4048 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4051 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4052 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
));
4053 Status
= STATUS_INTERNAL_ERROR
;
4057 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
4059 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
)
4062 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4063 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
));
4064 Status
= STATUS_INTERNAL_ERROR
;
4068 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
4070 if (tp
.item
->key
.offset
< end_data
&& tp
.item
->key
.offset
+ len
>= start_data
) {
4071 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4072 FIXME("FIXME - compression not supported at present\n");
4073 Status
= STATUS_NOT_SUPPORTED
;
4077 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4078 WARN("root %llx, inode %llx, extent %llx: encryption not supported (type %x)\n", subvol
->id
, inode
, tp
.item
->key
.offset
, ed
->encryption
);
4079 Status
= STATUS_NOT_SUPPORTED
;
4083 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4084 WARN("other encodings not supported\n");
4085 Status
= STATUS_NOT_SUPPORTED
;
4089 if (ed
->type
== EXTENT_TYPE_INLINE
) {
4090 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4091 delete_tree_item(Vcb
, &tp
, rollback
);
4094 ii
->st_blocks
-= len
;
4095 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4099 delete_tree_item(Vcb
, &tp
, rollback
);
4101 size
= len
- (end_data
- tp
.item
->key
.offset
);
4103 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4105 ERR("out of memory\n");
4106 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4110 ned
->generation
= Vcb
->superblock
.generation
;
4111 ned
->decoded_size
= size
;
4112 ned
->compression
= ed
->compression
;
4113 ned
->encryption
= ed
->encryption
;
4114 ned
->encoding
= ed
->encoding
;
4115 ned
->type
= ed
->type
;
4117 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4119 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4120 ERR("insert_tree_item failed\n");
4122 Status
= STATUS_INTERNAL_ERROR
;
4127 ii
->st_blocks
-= end_data
- tp
.item
->key
.offset
;
4128 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4132 delete_tree_item(Vcb
, &tp
, rollback
);
4134 size
= start_data
- tp
.item
->key
.offset
;
4136 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4138 ERR("out of memory\n");
4139 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4143 ned
->generation
= Vcb
->superblock
.generation
;
4144 ned
->decoded_size
= size
;
4145 ned
->compression
= ed
->compression
;
4146 ned
->encryption
= ed
->encryption
;
4147 ned
->encoding
= ed
->encoding
;
4148 ned
->type
= ed
->type
;
4150 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4152 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4153 ERR("insert_tree_item failed\n");
4155 Status
= STATUS_INTERNAL_ERROR
;
4160 ii
->st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4161 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4165 delete_tree_item(Vcb
, &tp
, rollback
);
4167 size
= start_data
- tp
.item
->key
.offset
;
4169 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4171 ERR("out of memory\n");
4172 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4176 ned
->generation
= Vcb
->superblock
.generation
;
4177 ned
->decoded_size
= size
;
4178 ned
->compression
= ed
->compression
;
4179 ned
->encryption
= ed
->encryption
;
4180 ned
->encoding
= ed
->encoding
;
4181 ned
->type
= ed
->type
;
4183 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
4185 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4186 ERR("insert_tree_item failed\n");
4188 Status
= STATUS_INTERNAL_ERROR
;
4192 size
= tp
.item
->key
.offset
+ len
- end_data
;
4194 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
4196 ERR("out of memory\n");
4197 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4201 ned
->generation
= Vcb
->superblock
.generation
;
4202 ned
->decoded_size
= size
;
4203 ned
->compression
= ed
->compression
;
4204 ned
->encryption
= ed
->encryption
;
4205 ned
->encoding
= ed
->encoding
;
4206 ned
->type
= ed
->type
;
4208 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- tp
.item
->key
.offset
], size
);
4210 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + size
, NULL
, rollback
)) {
4211 ERR("insert_tree_item failed\n");
4213 Status
= STATUS_INTERNAL_ERROR
;
4218 ii
->st_blocks
-= end_data
- start_data
;
4220 } else if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4221 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove all
4222 if (ed2
->address
!= 0) {
4223 Status
= decrease_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, subvol
, inode
, tp
.item
->key
.offset
- ed2
->offset
, 1, changed_sector_list
, rollback
);
4224 if (!NT_SUCCESS(Status
)) {
4225 ERR("decrease_extent_refcount_data returned %08x\n", Status
);
4230 ii
->st_blocks
-= len
;
4233 if (ed
->type
== EXTENT_TYPE_PREALLOC
)
4234 deleted_prealloc
= TRUE
;
4236 delete_tree_item(Vcb
, &tp
, rollback
);
4237 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove beginning
4241 if (ed2
->address
!= 0 && ii
)
4242 ii
->st_blocks
-= end_data
- tp
.item
->key
.offset
;
4244 delete_tree_item(Vcb
, &tp
, rollback
);
4246 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4248 ERR("out of memory\n");
4249 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4253 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4255 ned
->generation
= Vcb
->superblock
.generation
;
4256 ned
->decoded_size
= ed
->decoded_size
;
4257 ned
->compression
= ed
->compression
;
4258 ned
->encryption
= ed
->encryption
;
4259 ned
->encoding
= ed
->encoding
;
4260 ned
->type
= ed
->type
;
4261 ned2
->address
= ed2
->address
;
4262 ned2
->size
= ed2
->size
;
4263 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4264 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- tp
.item
->key
.offset
);
4266 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4267 ERR("insert_tree_item failed\n");
4269 Status
= STATUS_INTERNAL_ERROR
;
4272 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ len
) { // remove end
4276 if (ed2
->address
!= 0 && ii
)
4277 ii
->st_blocks
-= tp
.item
->key
.offset
+ len
- start_data
;
4279 delete_tree_item(Vcb
, &tp
, rollback
);
4281 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4283 ERR("out of memory\n");
4284 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4288 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4290 ned
->generation
= Vcb
->superblock
.generation
;
4291 ned
->decoded_size
= ed
->decoded_size
;
4292 ned
->compression
= ed
->compression
;
4293 ned
->encryption
= ed
->encryption
;
4294 ned
->encoding
= ed
->encoding
;
4295 ned
->type
= ed
->type
;
4296 ned2
->address
= ed2
->address
;
4297 ned2
->size
= ed2
->size
;
4298 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4299 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4301 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4302 ERR("insert_tree_item failed\n");
4304 Status
= STATUS_INTERNAL_ERROR
;
4307 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ len
) { // remove middle
4311 if (ed2
->address
!= 0 && ii
)
4312 ii
->st_blocks
-= end_data
- start_data
;
4314 delete_tree_item(Vcb
, &tp
, rollback
);
4316 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4318 ERR("out of memory\n");
4319 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4323 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4325 ned
->generation
= Vcb
->superblock
.generation
;
4326 ned
->decoded_size
= ed
->decoded_size
;
4327 ned
->compression
= ed
->compression
;
4328 ned
->encryption
= ed
->encryption
;
4329 ned
->encoding
= ed
->encoding
;
4330 ned
->type
= ed
->type
;
4331 ned2
->address
= ed2
->address
;
4332 ned2
->size
= ed2
->size
;
4333 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
4334 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
4336 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4337 ERR("insert_tree_item failed\n");
4339 Status
= STATUS_INTERNAL_ERROR
;
4343 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4345 ERR("out of memory\n");
4346 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4350 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
4352 ned
->generation
= Vcb
->superblock
.generation
;
4353 ned
->decoded_size
= ed
->decoded_size
;
4354 ned
->compression
= ed
->compression
;
4355 ned
->encryption
= ed
->encryption
;
4356 ned
->encoding
= ed
->encoding
;
4357 ned
->type
= ed
->type
;
4358 ned2
->address
= ed2
->address
;
4359 ned2
->size
= ed2
->size
;
4360 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- tp
.item
->key
.offset
));
4361 ned2
->num_bytes
= tp
.item
->key
.offset
+ len
- end_data
;
4363 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, end_data
, ned
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4364 ERR("insert_tree_item failed\n");
4366 Status
= STATUS_INTERNAL_ERROR
;
4370 if (ed2
->address
!= 0) {
4371 Status
= increase_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, subvol
, inode
, tp
.item
->key
.offset
- ed2
->offset
, 1, rollback
);
4373 if (!NT_SUCCESS(Status
)) {
4374 ERR("increase_extent_refcount_data returned %08x\n", Status
);
4386 if (tp
.item
->key
.obj_id
> inode
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= end_data
)
4391 // FIXME - do bitmap analysis of changed extents, and free what we can
4393 if (ii
&& deleted_prealloc
&& !is_file_prealloc_inode(Vcb
, subvol
, inode
, 0, sector_align(ii
->st_size
, Vcb
->superblock
.sector_size
)))
4394 ii
->flags
&= ~BTRFS_INODE_PREALLOC
;
4401 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4404 TRACE("(%p, (%llx, %llx), %llx, %llx, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, end_data
, changed_sector_list
);
4406 Status
= excise_extents_inode(Vcb
, fcb
->subvol
, fcb
->inode
, &fcb
->inode_item
, start_data
, end_data
, changed_sector_list
, rollback
);
4407 if (!NT_SUCCESS(Status
)) {
4408 ERR("excise_extents_inode returned %08x\n");
4412 return STATUS_SUCCESS
;
4415 static NTSTATUS
do_write_data(device_extension
* Vcb
, UINT64 address
, void* data
, UINT64 length
, LIST_ENTRY
* changed_sector_list
) {
4420 Status
= write_data(Vcb
, address
, data
, length
);
4421 if (!NT_SUCCESS(Status
)) {
4422 ERR("write_data returned %08x\n", Status
);
4426 if (changed_sector_list
) {
4427 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4429 ERR("out of memory\n");
4430 return STATUS_INSUFFICIENT_RESOURCES
;
4433 sc
->ol
.key
= address
;
4434 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4435 sc
->deleted
= FALSE
;
4437 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4438 if (!sc
->checksums
) {
4439 ERR("out of memory\n");
4441 return STATUS_INSUFFICIENT_RESOURCES
;
4444 for (i
= 0; i
< sc
->length
; i
++) {
4445 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4448 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4451 return STATUS_SUCCESS
;
4454 BOOL
insert_extent_chunk_inode(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, INODE_ITEM
* inode_item
, chunk
* c
, UINT64 start_data
,
4455 UINT64 length
, BOOL prealloc
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4460 ULONG edsize
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
4462 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %p, %p)\n", Vcb
, subvol
->id
, inode
, c
->offset
, start_data
, length
, data
, changed_sector_list
);
4464 if (!find_address_in_chunk(Vcb
, c
, length
, &address
))
4467 Status
= increase_extent_refcount_data(Vcb
, address
, length
, subvol
, inode
, start_data
, 1, rollback
);
4468 if (!NT_SUCCESS(Status
)) {
4469 ERR("increase_extent_refcount_data returned %08x\n", Status
);
4474 Status
= do_write_data(Vcb
, address
, data
, length
, changed_sector_list
);
4475 if (!NT_SUCCESS(Status
)) {
4476 ERR("do_write_data returned %08x\n", Status
);
4481 // add extent data to inode
4482 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
4484 ERR("out of memory\n");
4488 ed
->generation
= Vcb
->superblock
.generation
;
4489 ed
->decoded_size
= length
;
4490 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4491 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4492 ed
->encoding
= BTRFS_ENCODING_NONE
;
4493 ed
->type
= prealloc
? EXTENT_TYPE_PREALLOC
: EXTENT_TYPE_REGULAR
;
4495 ed2
= (EXTENT_DATA2
*)ed
->data
;
4496 ed2
->address
= address
;
4499 ed2
->num_bytes
= length
;
4501 if (!insert_tree_item(Vcb
, subvol
, inode
, TYPE_EXTENT_DATA
, start_data
, ed
, edsize
, NULL
, rollback
)) {
4502 ERR("insert_tree_item failed\n");
4507 increase_chunk_usage(c
, length
);
4508 add_to_space_list(c
, address
, length
, SPACE_TYPE_WRITING
);
4511 inode_item
->st_blocks
+= length
;
4514 inode_item
->flags
|= BTRFS_INODE_PREALLOC
;
4520 static BOOL
insert_extent_chunk(device_extension
* Vcb
, fcb
* fcb
, chunk
* c
, UINT64 start_data
, UINT64 length
, BOOL prealloc
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4521 return insert_extent_chunk_inode(Vcb
, fcb
->subvol
, fcb
->inode
, &fcb
->inode_item
, c
, start_data
, length
, prealloc
, data
, changed_sector_list
, rollback
);
4524 static BOOL
extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4525 LIST_ENTRY
* changed_sector_list
, traverse_ptr
* edtp
, traverse_ptr
* eitp
, LIST_ENTRY
* rollback
) {
4534 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
,
4535 length
, data
, changed_sector_list
, edtp
, eitp
);
4537 ed
= ExAllocatePoolWithTag(PagedPool
, edtp
->item
->size
, ALLOC_TAG
);
4539 ERR("out of memory\n");
4543 RtlCopyMemory(ed
, edtp
->item
->data
, edtp
->item
->size
);
4545 ed
->decoded_size
+= length
;
4546 ed2
= (EXTENT_DATA2
*)ed
->data
;
4547 ed2
->size
+= length
;
4548 ed2
->num_bytes
+= length
;
4550 delete_tree_item(Vcb
, edtp
, rollback
);
4552 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
)) {
4553 TRACE("insert_tree_item failed\n");
4559 ei
= ExAllocatePoolWithTag(PagedPool
, eitp
->item
->size
, ALLOC_TAG
);
4561 ERR("out of memory\n");
4566 RtlCopyMemory(ei
, eitp
->item
->data
, eitp
->item
->size
);
4568 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
)) {
4569 ERR("insert_tree_item failed\n");
4575 delete_tree_item(Vcb
, eitp
, rollback
);
4577 Status
= write_data(Vcb
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, data
, length
);
4578 if (!NT_SUCCESS(Status
)) {
4579 ERR("write_data returned %08x\n", Status
);
4583 if (changed_sector_list
) {
4584 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
4586 ERR("out of memory\n");
4590 sc
->ol
.key
= eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
;
4591 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
4592 sc
->deleted
= FALSE
;
4594 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
4595 if (!sc
->checksums
) {
4596 ERR("out of memory\n");
4601 for (i
= 0; i
< sc
->length
; i
++) {
4602 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
4604 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
4607 c
= get_chunk_from_address(Vcb
, eitp
->item
->key
.obj_id
);
4610 increase_chunk_usage(c
, length
);
4612 add_to_space_list(c
, eitp
->item
->key
.obj_id
+ eitp
->item
->key
.offset
, length
, SPACE_TYPE_WRITING
);
4615 fcb
->inode_item
.st_blocks
+= length
;
4620 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
4621 LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4623 traverse_ptr tp
, tp2
;
4624 BOOL success
= FALSE
;
4633 searchkey
.obj_id
= fcb
->inode
;
4634 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4635 searchkey
.offset
= start_data
;
4637 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4638 if (!NT_SUCCESS(Status
)) {
4639 ERR("error - find_item returned %08x\n", Status
);
4643 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
) {
4644 WARN("previous EXTENT_DATA not found\n");
4648 ed
= (EXTENT_DATA
*)tp
.item
->data
;
4650 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
4651 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
));
4655 if (ed
->type
!= EXTENT_TYPE_REGULAR
) {
4656 TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
4660 ed2
= (EXTENT_DATA2
*)ed
->data
;
4662 if (tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4663 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
));
4667 if (tp
.item
->key
.offset
+ ed2
->num_bytes
!= start_data
) {
4668 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", tp
.item
->key
.offset
, ed2
->num_bytes
, start_data
);
4672 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
4673 FIXME("FIXME: compression not yet supported\n");
4677 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
4678 WARN("encryption not supported\n");
4682 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
4683 WARN("other encodings not supported\n");
4687 if (ed2
->size
- ed2
->offset
!= ed2
->num_bytes
) {
4688 TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
4692 searchkey
.obj_id
= ed2
->address
;
4693 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
4694 searchkey
.offset
= ed2
->size
;
4696 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
4697 if (!NT_SUCCESS(Status
)) {
4698 ERR("error - find_item returned %08x\n", Status
);
4702 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
4703 ERR("error - extent %llx,%llx not found in tree\n", ed2
->address
, ed2
->size
);
4708 if (tp2
.item
->size
== sizeof(EXTENT_ITEM_V0
)) { // old extent ref, convert
4709 NTSTATUS Status
= convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
4710 if (!NT_SUCCESS(Status
)) {
4711 ERR("convert_old_data_extent returned %08x\n", Status
);
4715 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
4716 if (!NT_SUCCESS(Status
)) {
4717 ERR("error - find_item returned %08x\n", Status
);
4721 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
4722 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
4727 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
4729 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
4730 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
));
4734 // FIXME - test this
4735 if (extent_item_is_shared(ei
, tp2
.item
->size
- sizeof(EXTENT_ITEM
))) {
4736 NTSTATUS Status
= convert_shared_data_extent(Vcb
, ed2
->address
, ed2
->size
, rollback
);
4737 if (!NT_SUCCESS(Status
)) {
4738 ERR("convert_shared_data_extent returned %08x\n", Status
);
4742 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
);
4743 if (!NT_SUCCESS(Status
)) {
4744 ERR("error - find_item returned %08x\n", Status
);
4748 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
4749 WARN("extent item not found for address %llx, size %llx\n", ed2
->address
, ed2
->size
);
4753 ei
= (EXTENT_ITEM
*)tp2
.item
->data
;
4755 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
4756 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
));
4761 if (ei
->refcount
!= 1) {
4762 TRACE("extent refcount was not 1\n");
4766 if (ei
->flags
!= EXTENT_ITEM_DATA
) {
4767 ERR("error - extent was not a data extent\n");
4771 c
= get_chunk_from_address(Vcb
, ed2
->address
);
4773 le
= c
->space
.Flink
;
4774 while (le
!= &c
->space
) {
4775 s
= CONTAINING_RECORD(le
, space
, list_entry
);
4777 if (s
->offset
== ed2
->address
+ ed2
->size
) {
4778 if (s
->type
== SPACE_TYPE_FREE
&& s
->size
>= length
) {
4779 success
= extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, &tp
, &tp2
, rollback
);
4782 } else if (s
->offset
> ed2
->address
+ ed2
->size
)
4793 static NTSTATUS
insert_prealloc_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
4794 LIST_ENTRY
* le
= fcb
->Vcb
->chunks
.Flink
;
4798 // FIXME - how do we know which RAID level to put this to?
4799 flags
= BLOCK_FLAG_DATA
; // SINGLE
4801 // FIXME - if length is more than max chunk size, loop through and
4802 // create the new chunks first
4804 while (le
!= &fcb
->Vcb
->chunks
) {
4805 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
4807 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
4808 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, length
, TRUE
, NULL
, NULL
, rollback
))
4809 return STATUS_SUCCESS
;
4815 if ((c
= alloc_chunk(fcb
->Vcb
, flags
, rollback
))) {
4816 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
4817 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, length
, TRUE
, NULL
, NULL
, rollback
))
4818 return STATUS_SUCCESS
;
4822 // FIXME - rebalance chunks if free space elsewhere?
4823 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
4825 return STATUS_DISK_FULL
;
4828 NTSTATUS
insert_sparse_extent(device_extension
* Vcb
, root
* r
, UINT64 inode
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
4832 TRACE("(%p, %llx, %llx, %llx, %llx)\n", Vcb
, r
->id
, inode
, start
, length
);
4834 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4836 ERR("out of memory\n");
4837 return STATUS_INSUFFICIENT_RESOURCES
;
4840 ed
->generation
= Vcb
->superblock
.generation
;
4841 ed
->decoded_size
= length
;
4842 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4843 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4844 ed
->encoding
= BTRFS_ENCODING_NONE
;
4845 ed
->type
= EXTENT_TYPE_REGULAR
;
4847 ed2
= (EXTENT_DATA2
*)ed
->data
;
4851 ed2
->num_bytes
= length
;
4853 if (!insert_tree_item(Vcb
, r
, inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, rollback
)) {
4854 ERR("insert_tree_item failed\n");
4856 return STATUS_INTERNAL_ERROR
;
4859 return STATUS_SUCCESS
;
4862 // static void print_tree(tree* t) {
4863 // LIST_ENTRY* le = t->itemlist.Flink;
4864 // while (le != &t->itemlist) {
4865 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
4866 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
4871 static NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4872 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
4877 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
4879 // FIXME - split data up if not enough space for just one extent
4881 if (start_data
> 0 && try_extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, rollback
))
4882 return STATUS_SUCCESS
;
4884 // if there is a gap before start_data, plug it with a sparse extent
4885 if (start_data
> 0) {
4891 searchkey
.obj_id
= fcb
->inode
;
4892 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4893 searchkey
.offset
= start_data
;
4895 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
4896 if (!NT_SUCCESS(Status
)) {
4897 ERR("error - find_item returned %08x\n", Status
);
4901 // if (tp.item->key.obj_id != fcb->inode || tp.item->key.obj_type != TYPE_EXTENT_DATA || tp.item->key.offset >= start_data) {
4902 // traverse_ptr next_tp;
4904 // ERR("error - did not find EXTENT_DATA expected - looking for %llx,%x,%llx, found %llx,%x,%llx\n",
4905 // searchkey.obj_id, searchkey.obj_type, searchkey.offset, tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
4906 // print_tree(tp.tree);
4908 // if (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
4910 // 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);
4911 // print_tree(next_tp.tree);
4913 // free_traverse_ptr(&next_tp);
4915 // ERR("next item not found\n");
4918 // free_traverse_ptr(&tp);
4919 // return STATUS_INTERNAL_ERROR;
4922 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
&& tp
.item
->size
>= sizeof(EXTENT_DATA
)) {
4925 ed
= (EXTENT_DATA
*)tp
.item
->data
;
4926 ed2
= (EXTENT_DATA2
*)ed
->data
;
4928 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
4932 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| !ed
|| tp
.item
->key
.offset
+ len
< start_data
) {
4933 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
4934 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, 0, start_data
, rollback
);
4936 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
));
4938 Status
= insert_sparse_extent(Vcb
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
+ len
,
4939 start_data
- tp
.item
->key
.offset
- len
, rollback
);
4941 if (!NT_SUCCESS(Status
)) {
4942 ERR("insert_sparse_extent returned %08x\n", Status
);
4948 // FIXME - how do we know which RAID level to put this to?
4949 flags
= BLOCK_FLAG_DATA
; // SINGLE
4951 // if (!chunk_test) { // TESTING
4952 // if ((c = alloc_chunk(Vcb, flags, NULL))) {
4953 // ERR("chunk_item->type = %llx\n", c->chunk_item->type);
4954 // ERR("size = %llx\n", c->chunk_item->size);
4955 // ERR("used = %llx\n", c->used);
4957 // if (c->chunk_item->type == flags && (c->chunk_item->size - c->used) >= length) {
4958 // if (insert_extent_chunk(Vcb, fcb, c, start_data, length, data, changed_sector_list)) {
4959 // // chunk_test = TRUE;
4960 // ERR("SUCCESS\n");
4961 // return STATUS_SUCCESS;
4969 while (le
!= &Vcb
->chunks
) {
4970 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
4972 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
4973 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, FALSE
, data
, changed_sector_list
, rollback
))
4974 return STATUS_SUCCESS
;
4980 if ((c
= alloc_chunk(Vcb
, flags
, rollback
))) {
4981 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= length
) {
4982 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, length
, FALSE
, data
, changed_sector_list
, rollback
))
4983 return STATUS_SUCCESS
;
4987 // FIXME - rebalance chunks if free space elsewhere?
4988 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
4990 return STATUS_DISK_FULL
;
4993 void update_checksum_tree(device_extension
* Vcb
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
4994 LIST_ENTRY
* le
= changed_sector_list
->Flink
;
4996 traverse_ptr tp
, next_tp
;
5001 if (!Vcb
->checksum_root
) {
5002 ERR("no checksum root\n");
5006 while (le
!= changed_sector_list
) {
5007 UINT64 startaddr
, endaddr
;
5012 ULONG runlength
, index
;
5014 cs
= (changed_sector
*)le
;
5016 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5017 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5018 searchkey
.offset
= cs
->ol
.key
;
5020 // FIXME - create checksum_root if it doesn't exist at all
5022 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5023 if (!NT_SUCCESS(Status
)) { // tree is completely empty
5024 // FIXME - do proper check here that tree is empty
5026 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * cs
->length
, ALLOC_TAG
);
5028 ERR("out of memory\n");
5032 RtlCopyMemory(checksums
, cs
->checksums
, sizeof(UINT32
) * cs
->length
);
5034 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, cs
->ol
.key
, checksums
, sizeof(UINT32
) * cs
->length
, NULL
, rollback
)) {
5035 ERR("insert_tree_item failed\n");
5036 ExFreePool(checksums
);
5043 // FIXME - check entry is TYPE_EXTENT_CSUM?
5045 if (tp
.item
->key
.offset
< cs
->ol
.key
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= cs
->ol
.key
)
5046 startaddr
= tp
.item
->key
.offset
;
5048 startaddr
= cs
->ol
.key
;
5050 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5051 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5052 searchkey
.offset
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5054 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5055 if (!NT_SUCCESS(Status
)) {
5056 ERR("error - find_item returned %08x\n", Status
);
5060 tplen
= tp
.item
->size
/ sizeof(UINT32
);
5062 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
))
5063 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
5065 endaddr
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
5067 TRACE("cs starts at %llx (%x sectors)\n", cs
->ol
.key
, cs
->length
);
5068 TRACE("startaddr = %llx\n", startaddr
);
5069 TRACE("endaddr = %llx\n", endaddr
);
5071 len
= (endaddr
- startaddr
) / Vcb
->superblock
.sector_size
;
5073 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
5075 ERR("out of memory\n");
5079 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
5081 ERR("out of memory\n");
5082 ExFreePool(checksums
);
5086 RtlInitializeBitMap(&bmp
, bmparr
, len
);
5087 RtlSetAllBits(&bmp
);
5089 searchkey
.obj_id
= EXTENT_CSUM_ID
;
5090 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
5091 searchkey
.offset
= cs
->ol
.key
;
5093 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
);
5094 if (!NT_SUCCESS(Status
)) {
5095 ERR("error - find_item returned %08x\n", Status
);
5099 // set bit = free space, cleared bit = allocated sector
5101 // ERR("start loop\n");
5102 while (tp
.item
->key
.offset
< endaddr
) {
5103 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
5104 if (tp
.item
->key
.offset
>= startaddr
) {
5105 if (tp
.item
->size
> 0) {
5106 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, tp
.item
->size
);
5107 RtlClearBits(&bmp
, (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
, tp
.item
->size
/ sizeof(UINT32
));
5110 delete_tree_item(Vcb
, &tp
, rollback
);
5113 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
5118 // ERR("end loop\n");
5121 RtlSetBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5123 RtlCopyMemory(&checksums
[(cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
], cs
->checksums
, cs
->length
* sizeof(UINT32
));
5124 RtlClearBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
5127 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
5129 while (runlength
!= 0) {
5133 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
5134 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
5138 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
5140 ERR("out of memory\n");
5142 ExFreePool(checksums
);
5146 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
5148 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
)) {
5149 ERR("insert_tree_item failed\n");
5152 ExFreePool(checksums
);
5158 } while (runlength
> 0);
5160 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
5164 ExFreePool(checksums
);
5171 while (!IsListEmpty(changed_sector_list
)) {
5172 le
= RemoveHeadList(changed_sector_list
);
5173 cs
= (changed_sector
*)le
;
5176 ExFreePool(cs
->checksums
);
5182 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, LIST_ENTRY
* rollback
) {
5183 LIST_ENTRY changed_sector_list
;
5185 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5188 InitializeListHead(&changed_sector_list
);
5190 // FIXME - convert into inline extent if short enough
5192 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
5193 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), nocsum
? NULL
: &changed_sector_list
, rollback
);
5194 if (!NT_SUCCESS(Status
)) {
5195 ERR("error - excise_extents failed\n");
5199 fcb
->inode_item
.st_size
= end
;
5200 TRACE("setting st_size to %llx\n", end
);
5202 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
5203 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
5204 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
5205 // FIXME - inform cache manager of this
5207 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
5210 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5212 return STATUS_SUCCESS
;
5215 NTSTATUS
extend_file(fcb
* fcb
, file_ref
* fileref
, UINT64 end
, BOOL prealloc
, LIST_ENTRY
* rollback
) {
5216 UINT64 oldalloc
, newalloc
;
5222 TRACE("(%p, %x, %p)\n", fcb
, end
, rollback
);
5225 return stream_set_end_of_file_information(fcb
->Vcb
, end
, fcb
, fileref
, NULL
, FALSE
, rollback
) ;
5227 searchkey
.obj_id
= fcb
->inode
;
5228 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5229 searchkey
.offset
= 0xffffffffffffffff;
5231 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5232 if (!NT_SUCCESS(Status
)) {
5233 ERR("error - find_item returned %08x\n", Status
);
5238 if (tp
.item
->key
.obj_id
== fcb
->inode
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
) {
5239 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
5240 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5242 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5243 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
));
5244 return STATUS_INTERNAL_ERROR
;
5247 oldalloc
= tp
.item
->key
.offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
5248 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
5250 if (cur_inline
&& end
> fcb
->Vcb
->max_inline
) {
5251 LIST_ENTRY changed_sector_list
;
5252 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
5253 UINT64 origlength
, length
;
5256 TRACE("giving inline file proper extents\n");
5258 origlength
= ed
->decoded_size
;
5263 InitializeListHead(&changed_sector_list
);
5265 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5267 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
5269 data
= ExAllocatePoolWithTag(PagedPool
, length
, ALLOC_TAG
);
5271 ERR("could not allocate %llx bytes for data\n", length
);
5272 return STATUS_INSUFFICIENT_RESOURCES
;
5275 if (length
> origlength
)
5276 RtlZeroMemory(data
+ origlength
, length
- origlength
);
5278 RtlCopyMemory(data
, ed
->data
, origlength
);
5280 fcb
->inode_item
.st_blocks
-= origlength
;
5282 Status
= insert_extent(fcb
->Vcb
, fcb
, tp
.item
->key
.offset
, length
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
5283 if (!NT_SUCCESS(Status
)) {
5284 ERR("insert_extent returned %08x\n", Status
);
5289 oldalloc
= tp
.item
->key
.offset
+ length
;
5294 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
5300 if (end
> oldalloc
) {
5301 edsize
= sizeof(EXTENT_DATA
) - 1 + end
- tp
.item
->key
.offset
;
5302 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5305 ERR("out of memory\n");
5306 return STATUS_INSUFFICIENT_RESOURCES
;
5309 RtlZeroMemory(ed
, edsize
);
5310 RtlCopyMemory(ed
, tp
.item
->data
, tp
.item
->size
);
5312 ed
->decoded_size
= end
- tp
.item
->key
.offset
;
5314 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
5316 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
)) {
5317 ERR("error - failed to insert item\n");
5319 return STATUS_INTERNAL_ERROR
;
5323 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
5325 fcb
->inode_item
.st_size
= end
;
5326 TRACE("setting st_size to %llx\n", end
);
5328 fcb
->inode_item
.st_blocks
= end
;
5330 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5332 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5334 if (newalloc
> oldalloc
) {
5336 // FIXME - try and extend previous extent first
5338 Status
= insert_prealloc_extent(fcb
, oldalloc
, newalloc
- oldalloc
, rollback
);
5340 if (!NT_SUCCESS(Status
)) {
5341 ERR("insert_prealloc_extent returned %08x\n", Status
);
5345 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, oldalloc
, newalloc
- oldalloc
, rollback
);
5347 if (!NT_SUCCESS(Status
)) {
5348 ERR("insert_sparse_extent returned %08x\n", Status
);
5354 fcb
->inode_item
.st_size
= end
;
5355 TRACE("setting st_size to %llx\n", end
);
5357 TRACE("newalloc = %llx\n", newalloc
);
5359 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5360 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5363 if (end
> fcb
->Vcb
->max_inline
) {
5364 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
5367 Status
= insert_prealloc_extent(fcb
, 0, newalloc
, rollback
);
5369 if (!NT_SUCCESS(Status
)) {
5370 ERR("insert_prealloc_extent returned %08x\n", Status
);
5374 Status
= insert_sparse_extent(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, 0, newalloc
, rollback
);
5376 if (!NT_SUCCESS(Status
)) {
5377 ERR("insert_sparse_extent returned %08x\n", Status
);
5382 fcb
->inode_item
.st_size
= end
;
5383 TRACE("setting st_size to %llx\n", end
);
5385 TRACE("newalloc = %llx\n", newalloc
);
5387 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
5388 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5393 edsize
= sizeof(EXTENT_DATA
) - 1 + end
;
5394 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
5397 ERR("out of memory\n");
5398 return STATUS_INSUFFICIENT_RESOURCES
;
5401 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
5402 ed
->decoded_size
= end
;
5403 ed
->compression
= BTRFS_COMPRESSION_NONE
;
5404 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
5405 ed
->encoding
= BTRFS_ENCODING_NONE
;
5406 ed
->type
= EXTENT_TYPE_INLINE
;
5408 RtlZeroMemory(ed
->data
, end
);
5410 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed
, edsize
, NULL
, rollback
)) {
5411 ERR("error - failed to insert item\n");
5413 return STATUS_INTERNAL_ERROR
;
5416 fcb
->inode_item
.st_size
= end
;
5417 TRACE("setting st_size to %llx\n", end
);
5419 fcb
->inode_item
.st_blocks
= end
;
5421 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
5426 return STATUS_SUCCESS
;
5429 static UINT64
get_extent_item_refcount(device_extension
* Vcb
, UINT64 address
) {
5436 searchkey
.obj_id
= address
;
5437 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5438 searchkey
.offset
= 0xffffffffffffffff;
5440 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
5441 if (!NT_SUCCESS(Status
)) {
5442 ERR("error - find_item returned %08x\n", Status
);
5446 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5447 ERR("error - could not find EXTENT_ITEM for %llx\n", address
);
5451 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
5452 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
));
5456 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
5462 static BOOL
is_file_prealloc(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
) {
5463 return is_file_prealloc_inode(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, start_data
, end_data
);
5466 static NTSTATUS
do_cow_write(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5469 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, changed_sector_list
, rollback
);
5470 if (!NT_SUCCESS(Status
)) {
5471 ERR("error - excise_extents returned %08x\n", Status
);
5475 Status
= insert_extent(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, changed_sector_list
, rollback
);
5477 if (!NT_SUCCESS(Status
)) {
5478 ERR("error - insert_extent returned %08x\n", Status
);
5482 Status
= STATUS_SUCCESS
;
5488 static NTSTATUS
merge_data_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, LIST_ENTRY
* rollback
) {
5490 traverse_ptr tp
, next_tp
;
5495 searchkey
.obj_id
= fcb
->inode
;
5496 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5497 searchkey
.offset
= start_data
;
5499 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5500 if (!NT_SUCCESS(Status
)) {
5501 ERR("error - find_item returned %08x\n", Status
);
5505 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
) {
5506 ERR("error - EXTENT_DATA not found\n");
5507 return STATUS_INTERNAL_ERROR
;
5510 if (tp
.item
->key
.offset
> 0) {
5511 traverse_ptr tp2
, prev_tp
;
5515 b
= find_prev_item(Vcb
, &tp2
, &prev_tp
, FALSE
);
5518 if (!prev_tp
.item
->ignore
)
5526 if (prev_tp
.item
->key
.obj_id
== fcb
->inode
&& prev_tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA
)
5531 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5532 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5533 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
));
5534 return STATUS_INTERNAL_ERROR
;
5538 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
5543 if (next_tp
.item
->key
.obj_id
!= fcb
->inode
|| next_tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
5544 return STATUS_SUCCESS
;
5546 if (next_tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5547 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp
.item
->key
.obj_id
, next_tp
.item
->key
.obj_type
, next_tp
.item
->key
.offset
, next_tp
.item
->size
, sizeof(EXTENT_DATA
));
5548 return STATUS_INTERNAL_ERROR
;
5551 ned
= (EXTENT_DATA
*)next_tp
.item
->data
;
5552 if ((ned
->type
== EXTENT_TYPE_REGULAR
|| ned
->type
== EXTENT_TYPE_PREALLOC
) && next_tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5553 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", next_tp
.item
->key
.obj_id
, next_tp
.item
->key
.obj_type
, next_tp
.item
->key
.offset
, next_tp
.item
->size
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
5554 return STATUS_INTERNAL_ERROR
;
5557 if (ed
->type
== ned
->type
&& (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
)) {
5558 EXTENT_DATA2
*ed2
, *ned2
;
5560 ed2
= (EXTENT_DATA2
*)ed
->data
;
5561 ned2
= (EXTENT_DATA2
*)ned
->data
;
5563 if (next_tp
.item
->key
.offset
== tp
.item
->key
.offset
+ ed2
->num_bytes
&& ed2
->address
== ned2
->address
&& ed2
->size
== ned2
->size
&& ned2
->offset
== ed2
->offset
+ ed2
->num_bytes
) {
5564 EXTENT_DATA
* buf
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5569 ERR("out of memory\n");
5570 return STATUS_INSUFFICIENT_RESOURCES
;
5573 RtlCopyMemory(buf
, tp
.item
->data
, tp
.item
->size
);
5574 buf
->generation
= Vcb
->superblock
.generation
;
5576 buf2
= (EXTENT_DATA2
*)buf
->data
;
5577 buf2
->num_bytes
+= ned2
->num_bytes
;
5579 delete_tree_item(Vcb
, &tp
, rollback
);
5580 delete_tree_item(Vcb
, &next_tp
, rollback
);
5582 if (!insert_tree_item(Vcb
, fcb
->subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, buf
, tp
.item
->size
, &tp2
, rollback
)) {
5583 ERR("insert_tree_item failed\n");
5585 return STATUS_INTERNAL_ERROR
;
5588 Status
= decrease_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- buf2
->offset
, 1, NULL
, rollback
);
5589 if (!NT_SUCCESS(Status
)) {
5590 ERR("decrease_extent_refcount_data returned %08x\n", Status
);
5605 return STATUS_SUCCESS
;
5608 static NTSTATUS
do_prealloc_write(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, LIST_ENTRY
* changed_sector_list
, LIST_ENTRY
* rollback
) {
5611 traverse_ptr tp
, next_tp
;
5612 BOOL b
, deleted_prealloc
= FALSE
;
5613 UINT64 last_written
= start_data
;
5615 searchkey
.obj_id
= fcb
->inode
;
5616 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5617 searchkey
.offset
= start_data
;
5619 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5620 if (!NT_SUCCESS(Status
)) {
5621 ERR("error - find_item returned %08x\n", Status
);
5625 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
)
5626 return do_cow_write(Vcb
, fcb
, start_data
, end_data
, data
, changed_sector_list
, rollback
);
5629 EXTENT_DATA
* ed
= (EXTENT_DATA
*)tp
.item
->data
;
5630 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5632 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5633 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
));
5634 return STATUS_INTERNAL_ERROR
;
5637 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5638 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
));
5639 return STATUS_INTERNAL_ERROR
;
5642 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
);
5644 if (ed
->type
== EXTENT_TYPE_PREALLOC
) {
5645 if (tp
.item
->key
.offset
> last_written
) {
5646 Status
= do_cow_write(Vcb
, fcb
, last_written
, tp
.item
->key
.offset
, (UINT8
*)data
+ last_written
- start_data
, changed_sector_list
, rollback
);
5648 if (!NT_SUCCESS(Status
)) {
5649 ERR("do_cow_write returned %08x\n", Status
);
5654 last_written
= tp
.item
->key
.offset
;
5657 if (start_data
<= tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ ed2
->num_bytes
) { // replace all
5658 EXTENT_DATA
* ned
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5661 ERR("out of memory\n");
5663 return STATUS_INSUFFICIENT_RESOURCES
;
5666 RtlCopyMemory(ned
, tp
.item
->data
, tp
.item
->size
);
5668 ned
->type
= EXTENT_TYPE_REGULAR
;
5670 delete_tree_item(Vcb
, &tp
, rollback
);
5672 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, tp
.item
->size
, NULL
, rollback
)) {
5673 ERR("insert_tree_item failed\n");
5675 return STATUS_INTERNAL_ERROR
;
5678 Status
= do_write_data(Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ tp
.item
->key
.offset
- start_data
, ed2
->num_bytes
, changed_sector_list
);
5679 if (!NT_SUCCESS(Status
)) {
5680 ERR("do_write_data returned %08x\n", Status
);
5685 deleted_prealloc
= TRUE
;
5687 last_written
= tp
.item
->key
.offset
+ ed2
->num_bytes
;
5688 } else if (start_data
<= tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ ed2
->num_bytes
) { // replace beginning
5689 EXTENT_DATA
*ned
, *nedb
;
5692 ned
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5695 ERR("out of memory\n");
5697 return STATUS_INSUFFICIENT_RESOURCES
;
5700 nedb
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5703 ERR("out of memory\n");
5706 return STATUS_INSUFFICIENT_RESOURCES
;
5709 delete_tree_item(Vcb
, &tp
, rollback
);
5711 RtlCopyMemory(ned
, tp
.item
->data
, tp
.item
->size
);
5713 ned
->type
= EXTENT_TYPE_REGULAR
;
5714 ned2
= (EXTENT_DATA2
*)ned
->data
;
5715 ned2
->num_bytes
= end_data
- tp
.item
->key
.offset
;
5717 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, tp
.item
->size
, NULL
, rollback
)) {
5718 ERR("insert_tree_item failed\n");
5722 return STATUS_INTERNAL_ERROR
;
5725 RtlCopyMemory(nedb
, tp
.item
->data
, tp
.item
->size
);
5726 ned2
= (EXTENT_DATA2
*)nedb
->data
;
5727 ned2
->offset
+= end_data
- tp
.item
->key
.offset
;
5728 ned2
->num_bytes
-= end_data
- tp
.item
->key
.offset
;
5730 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, nedb
, tp
.item
->size
, NULL
, rollback
)) {
5731 ERR("insert_tree_item failed\n");
5734 return STATUS_INTERNAL_ERROR
;
5737 Status
= do_write_data(Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ tp
.item
->key
.offset
- start_data
, end_data
- tp
.item
->key
.offset
, changed_sector_list
);
5738 if (!NT_SUCCESS(Status
)) {
5739 ERR("do_write_data returned %08x\n", Status
);
5744 Status
= increase_extent_refcount_data(Vcb
, ned2
->address
, ned2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- ed2
->offset
, 1, rollback
);
5745 if (!NT_SUCCESS(Status
)) {
5746 ERR("increase_extent_refcount_data returned %08x\n", Status
);
5750 last_written
= end_data
;
5751 } else if (start_data
> tp
.item
->key
.offset
&& end_data
>= tp
.item
->key
.offset
+ ed2
->num_bytes
) { // replace end
5752 EXTENT_DATA
*ned
, *nedb
;
5755 // FIXME - test this
5757 ned
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5760 ERR("out of memory\n");
5762 return STATUS_INSUFFICIENT_RESOURCES
;
5765 nedb
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5768 ERR("out of memory\n");
5771 return STATUS_INSUFFICIENT_RESOURCES
;
5774 delete_tree_item(Vcb
, &tp
, rollback
);
5776 RtlCopyMemory(ned
, tp
.item
->data
, tp
.item
->size
);
5778 ned2
= (EXTENT_DATA2
*)ned
->data
;
5779 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
5781 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, tp
.item
->size
, NULL
, rollback
)) {
5782 ERR("insert_tree_item failed\n");
5786 return STATUS_INTERNAL_ERROR
;
5789 RtlCopyMemory(nedb
, tp
.item
->data
, tp
.item
->size
);
5791 nedb
->type
= EXTENT_TYPE_REGULAR
;
5792 ned2
= (EXTENT_DATA2
*)nedb
->data
;
5793 ned2
->offset
+= start_data
- tp
.item
->key
.offset
;
5794 ned2
->num_bytes
= tp
.item
->key
.offset
+ ed2
->num_bytes
- start_data
;
5796 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start_data
, nedb
, tp
.item
->size
, NULL
, rollback
)) {
5797 ERR("insert_tree_item failed\n");
5800 return STATUS_INTERNAL_ERROR
;
5803 Status
= do_write_data(Vcb
, ed2
->address
+ ned2
->offset
, data
, ned2
->num_bytes
, changed_sector_list
);
5804 if (!NT_SUCCESS(Status
)) {
5805 ERR("do_write_data returned %08x\n", Status
);
5810 Status
= increase_extent_refcount_data(Vcb
, ned2
->address
, ned2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- ed2
->offset
, 1, rollback
);
5811 if (!NT_SUCCESS(Status
)) {
5812 ERR("increase_extent_refcount_data returned %08x\n", Status
);
5817 last_written
= start_data
+ ned2
->num_bytes
;
5818 } else if (start_data
> tp
.item
->key
.offset
&& end_data
< tp
.item
->key
.offset
+ ed2
->num_bytes
) { // replace middle
5819 EXTENT_DATA
*ned
, *nedb
, *nedc
;
5822 // FIXME - test this
5824 ned
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5827 ERR("out of memory\n");
5829 return STATUS_INSUFFICIENT_RESOURCES
;
5832 nedb
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5835 ERR("out of memory\n");
5838 return STATUS_INSUFFICIENT_RESOURCES
;
5841 nedc
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
5844 ERR("out of memory\n");
5848 return STATUS_INSUFFICIENT_RESOURCES
;
5851 delete_tree_item(Vcb
, &tp
, rollback
);
5853 RtlCopyMemory(ned
, tp
.item
->data
, tp
.item
->size
);
5854 RtlCopyMemory(nedb
, tp
.item
->data
, tp
.item
->size
);
5855 RtlCopyMemory(nedc
, tp
.item
->data
, tp
.item
->size
);
5857 ned2
= (EXTENT_DATA2
*)ned
->data
;
5858 ned2
->num_bytes
= start_data
- tp
.item
->key
.offset
;
5860 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, tp
.item
->key
.offset
, ned
, tp
.item
->size
, NULL
, rollback
)) {
5861 ERR("insert_tree_item failed\n");
5866 return STATUS_INTERNAL_ERROR
;
5869 nedb
->type
= EXTENT_TYPE_REGULAR
;
5870 ned2
= (EXTENT_DATA2
*)nedb
->data
;
5871 ned2
->offset
+= start_data
- tp
.item
->key
.offset
;
5872 ned2
->num_bytes
= end_data
- start_data
;
5874 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start_data
, nedb
, tp
.item
->size
, NULL
, rollback
)) {
5875 ERR("insert_tree_item failed\n");
5879 return STATUS_INTERNAL_ERROR
;
5882 ned2
= (EXTENT_DATA2
*)nedc
->data
;
5883 ned2
->offset
+= end_data
- tp
.item
->key
.offset
;
5884 ned2
->num_bytes
-= end_data
- tp
.item
->key
.offset
;
5886 if (!insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, end_data
, nedc
, tp
.item
->size
, NULL
, rollback
)) {
5887 ERR("insert_tree_item failed\n");
5890 return STATUS_INTERNAL_ERROR
;
5893 ned2
= (EXTENT_DATA2
*)nedb
->data
;
5894 Status
= do_write_data(Vcb
, ed2
->address
+ ned2
->offset
, data
, end_data
- start_data
, changed_sector_list
);
5895 if (!NT_SUCCESS(Status
)) {
5896 ERR("do_write_data returned %08x\n", Status
);
5901 Status
= increase_extent_refcount_data(Vcb
, ed2
->address
, ed2
->size
, fcb
->subvol
, fcb
->inode
, tp
.item
->key
.offset
- ed2
->offset
, 2, rollback
);
5902 if (!NT_SUCCESS(Status
)) {
5903 ERR("increase_extent_refcount_data returned %08x\n", Status
);
5907 last_written
= end_data
;
5914 if (tp
.item
->key
.obj_id
> fcb
->inode
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= end_data
)
5919 if (last_written
< end_data
) {
5920 Status
= do_cow_write(Vcb
, fcb
, last_written
, end_data
, (UINT8
*)data
+ last_written
- start_data
, changed_sector_list
, rollback
);
5922 if (!NT_SUCCESS(Status
)) {
5923 ERR("do_cow_write returned %08x\n", Status
);
5928 Status
= merge_data_extents(Vcb
, fcb
, start_data
, end_data
, rollback
);
5929 if (!NT_SUCCESS(Status
)) {
5930 ERR("merge_data_extents returned %08x\n", Status
);
5934 if (deleted_prealloc
&& !is_file_prealloc(fcb
, 0, sector_align(fcb
->inode_item
.st_size
, Vcb
->superblock
.sector_size
)))
5935 fcb
->inode_item
.flags
&= ~BTRFS_INODE_PREALLOC
;
5937 return STATUS_SUCCESS
;
5940 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
) {
5942 traverse_ptr tp
, next_tp
;
5947 UINT64 size
, new_start
, new_end
, last_write
= 0;
5949 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
5951 searchkey
.obj_id
= fcb
->inode
;
5952 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
5953 searchkey
.offset
= start_data
;
5955 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
5956 if (!NT_SUCCESS(Status
)) {
5957 ERR("error - find_item returned %08x\n", Status
);
5961 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
> start_data
) {
5962 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
);
5963 Status
= STATUS_INTERNAL_ERROR
;
5968 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
5969 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
));
5970 Status
= STATUS_INTERNAL_ERROR
;
5974 ed
= (EXTENT_DATA
*)tp
.item
->data
;
5976 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && tp
.item
->size
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5977 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
));
5978 Status
= STATUS_INTERNAL_ERROR
;
5982 eds
= (EXTENT_DATA2
*)&ed
->data
[0];
5984 b
= find_next_item(Vcb
, &tp
, &next_tp
, TRUE
);
5987 case EXTENT_TYPE_REGULAR
:
5989 UINT64 rc
= get_extent_item_refcount(Vcb
, eds
->address
);
5992 ERR("get_extent_item_refcount failed\n");
5993 Status
= STATUS_INTERNAL_ERROR
;
6001 case EXTENT_TYPE_INLINE
:
6005 case EXTENT_TYPE_PREALLOC
:
6006 FIXME("FIXME - handle prealloc extents\n"); // FIXME
6007 Status
= STATUS_NOT_SUPPORTED
;
6011 ERR("error - unknown extent type %x\n", ed
->type
);
6012 Status
= STATUS_NOT_SUPPORTED
;
6016 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
6017 FIXME("FIXME: compression not yet supported\n");
6018 Status
= STATUS_NOT_SUPPORTED
;
6022 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
6023 WARN("encryption not supported\n");
6024 Status
= STATUS_INTERNAL_ERROR
;
6028 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
6029 WARN("other encodings not supported\n");
6030 Status
= STATUS_INTERNAL_ERROR
;
6034 size
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: eds
->num_bytes
;
6036 TRACE("extent: start = %llx, length = %llx\n", tp
.item
->key
.offset
, size
);
6038 new_start
= tp
.item
->key
.offset
< start_data
? start_data
: tp
.item
->key
.offset
;
6039 new_end
= tp
.item
->key
.offset
+ size
> start_data
+ length
? (start_data
+ length
) : (tp
.item
->key
.offset
+ size
);
6041 TRACE("new_start = %llx\n", new_start
);
6042 TRACE("new_end = %llx\n", new_end
);
6045 TRACE("doing COW write\n");
6047 Status
= excise_extents(Vcb
, fcb
, new_start
, new_start
+ new_end
, changed_sector_list
, rollback
);
6049 if (!NT_SUCCESS(Status
)) {
6050 ERR("error - excise_extents returned %08x\n", Status
);
6054 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
6056 if (!NT_SUCCESS(Status
)) {
6057 ERR("error - insert_extent returned %08x\n", Status
);
6063 writeaddr
= eds
->address
+ eds
->offset
+ new_start
- tp
.item
->key
.offset
;
6064 TRACE("doing non-COW write to %llx\n", writeaddr
);
6066 Status
= write_data(Vcb
, writeaddr
, (UINT8
*)data
+ new_start
- start_data
, new_end
- new_start
);
6068 if (!NT_SUCCESS(Status
)) {
6069 ERR("error - write_data returned %08x\n", Status
);
6073 if (changed_sector_list
) {
6077 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
6079 ERR("out of memory\n");
6080 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6084 sc
->ol
.key
= writeaddr
;
6085 sc
->length
= (new_end
- new_start
) / Vcb
->superblock
.sector_size
;
6086 sc
->deleted
= FALSE
;
6088 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
6089 if (!sc
->checksums
) {
6090 ERR("out of memory\n");
6092 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6096 for (i
= 0; i
< sc
->length
; i
++) {
6097 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ new_start
- start_data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
6100 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
6104 last_write
= new_end
;
6109 if (tp
.item
->key
.obj_id
!= fcb
->inode
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_DATA
|| tp
.item
->key
.offset
>= start_data
+ length
)
6114 if (last_write
< start_data
+ length
) {
6115 new_start
= last_write
;
6116 new_end
= start_data
+ length
;
6118 TRACE("new_start = %llx\n", new_start
);
6119 TRACE("new_end = %llx\n", new_end
);
6121 Status
= insert_extent(Vcb
, fcb
, new_start
, new_end
- new_start
, (UINT8
*)data
+ new_start
- start_data
, changed_sector_list
, rollback
);
6123 if (!NT_SUCCESS(Status
)) {
6124 ERR("error - insert_extent returned %08x\n", Status
);
6129 Status
= STATUS_SUCCESS
;
6136 #ifdef DEBUG_PARANOID
6137 static void print_loaded_trees(tree
* t
, int spaces
) {
6142 for (i
= 0; i
< spaces
; i
++) {
6148 ERR("%s(not loaded)\n", pref
);
6152 le
= t
->itemlist
.Flink
;
6153 while (le
!= &t
->itemlist
) {
6154 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
6156 ERR("%s%llx,%x,%llx ignore=%s\n", pref
, td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->ignore
? "TRUE" : "FALSE");
6158 if (t
->header
.level
> 0) {
6159 print_loaded_trees(td
->treeholder
.tree
, spaces
+1);
6166 static void check_extents_consistent(device_extension
* Vcb
, fcb
* fcb
) {
6168 traverse_ptr tp
, next_tp
;
6169 UINT64 length
, oldlength
, lastoff
, alloc
;
6174 if (fcb
->ads
|| fcb
->inode_item
.st_size
== 0 || fcb
->deleted
)
6177 TRACE("inode = %llx, subvol = %llx\n", fcb
->inode
, fcb
->subvol
->id
);
6179 searchkey
.obj_id
= fcb
->inode
;
6180 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
6181 searchkey
.offset
= 0;
6183 Status
= find_item(Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6184 if (!NT_SUCCESS(Status
)) {
6185 ERR("error - find_item returned %08x\n", Status
);
6189 if (keycmp(&searchkey
, &tp
.item
->key
)) {
6190 ERR("could not find EXTENT_DATA at offset 0\n");
6194 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6195 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
));
6199 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6200 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6202 length
= oldlength
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6203 lastoff
= tp
.item
->key
.offset
;
6205 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6208 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6212 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
)) {
6213 if (next_tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| next_tp
.item
->key
.obj_type
!= searchkey
.obj_type
)
6218 if (tp
.item
->size
< sizeof(EXTENT_DATA
)) {
6219 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
));
6223 ed
= (EXTENT_DATA
*)tp
.item
->data
;
6224 ed2
= (EXTENT_DATA2
*)&ed
->data
[0];
6226 length
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
6228 TRACE("(%llx,%x,%llx) length = %llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, length
);
6230 if (tp
.item
->key
.offset
!= lastoff
+ oldlength
) {
6231 ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb
->subvol
->id
, fcb
->inode
, tp
.item
->key
.offset
, lastoff
+ oldlength
);
6235 if (ed
->type
!= EXTENT_TYPE_REGULAR
|| ed2
->address
!= 0) {
6240 lastoff
= tp
.item
->key
.offset
;
6243 if (alloc
!= fcb
->inode_item
.st_blocks
) {
6244 ERR("allocation size was %llx, expected %llx\n", alloc
, fcb
->inode_item
.st_blocks
);
6248 // if (fcb->inode_item.st_blocks != lastoff + oldlength) {
6249 // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
6256 if (fcb
->subvol
->treeholder
.tree
)
6257 print_loaded_trees(fcb
->subvol
->treeholder
.tree
, 0);
6262 // static void check_extent_tree_consistent(device_extension* Vcb) {
6264 // traverse_ptr tp, next_tp;
6266 // BOOL b, inconsistency;
6268 // searchkey.obj_id = 0;
6269 // searchkey.obj_type = 0;
6270 // searchkey.offset = 0;
6272 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6273 // ERR("error - could not find any entries in extent_root\n");
6278 // inconsistency = FALSE;
6281 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6282 // // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6284 // if (tp.item->key.obj_id < lastaddr) {
6285 // // ERR("inconsistency!\n");
6287 // inconsistency = TRUE;
6290 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6293 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6295 // free_traverse_ptr(&tp);
6300 // free_traverse_ptr(&tp);
6302 // if (!inconsistency)
6305 // ERR("Inconsistency detected:\n");
6307 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
6308 // ERR("error - could not find any entries in extent_root\n");
6313 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
6314 // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
6316 // if (tp.item->key.obj_id < lastaddr) {
6317 // ERR("inconsistency!\n");
6320 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
6323 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
6325 // free_traverse_ptr(&tp);
6330 // free_traverse_ptr(&tp);
6336 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOL paging_io
, BOOL no_cache
, LIST_ENTRY
* rollback
) {
6337 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6338 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6342 UINT64 newlength
, start_data
, end_data
;
6346 LIST_ENTRY changed_sector_list
;
6347 INODE_ITEM
*ii
, *origii
;
6348 BOOL changed_length
= FALSE
, nocsum
, nocow
/*, lazy_writer = FALSE, write_eof = FALSE*/;
6355 BOOL paging_lock
= FALSE
;
6357 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
6360 WARN("returning success for zero-length write\n");
6361 return STATUS_SUCCESS
;
6365 ERR("error - FileObject was NULL\n");
6366 return STATUS_ACCESS_DENIED
;
6369 fcb
= FileObject
->FsContext
;
6370 ccb
= FileObject
->FsContext2
;
6371 fileref
= ccb
? ccb
->fileref
: NULL
;
6373 if (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
6374 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
);
6375 return STATUS_INVALID_DEVICE_REQUEST
;
6378 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1) {
6379 offset
= fcb
->Header
.FileSize
;
6380 // write_eof = TRUE;
6383 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
6385 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
6386 IO_STATUS_BLOCK iosb
;
6388 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
6390 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
6392 if (!NT_SUCCESS(iosb
.Status
)) {
6393 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6394 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
6400 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
6404 ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, TRUE
);
6408 nocsum
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
6409 nocow
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
;
6411 newlength
= fcb
->ads
? fcb
->adssize
: fcb
->inode_item
.st_size
;
6416 TRACE("newlength = %llx\n", newlength
);
6418 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
6419 // ERR("lazy writer on the TV\n");
6420 // lazy_writer = TRUE;
6423 if (offset
.QuadPart
+ *length
> newlength
) {
6425 if (offset
.QuadPart
>= newlength
) {
6426 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, offset
.QuadPart
, *length
);
6427 TRACE("filename %S\n", file_desc(FileObject
));
6428 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
6429 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
6430 Status
= STATUS_SUCCESS
;
6434 *length
= newlength
- offset
.QuadPart
;
6436 newlength
= offset
.QuadPart
+ *length
;
6437 changed_length
= TRUE
;
6439 TRACE("extending length to %llx\n", newlength
);
6443 make_inline
= fcb
->ads
? FALSE
: newlength
<= fcb
->Vcb
->max_inline
;
6445 if (changed_length
) {
6446 if (newlength
> fcb
->Header
.AllocationSize
.QuadPart
) {
6447 Status
= extend_file(fcb
, fileref
, newlength
, FALSE
, rollback
);
6448 if (!NT_SUCCESS(Status
)) {
6449 ERR("extend_file returned %08x\n", Status
);
6452 } else if (fcb
->ads
)
6453 fcb
->adssize
= newlength
;
6455 fcb
->inode_item
.st_size
= newlength
;
6457 fcb
->Header
.FileSize
.QuadPart
= newlength
;
6458 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6460 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
6461 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
6462 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
6468 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
6471 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6472 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6473 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6475 if (!FileObject
->PrivateCacheMap
) {
6476 TRACE("calling CcInitializeCacheMap...\n");
6477 CcInitializeCacheMap(FileObject
, &ccfs
, FALSE
, cache_callbacks
, FileObject
);
6479 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
6481 CcSetFileSizes(FileObject
, &ccfs
);
6485 // FIXME - uncomment this when async is working
6486 // wait = IoIsOperationSynchronous(Irp) ? TRUE : FALSE;
6489 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
6490 CcPrepareMdlWrite(FileObject
, &offset
, *length
, &Irp
->MdlAddress
, &Irp
->IoStatus
);
6492 Status
= Irp
->IoStatus
.Status
;
6495 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
);
6496 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
6497 TRACE("CcCopyWrite failed.\n");
6499 IoMarkIrpPending(Irp
);
6500 Status
= STATUS_PENDING
;
6503 TRACE("CcCopyWrite finished\n");
6506 Status
= STATUS_SUCCESS
;
6515 if (!get_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, &data
, &datalen
)) {
6516 ERR("get_xattr failed\n");
6517 Status
= STATUS_INTERNAL_ERROR
;
6521 if (changed_length
) {
6522 // find maximum length of xattr
6523 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
6525 searchkey
.obj_id
= fcb
->inode
;
6526 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
6527 searchkey
.offset
= fcb
->adshash
;
6529 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6530 if (!NT_SUCCESS(Status
)) {
6531 ERR("error - find_item returned %08x\n", Status
);
6535 if (keycmp(&tp
.item
->key
, &searchkey
)) {
6536 ERR("error - could not find key for xattr\n");
6537 Status
= STATUS_INTERNAL_ERROR
;
6541 if (tp
.item
->size
< datalen
) {
6542 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
);
6543 Status
= STATUS_INTERNAL_ERROR
;
6547 maxlen
-= tp
.item
->size
- datalen
; // subtract XATTR_ITEM overhead
6549 if (newlength
> maxlen
) {
6550 ERR("error - xattr too long (%llu > %u)\n", newlength
, maxlen
);
6551 Status
= STATUS_DISK_FULL
;
6555 fcb
->adssize
= newlength
;
6557 data2
= ExAllocatePoolWithTag(PagedPool
, newlength
, ALLOC_TAG
);
6559 ERR("out of memory\n");
6560 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6564 RtlCopyMemory(data2
, data
, datalen
);
6566 if (offset
.QuadPart
> datalen
)
6567 RtlZeroMemory(&data2
[datalen
], offset
.QuadPart
- datalen
);
6572 RtlCopyMemory(&data2
[offset
.QuadPart
], buf
, *length
);
6574 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, data2
, newlength
, rollback
);
6575 if (!NT_SUCCESS(Status
)) {
6576 ERR("set_xattr returned %08x\n", Status
);
6580 if (data
) ExFreePool(data
);
6581 if (data2
!= data
) ExFreePool(data2
);
6583 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6587 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
6588 bufhead
= sizeof(EXTENT_DATA
) - 1;
6590 start_data
= offset
.QuadPart
& ~(fcb
->Vcb
->superblock
.sector_size
- 1);
6591 end_data
= sector_align(offset
.QuadPart
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
6595 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
6596 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
6598 data
= ExAllocatePoolWithTag(PagedPool
, end_data
- start_data
+ bufhead
, ALLOC_TAG
);
6600 ERR("out of memory\n");
6601 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6605 RtlZeroMemory(data
+ bufhead
, end_data
- start_data
);
6607 TRACE("start_data = %llx\n", start_data
);
6608 TRACE("end_data = %llx\n", end_data
);
6610 if (offset
.QuadPart
> start_data
|| offset
.QuadPart
+ *length
< end_data
) {
6611 if (changed_length
) {
6612 if (fcb
->inode_item
.st_size
> start_data
)
6613 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
);
6615 Status
= STATUS_SUCCESS
;
6617 Status
= read_file(Vcb
, fcb
->subvol
, fcb
->inode
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
);
6619 if (!NT_SUCCESS(Status
)) {
6620 ERR("read_file returned %08x\n", Status
);
6626 RtlCopyMemory(data
+ bufhead
+ offset
.QuadPart
- start_data
, buf
, *length
);
6629 InitializeListHead(&changed_sector_list
);
6632 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6633 if (!NT_SUCCESS(Status
)) {
6634 ERR("error - excise_extents returned %08x\n", Status
);
6639 ed2
= (EXTENT_DATA
*)data
;
6640 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
6641 ed2
->decoded_size
= newlength
;
6642 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
6643 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
6644 ed2
->encoding
= BTRFS_ENCODING_NONE
;
6645 ed2
->type
= EXTENT_TYPE_INLINE
;
6647 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, ed2
, sizeof(EXTENT_DATA
) - 1 + newlength
, NULL
, rollback
);
6649 fcb
->inode_item
.st_blocks
+= newlength
;
6650 } else if (!nocow
) {
6651 if (is_file_prealloc(fcb
, start_data
, end_data
)) {
6652 Status
= do_prealloc_write(fcb
->Vcb
, fcb
, start_data
, end_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6654 if (!NT_SUCCESS(Status
)) {
6655 ERR("error - do_prealloc_write returned %08x\n", Status
);
6660 Status
= do_cow_write(fcb
->Vcb
, fcb
, start_data
, end_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6662 if (!NT_SUCCESS(Status
)) {
6663 ERR("error - do_cow_write returned %08x\n", Status
);
6671 Status
= do_nocow_write(fcb
->Vcb
, fcb
, start_data
, end_data
- start_data
, data
, nocsum
? NULL
: &changed_sector_list
, rollback
);
6673 if (!NT_SUCCESS(Status
)) {
6674 ERR("error - do_nocow_write returned %08x\n", Status
);
6683 KeQuerySystemTime(&time
);
6684 win_time_to_unix(time
, &now
);
6686 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
6689 // if (!FileObject->PrivateCacheMap) {
6690 // CC_FILE_SIZES ccfs;
6692 // ccfs.AllocationSize = fcb->Header.AllocationSize;
6693 // ccfs.FileSize = fcb->Header.FileSize;
6694 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
6696 // TRACE("calling CcInitializeCacheMap...\n");
6697 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
6699 // changed_length = FALSE;
6704 if (fileref
&& fileref
->parent
)
6705 origii
= &fileref
->parent
->fcb
->inode_item
;
6707 ERR("no parent fcb found for stream\n");
6708 Status
= STATUS_INTERNAL_ERROR
;
6712 origii
= &fcb
->inode_item
;
6714 origii
->transid
= Vcb
->superblock
.generation
;
6716 origii
->st_ctime
= now
;
6719 TRACE("setting st_size to %llx\n", newlength
);
6720 origii
->st_size
= newlength
;
6721 origii
->st_mtime
= now
;
6724 searchkey
.obj_id
= fcb
->inode
;
6725 searchkey
.obj_type
= TYPE_INODE_ITEM
;
6726 searchkey
.offset
= 0;
6728 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
6729 if (!NT_SUCCESS(Status
)) {
6730 ERR("error - find_item returned %08x\n", Status
);
6734 if (!keycmp(&tp
.item
->key
, &searchkey
))
6735 delete_tree_item(Vcb
, &tp
, rollback
);
6737 WARN("couldn't find existing INODE_ITEM\n");
6739 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
6741 ERR("out of memory\n");
6742 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6746 RtlCopyMemory(ii
, origii
, sizeof(INODE_ITEM
));
6747 insert_tree_item(Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
6749 // FIXME - update inode_item of open FCBs pointing to the same inode (i.e. hardlinked files)
6752 update_checksum_tree(Vcb
, &changed_sector_list
, rollback
);
6754 if (changed_length
) {
6757 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
6758 ccfs
.FileSize
= fcb
->Header
.FileSize
;
6759 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
6761 CcSetFileSizes(FileObject
, &ccfs
);
6764 // FIXME - make sure this still called if STATUS_PENDING and async
6766 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
6767 // ERR("CcCopyWrite failed.\n");
6771 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
6772 fcb
->subvol
->root_item
.ctime
= now
;
6774 Status
= STATUS_SUCCESS
;
6777 if (FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
6778 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6779 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
6780 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
6784 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
6789 NTSTATUS
write_file(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
6790 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
6791 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
6794 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
6795 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
6796 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
6797 BOOL locked
= FALSE
;
6798 // LARGE_INTEGER freq, time1, time2;
6799 LIST_ENTRY rollback
;
6801 InitializeListHead(&rollback
);
6804 return STATUS_MEDIA_WRITE_PROTECTED
;
6806 if (fcb
&& fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
)
6807 return STATUS_ACCESS_DENIED
;
6809 // time1 = KeQueryPerformanceCounter(&freq);
6813 Irp
->IoStatus
.Information
= 0;
6815 TRACE("offset = %llx\n", offset
.QuadPart
);
6816 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
6818 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
6819 buf
= map_user_buffer(Irp
);
6821 if (Irp
->MdlAddress
&& !buf
) {
6822 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
6823 Status
= STATUS_INSUFFICIENT_RESOURCES
;
6827 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
6829 TRACE("buf = %p\n", buf
);
6831 if (Irp
->Flags
& IRP_NOCACHE
) {
6832 acquire_tree_lock(Vcb
, TRUE
);
6836 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
6837 WARN("tried to write to locked region\n");
6838 Status
= STATUS_FILE_LOCK_CONFLICT
;
6842 // ERR("Irp->Flags = %x\n", Irp->Flags);
6843 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
, &rollback
);
6844 if (!NT_SUCCESS(Status
)) {
6845 if (Status
!= STATUS_PENDING
)
6846 ERR("write_file2 returned %08x\n", Status
);
6851 Status
= consider_write(Vcb
);
6853 if (NT_SUCCESS(Status
)) {
6854 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
6856 #ifdef DEBUG_PARANOID
6858 check_extents_consistent(Vcb
, FileObject
->FsContext
); // TESTING
6860 // check_extent_tree_consistent(Vcb);
6866 if (NT_SUCCESS(Status
))
6867 clear_rollback(&rollback
);
6869 do_rollback(Vcb
, &rollback
);
6871 release_tree_lock(Vcb
, TRUE
);
6874 // time2 = KeQueryPerformanceCounter(NULL);
6876 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);