1 /* Copyright (c) Mark Harmstone 2016-17
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 NTSTATUS
load_tree(device_extension
* Vcb
, UINT64 addr
, root
* r
, tree
** pt
, UINT64 generation
, PIRP Irp
) {
31 buf
= ExAllocatePoolWithTag(PagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
33 ERR("out of memory\n");
34 return STATUS_INSUFFICIENT_RESOURCES
;
37 Status
= read_data(Vcb
, addr
, Vcb
->superblock
.node_size
, NULL
, TRUE
, buf
, NULL
, &c
, Irp
, generation
, FALSE
, NormalPagePriority
);
38 if (!NT_SUCCESS(Status
)) {
39 ERR("read_data returned 0x%08x\n", Status
);
44 th
= (tree_header
*)buf
;
46 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
48 ERR("out of memory\n");
50 return STATUS_INSUFFICIENT_RESOURCES
;
53 RtlCopyMemory(&t
->header
, th
, sizeof(tree_header
));
54 t
->hash
= calc_crc32c(0xffffffff, (UINT8
*)&addr
, sizeof(UINT64
));
55 t
->has_address
= TRUE
;
62 t
->has_new_address
= FALSE
;
63 t
->updated_extents
= FALSE
;
65 t
->uniqueness_determined
= FALSE
;
67 InitializeListHead(&t
->itemlist
);
69 if (t
->header
.level
== 0) { // leaf node
70 leaf_node
* ln
= (leaf_node
*)(buf
+ sizeof(tree_header
));
73 if ((t
->header
.num_items
* sizeof(leaf_node
)) + sizeof(tree_header
) > Vcb
->superblock
.node_size
) {
74 ERR("tree at %llx has more items than expected (%x)\n", t
->header
.num_items
);
76 return STATUS_INSUFFICIENT_RESOURCES
;
79 for (i
= 0; i
< t
->header
.num_items
; i
++) {
80 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
82 ERR("out of memory\n");
84 return STATUS_INSUFFICIENT_RESOURCES
;
90 td
->data
= buf
+ sizeof(tree_header
) + ln
[i
].offset
;
94 if (ln
[i
].size
+ sizeof(tree_header
) + sizeof(leaf_node
) > Vcb
->superblock
.node_size
) {
95 ERR("overlarge item in tree %llx: %u > %u\n", addr
, ln
[i
].size
, Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
));
97 return STATUS_INTERNAL_ERROR
;
100 td
->size
= (UINT16
)ln
[i
].size
;
102 td
->inserted
= FALSE
;
104 InsertTailList(&t
->itemlist
, &td
->list_entry
);
106 t
->size
+= ln
[i
].size
;
109 t
->size
+= t
->header
.num_items
* sizeof(leaf_node
);
112 internal_node
* in
= (internal_node
*)(buf
+ sizeof(tree_header
));
115 if ((t
->header
.num_items
* sizeof(internal_node
)) + sizeof(tree_header
) > Vcb
->superblock
.node_size
) {
116 ERR("tree at %llx has more items than expected (%x)\n", t
->header
.num_items
);
118 return STATUS_INSUFFICIENT_RESOURCES
;
121 for (i
= 0; i
< t
->header
.num_items
; i
++) {
122 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
124 ERR("out of memory\n");
126 return STATUS_INSUFFICIENT_RESOURCES
;
131 td
->treeholder
.address
= in
[i
].address
;
132 td
->treeholder
.generation
= in
[i
].generation
;
133 td
->treeholder
.tree
= NULL
;
135 td
->inserted
= FALSE
;
137 InsertTailList(&t
->itemlist
, &td
->list_entry
);
140 t
->size
= t
->header
.num_items
* sizeof(internal_node
);
145 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
149 if (!Vcb
->trees_ptrs
[h
]) {
152 le
= Vcb
->trees_hash
.Flink
;
157 if (Vcb
->trees_ptrs
[h2
]) {
158 le
= Vcb
->trees_ptrs
[h2
];
166 le
= Vcb
->trees_ptrs
[h
];
169 while (le
!= &Vcb
->trees_hash
) {
170 tree
* t2
= CONTAINING_RECORD(le
, tree
, list_entry_hash
);
172 if (t2
->hash
>= t
->hash
) {
173 InsertHeadList(le
->Blink
, &t
->list_entry_hash
);
182 InsertTailList(&Vcb
->trees_hash
, &t
->list_entry_hash
);
184 if (!Vcb
->trees_ptrs
[h
] || t
->list_entry_hash
.Flink
== Vcb
->trees_ptrs
[h
])
185 Vcb
->trees_ptrs
[h
] = &t
->list_entry_hash
;
187 TRACE("returning %p\n", t
);
191 return STATUS_SUCCESS
;
194 static tree
* free_tree2(tree
* t
) {
200 if (r
&& r
->treeholder
.tree
!= t
)
205 t
->paritem
->treeholder
.tree
= NULL
;
208 while (!IsListEmpty(&t
->itemlist
)) {
209 tree_data
* td
= CONTAINING_RECORD(RemoveHeadList(&t
->itemlist
), tree_data
, list_entry
);
211 if (t
->header
.level
== 0 && td
->data
&& td
->inserted
)
212 ExFreePool(td
->data
);
214 ExFreeToPagedLookasideList(&t
->Vcb
->tree_data_lookaside
, td
);
217 RemoveEntryList(&t
->list_entry
);
220 r
->treeholder
.tree
= NULL
;
222 if (t
->list_entry_hash
.Flink
) {
223 UINT8 h
= t
->hash
>> 24;
224 if (t
->Vcb
->trees_ptrs
[h
] == &t
->list_entry_hash
) {
225 if (t
->list_entry_hash
.Flink
!= &t
->Vcb
->trees_hash
) {
226 tree
* t2
= CONTAINING_RECORD(t
->list_entry_hash
.Flink
, tree
, list_entry_hash
);
228 if ((t2
->hash
>> 24) == h
)
229 t
->Vcb
->trees_ptrs
[h
] = &t2
->list_entry_hash
;
231 t
->Vcb
->trees_ptrs
[h
] = NULL
;
233 t
->Vcb
->trees_ptrs
[h
] = NULL
;
236 RemoveEntryList(&t
->list_entry_hash
);
247 NTSTATUS
do_load_tree(device_extension
* Vcb
, tree_holder
* th
, root
* r
, tree
* t
, tree_data
* td
, BOOL
* loaded
, PIRP Irp
) {
250 ExAcquireResourceExclusiveLite(&r
->nonpaged
->load_tree_lock
, TRUE
);
256 Status
= load_tree(Vcb
, th
->address
, r
, &nt
, th
->generation
, Irp
);
257 if (!NT_SUCCESS(Status
)) {
258 ERR("load_tree returned %08x\n", Status
);
259 ExReleaseResourceLite(&r
->nonpaged
->load_tree_lock
);
265 #ifdef DEBUG_PARANOID
266 if (t
&& t
->header
.level
<= nt
->header
.level
) int3
;
277 ExReleaseResourceLite(&r
->nonpaged
->load_tree_lock
);
281 return STATUS_SUCCESS
;
284 tree
* free_tree(tree
* t
) {
288 ExAcquireResourceExclusiveLite(&r
->nonpaged
->load_tree_lock
, TRUE
);
292 ExReleaseResourceLite(&r
->nonpaged
->load_tree_lock
);
297 static __inline tree_data
* first_item(tree
* t
) {
298 LIST_ENTRY
* le
= t
->itemlist
.Flink
;
300 if (le
== &t
->itemlist
)
303 return CONTAINING_RECORD(le
, tree_data
, list_entry
);
306 static __inline tree_data
* prev_item(tree
* t
, tree_data
* td
) {
307 LIST_ENTRY
* le
= td
->list_entry
.Blink
;
309 if (le
== &t
->itemlist
)
312 return CONTAINING_RECORD(le
, tree_data
, list_entry
);
315 static __inline tree_data
* next_item(tree
* t
, tree_data
* td
) {
316 LIST_ENTRY
* le
= td
->list_entry
.Flink
;
318 if (le
== &t
->itemlist
)
321 return CONTAINING_RECORD(le
, tree_data
, list_entry
);
324 static NTSTATUS
next_item2(device_extension
* Vcb
, tree
* t
, tree_data
* td
, traverse_ptr
* tp
) {
325 tree_data
* td2
= next_item(t
, td
);
331 return STATUS_SUCCESS
;
339 } while (td2
&& !next_item(t2
, td2
));
342 return STATUS_NOT_FOUND
;
344 td2
= next_item(t2
, td2
);
346 return find_item_to_level(Vcb
, t2
->root
, tp
, &td2
->key
, FALSE
, t
->header
.level
, NULL
);
349 NTSTATUS
skip_to_difference(device_extension
* Vcb
, traverse_ptr
* tp
, traverse_ptr
* tp2
, BOOL
* ended1
, BOOL
* ended2
) {
352 tree_data
*td1
, *td2
;
362 } while (t1
&& t2
&& t1
->header
.address
== t2
->header
.address
);
365 traverse_ptr tp3
, tp4
;
367 Status
= next_item2(Vcb
, t1
, td1
, &tp3
);
368 if (Status
== STATUS_NOT_FOUND
)
370 else if (!NT_SUCCESS(Status
)) {
371 ERR("next_item2 returned %08x\n", Status
);
375 Status
= next_item2(Vcb
, t2
, td2
, &tp4
);
376 if (Status
== STATUS_NOT_FOUND
)
378 else if (!NT_SUCCESS(Status
)) {
379 ERR("next_item2 returned %08x\n", Status
);
383 if (*ended1
|| *ended2
) {
385 Status
= find_item(Vcb
, t1
->root
, tp
, &tp3
.item
->key
, FALSE
, NULL
);
386 if (!NT_SUCCESS(Status
)) {
387 ERR("find_item returned %08x\n", Status
);
390 } else if (!*ended2
) {
391 Status
= find_item(Vcb
, t2
->root
, tp2
, &tp4
.item
->key
, FALSE
, NULL
);
392 if (!NT_SUCCESS(Status
)) {
393 ERR("find_item returned %08x\n", Status
);
398 return STATUS_SUCCESS
;
401 if (tp3
.tree
->header
.address
!= tp4
.tree
->header
.address
) {
402 Status
= find_item(Vcb
, t1
->root
, tp
, &tp3
.item
->key
, FALSE
, NULL
);
403 if (!NT_SUCCESS(Status
)) {
404 ERR("find_item returned %08x\n", Status
);
408 Status
= find_item(Vcb
, t2
->root
, tp2
, &tp4
.item
->key
, FALSE
, NULL
);
409 if (!NT_SUCCESS(Status
)) {
410 ERR("find_item returned %08x\n", Status
);
414 return STATUS_SUCCESS
;
424 static NTSTATUS
find_item_in_tree(device_extension
* Vcb
, tree
* t
, traverse_ptr
* tp
, const KEY
* searchkey
, BOOL ignore
, UINT8 level
, PIRP Irp
) {
426 tree_data
*td
, *lasttd
;
433 if (!td
) return STATUS_NOT_FOUND
;
438 cmp
= keycmp(key2
, td
->key
);
442 td
= next_item(t
, td
);
445 if (t
->header
.level
== 0 && cmp
== 0 && !ignore
&& td
&& td
->ignore
) {
446 tree_data
* origtd
= td
;
448 while (td
&& td
->ignore
)
449 td
= next_item(t
, td
);
452 cmp
= keycmp(key2
, td
->key
);
461 } while (td
&& cmp
== 1);
463 if ((cmp
== -1 || !td
) && lasttd
)
466 if (t
->header
.level
== 0) {
467 if (td
->ignore
&& !ignore
) {
473 while (find_prev_item(Vcb
, &oldtp
, tp
, Irp
)) {
474 if (!tp
->item
->ignore
)
475 return STATUS_SUCCESS
;
480 // if no valid entries before where item should be, look afterwards instead
485 while (find_next_item(Vcb
, &oldtp
, tp
, TRUE
, Irp
)) {
486 if (!tp
->item
->ignore
)
487 return STATUS_SUCCESS
;
492 return STATUS_NOT_FOUND
;
498 return STATUS_SUCCESS
;
503 while (td
&& td
->treeholder
.tree
&& IsListEmpty(&td
->treeholder
.tree
->itemlist
)) {
504 td
= prev_item(t
, td
);
508 return STATUS_NOT_FOUND
;
510 if (t
->header
.level
<= level
) {
513 return STATUS_SUCCESS
;
516 if (!td
->treeholder
.tree
) {
517 Status
= do_load_tree(Vcb
, &td
->treeholder
, t
->root
, t
, td
, &loaded
, Irp
);
518 if (!NT_SUCCESS(Status
)) {
519 ERR("do_load_tree returned %08x\n", Status
);
524 Status
= find_item_in_tree(Vcb
, td
->treeholder
.tree
, tp
, searchkey
, ignore
, level
, Irp
);
530 NTSTATUS
find_item(_In_
_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _Out_ traverse_ptr
* tp
,
531 _In_
const KEY
* searchkey
, _In_ BOOL ignore
, _In_opt_ PIRP Irp
) {
535 if (!r
->treeholder
.tree
) {
536 Status
= do_load_tree(Vcb
, &r
->treeholder
, r
, NULL
, NULL
, &loaded
, Irp
);
537 if (!NT_SUCCESS(Status
)) {
538 ERR("do_load_tree returned %08x\n", Status
);
543 Status
= find_item_in_tree(Vcb
, r
->treeholder
.tree
, tp
, searchkey
, ignore
, 0, Irp
);
544 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
) {
545 ERR("find_item_in_tree returned %08x\n", Status
);
551 NTSTATUS
find_item_to_level(device_extension
* Vcb
, root
* r
, traverse_ptr
* tp
, const KEY
* searchkey
, BOOL ignore
, UINT8 level
, PIRP Irp
) {
555 if (!r
->treeholder
.tree
) {
556 Status
= do_load_tree(Vcb
, &r
->treeholder
, r
, NULL
, NULL
, &loaded
, Irp
);
557 if (!NT_SUCCESS(Status
)) {
558 ERR("do_load_tree returned %08x\n", Status
);
563 Status
= find_item_in_tree(Vcb
, r
->treeholder
.tree
, tp
, searchkey
, ignore
, level
, Irp
);
564 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
) {
565 ERR("find_item_in_tree returned %08x\n", Status
);
568 if (Status
== STATUS_NOT_FOUND
) {
569 tp
->tree
= r
->treeholder
.tree
;
576 BOOL
find_next_item(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, const traverse_ptr
* tp
, traverse_ptr
* next_tp
, BOOL ignore
, PIRP Irp
) {
578 tree_data
*td
= NULL
, *next
;
582 next
= next_item(tp
->tree
, tp
->item
);
585 while (next
&& next
->ignore
)
586 next
= next_item(tp
->tree
, next
);
590 next_tp
->tree
= tp
->tree
;
591 next_tp
->item
= next
;
593 #ifdef DEBUG_PARANOID
594 if (!ignore
&& next_tp
->item
->ignore
) {
595 ERR("error - returning ignored item\n");
603 if (!tp
->tree
->parent
)
609 td
= next_item(t
->parent
, t
->paritem
);
620 Status
= do_load_tree(Vcb
, &td
->treeholder
, t
->parent
->root
, t
->parent
, td
, &loaded
, Irp
);
621 if (!NT_SUCCESS(Status
)) {
622 ERR("do_load_tree returned %08x\n", Status
);
626 t
= td
->treeholder
.tree
;
628 while (t
->header
.level
!= 0) {
633 Status
= do_load_tree(Vcb
, &fi
->treeholder
, t
->parent
->root
, t
, fi
, &loaded
, Irp
);
634 if (!NT_SUCCESS(Status
)) {
635 ERR("do_load_tree returned %08x\n", Status
);
639 t
= fi
->treeholder
.tree
;
643 next_tp
->item
= first_item(t
);
645 if (!ignore
&& next_tp
->item
->ignore
) {
649 while ((b
= find_next_item(Vcb
, next_tp
, &ntp2
, TRUE
, Irp
))) {
652 if (!next_tp
->item
->ignore
)
660 #ifdef DEBUG_PARANOID
661 if (!ignore
&& next_tp
->item
->ignore
) {
662 ERR("error - returning ignored item\n");
670 static __inline tree_data
* last_item(tree
* t
) {
671 LIST_ENTRY
* le
= t
->itemlist
.Blink
;
673 if (le
== &t
->itemlist
)
676 return CONTAINING_RECORD(le
, tree_data
, list_entry
);
679 BOOL
find_prev_item(_Requires_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, const traverse_ptr
* tp
, traverse_ptr
* prev_tp
, PIRP Irp
) {
685 // FIXME - support ignore flag
686 if (prev_item(tp
->tree
, tp
->item
)) {
687 prev_tp
->tree
= tp
->tree
;
688 prev_tp
->item
= prev_item(tp
->tree
, tp
->item
);
693 if (!tp
->tree
->parent
)
697 while (t
&& (!t
->parent
|| !prev_item(t
->parent
, t
->paritem
))) {
704 td
= prev_item(t
->parent
, t
->paritem
);
706 Status
= do_load_tree(Vcb
, &td
->treeholder
, t
->parent
->root
, t
->parent
, td
, &loaded
, Irp
);
707 if (!NT_SUCCESS(Status
)) {
708 ERR("do_load_tree returned %08x\n", Status
);
712 t
= td
->treeholder
.tree
;
714 while (t
->header
.level
!= 0) {
719 Status
= do_load_tree(Vcb
, &li
->treeholder
, t
->parent
->root
, t
, li
, &loaded
, Irp
);
720 if (!NT_SUCCESS(Status
)) {
721 ERR("do_load_tree returned %08x\n", Status
);
725 t
= li
->treeholder
.tree
;
729 prev_tp
->item
= last_item(t
);
734 void free_trees_root(device_extension
* Vcb
, root
* r
) {
738 for (level
= 0; level
<= 255; level
++) {
741 le
= Vcb
->trees
.Flink
;
743 while (le
!= &Vcb
->trees
) {
744 LIST_ENTRY
* nextle
= le
->Flink
;
745 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
748 if (t
->header
.level
== level
) {
749 BOOL top
= !t
->paritem
;
754 if (top
&& r
->treeholder
.tree
== t
)
755 r
->treeholder
.tree
= NULL
;
757 if (IsListEmpty(&Vcb
->trees
))
759 } else if (t
->header
.level
> level
)
771 void free_trees(device_extension
* Vcb
) {
775 for (level
= 0; level
<= 255; level
++) {
778 le
= Vcb
->trees
.Flink
;
780 while (le
!= &Vcb
->trees
) {
781 LIST_ENTRY
* nextle
= le
->Flink
;
782 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
785 if (t
->header
.level
== level
) {
786 BOOL top
= !t
->paritem
;
791 if (top
&& r
->treeholder
.tree
== t
)
792 r
->treeholder
.tree
= NULL
;
794 if (IsListEmpty(&Vcb
->trees
))
796 } else if (t
->header
.level
> level
)
808 #pragma warning(push)
809 #pragma warning(suppress: 28194)
811 void add_rollback(_In_ LIST_ENTRY
* rollback
, _In_
enum rollback_type type
, _In_ __drv_aliasesMem
void* ptr
) {
814 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(rollback_item
), ALLOC_TAG
);
816 ERR("out of memory\n");
822 InsertTailList(rollback
, &ri
->list_entry
);
829 #pragma warning(push)
830 #pragma warning(suppress: 28194)
832 NTSTATUS
insert_tree_item(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _In_ root
* r
, _In_ UINT64 obj_id
,
833 _In_ UINT8 obj_type
, _In_ UINT64 offset
, _In_reads_bytes_opt_(size
) _When_(return >= 0, __drv_aliasesMem
) void* data
,
834 _In_ UINT16 size
, _Out_opt_ traverse_ptr
* ptp
, _In_opt_ PIRP Irp
) {
838 tree_data
*td
, *paritem
;
842 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
846 TRACE("(%p, %p, %llx, %x, %llx, %p, %x, %p)\n", Vcb
, r
, obj_id
, obj_type
, offset
, data
, size
, ptp
);
848 searchkey
.obj_id
= obj_id
;
849 searchkey
.obj_type
= obj_type
;
850 searchkey
.offset
= offset
;
852 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, TRUE
, Irp
);
853 if (Status
== STATUS_NOT_FOUND
) {
855 if (!r
->treeholder
.tree
) {
858 Status
= do_load_tree(Vcb
, &r
->treeholder
, r
, NULL
, NULL
, &loaded
, Irp
);
859 if (!NT_SUCCESS(Status
)) {
860 ERR("do_load_tree returned %08x\n", Status
);
865 if (r
->treeholder
.tree
&& r
->treeholder
.tree
->header
.num_items
== 0) {
866 tp
.tree
= r
->treeholder
.tree
;
869 ERR("error: unable to load tree for root %llx\n", r
->id
);
870 return STATUS_INTERNAL_ERROR
;
873 ERR("error: find_item returned %08x\n", Status
);
876 } else if (!NT_SUCCESS(Status
)) {
877 ERR("find_item returned %08x\n", Status
);
881 TRACE("tp.item = %p\n", tp
.item
);
884 TRACE("tp.item->key = %p\n", &tp
.item
->key
);
885 cmp
= keycmp(searchkey
, tp
.item
->key
);
887 if (cmp
== 0 && !tp
.item
->ignore
) {
888 ERR("error: key (%llx,%x,%llx) already present\n", obj_id
, obj_type
, offset
);
889 #ifdef DEBUG_PARANOID
892 return STATUS_INTERNAL_ERROR
;
897 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
899 ERR("out of memory\n");
900 return STATUS_INSUFFICIENT_RESOURCES
;
910 le
= tp
.tree
->itemlist
.Flink
;
911 while (le
!= &tp
.tree
->itemlist
) {
912 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
913 firstitem
= td2
->key
;
917 TRACE("inserting %llx,%x,%llx into tree beginning %llx,%x,%llx (num_items %x)\n", obj_id
, obj_type
, offset
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
, tp
.tree
->header
.num_items
);
920 if (cmp
== -1) { // very first key in root
921 InsertHeadList(&tp
.tree
->itemlist
, &td
->list_entry
);
923 paritem
= tp
.tree
->paritem
;
925 if (!keycmp(paritem
->key
, tp
.item
->key
)) {
926 paritem
->key
= searchkey
;
930 paritem
= paritem
->treeholder
.tree
->paritem
;
933 InsertHeadList(tp
.item
->list_entry
.Blink
, &td
->list_entry
); // make sure non-deleted item is before deleted ones
935 InsertHeadList(&tp
.item
->list_entry
, &td
->list_entry
);
937 tp
.tree
->header
.num_items
++;
938 tp
.tree
->size
+= size
+ sizeof(leaf_node
);
940 if (!tp
.tree
->write
) {
941 tp
.tree
->write
= TRUE
;
942 Vcb
->need_write
= TRUE
;
950 if (t
->paritem
&& t
->paritem
->ignore
) {
951 t
->paritem
->ignore
= FALSE
;
952 t
->parent
->header
.num_items
++;
953 t
->parent
->size
+= sizeof(internal_node
);
956 t
->header
.generation
= Vcb
->superblock
.generation
;
960 return STATUS_SUCCESS
;
966 NTSTATUS
delete_tree_item(_In_
_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, _Inout_ traverse_ptr
* tp
) {
970 TRACE("deleting item %llx,%x,%llx (ignore = %s)\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
, tp
->item
->ignore
? "TRUE" : "FALSE");
972 #ifdef DEBUG_PARANOID
973 if (tp
->item
->ignore
) {
974 ERR("trying to delete already-deleted item %llx,%x,%llx\n", tp
->item
->key
.obj_id
, tp
->item
->key
.obj_type
, tp
->item
->key
.offset
);
976 return STATUS_INTERNAL_ERROR
;
980 tp
->item
->ignore
= TRUE
;
982 if (!tp
->tree
->write
) {
983 tp
->tree
->write
= TRUE
;
984 Vcb
->need_write
= TRUE
;
987 tp
->tree
->header
.num_items
--;
989 if (tp
->tree
->header
.level
== 0)
990 tp
->tree
->size
-= sizeof(leaf_node
) + tp
->item
->size
;
992 tp
->tree
->size
-= sizeof(internal_node
);
994 gen
= tp
->tree
->Vcb
->superblock
.generation
;
998 t
->header
.generation
= gen
;
1002 return STATUS_SUCCESS
;
1005 void clear_rollback(LIST_ENTRY
* rollback
) {
1006 while (!IsListEmpty(rollback
)) {
1007 LIST_ENTRY
* le
= RemoveHeadList(rollback
);
1008 rollback_item
* ri
= CONTAINING_RECORD(le
, rollback_item
, list_entry
);
1011 case ROLLBACK_ADD_SPACE
:
1012 case ROLLBACK_SUBTRACT_SPACE
:
1013 case ROLLBACK_INSERT_EXTENT
:
1014 case ROLLBACK_DELETE_EXTENT
:
1015 ExFreePool(ri
->ptr
);
1026 void do_rollback(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1030 while (!IsListEmpty(rollback
)) {
1031 LIST_ENTRY
* le
= RemoveTailList(rollback
);
1032 ri
= CONTAINING_RECORD(le
, rollback_item
, list_entry
);
1035 case ROLLBACK_INSERT_EXTENT
:
1037 rollback_extent
* re
= ri
->ptr
;
1039 re
->ext
->ignore
= TRUE
;
1041 if (re
->ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| re
->ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
1042 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)re
->ext
->extent_data
.data
;
1044 if (ed2
->size
!= 0) {
1045 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
1048 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, re
->fcb
->subvol
->id
,
1049 re
->fcb
->inode
, re
->ext
->offset
- ed2
->offset
, -1,
1050 re
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, NULL
);
1052 if (!NT_SUCCESS(Status
))
1053 ERR("update_changed_extent_ref returned %08x\n", Status
);
1056 re
->fcb
->inode_item
.st_blocks
-= ed2
->num_bytes
;
1064 case ROLLBACK_DELETE_EXTENT
:
1066 rollback_extent
* re
= ri
->ptr
;
1068 re
->ext
->ignore
= FALSE
;
1070 if (re
->ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| re
->ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) {
1071 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)re
->ext
->extent_data
.data
;
1073 if (ed2
->size
!= 0) {
1074 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
1077 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, re
->fcb
->subvol
->id
,
1078 re
->fcb
->inode
, re
->ext
->offset
- ed2
->offset
, 1,
1079 re
->fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, NULL
);
1081 if (!NT_SUCCESS(Status
))
1082 ERR("update_changed_extent_ref returned %08x\n", Status
);
1085 re
->fcb
->inode_item
.st_blocks
+= ed2
->num_bytes
;
1093 case ROLLBACK_ADD_SPACE
:
1094 case ROLLBACK_SUBTRACT_SPACE
:
1096 rollback_space
* rs
= ri
->ptr
;
1099 ExAcquireResourceExclusiveLite(&rs
->chunk
->lock
, TRUE
);
1101 if (ri
->type
== ROLLBACK_ADD_SPACE
)
1102 space_list_subtract2(rs
->list
, rs
->list_size
, rs
->address
, rs
->length
, NULL
, NULL
);
1104 space_list_add2(rs
->list
, rs
->list_size
, rs
->address
, rs
->length
, NULL
, NULL
);
1107 if (ri
->type
== ROLLBACK_ADD_SPACE
)
1108 rs
->chunk
->used
+= rs
->length
;
1110 rs
->chunk
->used
-= rs
->length
;
1114 LIST_ENTRY
* le2
= le
->Blink
;
1116 while (le2
!= rollback
) {
1117 LIST_ENTRY
* le3
= le2
->Blink
;
1118 rollback_item
* ri2
= CONTAINING_RECORD(le2
, rollback_item
, list_entry
);
1120 if (ri2
->type
== ROLLBACK_ADD_SPACE
|| ri2
->type
== ROLLBACK_SUBTRACT_SPACE
) {
1121 rollback_space
* rs2
= ri2
->ptr
;
1123 if (rs2
->chunk
== rs
->chunk
) {
1124 if (ri2
->type
== ROLLBACK_ADD_SPACE
) {
1125 space_list_subtract2(rs2
->list
, rs2
->list_size
, rs2
->address
, rs2
->length
, NULL
, NULL
);
1126 rs
->chunk
->used
+= rs2
->length
;
1128 space_list_add2(rs2
->list
, rs2
->list_size
, rs2
->address
, rs2
->length
, NULL
, NULL
);
1129 rs
->chunk
->used
-= rs2
->length
;
1133 RemoveEntryList(&ri2
->list_entry
);
1141 ExReleaseResourceLite(&rs
->chunk
->lock
);
1154 static void find_tree_end(tree
* t
, KEY
* tree_end
, BOOL
* no_end
) {
1168 if (pi
->list_entry
.Flink
!= &p
->parent
->itemlist
) {
1169 tree_data
* td
= CONTAINING_RECORD(pi
->list_entry
.Flink
, tree_data
, list_entry
);
1171 *tree_end
= td
->key
;
1180 void clear_batch_list(device_extension
* Vcb
, LIST_ENTRY
* batchlist
) {
1181 while (!IsListEmpty(batchlist
)) {
1182 LIST_ENTRY
* le
= RemoveHeadList(batchlist
);
1183 batch_root
* br
= CONTAINING_RECORD(le
, batch_root
, list_entry
);
1185 while (!IsListEmpty(&br
->items
)) {
1186 LIST_ENTRY
* le2
= RemoveHeadList(&br
->items
);
1187 batch_item
* bi
= CONTAINING_RECORD(le2
, batch_item
, list_entry
);
1189 ExFreeToPagedLookasideList(&Vcb
->batch_item_lookaside
, bi
);
1196 static void add_delete_inode_extref(device_extension
* Vcb
, batch_item
* bi
, LIST_ENTRY
* listhead
) {
1199 INODE_REF
* delir
= (INODE_REF
*)bi
->data
;
1202 TRACE("entry in INODE_REF not found, adding Batch_DeleteInodeExtRef entry\n");
1204 bi2
= ExAllocateFromPagedLookasideList(&Vcb
->batch_item_lookaside
);
1206 ERR("out of memory\n");
1210 ier
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_EXTREF
) - 1 + delir
->n
, ALLOC_TAG
);
1212 ERR("out of memory\n");
1217 ier
->dir
= bi
->key
.offset
;
1218 ier
->index
= delir
->index
;
1220 RtlCopyMemory(ier
->name
, delir
->name
, delir
->n
);
1222 bi2
->key
.obj_id
= bi
->key
.obj_id
;
1223 bi2
->key
.obj_type
= TYPE_INODE_EXTREF
;
1224 bi2
->key
.offset
= calc_crc32c((UINT32
)bi
->key
.offset
, (UINT8
*)ier
->name
, ier
->n
);
1226 bi2
->datalen
= sizeof(INODE_EXTREF
) - 1 + ier
->n
;
1227 bi2
->operation
= Batch_DeleteInodeExtRef
;
1229 le
= bi
->list_entry
.Flink
;
1230 while (le
!= listhead
) {
1231 batch_item
* bi3
= CONTAINING_RECORD(le
, batch_item
, list_entry
);
1233 if (keycmp(bi3
->key
, bi2
->key
) != -1) {
1234 InsertHeadList(le
->Blink
, &bi2
->list_entry
);
1241 InsertTailList(listhead
, &bi2
->list_entry
);
1244 static NTSTATUS
handle_batch_collision(device_extension
* Vcb
, batch_item
* bi
, tree
* t
, tree_data
* td
, tree_data
* newtd
, LIST_ENTRY
* listhead
, BOOL
* ignore
) {
1245 if (bi
->operation
== Batch_Delete
|| bi
->operation
== Batch_SetXattr
|| bi
->operation
== Batch_DirItem
|| bi
->operation
== Batch_InodeRef
||
1246 bi
->operation
== Batch_InodeExtRef
|| bi
->operation
== Batch_DeleteDirItem
|| bi
->operation
== Batch_DeleteInodeRef
||
1247 bi
->operation
== Batch_DeleteInodeExtRef
|| bi
->operation
== Batch_DeleteXattr
) {
1248 UINT16 maxlen
= (UINT16
)(Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
));
1250 switch (bi
->operation
) {
1251 case Batch_SetXattr
: {
1252 if (td
->size
< sizeof(DIR_ITEM
)) {
1253 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", bi
->key
.obj_id
, bi
->key
.obj_type
, bi
->key
.offset
, td
->size
, sizeof(DIR_ITEM
));
1256 ULONG size
= td
->size
;
1257 DIR_ITEM
* newxa
= (DIR_ITEM
*)bi
->data
;
1258 DIR_ITEM
* xa
= (DIR_ITEM
*)td
->data
;
1263 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
1264 ERR("(%llx,%x,%llx) was truncated\n", bi
->key
.obj_id
, bi
->key
.obj_type
, bi
->key
.offset
);
1268 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
1270 if (xa
->n
== newxa
->n
&& RtlCompareMemory(newxa
->name
, xa
->name
, xa
->n
) == xa
->n
) {
1275 if (td
->size
+ bi
->datalen
- oldxasize
> maxlen
)
1276 ERR("DIR_ITEM would be over maximum size, truncating (%u + %u - %u > %u)\n", td
->size
, bi
->datalen
, oldxasize
, maxlen
);
1278 newdata
= ExAllocatePoolWithTag(PagedPool
, td
->size
+ bi
->datalen
- oldxasize
, ALLOC_TAG
);
1280 ERR("out of memory\n");
1281 return STATUS_INSUFFICIENT_RESOURCES
;
1284 pos
= (UINT8
*)xa
- td
->data
;
1285 if (pos
+ oldxasize
< td
->size
) // copy after changed xattr
1286 RtlCopyMemory(newdata
+ pos
+ bi
->datalen
, td
->data
+ pos
+ oldxasize
, (ULONG
)(td
->size
- pos
- oldxasize
));
1288 if (pos
> 0) { // copy before changed xattr
1289 RtlCopyMemory(newdata
, td
->data
, (ULONG
)pos
);
1290 xa
= (DIR_ITEM
*)(newdata
+ pos
);
1292 xa
= (DIR_ITEM
*)newdata
;
1294 RtlCopyMemory(xa
, bi
->data
, bi
->datalen
);
1296 bi
->datalen
= (UINT16
)min(td
->size
+ bi
->datalen
- oldxasize
, maxlen
);
1298 ExFreePool(bi
->data
);
1304 if ((UINT8
*)xa
- (UINT8
*)td
->data
+ oldxasize
>= size
) {
1305 // not found, add to end of data
1307 if (td
->size
+ bi
->datalen
> maxlen
)
1308 ERR("DIR_ITEM would be over maximum size, truncating (%u + %u > %u)\n", td
->size
, bi
->datalen
, maxlen
);
1310 newdata
= ExAllocatePoolWithTag(PagedPool
, td
->size
+ bi
->datalen
, ALLOC_TAG
);
1312 ERR("out of memory\n");
1313 return STATUS_INSUFFICIENT_RESOURCES
;
1316 RtlCopyMemory(newdata
, td
->data
, td
->size
);
1318 xa
= (DIR_ITEM
*)((UINT8
*)newdata
+ td
->size
);
1319 RtlCopyMemory(xa
, bi
->data
, bi
->datalen
);
1321 bi
->datalen
= min(bi
->datalen
+ td
->size
, maxlen
);
1323 ExFreePool(bi
->data
);
1328 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
1336 case Batch_DirItem
: {
1339 if (td
->size
+ bi
->datalen
> maxlen
) {
1340 ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", td
->size
, bi
->datalen
, maxlen
);
1341 return STATUS_INTERNAL_ERROR
;
1344 newdata
= ExAllocatePoolWithTag(PagedPool
, td
->size
+ bi
->datalen
, ALLOC_TAG
);
1346 ERR("out of memory\n");
1347 return STATUS_INSUFFICIENT_RESOURCES
;
1350 RtlCopyMemory(newdata
, td
->data
, td
->size
);
1352 RtlCopyMemory(newdata
+ td
->size
, bi
->data
, bi
->datalen
);
1354 bi
->datalen
+= td
->size
;
1356 ExFreePool(bi
->data
);
1362 case Batch_InodeRef
: {
1365 if (td
->size
+ bi
->datalen
> maxlen
) {
1366 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
1367 INODE_REF
* ir
= (INODE_REF
*)bi
->data
;
1372 BOOL inserted
= FALSE
;
1374 TRACE("INODE_REF would be too long, adding INODE_EXTREF instead\n");
1376 ierlen
= (UINT16
)(offsetof(INODE_EXTREF
, name
[0]) + ir
->n
);
1378 ier
= ExAllocatePoolWithTag(PagedPool
, ierlen
, ALLOC_TAG
);
1380 ERR("out of memory\n");
1381 return STATUS_INSUFFICIENT_RESOURCES
;
1384 ier
->dir
= bi
->key
.offset
;
1385 ier
->index
= ir
->index
;
1387 RtlCopyMemory(ier
->name
, ir
->name
, ier
->n
);
1389 bi2
= ExAllocateFromPagedLookasideList(&Vcb
->batch_item_lookaside
);
1391 ERR("out of memory\n");
1393 return STATUS_INSUFFICIENT_RESOURCES
;
1396 bi2
->key
.obj_id
= bi
->key
.obj_id
;
1397 bi2
->key
.obj_type
= TYPE_INODE_EXTREF
;
1398 bi2
->key
.offset
= calc_crc32c((UINT32
)ier
->dir
, (UINT8
*)ier
->name
, ier
->n
);
1400 bi2
->datalen
= ierlen
;
1401 bi2
->operation
= Batch_InodeExtRef
;
1403 le
= bi
->list_entry
.Flink
;
1404 while (le
!= listhead
) {
1405 batch_item
* bi3
= CONTAINING_RECORD(le
, batch_item
, list_entry
);
1407 if (keycmp(bi3
->key
, bi2
->key
) != -1) {
1408 InsertHeadList(le
->Blink
, &bi2
->list_entry
);
1416 InsertTailList(listhead
, &bi2
->list_entry
);
1419 return STATUS_SUCCESS
;
1421 ERR("INODE_REF would be over maximum size (%u + %u > %u)\n", td
->size
, bi
->datalen
, maxlen
);
1422 return STATUS_INTERNAL_ERROR
;
1426 newdata
= ExAllocatePoolWithTag(PagedPool
, td
->size
+ bi
->datalen
, ALLOC_TAG
);
1428 ERR("out of memory\n");
1429 return STATUS_INSUFFICIENT_RESOURCES
;
1432 RtlCopyMemory(newdata
, td
->data
, td
->size
);
1434 RtlCopyMemory(newdata
+ td
->size
, bi
->data
, bi
->datalen
);
1436 bi
->datalen
+= td
->size
;
1438 ExFreePool(bi
->data
);
1444 case Batch_InodeExtRef
: {
1447 if (td
->size
+ bi
->datalen
> maxlen
) {
1448 ERR("INODE_EXTREF would be over maximum size (%u + %u > %u)\n", td
->size
, bi
->datalen
, maxlen
);
1449 return STATUS_INTERNAL_ERROR
;
1452 newdata
= ExAllocatePoolWithTag(PagedPool
, td
->size
+ bi
->datalen
, ALLOC_TAG
);
1454 ERR("out of memory\n");
1455 return STATUS_INSUFFICIENT_RESOURCES
;
1458 RtlCopyMemory(newdata
, td
->data
, td
->size
);
1460 RtlCopyMemory(newdata
+ td
->size
, bi
->data
, bi
->datalen
);
1462 bi
->datalen
+= td
->size
;
1464 ExFreePool(bi
->data
);
1470 case Batch_DeleteDirItem
: {
1471 if (td
->size
< sizeof(DIR_ITEM
)) {
1472 ERR("DIR_ITEM was %u bytes, expected at least %u\n", td
->size
, sizeof(DIR_ITEM
));
1473 return STATUS_INTERNAL_ERROR
;
1475 DIR_ITEM
*di
, *deldi
;
1478 deldi
= (DIR_ITEM
*)bi
->data
;
1479 di
= (DIR_ITEM
*)td
->data
;
1483 if (di
->m
== deldi
->m
&& di
->n
== deldi
->n
&& RtlCompareMemory(di
->name
, deldi
->name
, di
->n
+ di
->m
) == di
->n
+ di
->m
) {
1484 UINT16 newlen
= td
->size
- (sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
);
1487 TRACE("deleting DIR_ITEM\n");
1489 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1493 ERR("out of memory\n");
1494 return STATUS_INSUFFICIENT_RESOURCES
;
1497 TRACE("modifying DIR_ITEM\n");
1499 if ((UINT8
*)di
> td
->data
) {
1500 RtlCopyMemory(newdi
, td
->data
, (UINT8
*)di
- td
->data
);
1501 dioff
= newdi
+ ((UINT8
*)di
- td
->data
);
1506 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] < td
->data
+ td
->size
)
1507 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], td
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - td
->data
));
1509 td2
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
1511 ERR("out of memory\n");
1512 return STATUS_INSUFFICIENT_RESOURCES
;
1518 td2
->ignore
= FALSE
;
1519 td2
->inserted
= TRUE
;
1521 InsertHeadList(td
->list_entry
.Blink
, &td2
->list_entry
);
1523 t
->header
.num_items
++;
1524 t
->size
+= newlen
+ sizeof(leaf_node
);
1531 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1532 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1535 TRACE("could not find DIR_ITEM to delete\n");
1537 return STATUS_SUCCESS
;
1544 case Batch_DeleteInodeRef
: {
1545 if (td
->size
< sizeof(INODE_REF
)) {
1546 ERR("INODE_REF was %u bytes, expected at least %u\n", td
->size
, sizeof(INODE_REF
));
1547 return STATUS_INTERNAL_ERROR
;
1549 INODE_REF
*ir
, *delir
;
1551 BOOL changed
= FALSE
;
1553 delir
= (INODE_REF
*)bi
->data
;
1554 ir
= (INODE_REF
*)td
->data
;
1560 if (len
< sizeof(INODE_REF
) || len
< offsetof(INODE_REF
, name
[0]) + ir
->n
) {
1561 ERR("INODE_REF was truncated\n");
1565 itemlen
= (UINT16
)offsetof(INODE_REF
, name
[0]) + ir
->n
;
1567 if (ir
->n
== delir
->n
&& RtlCompareMemory(ir
->name
, delir
->name
, ir
->n
) == ir
->n
) {
1568 UINT16 newlen
= td
->size
- itemlen
;
1573 TRACE("deleting INODE_REF\n");
1575 UINT8
*newir
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *iroff
;
1579 ERR("out of memory\n");
1580 return STATUS_INSUFFICIENT_RESOURCES
;
1583 TRACE("modifying INODE_REF\n");
1585 if ((UINT8
*)ir
> td
->data
) {
1586 RtlCopyMemory(newir
, td
->data
, (UINT8
*)ir
- td
->data
);
1587 iroff
= newir
+ ((UINT8
*)ir
- td
->data
);
1592 if ((UINT8
*)&ir
->name
[ir
->n
] < td
->data
+ td
->size
)
1593 RtlCopyMemory(iroff
, &ir
->name
[ir
->n
], td
->size
- ((UINT8
*)&ir
->name
[ir
->n
] - td
->data
));
1595 td2
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
1597 ERR("out of memory\n");
1598 return STATUS_INSUFFICIENT_RESOURCES
;
1604 td2
->ignore
= FALSE
;
1605 td2
->inserted
= TRUE
;
1607 InsertHeadList(td
->list_entry
.Blink
, &td2
->list_entry
);
1609 t
->header
.num_items
++;
1610 t
->size
+= newlen
+ sizeof(leaf_node
);
1617 if (len
> itemlen
) {
1619 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
1625 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
1626 TRACE("entry in INODE_REF not found, adding Batch_DeleteInodeExtRef entry\n");
1628 add_delete_inode_extref(Vcb
, bi
, listhead
);
1631 return STATUS_SUCCESS
;
1633 WARN("entry not found in INODE_REF\n");
1640 case Batch_DeleteInodeExtRef
: {
1641 if (td
->size
< sizeof(INODE_EXTREF
)) {
1642 ERR("INODE_EXTREF was %u bytes, expected at least %u\n", td
->size
, sizeof(INODE_EXTREF
));
1643 return STATUS_INTERNAL_ERROR
;
1645 INODE_EXTREF
*ier
, *delier
;
1648 delier
= (INODE_EXTREF
*)bi
->data
;
1649 ier
= (INODE_EXTREF
*)td
->data
;
1655 if (len
< sizeof(INODE_EXTREF
) || len
< offsetof(INODE_EXTREF
, name
[0]) + ier
->n
) {
1656 ERR("INODE_REF was truncated\n");
1660 itemlen
= (UINT16
)offsetof(INODE_EXTREF
, name
[0]) + ier
->n
;
1662 if (ier
->dir
== delier
->dir
&& ier
->n
== delier
->n
&& RtlCompareMemory(ier
->name
, delier
->name
, ier
->n
) == ier
->n
) {
1663 UINT16 newlen
= td
->size
- itemlen
;
1666 TRACE("deleting INODE_EXTREF\n");
1668 UINT8
*newier
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *ieroff
;
1672 ERR("out of memory\n");
1673 return STATUS_INSUFFICIENT_RESOURCES
;
1676 TRACE("modifying INODE_EXTREF\n");
1678 if ((UINT8
*)ier
> td
->data
) {
1679 RtlCopyMemory(newier
, td
->data
, (UINT8
*)ier
- td
->data
);
1680 ieroff
= newier
+ ((UINT8
*)ier
- td
->data
);
1685 if ((UINT8
*)&ier
->name
[ier
->n
] < td
->data
+ td
->size
)
1686 RtlCopyMemory(ieroff
, &ier
->name
[ier
->n
], td
->size
- ((UINT8
*)&ier
->name
[ier
->n
] - td
->data
));
1688 td2
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
1690 ERR("out of memory\n");
1692 return STATUS_INSUFFICIENT_RESOURCES
;
1698 td2
->ignore
= FALSE
;
1699 td2
->inserted
= TRUE
;
1701 InsertHeadList(td
->list_entry
.Blink
, &td2
->list_entry
);
1703 t
->header
.num_items
++;
1704 t
->size
+= newlen
+ sizeof(leaf_node
);
1711 if (len
> itemlen
) {
1713 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
1721 case Batch_DeleteXattr
: {
1722 if (td
->size
< sizeof(DIR_ITEM
)) {
1723 ERR("XATTR_ITEM was %u bytes, expected at least %u\n", td
->size
, sizeof(DIR_ITEM
));
1724 return STATUS_INTERNAL_ERROR
;
1726 DIR_ITEM
*di
, *deldi
;
1729 deldi
= (DIR_ITEM
*)bi
->data
;
1730 di
= (DIR_ITEM
*)td
->data
;
1734 if (di
->n
== deldi
->n
&& RtlCompareMemory(di
->name
, deldi
->name
, di
->n
) == di
->n
) {
1735 UINT16 newlen
= td
->size
- ((UINT16
)offsetof(DIR_ITEM
, name
[0]) + di
->n
+ di
->m
);
1738 TRACE("deleting XATTR_ITEM\n");
1740 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1744 ERR("out of memory\n");
1745 return STATUS_INSUFFICIENT_RESOURCES
;
1748 TRACE("modifying XATTR_ITEM\n");
1750 if ((UINT8
*)di
> td
->data
) {
1751 RtlCopyMemory(newdi
, td
->data
, (UINT8
*)di
- td
->data
);
1752 dioff
= newdi
+ ((UINT8
*)di
- td
->data
);
1756 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] < td
->data
+ td
->size
)
1757 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], td
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - td
->data
));
1759 td2
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
1761 ERR("out of memory\n");
1763 return STATUS_INSUFFICIENT_RESOURCES
;
1769 td2
->ignore
= FALSE
;
1770 td2
->inserted
= TRUE
;
1772 InsertHeadList(td
->list_entry
.Blink
, &td2
->list_entry
);
1774 t
->header
.num_items
++;
1775 t
->size
+= newlen
+ sizeof(leaf_node
);
1782 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1783 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1786 TRACE("could not find DIR_ITEM to delete\n");
1788 return STATUS_SUCCESS
;
1799 ERR("unexpected batch operation type\n");
1800 return STATUS_INTERNAL_ERROR
;
1807 t
->header
.num_items
--;
1808 t
->size
-= sizeof(leaf_node
) + td
->size
;
1813 newtd
->data
= bi
->data
;
1814 newtd
->size
= bi
->datalen
;
1815 InsertHeadList(td
->list_entry
.Blink
, &newtd
->list_entry
);
1818 ERR("(%llx,%x,%llx) already exists\n", bi
->key
.obj_id
, bi
->key
.obj_type
, bi
->key
.offset
);
1819 return STATUS_INTERNAL_ERROR
;
1823 return STATUS_SUCCESS
;
1826 static NTSTATUS
commit_batch_list_root(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, batch_root
* br
, PIRP Irp
) {
1830 TRACE("root: %llx\n", br
->r
->id
);
1832 le
= br
->items
.Flink
;
1833 while (le
!= &br
->items
) {
1834 batch_item
* bi
= CONTAINING_RECORD(le
, batch_item
, list_entry
);
1839 tree_data
*td
, *listhead
;
1842 BOOL ignore
= FALSE
;
1844 TRACE("(%llx,%x,%llx)\n", bi
->key
.obj_id
, bi
->key
.obj_type
, bi
->key
.offset
);
1846 Status
= find_item(Vcb
, br
->r
, &tp
, &bi
->key
, TRUE
, Irp
);
1847 if (!NT_SUCCESS(Status
)) { // FIXME - handle STATUS_NOT_FOUND
1848 ERR("find_item returned %08x\n", Status
);
1852 find_tree_end(tp
.tree
, &tree_end
, &no_end
);
1854 if (bi
->operation
== Batch_DeleteInode
) {
1855 if (tp
.item
->key
.obj_id
== bi
->key
.obj_id
) {
1860 if (!tp
.item
->ignore
) {
1861 tp
.item
->ignore
= TRUE
;
1862 tp
.tree
->header
.num_items
--;
1863 tp
.tree
->size
-= tp
.item
->size
+ sizeof(leaf_node
);
1864 tp
.tree
->write
= TRUE
;
1867 le2
= tp
.item
->list_entry
.Flink
;
1868 while (le2
!= &tp
.tree
->itemlist
) {
1869 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1871 if (td
->key
.obj_id
== bi
->key
.obj_id
) {
1874 tp
.tree
->header
.num_items
--;
1875 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
1876 tp
.tree
->write
= TRUE
;
1887 traverse_ptr next_tp
;
1891 if (!find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
1896 le2
= &tp
.item
->list_entry
;
1897 while (le2
!= &tp
.tree
->itemlist
) {
1898 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1900 if (td
->key
.obj_id
== bi
->key
.obj_id
) {
1903 tp
.tree
->header
.num_items
--;
1904 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
1905 tp
.tree
->write
= TRUE
;
1916 } else if (bi
->operation
== Batch_DeleteExtentData
) {
1917 if (tp
.item
->key
.obj_id
< bi
->key
.obj_id
|| (tp
.item
->key
.obj_id
== bi
->key
.obj_id
&& tp
.item
->key
.obj_type
< bi
->key
.obj_type
)) {
1920 if (find_next_item(Vcb
, &tp
, &tp2
, FALSE
, Irp
)) {
1921 if (tp2
.item
->key
.obj_id
== bi
->key
.obj_id
&& tp2
.item
->key
.obj_type
== bi
->key
.obj_type
) {
1923 find_tree_end(tp
.tree
, &tree_end
, &no_end
);
1928 if (tp
.item
->key
.obj_id
== bi
->key
.obj_id
&& tp
.item
->key
.obj_type
== bi
->key
.obj_type
) {
1933 if (!tp
.item
->ignore
) {
1934 tp
.item
->ignore
= TRUE
;
1935 tp
.tree
->header
.num_items
--;
1936 tp
.tree
->size
-= tp
.item
->size
+ sizeof(leaf_node
);
1937 tp
.tree
->write
= TRUE
;
1940 le2
= tp
.item
->list_entry
.Flink
;
1941 while (le2
!= &tp
.tree
->itemlist
) {
1942 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1944 if (td
->key
.obj_id
== bi
->key
.obj_id
&& td
->key
.obj_type
== bi
->key
.obj_type
) {
1947 tp
.tree
->header
.num_items
--;
1948 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
1949 tp
.tree
->write
= TRUE
;
1960 traverse_ptr next_tp
;
1964 if (!find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
1969 le2
= &tp
.item
->list_entry
;
1970 while (le2
!= &tp
.tree
->itemlist
) {
1971 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1973 if (td
->key
.obj_id
== bi
->key
.obj_id
&& td
->key
.obj_type
== bi
->key
.obj_type
) {
1976 tp
.tree
->header
.num_items
--;
1977 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
1978 tp
.tree
->write
= TRUE
;
1989 } else if (bi
->operation
== Batch_DeleteFreeSpace
) {
1990 if (tp
.item
->key
.obj_id
>= bi
->key
.obj_id
&& tp
.item
->key
.obj_id
< bi
->key
.obj_id
+ bi
->key
.offset
) {
1995 if (!tp
.item
->ignore
) {
1996 tp
.item
->ignore
= TRUE
;
1997 tp
.tree
->header
.num_items
--;
1998 tp
.tree
->size
-= tp
.item
->size
+ sizeof(leaf_node
);
1999 tp
.tree
->write
= TRUE
;
2002 le2
= tp
.item
->list_entry
.Flink
;
2003 while (le2
!= &tp
.tree
->itemlist
) {
2004 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2006 if (td
->key
.obj_id
>= bi
->key
.obj_id
&& td
->key
.obj_id
< bi
->key
.obj_id
+ bi
->key
.offset
) {
2009 tp
.tree
->header
.num_items
--;
2010 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
2011 tp
.tree
->write
= TRUE
;
2022 traverse_ptr next_tp
;
2026 if (!find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
))
2031 le2
= &tp
.item
->list_entry
;
2032 while (le2
!= &tp
.tree
->itemlist
) {
2033 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2035 if (td
->key
.obj_id
>= bi
->key
.obj_id
&& td
->key
.obj_id
< bi
->key
.obj_id
+ bi
->key
.offset
) {
2038 tp
.tree
->header
.num_items
--;
2039 tp
.tree
->size
-= td
->size
+ sizeof(leaf_node
);
2040 tp
.tree
->write
= TRUE
;
2052 if (bi
->operation
== Batch_Delete
|| bi
->operation
== Batch_DeleteDirItem
|| bi
->operation
== Batch_DeleteInodeRef
||
2053 bi
->operation
== Batch_DeleteInodeExtRef
|| bi
->operation
== Batch_DeleteXattr
)
2056 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2058 ERR("out of memory\n");
2059 return STATUS_INSUFFICIENT_RESOURCES
;
2063 td
->size
= bi
->datalen
;
2064 td
->data
= bi
->data
;
2066 td
->inserted
= TRUE
;
2069 cmp
= keycmp(bi
->key
, tp
.item
->key
);
2071 if (cmp
== -1) { // very first key in root
2075 InsertHeadList(&tp
.tree
->itemlist
, &td
->list_entry
);
2077 paritem
= tp
.tree
->paritem
;
2079 if (!keycmp(paritem
->key
, tp
.item
->key
)) {
2080 paritem
->key
= bi
->key
;
2084 paritem
= paritem
->treeholder
.tree
->paritem
;
2087 } else if (cmp
== 0) { // item already exists
2088 if (tp
.item
->ignore
) {
2090 InsertHeadList(tp
.item
->list_entry
.Blink
, &td
->list_entry
);
2092 Status
= handle_batch_collision(Vcb
, bi
, tp
.tree
, tp
.item
, td
, &br
->items
, &ignore
);
2093 if (!NT_SUCCESS(Status
)) {
2094 ERR("handle_batch_collision returned %08x\n", Status
);
2097 ExFreeToPagedLookasideList(&Vcb
->tree_data_lookaside
, td
);
2103 InsertHeadList(&tp
.item
->list_entry
, &td
->list_entry
);
2106 if (bi
->operation
== Batch_DeleteInodeRef
&& cmp
!= 0 && Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
2107 add_delete_inode_extref(Vcb
, bi
, &br
->items
);
2110 if (!ignore
&& td
) {
2111 tp
.tree
->header
.num_items
++;
2112 tp
.tree
->size
+= bi
->datalen
+ sizeof(leaf_node
);
2113 tp
.tree
->write
= TRUE
;
2119 while (listhead
->list_entry
.Blink
!= &tp
.tree
->itemlist
) {
2120 tree_data
* prevtd
= CONTAINING_RECORD(listhead
->list_entry
.Blink
, tree_data
, list_entry
);
2122 if (!keycmp(prevtd
->key
, listhead
->key
))
2129 while (le2
!= &br
->items
) {
2130 batch_item
* bi2
= CONTAINING_RECORD(le2
, batch_item
, list_entry
);
2132 if (bi2
->operation
== Batch_DeleteInode
|| bi2
->operation
== Batch_DeleteExtentData
|| bi2
->operation
== Batch_DeleteFreeSpace
)
2135 if (no_end
|| keycmp(bi2
->key
, tree_end
) == -1) {
2137 BOOL inserted
= FALSE
;
2141 if (bi2
->operation
== Batch_Delete
|| bi2
->operation
== Batch_DeleteDirItem
|| bi2
->operation
== Batch_DeleteInodeRef
||
2142 bi2
->operation
== Batch_DeleteInodeExtRef
|| bi2
->operation
== Batch_DeleteXattr
)
2145 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2147 ERR("out of memory\n");
2148 return STATUS_INSUFFICIENT_RESOURCES
;
2152 td
->size
= bi2
->datalen
;
2153 td
->data
= bi2
->data
;
2155 td
->inserted
= TRUE
;
2158 le3
= &listhead
->list_entry
;
2159 while (le3
!= &tp
.tree
->itemlist
) {
2160 tree_data
* td2
= CONTAINING_RECORD(le3
, tree_data
, list_entry
);
2162 cmp
= keycmp(bi2
->key
, td2
->key
);
2167 InsertHeadList(le3
->Blink
, &td
->list_entry
);
2169 } else if (bi2
->operation
== Batch_DeleteInodeRef
&& Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
2170 add_delete_inode_extref(Vcb
, bi2
, &br
->items
);
2173 Status
= handle_batch_collision(Vcb
, bi2
, tp
.tree
, td2
, td
, &br
->items
, &ignore
);
2174 if (!NT_SUCCESS(Status
)) {
2175 ERR("handle_batch_collision returned %08x\n", Status
);
2182 } else if (cmp
== -1) {
2184 InsertHeadList(le3
->Blink
, &td
->list_entry
);
2186 } else if (bi2
->operation
== Batch_DeleteInodeRef
&& Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
2187 add_delete_inode_extref(Vcb
, bi2
, &br
->items
);
2197 InsertTailList(&tp
.tree
->itemlist
, &td
->list_entry
);
2200 tp
.tree
->header
.num_items
++;
2201 tp
.tree
->size
+= bi2
->datalen
+ sizeof(leaf_node
);
2205 } else if (!inserted
&& bi2
->operation
== Batch_DeleteInodeRef
&& Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
) {
2206 add_delete_inode_extref(Vcb
, bi2
, &br
->items
);
2209 while (listhead
->list_entry
.Blink
!= &tp
.tree
->itemlist
) {
2210 tree_data
* prevtd
= CONTAINING_RECORD(listhead
->list_entry
.Blink
, tree_data
, list_entry
);
2212 if (!keycmp(prevtd
->key
, listhead
->key
))
2227 if (t
->paritem
&& t
->paritem
->ignore
) {
2228 t
->paritem
->ignore
= FALSE
;
2229 t
->parent
->header
.num_items
++;
2230 t
->parent
->size
+= sizeof(internal_node
);
2233 t
->header
.generation
= Vcb
->superblock
.generation
;
2241 // FIXME - remove as we are going along
2242 while (!IsListEmpty(&br
->items
)) {
2243 batch_item
* bi
= CONTAINING_RECORD(RemoveHeadList(&br
->items
), batch_item
, list_entry
);
2245 if ((bi
->operation
== Batch_DeleteDirItem
|| bi
->operation
== Batch_DeleteInodeRef
||
2246 bi
->operation
== Batch_DeleteInodeExtRef
|| bi
->operation
== Batch_DeleteXattr
) && bi
->data
)
2247 ExFreePool(bi
->data
);
2249 ExFreeToPagedLookasideList(&Vcb
->batch_item_lookaside
, bi
);
2252 return STATUS_SUCCESS
;
2255 NTSTATUS
commit_batch_list(_Requires_exclusive_lock_held_(_Curr_
->tree_lock
) device_extension
* Vcb
, LIST_ENTRY
* batchlist
, PIRP Irp
) {
2258 while (!IsListEmpty(batchlist
)) {
2259 LIST_ENTRY
* le
= RemoveHeadList(batchlist
);
2260 batch_root
* br2
= CONTAINING_RECORD(le
, batch_root
, list_entry
);
2262 Status
= commit_batch_list_root(Vcb
, br2
, Irp
);
2263 if (!NT_SUCCESS(Status
)) {
2264 ERR("commit_batch_list_root returned %08x\n", Status
);
2271 return STATUS_SUCCESS
;