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))
22 // #define DEBUG_WRITE_LOOPS
39 } EXTENT_ITEM_SKINNY_METADATA
;
46 LIST_ENTRY list_entry
;
49 static NTSTATUS
create_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
, LIST_ENTRY
* rollback
);
51 static BOOL
insert_tree_item_batch(LIST_ENTRY
* batchlist
, device_extension
* Vcb
, root
* r
, UINT64 objid
, UINT64 objtype
, UINT64 offset
,
52 void* data
, UINT16 datalen
, enum batch_operation operation
, PIRP Irp
, LIST_ENTRY
* rollback
);
54 static NTSTATUS STDCALL
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
55 write_context
* context
= conptr
;
57 context
->iosb
= Irp
->IoStatus
;
58 KeSetEvent(&context
->Event
, 0, FALSE
);
60 // return STATUS_SUCCESS;
61 return STATUS_MORE_PROCESSING_REQUIRED
;
64 NTSTATUS STDCALL
write_data_phys(PDEVICE_OBJECT device
, UINT64 address
, void* data
, UINT32 length
) {
68 PIO_STACK_LOCATION IrpSp
;
69 write_context
* context
= NULL
;
71 TRACE("(%p, %llx, %p, %x)\n", device
, address
, data
, length
);
73 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_context
), ALLOC_TAG
);
75 ERR("out of memory\n");
76 return STATUS_INSUFFICIENT_RESOURCES
;
79 RtlZeroMemory(context
, sizeof(write_context
));
81 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
83 offset
.QuadPart
= address
;
85 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
87 Irp
= IoAllocateIrp(device
->StackSize
, FALSE
);
90 ERR("IoAllocateIrp failed\n");
91 Status
= STATUS_INTERNAL_ERROR
;
95 IrpSp
= IoGetNextIrpStackLocation(Irp
);
96 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
98 if (device
->Flags
& DO_BUFFERED_IO
) {
99 Irp
->AssociatedIrp
.SystemBuffer
= data
;
101 Irp
->Flags
= IRP_BUFFERED_IO
;
102 } else if (device
->Flags
& DO_DIRECT_IO
) {
103 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
104 if (!Irp
->MdlAddress
) {
105 DbgPrint("IoAllocateMdl failed\n");
109 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
111 Irp
->UserBuffer
= data
;
114 IrpSp
->Parameters
.Write
.Length
= length
;
115 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
117 Irp
->UserIosb
= &context
->iosb
;
119 Irp
->UserEvent
= &context
->Event
;
121 IoSetCompletionRoutine(Irp
, write_completion
, context
, TRUE
, TRUE
, TRUE
);
123 Status
= IoCallDriver(device
, Irp
);
125 if (Status
== STATUS_PENDING
) {
126 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
127 Status
= context
->iosb
.Status
;
130 if (!NT_SUCCESS(Status
)) {
131 ERR("IoCallDriver returned %08x\n", Status
);
134 if (device
->Flags
& DO_DIRECT_IO
) {
135 MmUnlockPages(Irp
->MdlAddress
);
136 IoFreeMdl(Irp
->MdlAddress
);
149 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
150 // FIXME - loop through c->deleting and do TRIM if device supports it
151 // FIXME - also find way of doing TRIM of dropped chunks
153 while (!IsListEmpty(&c
->deleting
)) {
154 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
156 RemoveEntryList(&s
->list_entry
);
161 static void clean_space_cache(device_extension
* Vcb
) {
164 TRACE("(%p)\n", Vcb
);
166 while (!IsListEmpty(&Vcb
->chunks_changed
)) {
167 c
= CONTAINING_RECORD(Vcb
->chunks_changed
.Flink
, chunk
, list_entry_changed
);
169 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
171 clean_space_cache_chunk(Vcb
, c
);
172 RemoveEntryList(&c
->list_entry_changed
);
173 c
->list_entry_changed
.Flink
= NULL
;
175 ExReleaseResourceLite(&c
->lock
);
179 static BOOL
trees_consistent(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
180 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
183 le
= Vcb
->trees
.Flink
;
184 while (le
!= &Vcb
->trees
) {
185 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
188 if (t
->header
.num_items
== 0 && t
->parent
) {
189 #ifdef DEBUG_WRITE_LOOPS
190 ERR("empty tree found, looping again\n");
195 if (t
->size
> maxsize
) {
196 #ifdef DEBUG_WRITE_LOOPS
197 ERR("overlarge tree found (%u > %u), looping again\n", t
->size
, maxsize
);
202 if (!t
->has_new_address
) {
203 #ifdef DEBUG_WRITE_LOOPS
204 ERR("tree found without new address, looping again\n");
216 static NTSTATUS
add_parents(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
220 for (level
= 0; level
<= 255; level
++) {
221 BOOL nothing_found
= TRUE
;
223 TRACE("level = %u\n", level
);
225 le
= Vcb
->trees
.Flink
;
226 while (le
!= &Vcb
->trees
) {
227 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
229 if (t
->write
&& t
->header
.level
== level
) {
230 TRACE("tree %p: root = %llx, level = %x, parent = %p\n", t
, t
->header
.tree_id
, t
->header
.level
, t
->parent
);
232 nothing_found
= FALSE
;
235 if (!t
->parent
->write
)
236 TRACE("adding tree %p (level %x)\n", t
->parent
, t
->header
.level
);
238 t
->parent
->write
= TRUE
;
239 } else if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
244 searchkey
.obj_id
= t
->root
->id
;
245 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
246 searchkey
.offset
= 0xffffffffffffffff;
248 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
249 if (!NT_SUCCESS(Status
)) {
250 ERR("error - find_item returned %08x\n", Status
);
254 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
255 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
256 return STATUS_INTERNAL_ERROR
;
259 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
260 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
263 ERR("out of memory\n");
264 return STATUS_INSUFFICIENT_RESOURCES
;
267 RtlCopyMemory(ri
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
269 delete_tree_item(Vcb
, &tp
, rollback
);
271 if (!insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
, rollback
)) {
272 ERR("insert_tree_item failed\n");
273 return STATUS_INTERNAL_ERROR
;
286 return STATUS_SUCCESS
;
289 static void add_parents_to_cache(device_extension
* Vcb
, tree
* t
) {
296 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64 address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
297 EXTENT_ITEM_SKINNY_METADATA
* eism
;
298 traverse_ptr insert_tp
;
300 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
302 ERR("out of memory\n");
306 eism
->ei
.refcount
= 1;
307 eism
->ei
.generation
= Vcb
->superblock
.generation
;
308 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
309 eism
->type
= TYPE_TREE_BLOCK_REF
;
310 eism
->tbr
.offset
= root_id
;
312 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, Irp
, rollback
)) {
313 ERR("insert_tree_item failed\n");
318 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
320 space_list_subtract(Vcb
, c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
322 ExReleaseResourceLite(&c
->lock
);
324 add_parents_to_cache(Vcb
, insert_tp
.tree
);
329 static BOOL
insert_tree_extent(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64
* new_address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
331 EXTENT_ITEM_TREE2
* eit2
;
332 traverse_ptr insert_tp
;
334 TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb
, level
, root_id
, c
, new_address
, rollback
);
336 if (!find_address_in_chunk(Vcb
, c
, Vcb
->superblock
.node_size
, &address
))
339 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
340 BOOL b
= insert_tree_extent_skinny(Vcb
, level
, root_id
, c
, address
, Irp
, rollback
);
343 *new_address
= address
;
348 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
350 ERR("out of memory\n");
354 eit2
->eit
.extent_item
.refcount
= 1;
355 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
356 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
357 // eit2->eit.firstitem = wt->firstitem;
358 eit2
->eit
.level
= level
;
359 eit2
->type
= TYPE_TREE_BLOCK_REF
;
360 eit2
->tbr
.offset
= root_id
;
362 // #ifdef DEBUG_PARANOID
363 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
364 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
365 // 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);
370 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, Irp
, rollback
)) {
371 ERR("insert_tree_item failed\n");
376 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
378 space_list_subtract(Vcb
, c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
380 ExReleaseResourceLite(&c
->lock
);
382 add_parents_to_cache(Vcb
, insert_tp
.tree
);
384 *new_address
= address
;
389 NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
390 chunk
*origchunk
= NULL
, *c
;
392 UINT64 flags
= t
->flags
, addr
;
395 if (t
->root
->id
== BTRFS_ROOT_CHUNK
)
396 flags
= BLOCK_FLAG_SYSTEM
| BLOCK_FLAG_DUPLICATE
;
397 else if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
)
398 flags
= BLOCK_FLAG_DATA
| BLOCK_FLAG_METADATA
;
400 flags
= BLOCK_FLAG_METADATA
| BLOCK_FLAG_DUPLICATE
;
403 // TRACE("flags = %x\n", (UINT32)wt->flags);
405 // if (!chunk_test) { // TESTING
406 // if ((c = alloc_chunk(Vcb, flags))) {
407 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
408 // if (insert_tree_extent(Vcb, t, c)) {
409 // chunk_test = TRUE;
410 // return STATUS_SUCCESS;
416 if (t
->has_address
) {
417 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
419 if (!origchunk
->readonly
&& insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, origchunk
, &addr
, Irp
, rollback
)) {
420 t
->new_address
= addr
;
421 t
->has_new_address
= TRUE
;
422 return STATUS_SUCCESS
;
426 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
428 le
= Vcb
->chunks
.Flink
;
429 while (le
!= &Vcb
->chunks
) {
430 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
433 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
435 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
436 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, c
, &addr
, Irp
, rollback
)) {
437 ExReleaseResourceLite(&c
->lock
);
438 ExReleaseResourceLite(&Vcb
->chunk_lock
);
439 t
->new_address
= addr
;
440 t
->has_new_address
= TRUE
;
441 return STATUS_SUCCESS
;
445 ExReleaseResourceLite(&c
->lock
);
451 // allocate new chunk if necessary
452 if ((c
= alloc_chunk(Vcb
, flags
))) {
453 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
455 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
456 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, c
, &addr
, Irp
, rollback
)) {
457 ExReleaseResourceLite(&c
->lock
);
458 ExReleaseResourceLite(&Vcb
->chunk_lock
);
459 t
->new_address
= addr
;
460 t
->has_new_address
= TRUE
;
461 return STATUS_SUCCESS
;
465 ExReleaseResourceLite(&c
->lock
);
468 ExReleaseResourceLite(&Vcb
->chunk_lock
);
470 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
472 return STATUS_DISK_FULL
;
476 // static void check_tree_num_items(tree* t) {
480 // le2 = t->itemlist.Flink;
482 // while (le2 != &t->itemlist) {
483 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
489 // if (t->header.num_items != ni) {
490 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
493 // ERR("tree %p okay\n", t);
497 // static void check_trees_num_items(LIST_ENTRY* tc) {
498 // LIST_ENTRY* le = tc->Flink;
499 // while (le != tc) {
500 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
502 // check_tree_num_items(tc2->tree);
508 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
512 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
514 rc
= get_extent_refcount(Vcb
, address
, Vcb
->superblock
.node_size
, Irp
);
516 ERR("error - refcount for extent %llx was 0\n", address
);
517 return STATUS_INTERNAL_ERROR
;
521 root
= t
->parent
->header
.tree_id
;
523 root
= t
->header
.tree_id
;
525 Status
= decrease_extent_refcount_tree(Vcb
, address
, Vcb
->superblock
.node_size
, root
, t
->header
.level
, Irp
, rollback
);
526 if (!NT_SUCCESS(Status
)) {
527 ERR("decrease_extent_refcount_tree returned %08x\n", Status
);
532 chunk
* c
= get_chunk_from_address(Vcb
, address
);
535 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
537 decrease_chunk_usage(c
, Vcb
->superblock
.node_size
);
539 space_list_add(Vcb
, c
, TRUE
, address
, Vcb
->superblock
.node_size
, rollback
);
541 ExReleaseResourceLite(&c
->lock
);
543 ERR("could not find chunk for address %llx\n", address
);
546 return STATUS_SUCCESS
;
549 static NTSTATUS
add_changed_extent_ref_edr(changed_extent
* ce
, EXTENT_DATA_REF
* edr
, BOOL old
) {
550 LIST_ENTRY
*le2
, *list
;
551 changed_extent_ref
* cer
;
553 list
= old
? &ce
->old_refs
: &ce
->refs
;
556 while (le2
!= list
) {
557 cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
559 if (cer
->type
== TYPE_EXTENT_DATA_REF
&& cer
->edr
.root
== edr
->root
&& cer
->edr
.objid
== edr
->objid
&& cer
->edr
.offset
== edr
->offset
) {
560 cer
->edr
.count
+= edr
->count
;
567 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
569 ERR("out of memory\n");
570 return STATUS_INSUFFICIENT_RESOURCES
;
573 cer
->type
= TYPE_EXTENT_DATA_REF
;
574 RtlCopyMemory(&cer
->edr
, edr
, sizeof(EXTENT_DATA_REF
));
575 InsertTailList(list
, &cer
->list_entry
);
579 ce
->old_count
+= edr
->count
;
581 ce
->count
+= edr
->count
;
583 return STATUS_SUCCESS
;
586 static NTSTATUS
add_changed_extent_ref_sdr(changed_extent
* ce
, SHARED_DATA_REF
* sdr
, BOOL old
) {
587 LIST_ENTRY
*le2
, *list
;
588 changed_extent_ref
* cer
;
590 list
= old
? &ce
->old_refs
: &ce
->refs
;
593 while (le2
!= list
) {
594 cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
596 if (cer
->type
== TYPE_SHARED_DATA_REF
&& cer
->sdr
.offset
== sdr
->offset
) {
597 cer
->sdr
.count
+= sdr
->count
;
604 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
606 ERR("out of memory\n");
607 return STATUS_INSUFFICIENT_RESOURCES
;
610 cer
->type
= TYPE_SHARED_DATA_REF
;
611 RtlCopyMemory(&cer
->sdr
, sdr
, sizeof(SHARED_DATA_REF
));
612 InsertTailList(list
, &cer
->list_entry
);
616 ce
->old_count
+= sdr
->count
;
618 ce
->count
+= sdr
->count
;
620 return STATUS_SUCCESS
;
623 static BOOL
shared_tree_is_unique(device_extension
* Vcb
, tree
* t
, PIRP Irp
) {
628 searchkey
.obj_id
= t
->header
.address
;
629 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
630 searchkey
.offset
= 0xffffffffffffffff;
632 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
633 if (!NT_SUCCESS(Status
)) {
634 ERR("error - find_item returned %08x\n", Status
);
638 if (tp
.item
->key
.obj_id
== t
->header
.address
&& (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
|| tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
))
644 static NTSTATUS
update_tree_extents(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
646 UINT64 rc
= get_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, Irp
);
647 UINT64 flags
= get_extent_flags(Vcb
, t
->header
.address
, Irp
);
650 ERR("refcount for extent %llx was 0\n", t
->header
.address
);
651 return STATUS_INTERNAL_ERROR
;
654 if (flags
& EXTENT_ITEM_SHARED_BACKREFS
|| t
->header
.flags
& HEADER_FLAG_SHARED_BACKREF
|| !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
656 BOOL unique
= rc
> 1 ? FALSE
: (t
->parent
? shared_tree_is_unique(Vcb
, t
->parent
, Irp
) : FALSE
);
658 if (t
->header
.level
== 0) {
661 le
= t
->itemlist
.Flink
;
662 while (le
!= &t
->itemlist
) {
663 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
665 if (!td
->inserted
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
666 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
668 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
669 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
673 changed_extent
* ce
= NULL
;
674 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
679 le2
= c
->changed_extents
.Flink
;
680 while (le2
!= &c
->changed_extents
) {
681 changed_extent
* ce2
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
683 if (ce2
->address
== ed2
->address
) {
692 edr
.root
= t
->root
->id
;
693 edr
.objid
= td
->key
.obj_id
;
694 edr
.offset
= td
->key
.offset
- ed2
->offset
;
698 Status
= add_changed_extent_ref_edr(ce
, &edr
, TRUE
);
699 if (!NT_SUCCESS(Status
)) {
700 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
704 Status
= add_changed_extent_ref_edr(ce
, &edr
, FALSE
);
705 if (!NT_SUCCESS(Status
)) {
706 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
711 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, Irp
, rollback
);
712 if (!NT_SUCCESS(Status
)) {
713 ERR("increase_extent_refcount returned %08x\n", Status
);
717 if ((flags
& EXTENT_ITEM_SHARED_BACKREFS
&& unique
) || !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
718 UINT64 sdrrc
= find_extent_shared_data_refcount(Vcb
, ed2
->address
, t
->header
.address
, Irp
);
723 sdr
.offset
= t
->header
.address
;
726 Status
= decrease_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_SHARED_DATA_REF
, &sdr
, NULL
, 0,
727 t
->header
.address
, Irp
, rollback
);
728 if (!NT_SUCCESS(Status
)) {
729 ERR("decrease_extent_refcount returned %08x\n", Status
);
740 // FIXME - clear shared flag if unique?
750 le
= t
->itemlist
.Flink
;
751 while (le
!= &t
->itemlist
) {
752 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
757 tbr
.offset
= t
->root
->id
;
759 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
,
760 &tbr
, &td
->key
, t
->header
.level
- 1, Irp
, rollback
);
761 if (!NT_SUCCESS(Status
)) {
762 ERR("increase_extent_refcount returned %08x\n", Status
);
766 if (unique
|| !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
767 UINT64 sbrrc
= find_extent_shared_tree_refcount(Vcb
, td
->treeholder
.address
, t
->header
.address
, Irp
);
770 SHARED_BLOCK_REF sbr
;
772 sbr
.offset
= t
->header
.address
;
774 Status
= decrease_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, NULL
, 0,
775 t
->header
.address
, Irp
, rollback
);
776 if (!NT_SUCCESS(Status
)) {
777 ERR("decrease_extent_refcount returned %08x\n", Status
);
783 // FIXME - clear shared flag if unique?
791 UINT64 sbrrc
= find_extent_shared_tree_refcount(Vcb
, t
->header
.address
, t
->parent
->header
.address
, Irp
);
794 SHARED_BLOCK_REF sbr
;
796 sbr
.offset
= t
->parent
->header
.address
;
798 Status
= decrease_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, NULL
, 0,
799 t
->parent
->header
.address
, Irp
, rollback
);
800 if (!NT_SUCCESS(Status
)) {
801 ERR("decrease_extent_refcount returned %08x\n", Status
);
808 tbr
.offset
= t
->parent
->header
.tree_id
;
810 tbr
.offset
= t
->header
.tree_id
;
812 Status
= increase_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
,
813 t
->parent
? &t
->paritem
->key
: NULL
, t
->header
.level
, Irp
, rollback
);
814 if (!NT_SUCCESS(Status
)) {
815 ERR("increase_extent_refcount returned %08x\n", Status
);
819 // FIXME - clear shared flag if unique?
821 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
824 Status
= reduce_tree_extent(Vcb
, t
->header
.address
, t
, Irp
, rollback
);
826 if (!NT_SUCCESS(Status
)) {
827 ERR("reduce_tree_extent returned %08x\n", Status
);
831 t
->has_address
= FALSE
;
833 if (rc
> 1 && !(flags
& EXTENT_ITEM_SHARED_BACKREFS
)) {
834 if (t
->header
.tree_id
== t
->root
->id
) {
835 flags
|= EXTENT_ITEM_SHARED_BACKREFS
;
836 update_extent_flags(Vcb
, t
->header
.address
, flags
, Irp
);
839 if (t
->header
.level
> 0) {
842 le
= t
->itemlist
.Flink
;
843 while (le
!= &t
->itemlist
) {
844 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
847 if (t
->header
.tree_id
== t
->root
->id
) {
848 SHARED_BLOCK_REF sbr
;
850 sbr
.offset
= t
->header
.address
;
852 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, &td
->key
, t
->header
.level
- 1, Irp
, rollback
);
856 tbr
.offset
= t
->root
->id
;
858 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, &td
->key
, t
->header
.level
- 1, Irp
, rollback
);
861 if (!NT_SUCCESS(Status
)) {
862 ERR("increase_extent_refcount returned %08x\n", Status
);
872 le
= t
->itemlist
.Flink
;
873 while (le
!= &t
->itemlist
) {
874 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
876 if (!td
->inserted
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
877 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
879 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
880 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
883 changed_extent
* ce
= NULL
;
884 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
889 le2
= c
->changed_extents
.Flink
;
890 while (le2
!= &c
->changed_extents
) {
891 changed_extent
* ce2
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
893 if (ce2
->address
== ed2
->address
) {
902 if (t
->header
.tree_id
== t
->root
->id
) {
905 sdr
.offset
= t
->header
.address
;
909 Status
= add_changed_extent_ref_sdr(ce
, &sdr
, TRUE
);
910 if (!NT_SUCCESS(Status
)) {
911 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
915 Status
= add_changed_extent_ref_sdr(ce
, &sdr
, FALSE
);
916 if (!NT_SUCCESS(Status
)) {
917 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
922 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_SHARED_DATA_REF
, &sdr
, NULL
, 0, Irp
, rollback
);
926 edr
.root
= t
->root
->id
;
927 edr
.objid
= td
->key
.obj_id
;
928 edr
.offset
= td
->key
.offset
- ed2
->offset
;
932 Status
= add_changed_extent_ref_edr(ce
, &edr
, TRUE
);
933 if (!NT_SUCCESS(Status
)) {
934 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
938 Status
= add_changed_extent_ref_edr(ce
, &edr
, FALSE
);
939 if (!NT_SUCCESS(Status
)) {
940 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
945 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, Irp
, rollback
);
948 if (!NT_SUCCESS(Status
)) {
949 ERR("increase_extent_refcount returned %08x\n", Status
);
961 t
->updated_extents
= TRUE
;
962 t
->header
.tree_id
= t
->root
->id
;
964 return STATUS_SUCCESS
;
967 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
970 BOOL changed
= FALSE
;
971 UINT8 max_level
= 0, level
;
973 TRACE("(%p)\n", Vcb
);
975 le
= Vcb
->trees
.Flink
;
976 while (le
!= &Vcb
->trees
) {
977 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
979 if (t
->write
&& !t
->has_new_address
) {
982 Status
= get_tree_new_address(Vcb
, t
, Irp
, rollback
);
983 if (!NT_SUCCESS(Status
)) {
984 ERR("get_tree_new_address returned %08x\n", Status
);
988 TRACE("allocated extent %llx\n", t
->new_address
);
990 c
= get_chunk_from_address(Vcb
, t
->new_address
);
993 increase_chunk_usage(c
, Vcb
->superblock
.node_size
);
995 ERR("could not find chunk for address %llx\n", t
->new_address
);
996 return STATUS_INTERNAL_ERROR
;
1001 if (t
->header
.level
> max_level
)
1002 max_level
= t
->header
.level
;
1009 return STATUS_SUCCESS
;
1013 le
= Vcb
->trees
.Flink
;
1014 while (le
!= &Vcb
->trees
) {
1015 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1017 if (t
->write
&& !t
->updated_extents
&& t
->has_address
&& t
->header
.level
== level
) {
1018 Status
= update_tree_extents(Vcb
, t
, Irp
, rollback
);
1019 if (!NT_SUCCESS(Status
)) {
1020 ERR("update_tree_extents returned %08x\n", Status
);
1034 return STATUS_SUCCESS
;
1037 static NTSTATUS
update_root_root(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1041 TRACE("(%p)\n", Vcb
);
1043 le
= Vcb
->trees
.Flink
;
1044 while (le
!= &Vcb
->trees
) {
1045 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1047 if (t
->write
&& !t
->parent
) {
1048 if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
1052 searchkey
.obj_id
= t
->root
->id
;
1053 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1054 searchkey
.offset
= 0xffffffffffffffff;
1056 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
1057 if (!NT_SUCCESS(Status
)) {
1058 ERR("error - find_item returned %08x\n", Status
);
1062 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1063 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1065 return STATUS_INTERNAL_ERROR
;
1068 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, t
->new_address
);
1070 t
->root
->root_item
.block_number
= t
->new_address
;
1071 t
->root
->root_item
.root_level
= t
->header
.level
;
1072 t
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1073 t
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1075 // item is guaranteed to be at least sizeof(ROOT_ITEM), due to add_parents
1077 RtlCopyMemory(tp
.item
->data
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1080 t
->root
->treeholder
.address
= t
->new_address
;
1086 Status
= update_chunk_caches(Vcb
, Irp
, rollback
);
1087 if (!NT_SUCCESS(Status
)) {
1088 ERR("update_chunk_caches returned %08x\n", Status
);
1092 return STATUS_SUCCESS
;
1095 static NTSTATUS
write_trees(device_extension
* Vcb
, PIRP Irp
) {
1101 write_data_context
* wtc
;
1102 LIST_ENTRY tree_writes
;
1106 TRACE("(%p)\n", Vcb
);
1108 InitializeListHead(&tree_writes
);
1110 for (level
= 0; level
<= 255; level
++) {
1111 BOOL nothing_found
= TRUE
;
1113 TRACE("level = %u\n", level
);
1115 le
= Vcb
->trees
.Flink
;
1116 while (le
!= &Vcb
->trees
) {
1117 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1119 if (t
->write
&& t
->header
.level
== level
) {
1120 KEY firstitem
, searchkey
;
1123 EXTENT_ITEM_TREE
* eit
;
1125 if (!t
->has_new_address
) {
1126 ERR("error - tried to write tree with no new address\n");
1130 le2
= t
->itemlist
.Flink
;
1131 while (le2
!= &t
->itemlist
) {
1132 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1134 firstitem
= td
->key
;
1141 t
->paritem
->key
= firstitem
;
1142 t
->paritem
->treeholder
.address
= t
->new_address
;
1143 t
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
1146 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
1147 searchkey
.obj_id
= t
->new_address
;
1148 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1149 searchkey
.offset
= Vcb
->superblock
.node_size
;
1151 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1152 if (!NT_SUCCESS(Status
)) {
1153 ERR("error - find_item returned %08x\n", Status
);
1157 if (keycmp(searchkey
, tp
.item
->key
)) {
1158 // traverse_ptr next_tp;
1160 // tree_data* paritem;
1162 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
);
1164 // searchkey.obj_id = 0;
1165 // searchkey.obj_type = 0;
1166 // searchkey.offset = 0;
1168 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
1172 // if (tp.tree->paritem != paritem) {
1173 // paritem = tp.tree->paritem;
1174 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
1177 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1179 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
1181 // free_traverse_ptr(&tp);
1186 // free_traverse_ptr(&tp);
1188 return STATUS_INTERNAL_ERROR
;
1191 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
1192 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
));
1193 return STATUS_INTERNAL_ERROR
;
1196 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
1197 eit
->firstitem
= firstitem
;
1200 nothing_found
= FALSE
;
1210 TRACE("allocated tree extents\n");
1212 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_context
), ALLOC_TAG
);
1214 ERR("out of memory\n");
1215 return STATUS_INSUFFICIENT_RESOURCES
;
1218 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
1219 InitializeListHead(&wtc
->stripes
);
1221 wtc
->stripes_left
= 0;
1223 le
= Vcb
->trees
.Flink
;
1224 while (le
!= &Vcb
->trees
) {
1225 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1226 #ifdef DEBUG_PARANOID
1227 UINT32 num_items
= 0, size
= 0;
1233 #ifdef DEBUG_PARANOID
1234 le2
= t
->itemlist
.Flink
;
1235 while (le2
!= &t
->itemlist
) {
1236 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1240 if (t
->header
.level
== 0)
1246 if (t
->header
.level
== 0)
1247 size
+= num_items
* sizeof(leaf_node
);
1249 size
+= num_items
* sizeof(internal_node
);
1251 if (num_items
!= t
->header
.num_items
) {
1252 ERR("tree %llx, level %x: num_items was %x, expected %x\n", t
->root
->id
, t
->header
.level
, num_items
, t
->header
.num_items
);
1256 if (size
!= t
->size
) {
1257 ERR("tree %llx, level %x: size was %x, expected %x\n", t
->root
->id
, t
->header
.level
, size
, t
->size
);
1261 if (t
->header
.num_items
== 0 && t
->parent
) {
1262 ERR("tree %llx, level %x: tried to write empty tree with parent\n", t
->root
->id
, t
->header
.level
);
1266 if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
1267 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
));
1272 ERR("tree %p\n", t
);
1273 le2
= t
->itemlist
.Flink
;
1274 while (le2
!= &t
->itemlist
) {
1275 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1277 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
1284 t
->header
.address
= t
->new_address
;
1285 t
->header
.generation
= Vcb
->superblock
.generation
;
1286 t
->header
.tree_id
= t
->root
->id
;
1287 t
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
1288 t
->header
.fs_uuid
= Vcb
->superblock
.uuid
;
1289 t
->has_address
= TRUE
;
1291 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
1293 ERR("out of memory\n");
1294 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1298 body
= data
+ sizeof(tree_header
);
1300 RtlCopyMemory(data
, &t
->header
, sizeof(tree_header
));
1301 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
1303 if (t
->header
.level
== 0) {
1304 leaf_node
* itemptr
= (leaf_node
*)body
;
1307 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
1309 le2
= t
->itemlist
.Flink
;
1310 while (le2
!= &t
->itemlist
) {
1311 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1313 dataptr
= dataptr
- td
->size
;
1315 itemptr
[i
].key
= td
->key
;
1316 itemptr
[i
].offset
= (UINT8
*)dataptr
- (UINT8
*)body
;
1317 itemptr
[i
].size
= td
->size
;
1321 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
1327 internal_node
* itemptr
= (internal_node
*)body
;
1331 le2
= t
->itemlist
.Flink
;
1332 while (le2
!= &t
->itemlist
) {
1333 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1335 itemptr
[i
].key
= td
->key
;
1336 itemptr
[i
].address
= td
->treeholder
.address
;
1337 itemptr
[i
].generation
= td
->treeholder
.generation
;
1345 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
1347 *((UINT32
*)data
) = crc32
;
1348 TRACE("setting crc32 to %08x\n", crc32
);
1350 tw
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_write
), ALLOC_TAG
);
1352 ERR("out of memory\n");
1353 return STATUS_INSUFFICIENT_RESOURCES
;
1356 tw
->address
= t
->new_address
;
1357 tw
->length
= Vcb
->superblock
.node_size
;
1359 tw
->overlap
= FALSE
;
1361 if (IsListEmpty(&tree_writes
))
1362 InsertTailList(&tree_writes
, &tw
->list_entry
);
1365 BOOL inserted
= FALSE
;
1367 le2
= tree_writes
.Flink
;
1368 while (le2
!= &tree_writes
) {
1369 tree_write
* tw2
= CONTAINING_RECORD(le2
, tree_write
, list_entry
);
1371 if (tw2
->address
> tw
->address
) {
1372 InsertHeadList(le2
->Blink
, &tw
->list_entry
);
1381 InsertTailList(&tree_writes
, &tw
->list_entry
);
1388 Status
= STATUS_SUCCESS
;
1390 // merge together runs
1392 le
= tree_writes
.Flink
;
1393 while (le
!= &tree_writes
) {
1394 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1396 if (!c
|| tw
->address
< c
->offset
|| tw
->address
>= c
->offset
+ c
->chunk_item
->size
)
1397 c
= get_chunk_from_address(Vcb
, tw
->address
);
1399 tree_write
* tw2
= CONTAINING_RECORD(le
->Blink
, tree_write
, list_entry
);
1401 if (tw
->address
== tw2
->address
+ tw2
->length
) {
1402 data
= ExAllocatePoolWithTag(NonPagedPool
, tw2
->length
+ tw
->length
, ALLOC_TAG
);
1405 ERR("out of memory\n");
1406 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1410 RtlCopyMemory(data
, tw2
->data
, tw2
->length
);
1411 RtlCopyMemory(&data
[tw2
->length
], tw
->data
, tw
->length
);
1413 ExFreePool(tw2
->data
);
1415 tw2
->length
+= tw
->length
;
1417 ExFreePool(tw
->data
);
1418 RemoveEntryList(&tw
->list_entry
);
1421 le
= tw2
->list_entry
.Flink
;
1429 // mark RAID5/6 overlaps so we can do them one by one
1431 le
= tree_writes
.Flink
;
1432 while (le
!= &tree_writes
) {
1433 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1435 if (!c
|| tw
->address
< c
->offset
|| tw
->address
>= c
->offset
+ c
->chunk_item
->size
)
1436 c
= get_chunk_from_address(Vcb
, tw
->address
);
1437 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
1438 tree_write
* tw2
= CONTAINING_RECORD(le
->Blink
, tree_write
, list_entry
);
1439 UINT64 last_stripe
, this_stripe
;
1441 last_stripe
= (tw2
->address
+ tw2
->length
- 1 - c
->offset
) / (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 1));
1442 this_stripe
= (tw
->address
- c
->offset
) / (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 1));
1444 if (last_stripe
== this_stripe
)
1446 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
1447 tree_write
* tw2
= CONTAINING_RECORD(le
->Blink
, tree_write
, list_entry
);
1448 UINT64 last_stripe
, this_stripe
;
1450 last_stripe
= (tw2
->address
+ tw2
->length
- 1 - c
->offset
) / (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 2));
1451 this_stripe
= (tw
->address
- c
->offset
) / (c
->chunk_item
->stripe_length
* (c
->chunk_item
->num_stripes
- 2));
1453 if (last_stripe
== this_stripe
)
1460 le
= tree_writes
.Flink
;
1461 while (le
!= &tree_writes
) {
1462 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1465 TRACE("address: %llx, size: %x, overlap = %u\n", tw
->address
, tw
->length
, tw
->overlap
);
1467 Status
= write_data(Vcb
, tw
->address
, tw
->data
, TRUE
, tw
->length
, wtc
, NULL
, NULL
);
1468 if (!NT_SUCCESS(Status
)) {
1469 ERR("write_data returned %08x\n", Status
);
1477 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
1478 // launch writes and wait
1479 le
= wtc
->stripes
.Flink
;
1480 while (le
!= &wtc
->stripes
) {
1481 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1483 if (stripe
->status
!= WriteDataStatus_Ignore
)
1484 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
1489 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
1491 le
= wtc
->stripes
.Flink
;
1492 while (le
!= &wtc
->stripes
) {
1493 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1495 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
1496 Status
= stripe
->iosb
.Status
;
1503 free_write_data_stripes(wtc
);
1506 le
= tree_writes
.Flink
;
1507 while (le
!= &tree_writes
) {
1508 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1511 TRACE("address: %llx, size: %x, overlap = %u\n", tw
->address
, tw
->length
, tw
->overlap
);
1513 Status
= write_data_complete(Vcb
, tw
->address
, tw
->data
, tw
->length
, Irp
, NULL
);
1514 if (!NT_SUCCESS(Status
)) {
1515 ERR("write_data_complete returned %08x\n", Status
);
1526 while (!IsListEmpty(&tree_writes
)) {
1527 le
= RemoveHeadList(&tree_writes
);
1528 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1536 static void update_backup_superblock(device_extension
* Vcb
, superblock_backup
* sb
, PIRP Irp
) {
1540 RtlZeroMemory(sb
, sizeof(superblock_backup
));
1542 sb
->root_tree_addr
= Vcb
->superblock
.root_tree_addr
;
1543 sb
->root_tree_generation
= Vcb
->superblock
.generation
;
1544 sb
->root_level
= Vcb
->superblock
.root_level
;
1546 sb
->chunk_tree_addr
= Vcb
->superblock
.chunk_tree_addr
;
1547 sb
->chunk_tree_generation
= Vcb
->superblock
.chunk_root_generation
;
1548 sb
->chunk_root_level
= Vcb
->superblock
.chunk_root_level
;
1550 searchkey
.obj_id
= BTRFS_ROOT_EXTENT
;
1551 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1552 searchkey
.offset
= 0xffffffffffffffff;
1554 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1555 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1556 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1558 sb
->extent_tree_addr
= ri
->block_number
;
1559 sb
->extent_tree_generation
= ri
->generation
;
1560 sb
->extent_root_level
= ri
->root_level
;
1564 searchkey
.obj_id
= BTRFS_ROOT_FSTREE
;
1566 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1567 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1568 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1570 sb
->fs_tree_addr
= ri
->block_number
;
1571 sb
->fs_tree_generation
= ri
->generation
;
1572 sb
->fs_root_level
= ri
->root_level
;
1576 searchkey
.obj_id
= BTRFS_ROOT_DEVTREE
;
1578 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1579 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1580 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1582 sb
->dev_root_addr
= ri
->block_number
;
1583 sb
->dev_root_generation
= ri
->generation
;
1584 sb
->dev_root_level
= ri
->root_level
;
1588 searchkey
.obj_id
= BTRFS_ROOT_CHECKSUM
;
1590 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1591 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1592 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1594 sb
->csum_root_addr
= ri
->block_number
;
1595 sb
->csum_root_generation
= ri
->generation
;
1596 sb
->csum_root_level
= ri
->root_level
;
1600 sb
->total_bytes
= Vcb
->superblock
.total_bytes
;
1601 sb
->bytes_used
= Vcb
->superblock
.bytes_used
;
1602 sb
->num_devices
= Vcb
->superblock
.num_devices
;
1605 static NTSTATUS STDCALL
write_superblock(device_extension
* Vcb
, device
* device
) {
1610 Status
= STATUS_INTERNAL_ERROR
;
1613 RtlCopyMemory(&Vcb
->superblock
.dev_item
, &device
->devitem
, sizeof(DEV_ITEM
));
1615 // All the documentation says that the Linux driver only writes one superblock
1616 // if it thinks a disk is an SSD, but this doesn't seem to be the case!
1618 while (superblock_addrs
[i
] > 0 && device
->length
>= superblock_addrs
[i
] + sizeof(superblock
)) {
1619 TRACE("writing superblock %u\n", i
);
1621 Vcb
->superblock
.sb_phys_addr
= superblock_addrs
[i
];
1623 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
1625 TRACE("crc32 is %08x\n", crc32
);
1626 RtlCopyMemory(&Vcb
->superblock
.checksum
, &crc32
, sizeof(UINT32
));
1628 Status
= write_data_phys(device
->devobj
, superblock_addrs
[i
], &Vcb
->superblock
, sizeof(superblock
));
1630 if (!NT_SUCCESS(Status
))
1637 ERR("no superblocks written!\n");
1643 static NTSTATUS
write_superblocks(device_extension
* Vcb
, PIRP Irp
) {
1648 TRACE("(%p)\n", Vcb
);
1650 le
= Vcb
->trees
.Flink
;
1651 while (le
!= &Vcb
->trees
) {
1652 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1654 if (t
->write
&& !t
->parent
) {
1655 if (t
->root
== Vcb
->root_root
) {
1656 Vcb
->superblock
.root_tree_addr
= t
->new_address
;
1657 Vcb
->superblock
.root_level
= t
->header
.level
;
1658 } else if (t
->root
== Vcb
->chunk_root
) {
1659 Vcb
->superblock
.chunk_tree_addr
= t
->new_address
;
1660 Vcb
->superblock
.chunk_root_generation
= t
->header
.generation
;
1661 Vcb
->superblock
.chunk_root_level
= t
->header
.level
;
1668 for (i
= 0; i
< BTRFS_NUM_BACKUP_ROOTS
- 1; i
++) {
1669 RtlCopyMemory(&Vcb
->superblock
.backup
[i
], &Vcb
->superblock
.backup
[i
+1], sizeof(superblock_backup
));
1672 update_backup_superblock(Vcb
, &Vcb
->superblock
.backup
[BTRFS_NUM_BACKUP_ROOTS
- 1], Irp
);
1674 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
1675 if (Vcb
->devices
[i
].devobj
&& !Vcb
->devices
[i
].readonly
) {
1676 Status
= write_superblock(Vcb
, &Vcb
->devices
[i
]);
1677 if (!NT_SUCCESS(Status
)) {
1678 ERR("write_superblock returned %08x\n", Status
);
1684 return STATUS_SUCCESS
;
1687 static NTSTATUS
flush_changed_extent(device_extension
* Vcb
, chunk
* c
, changed_extent
* ce
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1688 LIST_ENTRY
*le
, *le2
;
1692 le
= ce
->refs
.Flink
;
1693 while (le
!= &ce
->refs
) {
1694 changed_extent_ref
* cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
1695 LIST_ENTRY
* le3
= le
->Flink
;
1696 UINT64 old_count
= 0;
1698 if (cer
->type
== TYPE_EXTENT_DATA_REF
) {
1699 le2
= ce
->old_refs
.Flink
;
1700 while (le2
!= &ce
->old_refs
) {
1701 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
1703 if (cer2
->type
== TYPE_EXTENT_DATA_REF
&& cer2
->edr
.root
== cer
->edr
.root
&& cer2
->edr
.objid
== cer
->edr
.objid
&& cer2
->edr
.offset
== cer
->edr
.offset
) {
1704 old_count
= cer2
->edr
.count
;
1706 RemoveEntryList(&cer2
->list_entry
);
1714 old_size
= ce
->old_count
> 0 ? ce
->old_size
: ce
->size
;
1716 if (cer
->edr
.count
> old_count
) {
1717 Status
= increase_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
, cer
->edr
.count
- old_count
, Irp
, rollback
);
1719 if (!NT_SUCCESS(Status
)) {
1720 ERR("increase_extent_refcount_data returned %08x\n", Status
);
1723 } else if (cer
->edr
.count
< old_count
) {
1724 Status
= decrease_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
,
1725 old_count
- cer
->edr
.count
, Irp
, rollback
);
1727 if (!NT_SUCCESS(Status
)) {
1728 ERR("decrease_extent_refcount_data returned %08x\n", Status
);
1733 if (ce
->size
!= ce
->old_size
&& ce
->old_count
> 0) {
1738 searchkey
.obj_id
= ce
->address
;
1739 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1740 searchkey
.offset
= ce
->old_size
;
1742 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1743 if (!NT_SUCCESS(Status
)) {
1744 ERR("error - find_item returned %08x\n", Status
);
1748 if (keycmp(searchkey
, tp
.item
->key
)) {
1749 ERR("could not find (%llx,%x,%llx) in extent tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1750 return STATUS_INTERNAL_ERROR
;
1753 if (tp
.item
->size
> 0) {
1754 data
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
1757 ERR("out of memory\n");
1758 return STATUS_INSUFFICIENT_RESOURCES
;
1761 RtlCopyMemory(data
, tp
.item
->data
, tp
.item
->size
);
1765 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, ce
->address
, TYPE_EXTENT_ITEM
, ce
->size
, data
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
1766 ERR("insert_tree_item failed\n");
1767 return STATUS_INTERNAL_ERROR
;
1770 delete_tree_item(Vcb
, &tp
, rollback
);
1772 } else if (cer
->type
== TYPE_SHARED_DATA_REF
) {
1773 le2
= ce
->old_refs
.Flink
;
1774 while (le2
!= &ce
->old_refs
) {
1775 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
1777 if (cer2
->type
== TYPE_SHARED_DATA_REF
&& cer2
->sdr
.offset
== cer
->sdr
.offset
) {
1778 // old_count = cer2->edr.count;
1780 RemoveEntryList(&cer2
->list_entry
);
1789 RemoveEntryList(&cer
->list_entry
);
1795 #ifdef DEBUG_PARANOID
1796 if (!IsListEmpty(&ce
->old_refs
))
1797 WARN("old_refs not empty\n");
1800 if (ce
->count
== 0 && !ce
->superseded
) {
1802 LIST_ENTRY changed_sector_list
;
1804 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
1806 ERR("out of memory\n");
1807 return STATUS_INSUFFICIENT_RESOURCES
;
1810 sc
->ol
.key
= ce
->address
;
1811 sc
->checksums
= NULL
;
1812 sc
->length
= ce
->size
/ Vcb
->superblock
.sector_size
;
1816 InitializeListHead(&changed_sector_list
);
1817 insert_into_ordered_list(&changed_sector_list
, &sc
->ol
);
1819 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
1820 commit_checksum_changes(Vcb
, &changed_sector_list
);
1821 ExReleaseResourceLite(&Vcb
->checksum_lock
);
1824 decrease_chunk_usage(c
, ce
->size
);
1826 space_list_add(Vcb
, c
, TRUE
, ce
->address
, ce
->size
, rollback
);
1829 RemoveEntryList(&ce
->list_entry
);
1832 return STATUS_SUCCESS
;
1835 static void update_checksum_tree(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1836 LIST_ENTRY
* le
= Vcb
->sector_checksums
.Flink
;
1838 traverse_ptr tp
, next_tp
;
1843 if (!Vcb
->checksum_root
) {
1844 ERR("no checksum root\n");
1848 while (le
!= &Vcb
->sector_checksums
) {
1849 UINT64 startaddr
, endaddr
;
1854 ULONG runlength
, index
;
1856 cs
= (changed_sector
*)le
;
1858 searchkey
.obj_id
= EXTENT_CSUM_ID
;
1859 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
1860 searchkey
.offset
= cs
->ol
.key
;
1862 // FIXME - create checksum_root if it doesn't exist at all
1864 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
1865 if (Status
== STATUS_NOT_FOUND
) { // tree is completely empty
1867 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * cs
->length
, ALLOC_TAG
);
1869 ERR("out of memory\n");
1873 RtlCopyMemory(checksums
, cs
->checksums
, sizeof(UINT32
) * cs
->length
);
1875 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, cs
->ol
.key
, checksums
, sizeof(UINT32
) * cs
->length
, NULL
, Irp
, rollback
)) {
1876 ERR("insert_tree_item failed\n");
1877 ExFreePool(checksums
);
1881 } else if (!NT_SUCCESS(Status
)) {
1882 ERR("find_item returned %08x\n", Status
);
1887 // FIXME - check entry is TYPE_EXTENT_CSUM?
1889 if (tp
.item
->key
.offset
< cs
->ol
.key
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= cs
->ol
.key
)
1890 startaddr
= tp
.item
->key
.offset
;
1892 startaddr
= cs
->ol
.key
;
1894 searchkey
.obj_id
= EXTENT_CSUM_ID
;
1895 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
1896 searchkey
.offset
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
1898 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
1899 if (!NT_SUCCESS(Status
)) {
1900 ERR("error - find_item returned %08x\n", Status
);
1904 tplen
= tp
.item
->size
/ sizeof(UINT32
);
1906 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
))
1907 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
1909 endaddr
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
1911 TRACE("cs starts at %llx (%x sectors)\n", cs
->ol
.key
, cs
->length
);
1912 TRACE("startaddr = %llx\n", startaddr
);
1913 TRACE("endaddr = %llx\n", endaddr
);
1915 len
= (endaddr
- startaddr
) / Vcb
->superblock
.sector_size
;
1917 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
1919 ERR("out of memory\n");
1923 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
1925 ERR("out of memory\n");
1926 ExFreePool(checksums
);
1930 RtlInitializeBitMap(&bmp
, bmparr
, len
);
1931 RtlSetAllBits(&bmp
);
1933 searchkey
.obj_id
= EXTENT_CSUM_ID
;
1934 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
1935 searchkey
.offset
= cs
->ol
.key
;
1937 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
1938 if (!NT_SUCCESS(Status
)) {
1939 ERR("error - find_item returned %08x\n", Status
);
1943 // set bit = free space, cleared bit = allocated sector
1945 // ERR("start loop\n");
1946 while (tp
.item
->key
.offset
< endaddr
) {
1947 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1948 if (tp
.item
->key
.offset
>= startaddr
) {
1949 if (tp
.item
->size
> 0) {
1950 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, tp
.item
->size
);
1951 RtlClearBits(&bmp
, (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
, tp
.item
->size
/ sizeof(UINT32
));
1954 delete_tree_item(Vcb
, &tp
, rollback
);
1957 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
1962 // ERR("end loop\n");
1965 RtlSetBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
1967 RtlCopyMemory(&checksums
[(cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
], cs
->checksums
, cs
->length
* sizeof(UINT32
));
1968 RtlClearBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
1971 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
1973 while (runlength
!= 0) {
1978 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
1979 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
1983 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
1985 ERR("out of memory\n");
1987 ExFreePool(checksums
);
1991 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
1993 off
= startaddr
+ UInt32x32To64(index
, Vcb
->superblock
.sector_size
);
1995 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, off
, data
, sizeof(UINT32
) * rl
, NULL
, Irp
, rollback
)) {
1996 ERR("insert_tree_item failed\n");
1999 ExFreePool(checksums
);
2005 } while (runlength
> 0);
2007 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
2011 ExFreePool(checksums
);
2018 while (!IsListEmpty(&Vcb
->sector_checksums
)) {
2019 le
= RemoveHeadList(&Vcb
->sector_checksums
);
2020 cs
= (changed_sector
*)le
;
2023 ExFreePool(cs
->checksums
);
2029 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2030 LIST_ENTRY
*le
= Vcb
->chunks
.Flink
, *le2
;
2034 BLOCK_GROUP_ITEM
* bgi
;
2036 BOOL flushed_extents
= FALSE
;
2038 TRACE("(%p)\n", Vcb
);
2040 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
2042 while (le
!= &Vcb
->chunks
) {
2043 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2045 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2047 le2
= c
->changed_extents
.Flink
;
2048 while (le2
!= &c
->changed_extents
) {
2049 LIST_ENTRY
* le3
= le2
->Flink
;
2050 changed_extent
* ce
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
2052 Status
= flush_changed_extent(Vcb
, c
, ce
, Irp
, rollback
);
2053 if (!NT_SUCCESS(Status
)) {
2054 ERR("flush_changed_extent returned %08x\n", Status
);
2055 ExReleaseResourceLite(&c
->lock
);
2059 flushed_extents
= TRUE
;
2064 // This is usually done by update_chunks, but we have to check again in case any new chunks
2065 // have been allocated since.
2067 Status
= create_chunk(Vcb
, c
, Irp
, rollback
);
2068 if (!NT_SUCCESS(Status
)) {
2069 ERR("create_chunk returned %08x\n", Status
);
2070 ExReleaseResourceLite(&c
->lock
);
2075 if (c
->used
!= c
->oldused
) {
2076 searchkey
.obj_id
= c
->offset
;
2077 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2078 searchkey
.offset
= c
->chunk_item
->size
;
2080 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2081 if (!NT_SUCCESS(Status
)) {
2082 ERR("error - find_item returned %08x\n", Status
);
2083 ExReleaseResourceLite(&c
->lock
);
2087 if (keycmp(searchkey
, tp
.item
->key
)) {
2088 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2090 Status
= STATUS_INTERNAL_ERROR
;
2091 ExReleaseResourceLite(&c
->lock
);
2095 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2096 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
));
2097 Status
= STATUS_INTERNAL_ERROR
;
2098 ExReleaseResourceLite(&c
->lock
);
2102 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2104 ERR("out of memory\n");
2105 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2106 ExReleaseResourceLite(&c
->lock
);
2110 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2111 bgi
->used
= c
->used
;
2113 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2115 delete_tree_item(Vcb
, &tp
, rollback
);
2117 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
2118 ERR("insert_tree_item failed\n");
2120 Status
= STATUS_INTERNAL_ERROR
;
2121 ExReleaseResourceLite(&c
->lock
);
2125 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2127 Vcb
->superblock
.bytes_used
+= c
->used
- c
->oldused
;
2129 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2131 c
->oldused
= c
->used
;
2134 ExReleaseResourceLite(&c
->lock
);
2139 if (flushed_extents
) {
2140 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
2141 if (!IsListEmpty(&Vcb
->sector_checksums
)) {
2142 update_checksum_tree(Vcb
, Irp
, rollback
);
2144 ExReleaseResourceLite(&Vcb
->checksum_lock
);
2147 Status
= STATUS_SUCCESS
;
2150 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2155 static void get_first_item(tree
* t
, KEY
* key
) {
2158 le
= t
->itemlist
.Flink
;
2159 while (le
!= &t
->itemlist
) {
2160 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2167 static NTSTATUS STDCALL
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2170 tree_data
* oldlastitem
;
2172 // // tree_data *firsttd, *lasttd;
2173 // // LIST_ENTRY* le;
2174 // #ifdef DEBUG_PARANOID
2175 // KEY lastkey1, lastkey2;
2176 // traverse_ptr tp, next_tp;
2177 // ULONG numitems1, numitems2;
2180 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2182 // #ifdef DEBUG_PARANOID
2183 // lastkey1.obj_id = 0xffffffffffffffff;
2184 // lastkey1.obj_type = 0xff;
2185 // lastkey1.offset = 0xffffffffffffffff;
2187 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2188 // ERR("error - find_item failed\n");
2190 // lastkey1 = tp.item->key;
2192 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2193 // free_traverse_ptr(&tp);
2197 // free_traverse_ptr(&tp);
2201 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2203 ERR("out of memory\n");
2204 return STATUS_INSUFFICIENT_RESOURCES
;
2207 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2208 nt
->header
.address
= 0;
2209 nt
->header
.generation
= Vcb
->superblock
.generation
;
2210 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2211 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2213 nt
->has_address
= FALSE
;
2215 nt
->parent
= t
->parent
;
2217 #ifdef DEBUG_PARANOID
2218 if (nt
->parent
&& nt
->parent
->header
.level
<= nt
->header
.level
) int3
;
2222 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2223 nt
->new_address
= 0;
2224 nt
->has_new_address
= FALSE
;
2225 nt
->updated_extents
= FALSE
;
2226 nt
->flags
= t
->flags
;
2227 InitializeListHead(&nt
->itemlist
);
2229 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2231 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2233 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2234 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2236 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2237 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2238 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2240 // // le = wt->tree->itemlist.Flink;
2241 // // while (le != &wt->tree->itemlist) {
2242 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2243 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2244 // // le = le->Flink;
2247 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2248 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2249 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2250 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2252 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2253 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2255 // // le = wt->tree->itemlist.Flink;
2256 // // while (le != &wt->tree->itemlist) {
2257 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2258 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2259 // // le = le->Flink;
2262 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2263 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2265 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2266 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2267 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2269 nt
->size
= t
->size
- size
;
2271 t
->header
.num_items
= numitems
;
2274 InterlockedIncrement(&Vcb
->open_trees
);
2275 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2278 // // td = wt->tree->items;
2280 // // if (!td->ignore) {
2281 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2283 // // td = td->next;
2286 // // oldlastitem->next = NULL;
2287 // // wt->tree->lastitem = oldlastitem;
2289 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2291 if (nt
->header
.level
> 0) {
2292 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2294 while (le
!= &nt
->itemlist
) {
2295 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2297 if (td2
->treeholder
.tree
) {
2298 td2
->treeholder
.tree
->parent
= nt
;
2299 #ifdef DEBUG_PARANOID
2300 if (td2
->treeholder
.tree
->parent
&& td2
->treeholder
.tree
->parent
->header
.level
<= td2
->treeholder
.tree
->header
.level
) int3
;
2309 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2311 ERR("out of memory\n");
2312 return STATUS_INSUFFICIENT_RESOURCES
;
2315 td
->key
= newfirstitem
->key
;
2317 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2320 td
->inserted
= TRUE
;
2321 td
->treeholder
.tree
= nt
;
2322 // td->treeholder.nonpaged->status = tree_holder_loaded;
2325 nt
->parent
->header
.num_items
++;
2326 nt
->parent
->size
+= sizeof(internal_node
);
2331 TRACE("adding new tree parent\n");
2333 if (nt
->header
.level
== 255) {
2334 ERR("cannot add parent to tree at level 255\n");
2335 return STATUS_INTERNAL_ERROR
;
2338 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2340 ERR("out of memory\n");
2341 return STATUS_INSUFFICIENT_RESOURCES
;
2344 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2345 pt
->header
.address
= 0;
2346 pt
->header
.num_items
= 2;
2347 pt
->header
.level
= nt
->header
.level
+ 1;
2348 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| HEADER_FLAG_WRITTEN
;
2350 pt
->has_address
= FALSE
;
2355 pt
->new_address
= 0;
2356 pt
->has_new_address
= FALSE
;
2357 pt
->updated_extents
= FALSE
;
2358 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2359 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2360 pt
->flags
= t
->flags
;
2361 InitializeListHead(&pt
->itemlist
);
2363 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2365 InterlockedIncrement(&Vcb
->open_trees
);
2366 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2368 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2370 ERR("out of memory\n");
2371 return STATUS_INSUFFICIENT_RESOURCES
;
2374 get_first_item(t
, &td
->key
);
2376 td
->inserted
= FALSE
;
2377 td
->treeholder
.address
= 0;
2378 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2379 td
->treeholder
.tree
= t
;
2380 // td->treeholder.nonpaged->status = tree_holder_loaded;
2381 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2384 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2386 ERR("out of memory\n");
2387 return STATUS_INSUFFICIENT_RESOURCES
;
2390 td
->key
= newfirstitem
->key
;
2392 td
->inserted
= FALSE
;
2393 td
->treeholder
.address
= 0;
2394 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2395 td
->treeholder
.tree
= nt
;
2396 // td->treeholder.nonpaged->status = tree_holder_loaded;
2397 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2402 t
->root
->treeholder
.tree
= pt
;
2407 #ifdef DEBUG_PARANOID
2408 if (t
->parent
&& t
->parent
->header
.level
<= t
->header
.level
) int3
;
2409 if (nt
->parent
&& nt
->parent
->header
.level
<= nt
->header
.level
) int3
;
2413 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2415 // #ifdef DEBUG_PARANOID
2416 // lastkey2.obj_id = 0xffffffffffffffff;
2417 // lastkey2.obj_type = 0xff;
2418 // lastkey2.offset = 0xffffffffffffffff;
2420 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2421 // ERR("error - find_item failed\n");
2423 // lastkey2 = tp.item->key;
2426 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2427 // free_traverse_ptr(&tp);
2431 // free_traverse_ptr(&tp);
2434 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2435 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2436 // ERR("numitems1 = %u\n", numitems1);
2437 // ERR("numitems2 = %u\n", numitems2);
2440 return STATUS_SUCCESS
;
2443 static NTSTATUS STDCALL
split_tree(device_extension
* Vcb
, tree
* t
) {
2445 UINT32 size
, ds
, numitems
;
2450 // FIXME - naïve implementation: maximizes number of filled trees
2452 le
= t
->itemlist
.Flink
;
2453 while (le
!= &t
->itemlist
) {
2454 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2457 if (t
->header
.level
== 0)
2458 ds
= sizeof(leaf_node
) + td
->size
;
2460 ds
= sizeof(internal_node
);
2462 // FIXME - move back if previous item was deleted item with same key
2463 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
2464 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
2473 return STATUS_SUCCESS
;
2476 BOOL
is_tree_unique(device_extension
* Vcb
, tree
* t
, PIRP Irp
) {
2485 if (t
->has_address
) {
2486 searchkey
.obj_id
= t
->header
.address
;
2487 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
2488 searchkey
.offset
= 0xffffffffffffffff;
2490 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2491 if (!NT_SUCCESS(Status
)) {
2492 ERR("error - find_item returned %08x\n", Status
);
2496 if (tp
.item
->key
.obj_id
!= t
->header
.address
|| (tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
))
2499 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& tp
.item
->size
== sizeof(EXTENT_ITEM_V0
))
2502 if (tp
.item
->size
< sizeof(EXTENT_ITEM
))
2505 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2507 if (ei
->refcount
> 1)
2510 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& ei
->flags
& EXTENT_ITEM_TREE_BLOCK
) {
2513 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
))
2516 ei2
= (EXTENT_ITEM2
*)&ei
[1];
2517 type
= (UINT8
*)&ei2
[1];
2519 type
= (UINT8
*)&ei
[1];
2521 if (type
>= tp
.item
->data
+ tp
.item
->size
|| *type
!= TYPE_TREE_BLOCK_REF
)
2531 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2533 tree_data
* nextparitem
= NULL
;
2535 tree
*next_tree
, *par
;
2538 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
2540 // FIXME - doesn't capture everything, as it doesn't ascend
2541 // FIXME - write proper function and put it in treefuncs.c
2542 le
= t
->paritem
->list_entry
.Flink
;
2543 while (le
!= &t
->parent
->itemlist
) {
2544 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2555 return STATUS_SUCCESS
;
2557 // FIXME - loop, and capture more than one tree if we can
2559 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
2560 // nextparitem = t->paritem;
2562 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2564 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
, NULL
);
2565 if (!NT_SUCCESS(Status
)) {
2566 ERR("do_load_tree returned %08x\n", Status
);
2570 if (!is_tree_unique(Vcb
, nextparitem
->treeholder
.tree
, Irp
))
2571 return STATUS_SUCCESS
;
2573 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2575 next_tree
= nextparitem
->treeholder
.tree
;
2577 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2578 // merge two trees into one
2580 t
->header
.num_items
+= next_tree
->header
.num_items
;
2581 t
->size
+= next_tree
->size
;
2583 if (next_tree
->header
.level
> 0) {
2584 le
= next_tree
->itemlist
.Flink
;
2586 while (le
!= &next_tree
->itemlist
) {
2587 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2589 if (td2
->treeholder
.tree
) {
2590 td2
->treeholder
.tree
->parent
= t
;
2591 #ifdef DEBUG_PARANOID
2592 if (td2
->treeholder
.tree
->parent
&& td2
->treeholder
.tree
->parent
->header
.level
<= td2
->treeholder
.tree
->header
.level
) int3
;
2600 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
2601 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
2602 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
2603 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2606 // le = t->itemlist.Flink;
2607 // while (le != &t->itemlist) {
2608 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2609 // if (!td->ignore) {
2610 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2615 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
2617 next_tree
->header
.num_items
= 0;
2618 next_tree
->size
= 0;
2620 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
2621 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, Irp
, rollback
);
2623 if (!NT_SUCCESS(Status
)) {
2624 ERR("reduce_tree_extent returned %08x\n", Status
);
2627 } else if (next_tree
->has_address
) {
2628 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, Irp
, rollback
);
2630 if (!NT_SUCCESS(Status
)) {
2631 ERR("reduce_tree_extent returned %08x\n", Status
);
2636 if (!nextparitem
->ignore
) {
2637 nextparitem
->ignore
= TRUE
;
2638 next_tree
->parent
->header
.num_items
--;
2639 next_tree
->parent
->size
-= sizeof(internal_node
);
2642 par
= next_tree
->parent
;
2648 RemoveEntryList(&nextparitem
->list_entry
);
2649 ExFreePool(next_tree
->paritem
);
2650 next_tree
->paritem
= NULL
;
2652 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2654 free_tree(next_tree
);
2656 // rebalance by moving items from second tree into first
2657 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
2658 KEY firstitem
= {0, 0, 0};
2659 BOOL changed
= FALSE
;
2661 TRACE("attempting rebalance\n");
2663 le
= next_tree
->itemlist
.Flink
;
2664 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
2665 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2669 if (next_tree
->header
.level
== 0)
2670 size
= sizeof(leaf_node
) + td
->size
;
2672 size
= sizeof(internal_node
);
2676 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2677 RemoveEntryList(&td
->list_entry
);
2678 InsertTailList(&t
->itemlist
, &td
->list_entry
);
2680 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
) {
2681 td
->treeholder
.tree
->parent
= t
;
2682 #ifdef DEBUG_PARANOID
2683 if (td
->treeholder
.tree
->parent
&& td
->treeholder
.tree
->parent
->header
.level
<= td
->treeholder
.tree
->header
.level
) int3
;
2688 next_tree
->size
-= size
;
2690 next_tree
->header
.num_items
--;
2691 t
->header
.num_items
++;
2698 le
= next_tree
->itemlist
.Flink
;
2702 le
= next_tree
->itemlist
.Flink
;
2703 while (le
!= &next_tree
->itemlist
) {
2704 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2707 firstitem
= td
->key
;
2714 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
2716 // FIXME - once ascension is working, make this work with parent's parent, etc.
2717 if (next_tree
->paritem
)
2718 next_tree
->paritem
->key
= firstitem
;
2728 return STATUS_SUCCESS
;
2731 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2736 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
2737 searchkey
.obj_id
= address
;
2738 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
2739 searchkey
.offset
= t
->header
.level
;
2741 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2742 if (!NT_SUCCESS(Status
)) {
2743 ERR("error - find_item returned %08x\n", Status
);
2747 if (!keycmp(tp
.item
->key
, searchkey
)) {
2748 EXTENT_ITEM_SKINNY_METADATA
* eism
;
2750 if (tp
.item
->size
> 0) {
2751 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2754 ERR("out of memory\n");
2755 return STATUS_INSUFFICIENT_RESOURCES
;
2758 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
2762 delete_tree_item(Vcb
, &tp
, rollback
);
2764 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
2765 ERR("insert_tree_item failed\n");
2767 return STATUS_INTERNAL_ERROR
;
2770 return STATUS_SUCCESS
;
2774 searchkey
.obj_id
= address
;
2775 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2776 searchkey
.offset
= 0xffffffffffffffff;
2778 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2779 if (!NT_SUCCESS(Status
)) {
2780 ERR("error - find_item returned %08x\n", Status
);
2784 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
2785 EXTENT_ITEM_TREE
* eit
;
2787 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
2788 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
));
2789 return STATUS_INTERNAL_ERROR
;
2792 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2795 ERR("out of memory\n");
2796 return STATUS_INSUFFICIENT_RESOURCES
;
2799 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
2801 delete_tree_item(Vcb
, &tp
, rollback
);
2805 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
, Irp
, rollback
)) {
2806 ERR("insert_tree_item failed\n");
2808 return STATUS_INTERNAL_ERROR
;
2811 return STATUS_SUCCESS
;
2814 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
2816 return STATUS_INTERNAL_ERROR
;
2819 static NTSTATUS STDCALL
do_splits(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2820 // LIST_ENTRY *le, *le2;
2823 UINT8 level
, max_level
;
2825 BOOL empty
, done_deletions
= FALSE
;
2829 TRACE("(%p)\n", Vcb
);
2833 for (level
= 0; level
<= 255; level
++) {
2834 LIST_ENTRY
*le
, *nextle
;
2838 TRACE("doing level %u\n", level
);
2840 le
= Vcb
->trees
.Flink
;
2842 while (le
!= &Vcb
->trees
) {
2843 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2847 if (t
->write
&& t
->header
.level
== level
) {
2850 if (t
->header
.num_items
== 0) {
2853 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
2858 done_deletions
= TRUE
;
2860 le2
= t
->itemlist
.Flink
;
2861 while (le2
!= &t
->itemlist
) {
2862 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2863 firstitem
= td
->key
;
2867 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
2868 t
->root
->id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
);
2870 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2872 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
2873 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, Irp
, rollback
);
2875 if (!NT_SUCCESS(Status
)) {
2876 ERR("reduce_tree_extent returned %08x\n", Status
);
2880 t
->has_new_address
= FALSE
;
2881 } else if (t
->has_address
) {
2882 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, Irp
, rollback
);
2884 if (!NT_SUCCESS(Status
)) {
2885 ERR("reduce_tree_extent returned %08x\n", Status
);
2889 t
->has_address
= FALSE
;
2892 if (!t
->paritem
->ignore
) {
2893 t
->paritem
->ignore
= TRUE
;
2894 t
->parent
->header
.num_items
--;
2895 t
->parent
->size
-= sizeof(internal_node
);
2898 RemoveEntryList(&t
->paritem
->list_entry
);
2899 ExFreePool(t
->paritem
);
2903 } else if (t
->header
.level
!= 0) {
2904 if (t
->has_new_address
) {
2905 Status
= update_extent_level(Vcb
, t
->new_address
, t
, 0, Irp
, rollback
);
2907 if (!NT_SUCCESS(Status
)) {
2908 ERR("update_extent_level returned %08x\n", Status
);
2913 t
->header
.level
= 0;
2915 } else if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2916 TRACE("splitting overlarge tree (%x > %x)\n", t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2917 Status
= split_tree(Vcb
, t
);
2919 if (!NT_SUCCESS(Status
)) {
2920 ERR("split_tree returned %08x\n", Status
);
2932 TRACE("nothing found for level %u\n", level
);
2937 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
2939 for (level
= 0; level
<= max_level
; level
++) {
2942 le
= Vcb
->trees
.Flink
;
2944 while (le
!= &Vcb
->trees
) {
2945 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2947 if (t
->write
&& t
->header
.level
== level
&& t
->header
.num_items
> 0 && t
->parent
&& t
->size
< min_size
&& is_tree_unique(Vcb
, t
, Irp
)) {
2948 Status
= try_tree_amalgamate(Vcb
, t
, Irp
, rollback
);
2949 if (!NT_SUCCESS(Status
)) {
2950 ERR("try_tree_amalgamate returned %08x\n", Status
);
2959 // simplify trees if top tree only has one entry
2961 if (done_deletions
) {
2962 for (level
= max_level
; level
> 0; level
--) {
2963 LIST_ENTRY
*le
, *nextle
;
2965 le
= Vcb
->trees
.Flink
;
2966 while (le
!= &Vcb
->trees
) {
2968 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2970 if (t
->write
&& t
->header
.level
== level
) {
2971 if (!t
->parent
&& t
->header
.num_items
== 1) {
2972 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
2974 tree
* child_tree
= NULL
;
2976 while (le2
!= &t
->itemlist
) {
2977 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2983 TRACE("deleting top-level tree in root %llx with one item\n", t
->root
->id
);
2985 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
2986 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, Irp
, rollback
);
2988 if (!NT_SUCCESS(Status
)) {
2989 ERR("reduce_tree_extent returned %08x\n", Status
);
2993 t
->has_new_address
= FALSE
;
2994 } else if (t
->has_address
) {
2995 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, Irp
, rollback
);
2997 if (!NT_SUCCESS(Status
)) {
2998 ERR("reduce_tree_extent returned %08x\n", Status
);
3002 t
->has_address
= FALSE
;
3005 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3006 KEY searchkey
= {0,0,0};
3009 Status
= find_item(Vcb
, t
->root
, &tp
, &searchkey
, FALSE
, Irp
);
3010 if (!NT_SUCCESS(Status
)) {
3011 ERR("error - find_item returned %08x\n", Status
);
3016 child_tree
= td
->treeholder
.tree
;
3019 child_tree
->parent
= NULL
;
3020 child_tree
->paritem
= NULL
;
3023 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3028 child_tree
->root
->treeholder
.tree
= child_tree
;
3037 return STATUS_SUCCESS
;
3040 static NTSTATUS
remove_root_extents(device_extension
* Vcb
, root
* r
, tree_holder
* th
, UINT8 level
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3045 Status
= load_tree(Vcb
, th
->address
, r
, &th
->tree
, NULL
, NULL
);
3047 if (!NT_SUCCESS(Status
)) {
3048 ERR("load_tree(%llx) returned %08x\n", th
->address
, Status
);
3053 if (th
->tree
->header
.level
> 0) {
3054 LIST_ENTRY
* le
= th
->tree
->itemlist
.Flink
;
3056 while (le
!= &th
->tree
->itemlist
) {
3057 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3060 Status
= remove_root_extents(Vcb
, r
, &td
->treeholder
, th
->tree
->header
.level
- 1, Irp
, rollback
);
3062 if (!NT_SUCCESS(Status
)) {
3063 ERR("remove_root_extents returned %08x\n", Status
);
3073 if (!th
->tree
|| th
->tree
->has_address
) {
3074 Status
= reduce_tree_extent(Vcb
, th
->address
, NULL
, Irp
, rollback
);
3076 if (!NT_SUCCESS(Status
)) {
3077 ERR("reduce_tree_extent(%llx) returned %08x\n", th
->address
, Status
);
3082 return STATUS_SUCCESS
;
3085 static NTSTATUS
drop_root(device_extension
* Vcb
, root
* r
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3090 Status
= remove_root_extents(Vcb
, r
, &r
->treeholder
, r
->root_item
.root_level
, Irp
, rollback
);
3091 if (!NT_SUCCESS(Status
)) {
3092 ERR("remove_root_extents returned %08x\n", Status
);
3096 // remove entry in uuid root (tree 9)
3097 if (Vcb
->uuid_root
) {
3098 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
.uuid
[0], sizeof(UINT64
));
3099 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
3100 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
3102 if (searchkey
.obj_id
!= 0 || searchkey
.offset
!= 0) {
3103 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
3104 if (!NT_SUCCESS(Status
)) {
3105 WARN("find_item returned %08x\n", Status
);
3107 if (!keycmp(tp
.item
->key
, searchkey
))
3108 delete_tree_item(Vcb
, &tp
, rollback
);
3110 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3117 searchkey
.obj_id
= r
->id
;
3118 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
3119 searchkey
.offset
= 0xffffffffffffffff;
3121 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3122 if (!NT_SUCCESS(Status
)) {
3123 ERR("find_item returned %08x\n", Status
);
3127 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
3128 delete_tree_item(Vcb
, &tp
, rollback
);
3130 WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3132 // delete items in tree cache
3134 free_trees_root(Vcb
, r
);
3136 return STATUS_SUCCESS
;
3139 static NTSTATUS
drop_roots(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3140 LIST_ENTRY
*le
= Vcb
->drop_roots
.Flink
, *le2
;
3143 while (le
!= &Vcb
->drop_roots
) {
3144 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3148 Status
= drop_root(Vcb
, r
, Irp
, rollback
);
3149 if (!NT_SUCCESS(Status
)) {
3150 ERR("drop_root(%llx) returned %08x\n", r
->id
, Status
);
3157 return STATUS_SUCCESS
;
3160 static NTSTATUS
update_dev_item(device_extension
* Vcb
, device
* device
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3166 searchkey
.obj_id
= 1;
3167 searchkey
.obj_type
= TYPE_DEV_ITEM
;
3168 searchkey
.offset
= device
->devitem
.dev_id
;
3170 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3171 if (!NT_SUCCESS(Status
)) {
3172 ERR("error - find_item returned %08x\n", Status
);
3176 if (keycmp(tp
.item
->key
, searchkey
)) {
3177 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
3178 return STATUS_INTERNAL_ERROR
;
3181 delete_tree_item(Vcb
, &tp
, rollback
);
3183 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
3185 ERR("out of memory\n");
3186 return STATUS_INSUFFICIENT_RESOURCES
;
3189 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
3191 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
, rollback
)) {
3192 ERR("insert_tree_item failed\n");
3193 return STATUS_INTERNAL_ERROR
;
3196 return STATUS_SUCCESS
;
3199 static void regen_bootstrap(device_extension
* Vcb
) {
3205 le
= Vcb
->sys_chunks
.Flink
;
3206 while (le
!= &Vcb
->sys_chunks
) {
3207 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3209 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
3211 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
3214 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
3221 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, ULONG size
) {
3222 sys_chunk
*sc
, *sc2
;
3225 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
3226 ERR("error - bootstrap is full\n");
3227 return STATUS_INTERNAL_ERROR
;
3230 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3232 ERR("out of memory\n");
3233 return STATUS_INSUFFICIENT_RESOURCES
;
3236 sc
->key
.obj_id
= obj_id
;
3237 sc
->key
.obj_type
= obj_type
;
3238 sc
->key
.offset
= offset
;
3240 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3242 ERR("out of memory\n");
3244 return STATUS_INSUFFICIENT_RESOURCES
;
3247 RtlCopyMemory(sc
->data
, data
, sc
->size
);
3249 le
= Vcb
->sys_chunks
.Flink
;
3250 while (le
!= &Vcb
->sys_chunks
) {
3251 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3253 if (keycmp(sc2
->key
, sc
->key
) == 1)
3258 InsertTailList(le
, &sc
->list_entry
);
3260 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
3262 regen_bootstrap(Vcb
);
3264 return STATUS_SUCCESS
;
3267 static NTSTATUS
create_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3269 CHUNK_ITEM_STRIPE
* cis
;
3270 BLOCK_GROUP_ITEM
* bgi
;
3274 ci
= ExAllocatePoolWithTag(PagedPool
, c
->size
, ALLOC_TAG
);
3276 ERR("out of memory\n");
3277 return STATUS_INSUFFICIENT_RESOURCES
;
3280 RtlCopyMemory(ci
, c
->chunk_item
, c
->size
);
3282 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
, NULL
, Irp
, rollback
)) {
3283 ERR("insert_tree_item failed\n");
3285 return STATUS_INTERNAL_ERROR
;
3288 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
) {
3289 Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
);
3290 if (!NT_SUCCESS(Status
)) {
3291 ERR("add_to_bootstrap returned %08x\n", Status
);
3296 // add BLOCK_GROUP_ITEM to tree 2
3298 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
3300 ERR("out of memory\n");
3301 return STATUS_INSUFFICIENT_RESOURCES
;
3304 bgi
->used
= c
->used
;
3305 bgi
->chunk_tree
= 0x100;
3306 bgi
->flags
= c
->chunk_item
->type
;
3308 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, c
->offset
, TYPE_BLOCK_GROUP_ITEM
, c
->chunk_item
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, Irp
, rollback
)) {
3309 ERR("insert_tree_item failed\n");
3311 return STATUS_INSUFFICIENT_RESOURCES
;
3314 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
3315 factor
= c
->chunk_item
->num_stripes
;
3316 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
3317 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
3318 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
3319 factor
= c
->chunk_item
->num_stripes
- 1;
3320 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
3321 factor
= c
->chunk_item
->num_stripes
- 2;
3322 else // SINGLE, DUPLICATE, RAID1
3325 // add DEV_EXTENTs to tree 4
3327 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3329 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3332 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
3334 ERR("out of memory\n");
3335 return STATUS_INSUFFICIENT_RESOURCES
;
3338 de
->chunktree
= Vcb
->chunk_root
->id
;
3340 de
->address
= c
->offset
;
3341 de
->length
= c
->chunk_item
->size
/ factor
;
3342 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
3344 if (!insert_tree_item(Vcb
, Vcb
->dev_root
, c
->devices
[i
]->devitem
.dev_id
, TYPE_DEV_EXTENT
, cis
[i
].offset
, de
, sizeof(DEV_EXTENT
), NULL
, Irp
, rollback
)) {
3345 ERR("insert_tree_item failed\n");
3347 return STATUS_INTERNAL_ERROR
;
3350 // FIXME - no point in calling this twice for the same device
3351 Status
= update_dev_item(Vcb
, c
->devices
[i
], Irp
, rollback
);
3352 if (!NT_SUCCESS(Status
)) {
3353 ERR("update_dev_item returned %08x\n", Status
);
3360 return STATUS_SUCCESS
;
3363 static void remove_from_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
) {
3367 le
= Vcb
->sys_chunks
.Flink
;
3368 while (le
!= &Vcb
->sys_chunks
) {
3369 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3371 if (sc2
->key
.obj_id
== obj_id
&& sc2
->key
.obj_type
== obj_type
&& sc2
->key
.offset
== offset
) {
3372 RemoveEntryList(&sc2
->list_entry
);
3374 Vcb
->superblock
.n
-= sizeof(KEY
) + sc2
->size
;
3376 ExFreePool(sc2
->data
);
3378 regen_bootstrap(Vcb
);
3386 static NTSTATUS STDCALL
set_xattr(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
,
3387 UINT8
* data
, UINT16 datalen
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3391 TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
3393 xasize
= sizeof(DIR_ITEM
) - 1 + (ULONG
)strlen(name
) + datalen
;
3395 xa
= ExAllocatePoolWithTag(PagedPool
, xasize
, ALLOC_TAG
);
3397 ERR("out of memory\n");
3398 return STATUS_INSUFFICIENT_RESOURCES
;
3402 xa
->key
.obj_type
= 0;
3404 xa
->transid
= Vcb
->superblock
.generation
;
3406 xa
->n
= (UINT16
)strlen(name
);
3407 xa
->type
= BTRFS_TYPE_EA
;
3408 RtlCopyMemory(xa
->name
, name
, strlen(name
));
3409 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
3411 if (!insert_tree_item_batch(batchlist
, Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, xa
, xasize
, Batch_SetXattr
, Irp
, rollback
))
3412 return STATUS_INTERNAL_ERROR
;
3414 return STATUS_SUCCESS
;
3417 static BOOL STDCALL
delete_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3423 TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb
, subvol
->id
, inode
, name
, crc32
);
3425 searchkey
.obj_id
= inode
;
3426 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
3427 searchkey
.offset
= crc32
;
3429 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3430 if (!NT_SUCCESS(Status
)) {
3431 ERR("error - find_item returned %08x\n", Status
);
3435 if (!keycmp(tp
.item
->key
, searchkey
)) { // key exists
3436 ULONG size
= tp
.item
->size
;
3438 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3439 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(DIR_ITEM
));
3443 xa
= (DIR_ITEM
*)tp
.item
->data
;
3448 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
3449 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3454 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
3456 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
3458 UINT8
*newdata
, *dioff
;
3460 newsize
= tp
.item
->size
- (sizeof(DIR_ITEM
) - 1 + xa
->n
+ xa
->m
);
3462 delete_tree_item(Vcb
, &tp
, rollback
);
3465 TRACE("xattr %s deleted\n", name
);
3470 // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
3471 newdata
= ExAllocatePoolWithTag(PagedPool
, newsize
, ALLOC_TAG
);
3473 ERR("out of memory\n");
3477 if ((UINT8
*)xa
> tp
.item
->data
) {
3478 RtlCopyMemory(newdata
, tp
.item
->data
, (UINT8
*)xa
- tp
.item
->data
);
3479 dioff
= newdata
+ ((UINT8
*)xa
- tp
.item
->data
);
3484 if ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
< tp
.item
->size
)
3485 RtlCopyMemory(dioff
, &xa
->name
[xa
->n
+xa
->m
], tp
.item
->size
- ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
));
3487 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, newsize
, NULL
, Irp
, rollback
);
3493 if (xa
->m
+ xa
->n
>= size
) { // FIXME - test this works
3494 WARN("xattr %s not found\n", name
);
3498 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
3504 WARN("xattr %s not found\n", name
);
3510 static NTSTATUS
insert_sparse_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3514 TRACE("((%llx, %llx), %llx, %llx)\n", fcb
->subvol
->id
, fcb
->inode
, start
, length
);
3516 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
3518 ERR("out of memory\n");
3519 return STATUS_INSUFFICIENT_RESOURCES
;
3522 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
3523 ed
->decoded_size
= length
;
3524 ed
->compression
= BTRFS_COMPRESSION_NONE
;
3525 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
3526 ed
->encoding
= BTRFS_ENCODING_NONE
;
3527 ed
->type
= EXTENT_TYPE_REGULAR
;
3529 ed2
= (EXTENT_DATA2
*)ed
->data
;
3533 ed2
->num_bytes
= length
;
3535 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, Irp
, rollback
)) {
3536 ERR("insert_tree_item failed\n");
3537 return STATUS_INTERNAL_ERROR
;
3540 return STATUS_SUCCESS
;
3543 static BOOL
insert_tree_item_batch(LIST_ENTRY
* batchlist
, device_extension
* Vcb
, root
* r
, UINT64 objid
, UINT64 objtype
, UINT64 offset
,
3544 void* data
, UINT16 datalen
, enum batch_operation operation
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3546 batch_root
* br
= NULL
;
3549 le
= batchlist
->Flink
;
3550 while (le
!= batchlist
) {
3551 batch_root
* br2
= CONTAINING_RECORD(le
, batch_root
, list_entry
);
3562 br
= ExAllocatePoolWithTag(PagedPool
, sizeof(batch_root
), ALLOC_TAG
);
3564 ERR("out of memory\n");
3569 InitializeListHead(&br
->items
);
3570 InsertTailList(batchlist
, &br
->list_entry
);
3573 bi
= ExAllocateFromPagedLookasideList(&Vcb
->batch_item_lookaside
);
3575 ERR("out of memory\n");
3579 bi
->key
.obj_id
= objid
;
3580 bi
->key
.obj_type
= objtype
;
3581 bi
->key
.offset
= offset
;
3583 bi
->datalen
= datalen
;
3584 bi
->operation
= operation
;
3586 le
= br
->items
.Blink
;
3587 while (le
!= &br
->items
) {
3588 batch_item
* bi2
= CONTAINING_RECORD(le
, batch_item
, list_entry
);
3590 if (keycmp(bi2
->key
, bi
->key
) == -1) {
3591 InsertHeadList(&bi2
->list_entry
, &bi
->list_entry
);
3598 InsertHeadList(&br
->items
, &bi
->list_entry
);
3611 LIST_ENTRY list_entry
;
3614 static void rationalize_extents(fcb
* fcb
, PIRP Irp
) {
3616 LIST_ENTRY extent_ranges
;
3618 BOOL changed
= FALSE
, truncating
= FALSE
;
3619 UINT32 num_extents
= 0;
3621 InitializeListHead(&extent_ranges
);
3623 le
= fcb
->extents
.Flink
;
3624 while (le
!= &fcb
->extents
) {
3625 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3627 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && ext
->data
->compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
3628 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
3630 if (ed2
->size
!= 0) {
3633 le2
= extent_ranges
.Flink
;
3634 while (le2
!= &extent_ranges
) {
3635 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
3637 if (er2
->address
== ed2
->address
) {
3638 er2
->skip_start
= min(er2
->skip_start
, ed2
->offset
);
3639 er2
->skip_end
= min(er2
->skip_end
, ed2
->size
- ed2
->offset
- ed2
->num_bytes
);
3641 } else if (er2
->address
> ed2
->address
)
3647 er
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_range
), ALLOC_TAG
); // FIXME - should be from lookaside?
3649 ERR("out of memory\n");
3653 er
->address
= ed2
->address
;
3654 er
->length
= ed2
->size
;
3655 er
->offset
= ext
->offset
- ed2
->offset
;
3656 er
->changed
= FALSE
;
3658 er
->skip_start
= ed2
->offset
;
3659 er
->skip_end
= ed2
->size
- ed2
->offset
- ed2
->num_bytes
;
3661 if (er
->skip_start
!= 0 || er
->skip_end
!= 0)
3664 InsertHeadList(le2
->Blink
, &er
->list_entry
);
3673 if (num_extents
== 0 || (num_extents
== 1 && !truncating
))
3676 le
= extent_ranges
.Flink
;
3677 while (le
!= &extent_ranges
) {
3678 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
3683 er
->chunk
= get_chunk_from_address(fcb
->Vcb
, er
->address
);
3686 ERR("get_chunk_from_address(%llx) failed\n", er
->address
);
3691 while (le2
!= &extent_ranges
) {
3692 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
3694 if (!er2
->chunk
&& er2
->address
>= er
->chunk
->offset
&& er2
->address
< er
->chunk
->offset
+ er
->chunk
->chunk_item
->size
)
3695 er2
->chunk
= er
->chunk
;
3705 // truncate beginning or end of extent if unused
3707 le
= extent_ranges
.Flink
;
3708 while (le
!= &extent_ranges
) {
3709 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
3711 if (er
->skip_start
> 0) {
3712 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
3713 while (le2
!= &fcb
->extents
) {
3714 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3716 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && ext
->data
->compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
3717 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
3719 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
3722 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3723 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
3724 if (!NT_SUCCESS(Status
)) {
3725 ERR("update_changed_extent_ref returned %08x\n", Status
);
3729 ext
->data
->decoded_size
-= er
->skip_start
;
3730 ed2
->size
-= er
->skip_start
;
3731 ed2
->address
+= er
->skip_start
;
3732 ed2
->offset
-= er
->skip_start
;
3734 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3735 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
3742 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3743 LIST_ENTRY changed_sector_list
;
3745 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
3747 ERR("out of memory\n");
3751 sc
->ol
.key
= er
->address
;
3752 sc
->checksums
= NULL
;
3753 sc
->length
= er
->skip_start
/ fcb
->Vcb
->superblock
.sector_size
;
3757 InitializeListHead(&changed_sector_list
);
3758 insert_into_ordered_list(&changed_sector_list
, &sc
->ol
);
3760 commit_checksum_changes(fcb
->Vcb
, &changed_sector_list
);
3763 decrease_chunk_usage(er
->chunk
, er
->skip_start
);
3765 space_list_add(fcb
->Vcb
, er
->chunk
, TRUE
, er
->address
, er
->skip_start
, NULL
);
3767 er
->address
+= er
->skip_start
;
3768 er
->length
-= er
->skip_start
;
3771 if (er
->skip_end
> 0) {
3772 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
3773 while (le2
!= &fcb
->extents
) {
3774 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
3776 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && ext
->data
->compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
3777 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
3779 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
3782 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3783 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
3784 if (!NT_SUCCESS(Status
)) {
3785 ERR("update_changed_extent_ref returned %08x\n", Status
);
3789 ext
->data
->decoded_size
-= er
->skip_end
;
3790 ed2
->size
-= er
->skip_end
;
3792 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3793 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
3800 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
)) {
3801 LIST_ENTRY changed_sector_list
;
3803 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
3805 ERR("out of memory\n");
3809 sc
->ol
.key
= er
->address
+ er
->length
- er
->skip_end
;
3810 sc
->checksums
= NULL
;
3811 sc
->length
= er
->skip_end
/ fcb
->Vcb
->superblock
.sector_size
;
3815 InitializeListHead(&changed_sector_list
);
3816 insert_into_ordered_list(&changed_sector_list
, &sc
->ol
);
3818 commit_checksum_changes(fcb
->Vcb
, &changed_sector_list
);
3821 decrease_chunk_usage(er
->chunk
, er
->skip_end
);
3823 space_list_add(fcb
->Vcb
, er
->chunk
, TRUE
, er
->address
+ er
->length
- er
->skip_end
, er
->skip_end
, NULL
);
3825 er
->length
-= er
->skip_end
;
3832 if (num_extents
< 2)
3835 // merge together adjacent extents
3836 le
= extent_ranges
.Flink
;
3837 while (le
!= &extent_ranges
) {
3838 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
3840 if (le
->Flink
!= &extent_ranges
&& er
->length
< MAX_EXTENT_SIZE
) {
3841 extent_range
* er2
= CONTAINING_RECORD(le
->Flink
, extent_range
, list_entry
);
3843 if (er
->chunk
== er2
->chunk
) {
3844 if (er2
->address
== er
->address
+ er
->length
&& er2
->offset
>= er
->offset
+ er
->length
) {
3845 if (er
->length
+ er2
->length
<= MAX_EXTENT_SIZE
) {
3846 er
->length
+= er2
->length
;
3849 RemoveEntryList(&er2
->list_entry
);
3854 // } else { // FIXME - make changing of beginning of offset work
3855 // er2->length = er2->address + er->length - er->address - MAX_EXTENT_SIZE;
3856 // er2->address = er->address + MAX_EXTENT_SIZE;
3857 // er->length = MAX_EXTENT_SIZE;
3869 le
= fcb
->extents
.Flink
;
3870 while (le
!= &fcb
->extents
) {
3871 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3873 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && ext
->data
->compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
3874 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
3876 if (ed2
->size
!= 0) {
3879 le2
= extent_ranges
.Flink
;
3880 while (le2
!= &extent_ranges
) {
3881 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
3883 if (ed2
->address
>= er2
->address
&& ed2
->address
+ ed2
->size
<= er2
->address
+ er2
->length
&& er2
->changed
) {
3886 Status
= update_changed_extent_ref(fcb
->Vcb
, er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3887 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
3888 if (!NT_SUCCESS(Status
)) {
3889 ERR("update_changed_extent_ref returned %08x\n", Status
);
3893 ed2
->offset
+= ed2
->address
- er2
->address
;
3894 ed2
->address
= er2
->address
;
3895 ed2
->size
= er2
->length
;
3896 ext
->data
->decoded_size
= ed2
->size
;
3898 add_changed_extent_ref(er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
3899 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
3913 while (!IsListEmpty(&extent_ranges
)) {
3914 le
= RemoveHeadList(&extent_ranges
);
3915 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
3921 void flush_fcb(fcb
* fcb
, BOOL cache
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3927 #ifdef DEBUG_PARANOID
3928 UINT64 old_size
= 0;
3929 BOOL extents_changed
;
3932 // ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
3934 while (!IsListEmpty(&fcb
->index_list
)) {
3935 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->index_list
);
3936 index_entry
* ie
= CONTAINING_RECORD(le
, index_entry
, list_entry
);
3938 if (ie
->utf8
.Buffer
) ExFreePool(ie
->utf8
.Buffer
);
3939 if (ie
->filepart_uc
.Buffer
) ExFreePool(ie
->filepart_uc
.Buffer
);
3943 fcb
->index_loaded
= FALSE
;
3947 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, Irp
, rollback
);
3949 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, (UINT8
*)fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
, Irp
, rollback
);
3950 if (!NT_SUCCESS(Status
)) {
3951 ERR("set_xattr returned %08x\n", Status
);
3958 #ifdef DEBUG_PARANOID
3959 extents_changed
= fcb
->extents_changed
;
3962 if (fcb
->extents_changed
) {
3964 traverse_ptr next_tp
;
3966 BOOL prealloc
= FALSE
, extents_inline
= FALSE
;
3969 // delete ignored extent items
3970 le
= fcb
->extents
.Flink
;
3971 while (le
!= &fcb
->extents
) {
3972 LIST_ENTRY
* le2
= le
->Flink
;
3973 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3976 RemoveEntryList(&ext
->list_entry
);
3977 ExFreePool(ext
->data
);
3984 if (!IsListEmpty(&fcb
->extents
)) {
3985 rationalize_extents(fcb
, Irp
);
3987 // merge together adjacent EXTENT_DATAs pointing to same extent
3989 le
= fcb
->extents
.Flink
;
3990 while (le
!= &fcb
->extents
) {
3991 LIST_ENTRY
* le2
= le
->Flink
;
3992 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
3994 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && le
->Flink
!= &fcb
->extents
) {
3995 extent
* nextext
= CONTAINING_RECORD(le
->Flink
, extent
, list_entry
);
3997 if (ext
->data
->type
== nextext
->data
->type
) {
3998 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
3999 EXTENT_DATA2
* ned2
= (EXTENT_DATA2
*)nextext
->data
->data
;
4001 if (ed2
->size
!= 0 && ed2
->address
== ned2
->address
&& ed2
->size
== ned2
->size
&&
4002 nextext
->offset
== ext
->offset
+ ed2
->num_bytes
&& ned2
->offset
== ed2
->offset
+ ed2
->num_bytes
) {
4005 ext
->data
->generation
= fcb
->Vcb
->superblock
.generation
;
4006 ed2
->num_bytes
+= ned2
->num_bytes
;
4008 RemoveEntryList(&nextext
->list_entry
);
4009 ExFreePool(nextext
->data
);
4010 ExFreePool(nextext
);
4012 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
4015 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
4017 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
4018 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
4019 if (!NT_SUCCESS(Status
)) {
4020 ERR("update_changed_extent_ref returned %08x\n", Status
);
4034 if (!fcb
->created
) {
4035 // delete existing EXTENT_DATA items
4037 searchkey
.obj_id
= fcb
->inode
;
4038 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4039 searchkey
.offset
= 0;
4041 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4042 if (!NT_SUCCESS(Status
)) {
4043 ERR("error - find_item returned %08x\n", Status
);
4048 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
4049 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4051 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
4056 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
> searchkey
.obj_type
))
4062 if (!fcb
->deleted
) {
4063 // add new EXTENT_DATAs
4067 le
= fcb
->extents
.Flink
;
4068 while (le
!= &fcb
->extents
) {
4069 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4072 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && ext
->offset
> last_end
) {
4073 Status
= insert_sparse_extent(fcb
, last_end
, ext
->offset
- last_end
, Irp
, rollback
);
4074 if (!NT_SUCCESS(Status
)) {
4075 ERR("insert_sparse_extent returned %08x\n", Status
);
4080 ed
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
4082 ERR("out of memory\n");
4083 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4087 RtlCopyMemory(ed
, ext
->data
, ext
->datalen
);
4089 if (!insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, ext
->offset
,
4090 ed
, ext
->datalen
, Batch_Insert
, Irp
, rollback
)) {
4091 ERR("insert_tree_item_batch failed\n");
4092 Status
= STATUS_INTERNAL_ERROR
;
4096 if (ext
->datalen
>= sizeof(EXTENT_DATA
) && ed
->type
== EXTENT_TYPE_PREALLOC
)
4099 if (ext
->datalen
>= sizeof(EXTENT_DATA
) && ed
->type
== EXTENT_TYPE_INLINE
)
4100 extents_inline
= TRUE
;
4102 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
)) {
4103 if (ed
->type
== EXTENT_TYPE_INLINE
)
4104 last_end
= ext
->offset
+ ed
->decoded_size
;
4106 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4108 last_end
= ext
->offset
+ ed2
->num_bytes
;
4115 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && !extents_inline
&&
4116 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
) {
4117 Status
= insert_sparse_extent(fcb
, last_end
, sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
, Irp
, rollback
);
4118 if (!NT_SUCCESS(Status
)) {
4119 ERR("insert_sparse_extent returned %08x\n", Status
);
4124 // update prealloc flag in INODE_ITEM
4127 fcb
->inode_item
.flags
&= ~BTRFS_INODE_PREALLOC
;
4129 fcb
->inode_item
.flags
|= BTRFS_INODE_PREALLOC
;
4131 fcb
->inode_item_changed
= TRUE
;
4134 fcb
->extents_changed
= FALSE
;
4137 if ((!fcb
->created
&& fcb
->inode_item_changed
) || cache
) {
4138 searchkey
.obj_id
= fcb
->inode
;
4139 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4140 searchkey
.offset
= 0xffffffffffffffff;
4142 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4143 if (!NT_SUCCESS(Status
)) {
4144 ERR("error - find_item returned %08x\n", Status
);
4148 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4150 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4152 ERR("out of memory\n");
4156 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4158 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
, rollback
)) {
4159 ERR("insert_tree_item failed\n");
4165 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4170 #ifdef DEBUG_PARANOID
4171 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp
.item
->data
;
4173 old_size
= ii2
->st_size
;
4176 ii_offset
= tp
.item
->key
.offset
;
4180 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4182 searchkey
.obj_id
= fcb
->inode
;
4183 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4184 searchkey
.offset
= ii_offset
;
4186 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4187 if (!NT_SUCCESS(Status
)) {
4188 ERR("error - find_item returned %08x\n", Status
);
4192 if (keycmp(tp
.item
->key
, searchkey
)) {
4193 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4197 RtlCopyMemory(tp
.item
->data
, &fcb
->inode_item
, min(tp
.item
->size
, sizeof(INODE_ITEM
)));
4202 #ifdef DEBUG_PARANOID
4203 if (!extents_changed
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& old_size
!= fcb
->inode_item
.st_size
) {
4204 ERR("error - size has changed but extents not marked as changed\n");
4209 fcb
->created
= FALSE
;
4214 // delete XATTR_ITEMs
4216 searchkey
.obj_id
= fcb
->inode
;
4217 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
4218 searchkey
.offset
= 0;
4220 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4221 if (!NT_SUCCESS(Status
)) {
4222 ERR("error - find_item returned %08x\n", Status
);
4226 while (find_next_item(fcb
->Vcb
, &tp
, &tp2
, FALSE
, Irp
)) {
4229 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
4230 // FIXME - do metadata thing here too?
4231 if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
4232 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4233 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4242 if (!cache
&& fcb
->inode_item_changed
) {
4243 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4245 ERR("out of memory\n");
4249 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4251 if (!insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, ii_offset
, ii
, sizeof(INODE_ITEM
),
4252 Batch_Insert
, Irp
, rollback
)) {
4253 ERR("insert_tree_item_batch failed\n");
4257 fcb
->inode_item_changed
= FALSE
;
4260 if (fcb
->sd_dirty
) {
4261 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, EA_NTACL_HASH
, (UINT8
*)fcb
->sd
, RtlLengthSecurityDescriptor(fcb
->sd
), Irp
, rollback
);
4262 if (!NT_SUCCESS(Status
)) {
4263 ERR("set_xattr returned %08x\n", Status
);
4266 fcb
->sd_dirty
= FALSE
;
4269 if (fcb
->atts_changed
) {
4270 if (!fcb
->atts_deleted
) {
4271 UINT8 val
[16], *val2
;
4272 ULONG atts
= fcb
->atts
;
4274 TRACE("inserting new DOSATTRIB xattr\n");
4276 val2
= &val
[sizeof(val
) - 1];
4279 UINT8 c
= atts
% 16;
4280 *val2
= (c
>= 0 && c
<= 9) ? (c
+ '0') : (c
- 0xa + 'a');
4284 } while (atts
!= 0);
4290 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, val2
, val
+ sizeof(val
) - val2
, Irp
, rollback
);
4291 if (!NT_SUCCESS(Status
)) {
4292 ERR("set_xattr returned %08x\n", Status
);
4296 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, Irp
, rollback
);
4298 fcb
->atts_changed
= FALSE
;
4299 fcb
->atts_deleted
= FALSE
;
4302 if (fcb
->reparse_xattr_changed
) {
4303 if (fcb
->reparse_xattr
.Buffer
&& fcb
->reparse_xattr
.Length
> 0) {
4304 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, (UINT8
*)fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
, Irp
, rollback
);
4305 if (!NT_SUCCESS(Status
)) {
4306 ERR("set_xattr returned %08x\n", Status
);
4310 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, Irp
, rollback
);
4312 fcb
->reparse_xattr_changed
= FALSE
;
4315 if (fcb
->ea_changed
) {
4316 if (fcb
->ea_xattr
.Buffer
&& fcb
->ea_xattr
.Length
> 0) {
4317 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_EA
, EA_EA_HASH
, (UINT8
*)fcb
->ea_xattr
.Buffer
, fcb
->ea_xattr
.Length
, Irp
, rollback
);
4318 if (!NT_SUCCESS(Status
)) {
4319 ERR("set_xattr returned %08x\n", Status
);
4323 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_EA
, EA_EA_HASH
, Irp
, rollback
);
4325 fcb
->ea_changed
= FALSE
;
4331 // ExReleaseResourceLite(fcb->Header.Resource);
4335 static NTSTATUS
drop_chunk(device_extension
* Vcb
, chunk
* c
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4340 CHUNK_ITEM_STRIPE
* cis
;
4342 TRACE("dropping chunk %llx\n", c
->offset
);
4344 // remove free space cache
4346 c
->cache
->deleted
= TRUE
;
4348 flush_fcb(c
->cache
, TRUE
, batchlist
, Irp
, rollback
);
4352 searchkey
.obj_id
= FREE_SPACE_CACHE_ID
;
4353 searchkey
.obj_type
= 0;
4354 searchkey
.offset
= c
->offset
;
4356 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4357 if (!NT_SUCCESS(Status
)) {
4358 ERR("error - find_item returned %08x\n", Status
);
4362 if (!keycmp(tp
.item
->key
, searchkey
)) {
4363 delete_tree_item(Vcb
, &tp
, rollback
);
4367 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
4368 factor
= c
->chunk_item
->num_stripes
;
4369 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
4370 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
4371 else // SINGLE, DUPLICATE, RAID1
4374 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
4375 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
4377 // remove DEV_EXTENTs from tree 4
4378 searchkey
.obj_id
= cis
[i
].dev_id
;
4379 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
4380 searchkey
.offset
= cis
[i
].offset
;
4382 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
4383 if (!NT_SUCCESS(Status
)) {
4384 ERR("error - find_item returned %08x\n", Status
);
4388 if (!keycmp(tp
.item
->key
, searchkey
)) {
4389 delete_tree_item(Vcb
, &tp
, rollback
);
4391 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
4392 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
4394 c
->devices
[i
]->devitem
.bytes_used
-= de
->length
;
4396 space_list_add2(Vcb
, &c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, de
->length
, rollback
);
4399 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
4401 UINT64 len
= c
->chunk_item
->size
/ factor
;
4403 c
->devices
[i
]->devitem
.bytes_used
-= len
;
4404 space_list_add2(Vcb
, &c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, len
, rollback
);
4408 // modify DEV_ITEMs in chunk tree
4409 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
4410 if (c
->devices
[i
]) {
4414 searchkey
.obj_id
= 1;
4415 searchkey
.obj_type
= TYPE_DEV_ITEM
;
4416 searchkey
.offset
= c
->devices
[i
]->devitem
.dev_id
;
4418 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
4419 if (!NT_SUCCESS(Status
)) {
4420 ERR("error - find_item returned %08x\n", Status
);
4424 if (keycmp(tp
.item
->key
, searchkey
)) {
4425 ERR("error - could not find DEV_ITEM for device %llx\n", searchkey
.offset
);
4426 return STATUS_INTERNAL_ERROR
;
4429 delete_tree_item(Vcb
, &tp
, rollback
);
4431 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
4433 ERR("out of memory\n");
4434 return STATUS_INSUFFICIENT_RESOURCES
;
4437 RtlCopyMemory(di
, &c
->devices
[i
]->devitem
, sizeof(DEV_ITEM
));
4439 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, c
->devices
[i
]->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
, rollback
)) {
4440 ERR("insert_tree_item failed\n");
4441 return STATUS_INTERNAL_ERROR
;
4444 for (j
= i
+ 1; j
< c
->chunk_item
->num_stripes
; j
++) {
4445 if (c
->devices
[j
] == c
->devices
[i
])
4446 c
->devices
[j
] = NULL
;
4452 // remove CHUNK_ITEM from chunk tree
4453 searchkey
.obj_id
= 0x100;
4454 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
4455 searchkey
.offset
= c
->offset
;
4457 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
4458 if (!NT_SUCCESS(Status
)) {
4459 ERR("error - find_item returned %08x\n", Status
);
4463 if (!keycmp(tp
.item
->key
, searchkey
))
4464 delete_tree_item(Vcb
, &tp
, rollback
);
4466 WARN("could not find CHUNK_ITEM for chunk %llx\n", c
->offset
);
4468 // remove BLOCK_GROUP_ITEM from extent tree
4469 searchkey
.obj_id
= c
->offset
;
4470 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
4471 searchkey
.offset
= 0xffffffffffffffff;
4473 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
4474 if (!NT_SUCCESS(Status
)) {
4475 ERR("error - find_item returned %08x\n", Status
);
4479 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
4480 delete_tree_item(Vcb
, &tp
, rollback
);
4482 WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c
->offset
);
4485 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
)
4486 remove_from_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
);
4488 RemoveEntryList(&c
->list_entry
);
4490 if (c
->list_entry_changed
.Flink
)
4491 RemoveEntryList(&c
->list_entry_changed
);
4493 ExFreePool(c
->chunk_item
);
4494 ExFreePool(c
->devices
);
4496 while (!IsListEmpty(&c
->space
)) {
4497 space
* s
= CONTAINING_RECORD(c
->space
.Flink
, space
, list_entry
);
4499 RemoveEntryList(&s
->list_entry
);
4503 while (!IsListEmpty(&c
->deleting
)) {
4504 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
4506 RemoveEntryList(&s
->list_entry
);
4510 ExDeleteResourceLite(&c
->lock
);
4511 ExDeleteResourceLite(&c
->changed_extents_lock
);
4515 return STATUS_SUCCESS
;
4518 static NTSTATUS
update_chunks(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4519 LIST_ENTRY
*le
= Vcb
->chunks_changed
.Flink
, *le2
;
4521 UINT64 used_minus_cache
;
4523 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
4525 // FIXME - do tree chunks before data chunks
4527 while (le
!= &Vcb
->chunks_changed
) {
4528 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry_changed
);
4532 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
4534 used_minus_cache
= c
->used
;
4536 // subtract self-hosted cache
4537 if (used_minus_cache
> 0 && c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->cache
&& c
->cache
->inode_item
.st_size
== c
->used
) {
4540 le3
= c
->cache
->extents
.Flink
;
4541 while (le3
!= &c
->cache
->extents
) {
4542 extent
* ext
= CONTAINING_RECORD(le3
, extent
, list_entry
);
4543 EXTENT_DATA
* ed
= ext
->data
;
4546 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
4547 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
4551 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
4552 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4554 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
4555 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
,
4556 sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
4560 if (ed2
->size
!= 0 && ed2
->address
>= c
->offset
&& ed2
->address
+ ed2
->size
<= c
->offset
+ c
->chunk_item
->size
)
4561 used_minus_cache
-= ed2
->size
;
4569 if (used_minus_cache
== 0) {
4570 Status
= drop_chunk(Vcb
, c
, batchlist
, Irp
, rollback
);
4571 if (!NT_SUCCESS(Status
)) {
4572 ERR("drop_chunk returned %08x\n", Status
);
4573 ExReleaseResourceLite(&c
->lock
);
4574 ExReleaseResourceLite(&Vcb
->chunk_lock
);
4577 } else if (c
->created
) {
4578 Status
= create_chunk(Vcb
, c
, Irp
, rollback
);
4579 if (!NT_SUCCESS(Status
)) {
4580 ERR("create_chunk returned %08x\n", Status
);
4581 ExReleaseResourceLite(&c
->lock
);
4582 ExReleaseResourceLite(&Vcb
->chunk_lock
);
4587 if (used_minus_cache
> 0)
4588 ExReleaseResourceLite(&c
->lock
);
4593 ExReleaseResourceLite(&Vcb
->chunk_lock
);
4595 return STATUS_SUCCESS
;
4598 static NTSTATUS
delete_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, UINT64 parinode
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4603 searchkey
.obj_id
= parsubvolid
;
4604 searchkey
.obj_type
= TYPE_ROOT_REF
;
4605 searchkey
.offset
= subvolid
;
4607 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4608 if (!NT_SUCCESS(Status
)) {
4609 ERR("error - find_item returned %08x\n", Status
);
4613 if (!keycmp(searchkey
, tp
.item
->key
)) {
4614 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
4615 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(ROOT_REF
));
4616 return STATUS_INTERNAL_ERROR
;
4621 rr
= (ROOT_REF
*)tp
.item
->data
;
4622 len
= tp
.item
->size
;
4627 if (len
< sizeof(ROOT_REF
) || len
< sizeof(ROOT_REF
) - 1 + rr
->n
) {
4628 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4632 itemlen
= sizeof(ROOT_REF
) - sizeof(char) + rr
->n
;
4634 if (rr
->dir
== parinode
&& rr
->n
== utf8
->Length
&& RtlCompareMemory(rr
->name
, utf8
->Buffer
, rr
->n
) == rr
->n
) {
4635 ULONG newlen
= tp
.item
->size
- itemlen
;
4637 delete_tree_item(Vcb
, &tp
, rollback
);
4640 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4642 UINT8
*newrr
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *rroff
;
4645 ERR("out of memory\n");
4646 return STATUS_INSUFFICIENT_RESOURCES
;
4649 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4651 if ((UINT8
*)rr
> tp
.item
->data
) {
4652 RtlCopyMemory(newrr
, tp
.item
->data
, (UINT8
*)rr
- tp
.item
->data
);
4653 rroff
= newrr
+ ((UINT8
*)rr
- tp
.item
->data
);
4658 if ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
< tp
.item
->size
)
4659 RtlCopyMemory(rroff
, &rr
->name
[rr
->n
], tp
.item
->size
- ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
));
4661 insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newrr
, newlen
, NULL
, Irp
, rollback
);
4667 if (len
> itemlen
) {
4669 rr
= (ROOT_REF
*)&rr
->name
[rr
->n
];
4675 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey
.offset
, searchkey
.obj_id
);
4676 return STATUS_NOT_FOUND
;
4679 return STATUS_SUCCESS
;
4682 static NTSTATUS
add_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, ROOT_REF
* rr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4687 searchkey
.obj_id
= parsubvolid
;
4688 searchkey
.obj_type
= TYPE_ROOT_REF
;
4689 searchkey
.offset
= subvolid
;
4691 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4692 if (!NT_SUCCESS(Status
)) {
4693 ERR("error - find_item returned %08x\n", Status
);
4697 if (!keycmp(searchkey
, tp
.item
->key
)) {
4698 ULONG rrsize
= tp
.item
->size
+ sizeof(ROOT_REF
) - 1 + rr
->n
;
4701 rr2
= ExAllocatePoolWithTag(PagedPool
, rrsize
, ALLOC_TAG
);
4703 ERR("out of memory\n");
4704 return STATUS_INSUFFICIENT_RESOURCES
;
4707 if (tp
.item
->size
> 0)
4708 RtlCopyMemory(rr2
, tp
.item
->data
, tp
.item
->size
);
4710 RtlCopyMemory(rr2
+ tp
.item
->size
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
);
4713 delete_tree_item(Vcb
, &tp
, rollback
);
4715 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr2
, rrsize
, NULL
, Irp
, rollback
)) {
4716 ERR("error - failed to insert item\n");
4718 return STATUS_INTERNAL_ERROR
;
4721 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
, NULL
, Irp
, rollback
)) {
4722 ERR("error - failed to insert item\n");
4724 return STATUS_INTERNAL_ERROR
;
4728 return STATUS_SUCCESS
;
4731 static NTSTATUS STDCALL
update_root_backref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4738 searchkey
.obj_id
= parsubvolid
;
4739 searchkey
.obj_type
= TYPE_ROOT_REF
;
4740 searchkey
.offset
= subvolid
;
4742 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4743 if (!NT_SUCCESS(Status
)) {
4744 ERR("error - find_item returned %08x\n", Status
);
4748 if (!keycmp(tp
.item
->key
, searchkey
) && tp
.item
->size
> 0) {
4749 datalen
= tp
.item
->size
;
4751 data
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
4753 ERR("out of memory\n");
4754 return STATUS_INSUFFICIENT_RESOURCES
;
4757 RtlCopyMemory(data
, tp
.item
->data
, datalen
);
4762 searchkey
.obj_id
= subvolid
;
4763 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
4764 searchkey
.offset
= parsubvolid
;
4766 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4767 if (!NT_SUCCESS(Status
)) {
4768 ERR("error - find_item returned %08x\n", Status
);
4776 if (!keycmp(tp
.item
->key
, searchkey
))
4777 delete_tree_item(Vcb
, &tp
, rollback
);
4780 if (!insert_tree_item(Vcb
, Vcb
->root_root
, subvolid
, TYPE_ROOT_BACKREF
, parsubvolid
, data
, datalen
, NULL
, Irp
, rollback
)) {
4781 ERR("error - failed to insert item\n");
4783 return STATUS_INTERNAL_ERROR
;
4787 return STATUS_SUCCESS
;
4790 static NTSTATUS
add_root_item_to_cache(device_extension
* Vcb
, UINT64 root
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4795 searchkey
.obj_id
= root
;
4796 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
4797 searchkey
.offset
= 0xffffffffffffffff;
4799 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4800 if (!NT_SUCCESS(Status
)) {
4801 ERR("error - find_item returned %08x\n", Status
);
4805 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4806 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
4808 return STATUS_INTERNAL_ERROR
;
4811 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
4812 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
4814 ERR("out of memory\n");
4815 return STATUS_INSUFFICIENT_RESOURCES
;
4818 if (tp
.item
->size
> 0)
4819 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
4821 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
4823 delete_tree_item(Vcb
, &tp
, rollback
);
4825 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
, rollback
)) {
4826 ERR("insert_tree_item failed\n");
4827 return STATUS_INTERNAL_ERROR
;
4830 tp
.tree
->write
= TRUE
;
4833 return STATUS_SUCCESS
;
4836 static NTSTATUS
add_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT32 crc32
, DIR_ITEM
* di
, ULONG disize
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4842 searchkey
.obj_id
= inode
;
4843 searchkey
.obj_type
= TYPE_DIR_ITEM
;
4844 searchkey
.offset
= crc32
;
4846 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4847 if (!NT_SUCCESS(Status
)) {
4848 ERR("error - find_item returned %08x\n", Status
);
4852 if (!keycmp(tp
.item
->key
, searchkey
)) {
4853 ULONG maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
4855 if (tp
.item
->size
+ disize
> maxlen
) {
4856 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp
.item
->size
, disize
, maxlen
);
4857 return STATUS_INTERNAL_ERROR
;
4860 di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ disize
, ALLOC_TAG
);
4862 ERR("out of memory\n");
4863 return STATUS_INSUFFICIENT_RESOURCES
;
4866 if (tp
.item
->size
> 0)
4867 RtlCopyMemory(di2
, tp
.item
->data
, tp
.item
->size
);
4869 RtlCopyMemory(di2
+ tp
.item
->size
, di
, disize
);
4871 delete_tree_item(Vcb
, &tp
, rollback
);
4873 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di2
, tp
.item
->size
+ disize
, NULL
, Irp
, rollback
);
4877 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di
, disize
, NULL
, Irp
, rollback
);
4880 return STATUS_SUCCESS
;
4883 static NTSTATUS
add_inode_extref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, UINT64 index
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4889 searchkey
.obj_id
= inode
;
4890 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
4891 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
4893 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4894 if (!NT_SUCCESS(Status
)) {
4895 ERR("error - find_item returned %08x\n", Status
);
4899 if (!keycmp(searchkey
, tp
.item
->key
)) {
4900 ULONG iersize
= tp
.item
->size
+ sizeof(INODE_EXTREF
) - 1 + utf8
->Length
;
4902 UINT32 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
4904 if (iersize
> maxlen
) {
4905 ERR("item would be too long (%u > %u)\n", iersize
, maxlen
);
4906 return STATUS_INTERNAL_ERROR
;
4909 ier2
= ExAllocatePoolWithTag(PagedPool
, iersize
, ALLOC_TAG
);
4911 ERR("out of memory\n");
4912 return STATUS_INSUFFICIENT_RESOURCES
;
4915 if (tp
.item
->size
> 0)
4916 RtlCopyMemory(ier2
, tp
.item
->data
, tp
.item
->size
);
4918 ier
= (INODE_EXTREF
*)&ier2
[tp
.item
->size
];
4919 ier
->dir
= parinode
;
4921 ier
->n
= utf8
->Length
;
4922 RtlCopyMemory(ier
->name
, utf8
->Buffer
, utf8
->Length
);
4924 delete_tree_item(Vcb
, &tp
, rollback
);
4926 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ier2
, iersize
, NULL
, Irp
, rollback
)) {
4927 ERR("error - failed to insert item\n");
4928 return STATUS_INTERNAL_ERROR
;
4931 ier
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_EXTREF
) - 1 + utf8
->Length
, ALLOC_TAG
);
4933 ERR("out of memory\n");
4934 return STATUS_INSUFFICIENT_RESOURCES
;
4937 ier
->dir
= parinode
;
4939 ier
->n
= utf8
->Length
;
4940 RtlCopyMemory(ier
->name
, utf8
->Buffer
, utf8
->Length
);
4942 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ier
, sizeof(INODE_EXTREF
) - 1 + utf8
->Length
, NULL
, Irp
, rollback
)) {
4943 ERR("error - failed to insert item\n");
4944 return STATUS_INTERNAL_ERROR
;
4948 return STATUS_SUCCESS
;
4951 static NTSTATUS
add_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, UINT64 index
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4957 searchkey
.obj_id
= inode
;
4958 searchkey
.obj_type
= TYPE_INODE_REF
;
4959 searchkey
.offset
= parinode
;
4961 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4962 if (!NT_SUCCESS(Status
)) {
4963 ERR("error - find_item returned %08x\n", Status
);
4967 if (!keycmp(searchkey
, tp
.item
->key
)) {
4968 ULONG irsize
= tp
.item
->size
+ sizeof(INODE_REF
) - 1 + utf8
->Length
;
4970 UINT32 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
4972 if (irsize
> maxlen
) {
4973 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
4974 TRACE("INODE_REF too long, creating INODE_EXTREF\n");
4975 return add_inode_extref(Vcb
, subvol
, inode
, parinode
, index
, utf8
, Irp
, rollback
);
4977 ERR("item would be too long (%u > %u)\n", irsize
, maxlen
);
4978 return STATUS_INTERNAL_ERROR
;
4982 ir2
= ExAllocatePoolWithTag(PagedPool
, irsize
, ALLOC_TAG
);
4984 ERR("out of memory\n");
4985 return STATUS_INSUFFICIENT_RESOURCES
;
4988 if (tp
.item
->size
> 0)
4989 RtlCopyMemory(ir2
, tp
.item
->data
, tp
.item
->size
);
4991 ir
= (INODE_REF
*)&ir2
[tp
.item
->size
];
4993 ir
->n
= utf8
->Length
;
4994 RtlCopyMemory(ir
->name
, utf8
->Buffer
, utf8
->Length
);
4996 delete_tree_item(Vcb
, &tp
, rollback
);
4998 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ir2
, irsize
, NULL
, Irp
, rollback
)) {
4999 ERR("error - failed to insert item\n");
5000 return STATUS_INTERNAL_ERROR
;
5003 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + utf8
->Length
, ALLOC_TAG
);
5005 ERR("out of memory\n");
5006 return STATUS_INSUFFICIENT_RESOURCES
;
5010 ir
->n
= utf8
->Length
;
5011 RtlCopyMemory(ir
->name
, utf8
->Buffer
, utf8
->Length
);
5013 if (!insert_tree_item(Vcb
, subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ir
, sizeof(INODE_REF
) - 1 + ir
->n
, NULL
, Irp
, rollback
)) {
5014 ERR("error - failed to insert item\n");
5015 return STATUS_INTERNAL_ERROR
;
5019 return STATUS_SUCCESS
;
5022 static NTSTATUS
flush_fileref(file_ref
* fileref
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5025 // if fileref created and then immediately deleted, do nothing
5026 if (fileref
->created
&& fileref
->deleted
) {
5027 fileref
->dirty
= FALSE
;
5028 return STATUS_SUCCESS
;
5031 if (fileref
->fcb
->ads
) {
5032 fileref
->dirty
= FALSE
;
5033 return STATUS_SUCCESS
;
5036 if (fileref
->created
) {
5041 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5043 disize
= sizeof(DIR_ITEM
) - 1 + fileref
->utf8
.Length
;
5044 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
5046 ERR("out of memory\n");
5047 return STATUS_INSUFFICIENT_RESOURCES
;
5050 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
5051 di
->key
.obj_id
= fileref
->fcb
->inode
;
5052 di
->key
.obj_type
= TYPE_INODE_ITEM
;
5054 } else { // subvolume
5055 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
5056 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
5057 di
->key
.offset
= 0xffffffffffffffff;
5060 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
5062 di
->n
= (UINT16
)fileref
->utf8
.Length
;
5063 di
->type
= fileref
->fcb
->type
;
5064 RtlCopyMemory(di
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5066 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
5068 ERR("out of memory\n");
5069 return STATUS_INSUFFICIENT_RESOURCES
;
5072 RtlCopyMemory(di2
, di
, disize
);
5074 if (!insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
, fileref
->index
,
5075 di
, disize
, Batch_Insert
, Irp
, rollback
)) {
5076 ERR("insert_tree_item_batch failed\n");
5077 return STATUS_INTERNAL_ERROR
;
5080 if (!insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
, crc32
,
5081 di2
, disize
, Batch_DirItem
, Irp
, rollback
)) {
5082 ERR("insert_tree_item_batch failed\n");
5083 return STATUS_INTERNAL_ERROR
;
5086 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
5089 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + fileref
->utf8
.Length
, ALLOC_TAG
);
5091 ERR("out of memory\n");
5092 return STATUS_INSUFFICIENT_RESOURCES
;
5095 ir
->index
= fileref
->index
;
5096 ir
->n
= fileref
->utf8
.Length
;
5097 RtlCopyMemory(ir
->name
, fileref
->utf8
.Buffer
, ir
->n
);
5099 if (!insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
5100 ir
, sizeof(INODE_REF
) - 1 + ir
->n
, Batch_InodeRef
, Irp
, rollback
)) {
5101 ERR("insert_tree_item_batch failed\n");
5102 return STATUS_INTERNAL_ERROR
;
5108 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->utf8
.Length
;
5110 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
5112 ERR("out of memory\n");
5113 return STATUS_INSUFFICIENT_RESOURCES
;
5116 rr
->dir
= fileref
->parent
->fcb
->inode
;
5117 rr
->index
= fileref
->index
;
5118 rr
->n
= fileref
->utf8
.Length
;
5119 RtlCopyMemory(rr
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5121 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
, rollback
);
5122 if (!NT_SUCCESS(Status
)) {
5123 ERR("add_root_ref returned %08x\n", Status
);
5127 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
5128 if (!NT_SUCCESS(Status
)) {
5129 ERR("update_root_backref returned %08x\n", Status
);
5134 fileref
->created
= FALSE
;
5135 } else if (fileref
->deleted
) {
5141 if (fileref
->oldutf8
.Buffer
)
5142 name
= &fileref
->oldutf8
;
5144 name
= &fileref
->utf8
;
5146 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)name
->Buffer
, name
->Length
);
5148 TRACE("deleting %.*S\n", file_desc_fileref(fileref
));
5150 // delete DIR_ITEM (0x54)
5152 Status
= delete_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, crc32
, name
, Irp
, rollback
);
5153 if (!NT_SUCCESS(Status
)) {
5154 ERR("delete_dir_item returned %08x\n", Status
);
5158 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
5159 // delete INODE_REF (0xc)
5161 Status
= delete_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, name
, Irp
, rollback
);
5162 if (!NT_SUCCESS(Status
)) {
5163 ERR("delete_inode_ref returned %08x\n", Status
);
5166 } else { // subvolume
5167 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, name
, Irp
, rollback
);
5168 if (!NT_SUCCESS(Status
)) {
5169 ERR("delete_root_ref returned %08x\n", Status
);
5172 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
5173 if (!NT_SUCCESS(Status
)) {
5174 ERR("update_root_backref returned %08x\n", Status
);
5179 // delete DIR_INDEX (0x60)
5181 searchkey
.obj_id
= fileref
->parent
->fcb
->inode
;
5182 searchkey
.obj_type
= TYPE_DIR_INDEX
;
5183 searchkey
.offset
= fileref
->index
;
5185 Status
= find_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
5186 if (!NT_SUCCESS(Status
)) {
5187 ERR("error - find_item returned %08x\n", Status
);
5188 Status
= STATUS_INTERNAL_ERROR
;
5192 if (!keycmp(searchkey
, tp
.item
->key
)) {
5193 delete_tree_item(fileref
->fcb
->Vcb
, &tp
, rollback
);
5194 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5197 if (fileref
->oldutf8
.Buffer
) {
5198 ExFreePool(fileref
->oldutf8
.Buffer
);
5199 fileref
->oldutf8
.Buffer
= NULL
;
5201 } else { // rename or change type
5202 PANSI_STRING oldutf8
= fileref
->oldutf8
.Buffer
? &fileref
->oldutf8
: &fileref
->utf8
;
5203 UINT32 crc32
, oldcrc32
;
5209 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5211 if (!fileref
->oldutf8
.Buffer
)
5214 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->oldutf8
.Buffer
, fileref
->oldutf8
.Length
);
5216 // delete DIR_ITEM (0x54)
5218 Status
= delete_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, oldcrc32
, oldutf8
, Irp
, rollback
);
5219 if (!NT_SUCCESS(Status
)) {
5220 ERR("delete_dir_item returned %08x\n", Status
);
5224 // add DIR_ITEM (0x54)
5226 disize
= sizeof(DIR_ITEM
) - 1 + fileref
->utf8
.Length
;
5227 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
5229 ERR("out of memory\n");
5230 return STATUS_INSUFFICIENT_RESOURCES
;
5233 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
5235 ERR("out of memory\n");
5237 return STATUS_INSUFFICIENT_RESOURCES
;
5240 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
5241 di
->key
.obj_id
= fileref
->fcb
->inode
;
5242 di
->key
.obj_type
= TYPE_INODE_ITEM
;
5244 } else { // subvolume
5245 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
5246 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
5247 di
->key
.offset
= 0xffffffffffffffff;
5250 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
5252 di
->n
= (UINT16
)fileref
->utf8
.Length
;
5253 di
->type
= fileref
->fcb
->type
;
5254 RtlCopyMemory(di
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5256 RtlCopyMemory(di2
, di
, disize
);
5258 Status
= add_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, crc32
, di
, disize
, Irp
, rollback
);
5259 if (!NT_SUCCESS(Status
)) {
5260 ERR("add_dir_item returned %08x\n", Status
);
5264 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
5265 // delete INODE_REF (0xc)
5267 Status
= delete_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
, rollback
);
5268 if (!NT_SUCCESS(Status
)) {
5269 ERR("delete_inode_ref returned %08x\n", Status
);
5273 // add INODE_REF (0xc)
5275 Status
= add_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, fileref
->index
, &fileref
->utf8
, Irp
, rollback
);
5276 if (!NT_SUCCESS(Status
)) {
5277 ERR("add_inode_ref returned %08x\n", Status
);
5280 } else { // subvolume
5284 // FIXME - make sure this works with duff subvols within snapshots
5286 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
, rollback
);
5287 if (!NT_SUCCESS(Status
)) {
5288 ERR("delete_root_ref returned %08x\n", Status
);
5291 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->utf8
.Length
;
5293 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
5295 ERR("out of memory\n");
5296 return STATUS_INSUFFICIENT_RESOURCES
;
5299 rr
->dir
= fileref
->parent
->fcb
->inode
;
5300 rr
->index
= fileref
->index
;
5301 rr
->n
= fileref
->utf8
.Length
;
5302 RtlCopyMemory(rr
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
5304 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
, rollback
);
5305 if (!NT_SUCCESS(Status
)) {
5306 ERR("add_root_ref returned %08x\n", Status
);
5310 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
5311 if (!NT_SUCCESS(Status
)) {
5312 ERR("update_root_backref returned %08x\n", Status
);
5317 // delete DIR_INDEX (0x60)
5319 searchkey
.obj_id
= fileref
->parent
->fcb
->inode
;
5320 searchkey
.obj_type
= TYPE_DIR_INDEX
;
5321 searchkey
.offset
= fileref
->index
;
5323 Status
= find_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
5324 if (!NT_SUCCESS(Status
)) {
5325 ERR("error - find_item returned %08x\n", Status
);
5326 Status
= STATUS_INTERNAL_ERROR
;
5330 if (!keycmp(searchkey
, tp
.item
->key
)) {
5331 delete_tree_item(fileref
->fcb
->Vcb
, &tp
, rollback
);
5332 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5334 WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, fileref
->fcb
->subvol
->id
);
5336 // add DIR_INDEX (0x60)
5338 if (!insert_tree_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
, fileref
->index
, di2
, disize
, NULL
, Irp
, rollback
)) {
5339 ERR("insert_tree_item failed\n");
5340 Status
= STATUS_INTERNAL_ERROR
;
5344 if (fileref
->oldutf8
.Buffer
) {
5345 ExFreePool(fileref
->oldutf8
.Buffer
);
5346 fileref
->oldutf8
.Buffer
= NULL
;
5350 fileref
->dirty
= FALSE
;
5352 return STATUS_SUCCESS
;
5355 NTSTATUS STDCALL
do_write(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5357 LIST_ENTRY
*le
, batchlist
;
5358 BOOL cache_changed
= FALSE
;
5359 #ifdef DEBUG_FLUSH_TIMES
5360 UINT64 filerefs
= 0, fcbs
= 0;
5361 LARGE_INTEGER freq
, time1
, time2
;
5363 #ifdef DEBUG_WRITE_LOOPS
5367 TRACE("(%p)\n", Vcb
);
5369 InitializeListHead(&batchlist
);
5371 #ifdef DEBUG_FLUSH_TIMES
5372 time1
= KeQueryPerformanceCounter(&freq
);
5375 while (!IsListEmpty(&Vcb
->dirty_filerefs
)) {
5376 dirty_fileref
* dirt
;
5378 le
= RemoveHeadList(&Vcb
->dirty_filerefs
);
5380 dirt
= CONTAINING_RECORD(le
, dirty_fileref
, list_entry
);
5382 flush_fileref(dirt
->fileref
, &batchlist
, Irp
, rollback
);
5383 free_fileref(dirt
->fileref
);
5386 #ifdef DEBUG_FLUSH_TIMES
5391 commit_batch_list(Vcb
, &batchlist
, Irp
, rollback
);
5393 #ifdef DEBUG_FLUSH_TIMES
5394 time2
= KeQueryPerformanceCounter(NULL
);
5396 ERR("flushed %llu filerefs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
5398 time1
= KeQueryPerformanceCounter(&freq
);
5401 // We process deleted streams first, so we don't run over our xattr
5402 // limit unless we absolutely have to.
5404 le
= Vcb
->dirty_fcbs
.Flink
;
5405 while (le
!= &Vcb
->dirty_fcbs
) {
5407 LIST_ENTRY
* le2
= le
->Flink
;
5409 dirt
= CONTAINING_RECORD(le
, dirty_fcb
, list_entry
);
5411 if (dirt
->fcb
->deleted
&& dirt
->fcb
->ads
) {
5412 RemoveEntryList(le
);
5414 flush_fcb(dirt
->fcb
, FALSE
, &batchlist
, Irp
, rollback
);
5415 free_fcb(dirt
->fcb
);
5418 #ifdef DEBUG_FLUSH_TIMES
5426 le
= Vcb
->dirty_fcbs
.Flink
;
5427 while (le
!= &Vcb
->dirty_fcbs
) {
5429 LIST_ENTRY
* le2
= le
->Flink
;
5431 dirt
= CONTAINING_RECORD(le
, dirty_fcb
, list_entry
);
5433 if (dirt
->fcb
->subvol
!= Vcb
->root_root
|| dirt
->fcb
->deleted
) {
5434 RemoveEntryList(le
);
5436 flush_fcb(dirt
->fcb
, FALSE
, &batchlist
, Irp
, rollback
);
5437 free_fcb(dirt
->fcb
);
5440 #ifdef DEBUG_FLUSH_TIMES
5448 commit_batch_list(Vcb
, &batchlist
, Irp
, rollback
);
5450 #ifdef DEBUG_FLUSH_TIMES
5451 time2
= KeQueryPerformanceCounter(NULL
);
5453 ERR("flushed %llu fcbs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
5456 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
5457 if (!IsListEmpty(&Vcb
->sector_checksums
)) {
5458 update_checksum_tree(Vcb
, Irp
, rollback
);
5460 ExReleaseResourceLite(&Vcb
->checksum_lock
);
5462 if (!IsListEmpty(&Vcb
->drop_roots
)) {
5463 Status
= drop_roots(Vcb
, Irp
, rollback
);
5465 if (!NT_SUCCESS(Status
)) {
5466 ERR("drop_roots returned %08x\n", Status
);
5471 if (!IsListEmpty(&Vcb
->chunks_changed
)) {
5472 Status
= update_chunks(Vcb
, &batchlist
, Irp
, rollback
);
5474 if (!NT_SUCCESS(Status
)) {
5475 ERR("update_chunks returned %08x\n", Status
);
5480 commit_batch_list(Vcb
, &batchlist
, Irp
, rollback
);
5482 // If only changing superblock, e.g. changing label, we still need to rewrite
5483 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
5484 if (!Vcb
->root_root
->treeholder
.tree
|| !Vcb
->root_root
->treeholder
.tree
->write
) {
5489 searchkey
.obj_id
= 0;
5490 searchkey
.obj_type
= 0;
5491 searchkey
.offset
= 0;
5493 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5494 if (!NT_SUCCESS(Status
)) {
5495 ERR("error - find_item returned %08x\n", Status
);
5499 Vcb
->root_root
->treeholder
.tree
->write
= TRUE
;
5502 // make sure we always update the extent tree
5503 Status
= add_root_item_to_cache(Vcb
, BTRFS_ROOT_EXTENT
, Irp
, rollback
);
5504 if (!NT_SUCCESS(Status
)) {
5505 ERR("add_root_item_to_cache returned %08x\n", Status
);
5510 Status
= add_parents(Vcb
, Irp
, rollback
);
5511 if (!NT_SUCCESS(Status
)) {
5512 ERR("add_parents returned %08x\n", Status
);
5516 Status
= do_splits(Vcb
, Irp
, rollback
);
5517 if (!NT_SUCCESS(Status
)) {
5518 ERR("do_splits returned %08x\n", Status
);
5522 Status
= allocate_tree_extents(Vcb
, Irp
, rollback
);
5523 if (!NT_SUCCESS(Status
)) {
5524 ERR("add_parents returned %08x\n", Status
);
5528 Status
= update_chunk_usage(Vcb
, Irp
, rollback
);
5529 if (!NT_SUCCESS(Status
)) {
5530 ERR("update_chunk_usage returned %08x\n", Status
);
5534 Status
= allocate_cache(Vcb
, &cache_changed
, Irp
, rollback
);
5535 if (!NT_SUCCESS(Status
)) {
5536 ERR("allocate_cache returned %08x\n", Status
);
5540 #ifdef DEBUG_WRITE_LOOPS
5544 ERR("cache has changed, looping again\n");
5546 } while (cache_changed
|| !trees_consistent(Vcb
, rollback
));
5548 #ifdef DEBUG_WRITE_LOOPS
5549 ERR("%u loops\n", loops
);
5552 TRACE("trees consistent\n");
5554 Status
= update_root_root(Vcb
, Irp
, rollback
);
5555 if (!NT_SUCCESS(Status
)) {
5556 ERR("update_root_root returned %08x\n", Status
);
5560 Status
= write_trees(Vcb
, Irp
);
5561 if (!NT_SUCCESS(Status
)) {
5562 ERR("write_trees returned %08x\n", Status
);
5566 Vcb
->superblock
.cache_generation
= Vcb
->superblock
.generation
;
5568 Status
= write_superblocks(Vcb
, Irp
);
5569 if (!NT_SUCCESS(Status
)) {
5570 ERR("write_superblocks returned %08x\n", Status
);
5574 clean_space_cache(Vcb
);
5576 Vcb
->superblock
.generation
++;
5578 Status
= STATUS_SUCCESS
;
5580 le
= Vcb
->trees
.Flink
;
5581 while (le
!= &Vcb
->trees
) {
5582 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
5584 #ifdef DEBUG_PARANOID
5588 searchkey
.obj_id
= t
->header
.address
;
5589 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
5590 searchkey
.offset
= 0xffffffffffffffff;
5592 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5593 if (!NT_SUCCESS(Status
)) {
5594 ERR("error - find_item returned %08x\n", Status
);
5598 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5599 searchkey
.obj_id
= t
->header
.address
;
5600 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5601 searchkey
.offset
= 0xffffffffffffffff;
5603 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5604 if (!NT_SUCCESS(Status
)) {
5605 ERR("error - find_item returned %08x\n", Status
);
5609 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5610 ERR("error - could not find entry in extent tree for tree at %llx\n", t
->header
.address
);
5621 Vcb
->need_write
= FALSE
;
5623 while (!IsListEmpty(&Vcb
->drop_roots
)) {
5624 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->drop_roots
);
5625 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
5627 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
5628 ExFreePool(r
->nonpaged
);
5633 TRACE("do_write returning %08x\n", Status
);
5639 static void print_stats(device_extension
* Vcb
) {
5640 ERR("READ STATS:\n");
5641 ERR("number of reads: %llu\n", Vcb
->stats
.num_reads
);
5642 ERR("data read: %llu bytes\n", Vcb
->stats
.data_read
);
5643 ERR("total time taken: %llu\n", Vcb
->stats
.read_total_time
);
5644 ERR("csum time taken: %llu\n", Vcb
->stats
.read_csum_time
);
5645 ERR("disk time taken: %llu\n", Vcb
->stats
.read_disk_time
);
5646 ERR("other time taken: %llu\n", Vcb
->stats
.read_total_time
- Vcb
->stats
.read_csum_time
- Vcb
->stats
.read_disk_time
);
5648 RtlZeroMemory(&Vcb
->stats
, sizeof(debug_stats
));
5652 static void do_flush(device_extension
* Vcb
) {
5653 LIST_ENTRY rollback
;
5655 InitializeListHead(&rollback
);
5657 FsRtlEnterFileSystem();
5659 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
5665 if (Vcb
->need_write
&& !Vcb
->readonly
)
5666 do_write(Vcb
, NULL
, &rollback
);
5670 clear_rollback(Vcb
, &rollback
);
5672 ExReleaseResourceLite(&Vcb
->tree_lock
);
5674 FsRtlExitFileSystem();
5677 void STDCALL
flush_thread(void* context
) {
5678 DEVICE_OBJECT
* devobj
= context
;
5679 device_extension
* Vcb
= devobj
->DeviceExtension
;
5680 LARGE_INTEGER due_time
;
5682 ObReferenceObject(devobj
);
5684 KeInitializeTimer(&Vcb
->flush_thread_timer
);
5686 due_time
.QuadPart
= (UINT64
)Vcb
->options
.flush_interval
* -10000000;
5688 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
5691 KeWaitForSingleObject(&Vcb
->flush_thread_timer
, Executive
, KernelMode
, FALSE
, NULL
);
5693 if (!(devobj
->Vpb
->Flags
& VPB_MOUNTED
) || Vcb
->removing
)
5698 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
5701 ObDereferenceObject(devobj
);
5702 KeCancelTimer(&Vcb
->flush_thread_timer
);
5704 KeSetEvent(&Vcb
->flush_thread_finished
, 0, FALSE
);
5706 PsTerminateSystemThread(STATUS_SUCCESS
);