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"
31 LIST_ENTRY list_entry
;
34 UINT64
get_extent_data_ref_hash2(UINT64 root
, UINT64 objid
, UINT64 offset
) {
35 UINT32 high_crc
= 0xffffffff, low_crc
= 0xffffffff;
37 high_crc
= calc_crc32c(high_crc
, (UINT8
*)&root
, sizeof(UINT64
));
38 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&objid
, sizeof(UINT64
));
39 low_crc
= calc_crc32c(low_crc
, (UINT8
*)&offset
, sizeof(UINT64
));
41 return ((UINT64
)high_crc
<< 31) ^ (UINT64
)low_crc
;
44 static __inline UINT64
get_extent_data_ref_hash(EXTENT_DATA_REF
* edr
) {
45 return get_extent_data_ref_hash2(edr
->root
, edr
->objid
, edr
->offset
);
48 static UINT64
get_extent_hash(UINT8 type
, void* data
) {
49 if (type
== TYPE_EXTENT_DATA_REF
) {
50 return get_extent_data_ref_hash((EXTENT_DATA_REF
*)data
);
51 } else if (type
== TYPE_SHARED_BLOCK_REF
) {
52 SHARED_BLOCK_REF
* sbr
= (SHARED_BLOCK_REF
*)data
;
54 } else if (type
== TYPE_SHARED_DATA_REF
) {
55 SHARED_DATA_REF
* sdr
= (SHARED_DATA_REF
*)data
;
57 } else if (type
== TYPE_TREE_BLOCK_REF
) {
58 TREE_BLOCK_REF
* tbr
= (TREE_BLOCK_REF
*)data
;
61 ERR("unhandled extent type %x\n", type
);
66 static void free_extent_refs(LIST_ENTRY
* extent_refs
) {
67 while (!IsListEmpty(extent_refs
)) {
68 LIST_ENTRY
* le
= RemoveHeadList(extent_refs
);
69 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
75 static NTSTATUS
add_shared_data_extent_ref(LIST_ENTRY
* extent_refs
, UINT64 parent
, UINT32 count
) {
79 if (!IsListEmpty(extent_refs
)) {
80 le
= extent_refs
->Flink
;
82 while (le
!= extent_refs
) {
83 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
85 if (er
->type
== TYPE_SHARED_DATA_REF
&& er
->sdr
.offset
== parent
) {
86 er
->sdr
.count
+= count
;
87 return STATUS_SUCCESS
;
94 er2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
96 ERR("out of memory\n");
97 return STATUS_INSUFFICIENT_RESOURCES
;
100 er2
->type
= TYPE_SHARED_DATA_REF
;
101 er2
->sdr
.offset
= parent
;
102 er2
->sdr
.count
= count
;
104 InsertTailList(extent_refs
, &er2
->list_entry
);
106 return STATUS_SUCCESS
;
109 static NTSTATUS
add_shared_block_extent_ref(LIST_ENTRY
* extent_refs
, UINT64 parent
) {
113 if (!IsListEmpty(extent_refs
)) {
114 le
= extent_refs
->Flink
;
116 while (le
!= extent_refs
) {
117 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
119 if (er
->type
== TYPE_SHARED_BLOCK_REF
&& er
->sbr
.offset
== parent
)
120 return STATUS_SUCCESS
;
126 er2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
128 ERR("out of memory\n");
129 return STATUS_INSUFFICIENT_RESOURCES
;
132 er2
->type
= TYPE_SHARED_BLOCK_REF
;
133 er2
->sbr
.offset
= parent
;
135 InsertTailList(extent_refs
, &er2
->list_entry
);
137 return STATUS_SUCCESS
;
140 static NTSTATUS
add_tree_block_extent_ref(LIST_ENTRY
* extent_refs
, UINT64 root
) {
144 if (!IsListEmpty(extent_refs
)) {
145 le
= extent_refs
->Flink
;
147 while (le
!= extent_refs
) {
148 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
150 if (er
->type
== TYPE_TREE_BLOCK_REF
&& er
->tbr
.offset
== root
)
151 return STATUS_SUCCESS
;
157 er2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_ref
), ALLOC_TAG
);
159 ERR("out of memory\n");
160 return STATUS_INSUFFICIENT_RESOURCES
;
163 er2
->type
= TYPE_TREE_BLOCK_REF
;
164 er2
->tbr
.offset
= root
;
166 InsertTailList(extent_refs
, &er2
->list_entry
);
168 return STATUS_SUCCESS
;
171 static void sort_extent_refs(LIST_ENTRY
* extent_refs
) {
174 if (IsListEmpty(extent_refs
))
179 InitializeListHead(&newlist
);
181 while (!IsListEmpty(extent_refs
)) {
182 extent_ref
* er
= CONTAINING_RECORD(RemoveHeadList(extent_refs
), extent_ref
, list_entry
);
184 BOOL inserted
= FALSE
;
187 while (le
!= &newlist
) {
188 extent_ref
* er2
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
190 if (er
->type
< er2
->type
|| (er
->type
== er2
->type
&& er
->hash
> er2
->hash
)) {
191 InsertHeadList(le
->Blink
, &er
->list_entry
);
200 InsertTailList(&newlist
, &er
->list_entry
);
203 newlist
.Flink
->Blink
= extent_refs
;
204 newlist
.Blink
->Flink
= extent_refs
;
205 extent_refs
->Flink
= newlist
.Flink
;
206 extent_refs
->Blink
= newlist
.Blink
;
209 static NTSTATUS
construct_extent_item(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT64 flags
, LIST_ENTRY
* extent_refs
,
210 KEY
* firstitem
, UINT8 level
, PIRP Irp
) {
212 LIST_ENTRY
*le
, *next_le
;
215 BOOL all_inline
= TRUE
;
216 extent_ref
* first_noninline
= NULL
;
220 // FIXME - write skinny extents if is tree and incompat flag set
222 if (IsListEmpty(extent_refs
)) {
223 WARN("no extent refs found\n");
224 return STATUS_SUCCESS
;
228 inline_len
= sizeof(EXTENT_ITEM
);
230 if (flags
& EXTENT_ITEM_TREE_BLOCK
)
231 inline_len
+= sizeof(EXTENT_ITEM2
);
233 le
= extent_refs
->Flink
;
234 while (le
!= extent_refs
) {
235 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
240 rc
= get_extent_data_refcount(er
->type
, &er
->edr
);
243 RemoveEntryList(&er
->list_entry
);
247 UINT16 extlen
= get_extent_data_len(er
->type
);
251 er
->hash
= get_extent_hash(er
->type
, &er
->edr
);
254 if ((UINT16
)(inline_len
+ 1 + extlen
) > Vcb
->superblock
.node_size
>> 2) {
256 first_noninline
= er
;
258 inline_len
+= extlen
+ 1;
265 ei
= ExAllocatePoolWithTag(PagedPool
, inline_len
, ALLOC_TAG
);
267 ERR("out of memory\n");
268 return STATUS_INSUFFICIENT_RESOURCES
;
271 ei
->refcount
= refcount
;
272 ei
->generation
= Vcb
->superblock
.generation
;
275 if (flags
& EXTENT_ITEM_TREE_BLOCK
) {
276 EXTENT_ITEM2
* ei2
= (EXTENT_ITEM2
*)&ei
[1];
279 ei2
->firstitem
.obj_id
= firstitem
->obj_id
;
280 ei2
->firstitem
.obj_type
= firstitem
->obj_type
;
281 ei2
->firstitem
.offset
= firstitem
->offset
;
283 ei2
->firstitem
.obj_id
= 0;
284 ei2
->firstitem
.obj_type
= 0;
285 ei2
->firstitem
.offset
= 0;
290 siptr
= (UINT8
*)&ei2
[1];
292 siptr
= (UINT8
*)&ei
[1];
294 sort_extent_refs(extent_refs
);
296 le
= extent_refs
->Flink
;
297 while (le
!= extent_refs
) {
298 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
299 ULONG extlen
= get_extent_data_len(er
->type
);
301 if (!all_inline
&& er
== first_noninline
)
308 RtlCopyMemory(siptr
, &er
->edr
, extlen
);
315 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, ei
, inline_len
, NULL
, Irp
);
316 if (!NT_SUCCESS(Status
)) {
317 ERR("insert_tree_item returned %08x\n", Status
);
323 le
= &first_noninline
->list_entry
;
325 while (le
!= extent_refs
) {
326 extent_ref
* er
= CONTAINING_RECORD(le
, extent_ref
, list_entry
);
330 if (er
->type
== TYPE_EXTENT_DATA_REF
) {
331 len
= sizeof(EXTENT_DATA_REF
);
333 data
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
336 ERR("out of memory\n");
337 return STATUS_INSUFFICIENT_RESOURCES
;
340 RtlCopyMemory(data
, &er
->edr
, len
);
341 } else if (er
->type
== TYPE_SHARED_DATA_REF
) {
342 len
= sizeof(UINT32
);
344 data
= ExAllocatePoolWithTag(PagedPool
, len
, ALLOC_TAG
);
347 ERR("out of memory\n");
348 return STATUS_INSUFFICIENT_RESOURCES
;
351 *((UINT32
*)data
) = er
->sdr
.count
;
357 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, er
->type
, er
->hash
, data
, len
, NULL
, Irp
);
358 if (!NT_SUCCESS(Status
)) {
359 ERR("insert_tree_item returned %08x\n", Status
);
360 if (data
) ExFreePool(data
);
368 return STATUS_SUCCESS
;
371 static NTSTATUS
convert_old_extent(device_extension
* Vcb
, UINT64 address
, BOOL tree
, KEY
* firstitem
, UINT8 level
, PIRP Irp
) {
374 traverse_ptr tp
, next_tp
;
375 LIST_ENTRY extent_refs
;
378 InitializeListHead(&extent_refs
);
380 searchkey
.obj_id
= address
;
381 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
382 searchkey
.offset
= 0xffffffffffffffff;
384 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
385 if (!NT_SUCCESS(Status
)) {
386 ERR("find_item returned %08x\n", Status
);
390 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
391 ERR("old-style extent %llx not found\n", address
);
392 return STATUS_INTERNAL_ERROR
;
395 size
= tp
.item
->key
.offset
;
397 Status
= delete_tree_item(Vcb
, &tp
);
398 if (!NT_SUCCESS(Status
)) {
399 ERR("delete_tree_item returned %08x\n", Status
);
403 while (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
406 if (tp
.item
->key
.obj_id
== address
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_REF_V0
&& tp
.item
->size
>= sizeof(EXTENT_REF_V0
)) {
407 EXTENT_REF_V0
* erv0
= (EXTENT_REF_V0
*)tp
.item
->data
;
410 if (tp
.item
->key
.offset
== tp
.item
->key
.obj_id
) { // top of the tree
411 Status
= add_tree_block_extent_ref(&extent_refs
, erv0
->root
);
412 if (!NT_SUCCESS(Status
)) {
413 ERR("add_tree_block_extent_ref returned %08x\n", Status
);
417 Status
= add_shared_block_extent_ref(&extent_refs
, tp
.item
->key
.offset
);
418 if (!NT_SUCCESS(Status
)) {
419 ERR("add_shared_block_extent_ref returned %08x\n", Status
);
424 Status
= add_shared_data_extent_ref(&extent_refs
, tp
.item
->key
.offset
, erv0
->count
);
425 if (!NT_SUCCESS(Status
)) {
426 ERR("add_shared_data_extent_ref returned %08x\n", Status
);
431 Status
= delete_tree_item(Vcb
, &tp
);
432 if (!NT_SUCCESS(Status
)) {
433 ERR("delete_tree_item returned %08x\n", Status
);
438 if (tp
.item
->key
.obj_id
> address
|| tp
.item
->key
.obj_type
> TYPE_EXTENT_REF_V0
)
442 Status
= construct_extent_item(Vcb
, address
, size
, tree
? (EXTENT_ITEM_TREE_BLOCK
| EXTENT_ITEM_SHARED_BACKREFS
) : EXTENT_ITEM_DATA
,
443 &extent_refs
, firstitem
, level
, Irp
);
444 if (!NT_SUCCESS(Status
))
445 ERR("construct_extent_item returned %08x\n", Status
);
448 free_extent_refs(&extent_refs
);
453 NTSTATUS
increase_extent_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT8 type
, void* data
, KEY
* firstitem
, UINT8 level
, PIRP Irp
) {
457 ULONG len
, max_extent_item_size
;
458 UINT16 datalen
= get_extent_data_len(type
);
461 UINT64 inline_rc
, offset
;
465 BOOL is_tree
= type
== TYPE_TREE_BLOCK_REF
|| type
== TYPE_SHARED_BLOCK_REF
;
468 ERR("unrecognized extent type %x\n", type
);
469 return STATUS_INTERNAL_ERROR
;
472 searchkey
.obj_id
= address
;
473 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
474 searchkey
.offset
= 0xffffffffffffffff;
476 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
477 if (!NT_SUCCESS(Status
)) {
478 ERR("error - find_item returned %08x\n", Status
);
482 // If entry doesn't exist yet, create new inline extent item
484 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| (tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
)) {
487 eisize
= sizeof(EXTENT_ITEM
);
488 if (is_tree
&& !(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) eisize
+= sizeof(EXTENT_ITEM2
);
489 eisize
+= sizeof(UINT8
);
492 ei
= ExAllocatePoolWithTag(PagedPool
, eisize
, ALLOC_TAG
);
494 ERR("out of memory\n");
495 return STATUS_INSUFFICIENT_RESOURCES
;
498 ei
->refcount
= get_extent_data_refcount(type
, data
);
499 ei
->generation
= Vcb
->superblock
.generation
;
500 ei
->flags
= is_tree
? EXTENT_ITEM_TREE_BLOCK
: EXTENT_ITEM_DATA
;
501 ptr
= (UINT8
*)&ei
[1];
503 if (is_tree
&& !(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
504 EXTENT_ITEM2
* ei2
= (EXTENT_ITEM2
*)ptr
;
505 ei2
->firstitem
= *firstitem
;
507 ptr
= (UINT8
*)&ei2
[1];
511 RtlCopyMemory(ptr
+ 1, data
, datalen
);
513 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
&& is_tree
)
514 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, ei
, eisize
, NULL
, Irp
);
516 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, size
, ei
, eisize
, NULL
, Irp
);
518 if (!NT_SUCCESS(Status
)) {
519 ERR("insert_tree_item returned %08x\n", Status
);
523 return STATUS_SUCCESS
;
524 } else if (tp
.item
->key
.obj_id
== address
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& tp
.item
->key
.offset
!= size
) {
525 ERR("extent %llx exists, but with size %llx rather than %llx expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.offset
, size
);
526 return STATUS_INTERNAL_ERROR
;
529 skinny
= tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
;
531 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
) && !skinny
) {
532 Status
= convert_old_extent(Vcb
, address
, is_tree
, firstitem
, level
, Irp
);
534 if (!NT_SUCCESS(Status
)) {
535 ERR("convert_old_extent returned %08x\n", Status
);
539 return increase_extent_refcount(Vcb
, address
, size
, type
, data
, firstitem
, level
, Irp
);
542 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
543 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
));
544 return STATUS_INTERNAL_ERROR
;
547 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
549 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
550 ptr
= (UINT8
*)&ei
[1];
552 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
&& !skinny
) {
553 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
)) {
554 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
) + sizeof(EXTENT_ITEM2
));
555 return STATUS_INTERNAL_ERROR
;
558 len
-= sizeof(EXTENT_ITEM2
);
559 ptr
+= sizeof(EXTENT_ITEM2
);
564 // Loop through existing inline extent entries
567 UINT8 secttype
= *ptr
;
568 ULONG sectlen
= get_extent_data_len(secttype
);
569 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
574 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
575 return STATUS_INTERNAL_ERROR
;
579 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
580 return STATUS_INTERNAL_ERROR
;
583 // If inline extent already present, increase refcount and return
585 if (secttype
== type
) {
586 if (type
== TYPE_EXTENT_DATA_REF
) {
587 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)(ptr
+ sizeof(UINT8
));
588 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)data
;
590 if (sectedr
->root
== edr
->root
&& sectedr
->objid
== edr
->objid
&& sectedr
->offset
== edr
->offset
) {
591 UINT32 rc
= get_extent_data_refcount(type
, data
);
592 EXTENT_DATA_REF
* sectedr2
;
594 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
596 ERR("out of memory\n");
597 return STATUS_INSUFFICIENT_RESOURCES
;
600 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
602 newei
->refcount
+= rc
;
604 sectedr2
= (EXTENT_DATA_REF
*)((UINT8
*)newei
+ ((UINT8
*)sectedr
- tp
.item
->data
));
605 sectedr2
->count
+= rc
;
607 Status
= delete_tree_item(Vcb
, &tp
);
608 if (!NT_SUCCESS(Status
)) {
609 ERR("delete_tree_item returned %08x\n", Status
);
613 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
614 if (!NT_SUCCESS(Status
)) {
615 ERR("insert_tree_item returned %08x\n", Status
);
619 return STATUS_SUCCESS
;
621 } else if (type
== TYPE_TREE_BLOCK_REF
) {
622 TREE_BLOCK_REF
* secttbr
= (TREE_BLOCK_REF
*)(ptr
+ sizeof(UINT8
));
623 TREE_BLOCK_REF
* tbr
= (TREE_BLOCK_REF
*)data
;
625 if (secttbr
->offset
== tbr
->offset
) {
626 TRACE("trying to increase refcount of non-shared tree extent\n");
627 return STATUS_SUCCESS
;
629 } else if (type
== TYPE_SHARED_BLOCK_REF
) {
630 SHARED_BLOCK_REF
* sectsbr
= (SHARED_BLOCK_REF
*)(ptr
+ sizeof(UINT8
));
631 SHARED_BLOCK_REF
* sbr
= (SHARED_BLOCK_REF
*)data
;
633 if (sectsbr
->offset
== sbr
->offset
)
634 return STATUS_SUCCESS
;
635 } else if (type
== TYPE_SHARED_DATA_REF
) {
636 SHARED_DATA_REF
* sectsdr
= (SHARED_DATA_REF
*)(ptr
+ sizeof(UINT8
));
637 SHARED_DATA_REF
* sdr
= (SHARED_DATA_REF
*)data
;
639 if (sectsdr
->offset
== sdr
->offset
) {
640 UINT32 rc
= get_extent_data_refcount(type
, data
);
641 SHARED_DATA_REF
* sectsdr2
;
643 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
645 ERR("out of memory\n");
646 return STATUS_INSUFFICIENT_RESOURCES
;
649 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
651 newei
->refcount
+= rc
;
653 sectsdr2
= (SHARED_DATA_REF
*)((UINT8
*)newei
+ ((UINT8
*)sectsdr
- tp
.item
->data
));
654 sectsdr2
->count
+= rc
;
656 Status
= delete_tree_item(Vcb
, &tp
);
657 if (!NT_SUCCESS(Status
)) {
658 ERR("delete_tree_item returned %08x\n", Status
);
662 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
663 if (!NT_SUCCESS(Status
)) {
664 ERR("insert_tree_item returned %08x\n", Status
);
668 return STATUS_SUCCESS
;
671 ERR("unhandled extent type %x\n", type
);
672 return STATUS_INTERNAL_ERROR
;
677 ptr
+= sizeof(UINT8
) + sectlen
;
678 inline_rc
+= sectcount
;
681 offset
= get_extent_hash(type
, data
);
683 max_extent_item_size
= (Vcb
->superblock
.node_size
>> 4) - sizeof(leaf_node
);
685 // If we can, add entry as inline extent item
687 if (inline_rc
== ei
->refcount
&& tp
.item
->size
+ sizeof(UINT8
) + datalen
< max_extent_item_size
) {
688 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
689 ptr
= (UINT8
*)&ei
[1];
691 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
&& !skinny
) {
692 len
-= sizeof(EXTENT_ITEM2
);
693 ptr
+= sizeof(EXTENT_ITEM2
);
696 // Confusingly, it appears that references are sorted forward by type (i.e. EXTENT_DATA_REFs before
697 // SHARED_DATA_REFs), but then backwards by hash...
700 UINT8 secttype
= *ptr
;
701 ULONG sectlen
= get_extent_data_len(secttype
);
706 if (secttype
== type
) {
707 UINT64 sectoff
= get_extent_hash(secttype
, ptr
+ 1);
709 if (sectoff
< offset
)
713 len
-= sectlen
+ sizeof(UINT8
);
714 ptr
+= sizeof(UINT8
) + sectlen
;
717 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ sizeof(UINT8
) + datalen
, ALLOC_TAG
);
718 RtlCopyMemory(newei
, tp
.item
->data
, ptr
- tp
.item
->data
);
720 newei
->refcount
+= get_extent_data_refcount(type
, data
);
723 RtlCopyMemory((UINT8
*)newei
+ (ptr
- tp
.item
->data
) + sizeof(UINT8
) + datalen
, ptr
, len
);
725 ptr
= (ptr
- tp
.item
->data
) + (UINT8
*)newei
;
728 RtlCopyMemory(ptr
+ 1, data
, datalen
);
730 Status
= delete_tree_item(Vcb
, &tp
);
731 if (!NT_SUCCESS(Status
)) {
732 ERR("delete_tree_item returned %08x\n", Status
);
736 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
+ sizeof(UINT8
) + datalen
, NULL
, Irp
);
737 if (!NT_SUCCESS(Status
)) {
738 ERR("insert_tree_item returned %08x\n", Status
);
742 return STATUS_SUCCESS
;
745 // Look for existing non-inline entry, and increase refcount if found
747 if (inline_rc
!= ei
->refcount
) {
750 searchkey
.obj_id
= address
;
751 searchkey
.obj_type
= type
;
752 searchkey
.offset
= offset
;
754 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
, Irp
);
755 if (!NT_SUCCESS(Status
)) {
756 ERR("error - find_item returned %08x\n", Status
);
760 if (!keycmp(tp2
.item
->key
, searchkey
)) {
761 if (type
== TYPE_SHARED_DATA_REF
&& tp2
.item
->size
< sizeof(UINT32
)) {
762 ERR("(%llx,%x,%llx) was %x bytes, expecting %x\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, tp2
.item
->size
, sizeof(UINT32
));
763 return STATUS_INTERNAL_ERROR
;
764 } else if (type
!= TYPE_SHARED_DATA_REF
&& tp2
.item
->size
< datalen
) {
765 ERR("(%llx,%x,%llx) was %x bytes, expecting %x\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, tp2
.item
->size
, datalen
);
766 return STATUS_INTERNAL_ERROR
;
769 data2
= ExAllocatePoolWithTag(PagedPool
, tp2
.item
->size
, ALLOC_TAG
);
771 ERR("out of memory\n");
772 return STATUS_INSUFFICIENT_RESOURCES
;
775 RtlCopyMemory(data2
, tp2
.item
->data
, tp2
.item
->size
);
777 if (type
== TYPE_EXTENT_DATA_REF
) {
778 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)data2
;
780 edr
->count
+= get_extent_data_refcount(type
, data
);
781 } else if (type
== TYPE_TREE_BLOCK_REF
) {
782 TRACE("trying to increase refcount of non-shared tree extent\n");
783 return STATUS_SUCCESS
;
784 } else if (type
== TYPE_SHARED_BLOCK_REF
)
785 return STATUS_SUCCESS
;
786 else if (type
== TYPE_SHARED_DATA_REF
) {
787 UINT32
* sdr
= (UINT32
*)data2
;
789 *sdr
+= get_extent_data_refcount(type
, data
);
791 ERR("unhandled extent type %x\n", type
);
792 return STATUS_INTERNAL_ERROR
;
795 Status
= delete_tree_item(Vcb
, &tp2
);
796 if (!NT_SUCCESS(Status
)) {
797 ERR("delete_tree_item returned %08x\n", Status
);
801 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, data2
, tp2
.item
->size
, NULL
, Irp
);
802 if (!NT_SUCCESS(Status
)) {
803 ERR("insert_tree_item returned %08x\n", Status
);
807 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
809 ERR("out of memory\n");
810 return STATUS_INSUFFICIENT_RESOURCES
;
813 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
815 newei
->refcount
+= get_extent_data_refcount(type
, data
);
817 Status
= delete_tree_item(Vcb
, &tp
);
818 if (!NT_SUCCESS(Status
)) {
819 ERR("delete_tree_item returned %08x\n", Status
);
823 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
824 if (!NT_SUCCESS(Status
)) {
825 ERR("insert_tree_item returned %08x\n", Status
);
829 return STATUS_SUCCESS
;
833 // Otherwise, add new non-inline entry
835 if (type
== TYPE_SHARED_DATA_REF
) {
836 SHARED_DATA_REF
* sdr
= (SHARED_DATA_REF
*)data
;
838 data2
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
), ALLOC_TAG
);
840 ERR("out of memory\n");
841 return STATUS_INSUFFICIENT_RESOURCES
;
844 datalen
= sizeof(UINT32
);
846 *((UINT32
*)data2
) = sdr
->count
;
847 } else if (type
== TYPE_TREE_BLOCK_REF
|| type
== TYPE_SHARED_BLOCK_REF
) {
851 data2
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
853 ERR("out of memory\n");
854 return STATUS_INSUFFICIENT_RESOURCES
;
857 RtlCopyMemory(data2
, data
, datalen
);
860 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, type
, offset
, data2
, datalen
, NULL
, Irp
);
861 if (!NT_SUCCESS(Status
)) {
862 ERR("insert_tree_item returned %08x\n", Status
);
866 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
868 ERR("out of memory\n");
869 return STATUS_INSUFFICIENT_RESOURCES
;
872 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
874 newei
->refcount
+= get_extent_data_refcount(type
, data
);
876 Status
= delete_tree_item(Vcb
, &tp
);
877 if (!NT_SUCCESS(Status
)) {
878 ERR("delete_tree_item returned %08x\n", Status
);
882 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
883 if (!NT_SUCCESS(Status
)) {
884 ERR("insert_tree_item returned %08x\n", Status
);
888 return STATUS_SUCCESS
;
891 NTSTATUS
increase_extent_refcount_data(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 inode
, UINT64 offset
, UINT32 refcount
, PIRP Irp
) {
897 edr
.count
= refcount
;
899 return increase_extent_refcount(Vcb
, address
, size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, Irp
);
902 NTSTATUS
decrease_extent_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT8 type
, void* data
, KEY
* firstitem
,
903 UINT8 level
, UINT64 parent
, BOOL superseded
, PIRP Irp
) {
906 traverse_ptr tp
, tp2
;
911 UINT32 rc
= data
? get_extent_data_refcount(type
, data
) : 1;
912 ULONG datalen
= get_extent_data_len(type
);
913 BOOL is_tree
= (type
== TYPE_TREE_BLOCK_REF
|| type
== TYPE_SHARED_BLOCK_REF
), skinny
= FALSE
;
915 if (is_tree
&& Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
916 searchkey
.obj_id
= address
;
917 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
918 searchkey
.offset
= 0xffffffffffffffff;
920 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
921 if (!NT_SUCCESS(Status
)) {
922 ERR("error - find_item returned %08x\n", Status
);
926 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
931 searchkey
.obj_id
= address
;
932 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
933 searchkey
.offset
= 0xffffffffffffffff;
935 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
936 if (!NT_SUCCESS(Status
)) {
937 ERR("error - find_item returned %08x\n", Status
);
941 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
942 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
943 return STATUS_INTERNAL_ERROR
;
946 if (tp
.item
->key
.offset
!= size
) {
947 ERR("extent %llx had length %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, size
);
948 return STATUS_INTERNAL_ERROR
;
951 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
952 Status
= convert_old_extent(Vcb
, address
, is_tree
, firstitem
, level
, Irp
);
954 if (!NT_SUCCESS(Status
)) {
955 ERR("convert_old_extent returned %08x\n", Status
);
959 return decrease_extent_refcount(Vcb
, address
, size
, type
, data
, firstitem
, level
, parent
, superseded
, Irp
);
963 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
964 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
));
965 return STATUS_INTERNAL_ERROR
;
968 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
970 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
971 ptr
= (UINT8
*)&ei
[1];
973 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
&& !skinny
) {
974 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
)) {
975 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
) + sizeof(EXTENT_ITEM2
));
976 return STATUS_INTERNAL_ERROR
;
979 len
-= sizeof(EXTENT_ITEM2
);
980 ptr
+= sizeof(EXTENT_ITEM2
);
983 if (ei
->refcount
< rc
) {
984 ERR("error - extent has refcount %llx, trying to reduce by %x\n", ei
->refcount
, rc
);
985 return STATUS_INTERNAL_ERROR
;
990 // Loop through inline extent entries
993 UINT8 secttype
= *ptr
;
994 UINT16 sectlen
= get_extent_data_len(secttype
);
995 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
1000 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
1001 return STATUS_INTERNAL_ERROR
;
1005 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
1006 return STATUS_INTERNAL_ERROR
;
1009 if (secttype
== type
) {
1010 if (type
== TYPE_EXTENT_DATA_REF
) {
1011 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)(ptr
+ sizeof(UINT8
));
1012 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)data
;
1014 if (sectedr
->root
== edr
->root
&& sectedr
->objid
== edr
->objid
&& sectedr
->offset
== edr
->offset
) {
1018 if (ei
->refcount
== edr
->count
) {
1019 Status
= delete_tree_item(Vcb
, &tp
);
1020 if (!NT_SUCCESS(Status
)) {
1021 ERR("delete_tree_item returned %08x\n", Status
);
1026 add_checksum_entry(Vcb
, address
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
), NULL
, Irp
);
1028 return STATUS_SUCCESS
;
1031 if (sectedr
->count
< edr
->count
) {
1032 ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr
->count
, edr
->count
);
1033 return STATUS_INTERNAL_ERROR
;
1036 if (sectedr
->count
> edr
->count
) // reduce section refcount
1037 neweilen
= tp
.item
->size
;
1038 else // remove section entirely
1039 neweilen
= tp
.item
->size
- sizeof(UINT8
) - sectlen
;
1041 newei
= ExAllocatePoolWithTag(PagedPool
, neweilen
, ALLOC_TAG
);
1043 ERR("out of memory\n");
1044 return STATUS_INSUFFICIENT_RESOURCES
;
1047 if (sectedr
->count
> edr
->count
) {
1048 EXTENT_DATA_REF
* newedr
= (EXTENT_DATA_REF
*)((UINT8
*)newei
+ ((UINT8
*)sectedr
- tp
.item
->data
));
1050 RtlCopyMemory(newei
, ei
, neweilen
);
1052 newedr
->count
-= rc
;
1054 RtlCopyMemory(newei
, ei
, ptr
- tp
.item
->data
);
1057 RtlCopyMemory((UINT8
*)newei
+ (ptr
- tp
.item
->data
), ptr
+ sectlen
+ sizeof(UINT8
), len
- sectlen
);
1060 newei
->refcount
-= rc
;
1062 Status
= delete_tree_item(Vcb
, &tp
);
1063 if (!NT_SUCCESS(Status
)) {
1064 ERR("delete_tree_item returned %08x\n", Status
);
1068 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, neweilen
, NULL
, Irp
);
1069 if (!NT_SUCCESS(Status
)) {
1070 ERR("insert_tree_item returned %08x\n", Status
);
1074 return STATUS_SUCCESS
;
1076 } else if (type
== TYPE_SHARED_DATA_REF
) {
1077 SHARED_DATA_REF
* sectsdr
= (SHARED_DATA_REF
*)(ptr
+ sizeof(UINT8
));
1078 SHARED_DATA_REF
* sdr
= (SHARED_DATA_REF
*)data
;
1080 if (sectsdr
->offset
== sdr
->offset
) {
1084 if (ei
->refcount
== sectsdr
->count
) {
1085 Status
= delete_tree_item(Vcb
, &tp
);
1086 if (!NT_SUCCESS(Status
)) {
1087 ERR("delete_tree_item returned %08x\n", Status
);
1092 add_checksum_entry(Vcb
, address
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
), NULL
, Irp
);
1094 return STATUS_SUCCESS
;
1097 if (sectsdr
->count
< sdr
->count
) {
1098 ERR("error - SHARED_DATA_REF has refcount %x, trying to reduce by %x\n", sectsdr
->count
, sdr
->count
);
1099 return STATUS_INTERNAL_ERROR
;
1102 if (sectsdr
->count
> sdr
->count
) // reduce section refcount
1103 neweilen
= tp
.item
->size
;
1104 else // remove section entirely
1105 neweilen
= tp
.item
->size
- sizeof(UINT8
) - sectlen
;
1107 newei
= ExAllocatePoolWithTag(PagedPool
, neweilen
, ALLOC_TAG
);
1109 ERR("out of memory\n");
1110 return STATUS_INSUFFICIENT_RESOURCES
;
1113 if (sectsdr
->count
> sdr
->count
) {
1114 SHARED_DATA_REF
* newsdr
= (SHARED_DATA_REF
*)((UINT8
*)newei
+ ((UINT8
*)sectsdr
- tp
.item
->data
));
1116 RtlCopyMemory(newei
, ei
, neweilen
);
1118 newsdr
->count
-= rc
;
1120 RtlCopyMemory(newei
, ei
, ptr
- tp
.item
->data
);
1123 RtlCopyMemory((UINT8
*)newei
+ (ptr
- tp
.item
->data
), ptr
+ sectlen
+ sizeof(UINT8
), len
- sectlen
);
1126 newei
->refcount
-= rc
;
1128 Status
= delete_tree_item(Vcb
, &tp
);
1129 if (!NT_SUCCESS(Status
)) {
1130 ERR("delete_tree_item returned %08x\n", Status
);
1134 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, neweilen
, NULL
, Irp
);
1135 if (!NT_SUCCESS(Status
)) {
1136 ERR("insert_tree_item returned %08x\n", Status
);
1140 return STATUS_SUCCESS
;
1142 } else if (type
== TYPE_TREE_BLOCK_REF
) {
1143 TREE_BLOCK_REF
* secttbr
= (TREE_BLOCK_REF
*)(ptr
+ sizeof(UINT8
));
1144 TREE_BLOCK_REF
* tbr
= (TREE_BLOCK_REF
*)data
;
1146 if (secttbr
->offset
== tbr
->offset
) {
1150 if (ei
->refcount
== 1) {
1151 Status
= delete_tree_item(Vcb
, &tp
);
1152 if (!NT_SUCCESS(Status
)) {
1153 ERR("delete_tree_item returned %08x\n", Status
);
1157 return STATUS_SUCCESS
;
1160 neweilen
= tp
.item
->size
- sizeof(UINT8
) - sectlen
;
1162 newei
= ExAllocatePoolWithTag(PagedPool
, neweilen
, ALLOC_TAG
);
1164 ERR("out of memory\n");
1165 return STATUS_INSUFFICIENT_RESOURCES
;
1168 RtlCopyMemory(newei
, ei
, ptr
- tp
.item
->data
);
1171 RtlCopyMemory((UINT8
*)newei
+ (ptr
- tp
.item
->data
), ptr
+ sectlen
+ sizeof(UINT8
), len
- sectlen
);
1175 Status
= delete_tree_item(Vcb
, &tp
);
1176 if (!NT_SUCCESS(Status
)) {
1177 ERR("delete_tree_item returned %08x\n", Status
);
1181 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, neweilen
, NULL
, Irp
);
1182 if (!NT_SUCCESS(Status
)) {
1183 ERR("insert_tree_item returned %08x\n", Status
);
1187 return STATUS_SUCCESS
;
1189 } else if (type
== TYPE_SHARED_BLOCK_REF
) {
1190 SHARED_BLOCK_REF
* sectsbr
= (SHARED_BLOCK_REF
*)(ptr
+ sizeof(UINT8
));
1191 SHARED_BLOCK_REF
* sbr
= (SHARED_BLOCK_REF
*)data
;
1193 if (sectsbr
->offset
== sbr
->offset
) {
1197 if (ei
->refcount
== 1) {
1198 Status
= delete_tree_item(Vcb
, &tp
);
1199 if (!NT_SUCCESS(Status
)) {
1200 ERR("delete_tree_item returned %08x\n", Status
);
1204 return STATUS_SUCCESS
;
1207 neweilen
= tp
.item
->size
- sizeof(UINT8
) - sectlen
;
1209 newei
= ExAllocatePoolWithTag(PagedPool
, neweilen
, ALLOC_TAG
);
1211 ERR("out of memory\n");
1212 return STATUS_INSUFFICIENT_RESOURCES
;
1215 RtlCopyMemory(newei
, ei
, ptr
- tp
.item
->data
);
1218 RtlCopyMemory((UINT8
*)newei
+ (ptr
- tp
.item
->data
), ptr
+ sectlen
+ sizeof(UINT8
), len
- sectlen
);
1222 Status
= delete_tree_item(Vcb
, &tp
);
1223 if (!NT_SUCCESS(Status
)) {
1224 ERR("delete_tree_item returned %08x\n", Status
);
1228 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, neweilen
, NULL
, Irp
);
1229 if (!NT_SUCCESS(Status
)) {
1230 ERR("insert_tree_item returned %08x\n", Status
);
1234 return STATUS_SUCCESS
;
1237 ERR("unhandled extent type %x\n", type
);
1238 return STATUS_INTERNAL_ERROR
;
1243 ptr
+= sizeof(UINT8
) + sectlen
;
1244 inline_rc
+= sectcount
;
1247 if (inline_rc
== ei
->refcount
) {
1248 ERR("entry not found in inline extent item for address %llx\n", address
);
1249 return STATUS_INTERNAL_ERROR
;
1252 if (type
== TYPE_SHARED_DATA_REF
)
1253 datalen
= sizeof(UINT32
);
1254 else if (type
== TYPE_TREE_BLOCK_REF
|| type
== TYPE_SHARED_BLOCK_REF
)
1257 searchkey
.obj_id
= address
;
1258 searchkey
.obj_type
= type
;
1259 searchkey
.offset
= (type
== TYPE_SHARED_DATA_REF
|| type
== TYPE_EXTENT_REF_V0
) ? parent
: get_extent_hash(type
, data
);
1261 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
, Irp
);
1262 if (!NT_SUCCESS(Status
)) {
1263 ERR("error - find_item returned %08x\n", Status
);
1267 if (keycmp(tp2
.item
->key
, searchkey
)) {
1268 ERR("(%llx,%x,%llx) not found\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
);
1269 return STATUS_INTERNAL_ERROR
;
1272 if (tp2
.item
->size
< datalen
) {
1273 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, tp2
.item
->size
, datalen
);
1274 return STATUS_INTERNAL_ERROR
;
1277 if (type
== TYPE_EXTENT_DATA_REF
) {
1278 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)tp2
.item
->data
;
1279 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)data
;
1281 if (sectedr
->root
== edr
->root
&& sectedr
->objid
== edr
->objid
&& sectedr
->offset
== edr
->offset
) {
1284 if (ei
->refcount
== edr
->count
) {
1285 Status
= delete_tree_item(Vcb
, &tp
);
1286 if (!NT_SUCCESS(Status
)) {
1287 ERR("delete_tree_item returned %08x\n", Status
);
1291 Status
= delete_tree_item(Vcb
, &tp2
);
1292 if (!NT_SUCCESS(Status
)) {
1293 ERR("delete_tree_item returned %08x\n", Status
);
1298 add_checksum_entry(Vcb
, address
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
), NULL
, Irp
);
1300 return STATUS_SUCCESS
;
1303 if (sectedr
->count
< edr
->count
) {
1304 ERR("error - extent section has refcount %x, trying to reduce by %x\n", sectedr
->count
, edr
->count
);
1305 return STATUS_INTERNAL_ERROR
;
1308 Status
= delete_tree_item(Vcb
, &tp2
);
1309 if (!NT_SUCCESS(Status
)) {
1310 ERR("delete_tree_item returned %08x\n", Status
);
1314 if (sectedr
->count
> edr
->count
) {
1315 EXTENT_DATA_REF
* newedr
= ExAllocatePoolWithTag(PagedPool
, tp2
.item
->size
, ALLOC_TAG
);
1318 ERR("out of memory\n");
1319 return STATUS_INSUFFICIENT_RESOURCES
;
1322 RtlCopyMemory(newedr
, sectedr
, tp2
.item
->size
);
1324 newedr
->count
-= edr
->count
;
1326 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, newedr
, tp2
.item
->size
, NULL
, Irp
);
1327 if (!NT_SUCCESS(Status
)) {
1328 ERR("insert_tree_item returned %08x\n", Status
);
1333 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
1335 ERR("out of memory\n");
1336 return STATUS_INSUFFICIENT_RESOURCES
;
1339 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
1341 newei
->refcount
-= rc
;
1343 Status
= delete_tree_item(Vcb
, &tp
);
1344 if (!NT_SUCCESS(Status
)) {
1345 ERR("delete_tree_item returned %08x\n", Status
);
1349 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
1350 if (!NT_SUCCESS(Status
)) {
1351 ERR("insert_tree_item returned %08x\n", Status
);
1355 return STATUS_SUCCESS
;
1357 ERR("error - hash collision?\n");
1358 return STATUS_INTERNAL_ERROR
;
1360 } else if (type
== TYPE_SHARED_DATA_REF
) {
1361 SHARED_DATA_REF
* sdr
= (SHARED_DATA_REF
*)data
;
1363 if (tp2
.item
->key
.offset
== sdr
->offset
) {
1364 UINT32
* sectsdrcount
= (UINT32
*)tp2
.item
->data
;
1367 if (ei
->refcount
== sdr
->count
) {
1368 Status
= delete_tree_item(Vcb
, &tp
);
1369 if (!NT_SUCCESS(Status
)) {
1370 ERR("delete_tree_item returned %08x\n", Status
);
1374 Status
= delete_tree_item(Vcb
, &tp2
);
1375 if (!NT_SUCCESS(Status
)) {
1376 ERR("delete_tree_item returned %08x\n", Status
);
1381 add_checksum_entry(Vcb
, address
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
), NULL
, Irp
);
1383 return STATUS_SUCCESS
;
1386 if (*sectsdrcount
< sdr
->count
) {
1387 ERR("error - extent section has refcount %x, trying to reduce by %x\n", *sectsdrcount
, sdr
->count
);
1388 return STATUS_INTERNAL_ERROR
;
1391 Status
= delete_tree_item(Vcb
, &tp2
);
1392 if (!NT_SUCCESS(Status
)) {
1393 ERR("delete_tree_item returned %08x\n", Status
);
1397 if (*sectsdrcount
> sdr
->count
) {
1398 UINT32
* newsdr
= ExAllocatePoolWithTag(PagedPool
, tp2
.item
->size
, ALLOC_TAG
);
1401 ERR("out of memory\n");
1402 return STATUS_INSUFFICIENT_RESOURCES
;
1405 *newsdr
= *sectsdrcount
- sdr
->count
;
1407 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, newsdr
, tp2
.item
->size
, NULL
, Irp
);
1408 if (!NT_SUCCESS(Status
)) {
1409 ERR("insert_tree_item returned %08x\n", Status
);
1414 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
1416 ERR("out of memory\n");
1417 return STATUS_INSUFFICIENT_RESOURCES
;
1420 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
1422 newei
->refcount
-= rc
;
1424 Status
= delete_tree_item(Vcb
, &tp
);
1425 if (!NT_SUCCESS(Status
)) {
1426 ERR("delete_tree_item returned %08x\n", Status
);
1430 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
1431 if (!NT_SUCCESS(Status
)) {
1432 ERR("insert_tree_item returned %08x\n", Status
);
1436 return STATUS_SUCCESS
;
1438 ERR("error - collision?\n");
1439 return STATUS_INTERNAL_ERROR
;
1441 } else if (type
== TYPE_TREE_BLOCK_REF
|| type
== TYPE_SHARED_BLOCK_REF
) {
1444 if (ei
->refcount
== 1) {
1445 Status
= delete_tree_item(Vcb
, &tp
);
1446 if (!NT_SUCCESS(Status
)) {
1447 ERR("delete_tree_item returned %08x\n", Status
);
1451 Status
= delete_tree_item(Vcb
, &tp2
);
1452 if (!NT_SUCCESS(Status
)) {
1453 ERR("delete_tree_item returned %08x\n", Status
);
1457 return STATUS_SUCCESS
;
1460 Status
= delete_tree_item(Vcb
, &tp2
);
1461 if (!NT_SUCCESS(Status
)) {
1462 ERR("delete_tree_item returned %08x\n", Status
);
1466 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
1468 ERR("out of memory\n");
1469 return STATUS_INSUFFICIENT_RESOURCES
;
1472 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
1474 newei
->refcount
-= rc
;
1476 Status
= delete_tree_item(Vcb
, &tp
);
1477 if (!NT_SUCCESS(Status
)) {
1478 ERR("delete_tree_item returned %08x\n", Status
);
1482 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
1483 if (!NT_SUCCESS(Status
)) {
1484 ERR("insert_tree_item returned %08x\n", Status
);
1488 return STATUS_SUCCESS
;
1489 } else if (type
== TYPE_EXTENT_REF_V0
) {
1490 EXTENT_REF_V0
* erv0
= (EXTENT_REF_V0
*)tp2
.item
->data
;
1493 if (ei
->refcount
== erv0
->count
) {
1494 Status
= delete_tree_item(Vcb
, &tp
);
1495 if (!NT_SUCCESS(Status
)) {
1496 ERR("delete_tree_item returned %08x\n", Status
);
1500 Status
= delete_tree_item(Vcb
, &tp2
);
1501 if (!NT_SUCCESS(Status
)) {
1502 ERR("delete_tree_item returned %08x\n", Status
);
1507 add_checksum_entry(Vcb
, address
, (ULONG
)(size
/ Vcb
->superblock
.sector_size
), NULL
, Irp
);
1509 return STATUS_SUCCESS
;
1512 Status
= delete_tree_item(Vcb
, &tp2
);
1513 if (!NT_SUCCESS(Status
)) {
1514 ERR("delete_tree_item returned %08x\n", Status
);
1518 newei
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
1520 ERR("out of memory\n");
1521 return STATUS_INSUFFICIENT_RESOURCES
;
1524 RtlCopyMemory(newei
, tp
.item
->data
, tp
.item
->size
);
1526 newei
->refcount
-= rc
;
1528 Status
= delete_tree_item(Vcb
, &tp
);
1529 if (!NT_SUCCESS(Status
)) {
1530 ERR("delete_tree_item returned %08x\n", Status
);
1534 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newei
, tp
.item
->size
, NULL
, Irp
);
1535 if (!NT_SUCCESS(Status
)) {
1536 ERR("insert_tree_item returned %08x\n", Status
);
1540 return STATUS_SUCCESS
;
1542 ERR("unhandled extent type %x\n", type
);
1543 return STATUS_INTERNAL_ERROR
;
1547 NTSTATUS
decrease_extent_refcount_data(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 inode
,
1548 UINT64 offset
, UINT32 refcount
, BOOL superseded
, PIRP Irp
) {
1549 EXTENT_DATA_REF edr
;
1553 edr
.offset
= offset
;
1554 edr
.count
= refcount
;
1556 return decrease_extent_refcount(Vcb
, address
, size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, 0, superseded
, Irp
);
1559 NTSTATUS
decrease_extent_refcount_tree(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT64 root
,
1560 UINT8 level
, PIRP Irp
) {
1565 return decrease_extent_refcount(Vcb
, address
, size
, TYPE_TREE_BLOCK_REF
, &tbr
, NULL
/*FIXME*/, level
, 0, FALSE
, Irp
);
1568 static UINT32
find_extent_data_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 objid
, UINT64 offset
, PIRP Irp
) {
1573 searchkey
.obj_id
= address
;
1574 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1575 searchkey
.offset
= 0xffffffffffffffff;
1577 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1578 if (!NT_SUCCESS(Status
)) {
1579 ERR("error - find_item returned %08x\n", Status
);
1583 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1584 TRACE("could not find address %llx in extent tree\n", address
);
1588 if (tp
.item
->key
.offset
!= size
) {
1589 ERR("extent %llx had size %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, size
);
1593 if (tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
1594 EXTENT_ITEM
* ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1595 UINT32 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
1596 UINT8
* ptr
= (UINT8
*)&ei
[1];
1599 UINT8 secttype
= *ptr
;
1600 ULONG sectlen
= get_extent_data_len(secttype
);
1601 UINT32 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
1605 if (sectlen
> len
) {
1606 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
1611 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
1615 if (secttype
== TYPE_EXTENT_DATA_REF
) {
1616 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)(ptr
+ sizeof(UINT8
));
1618 if (sectedr
->root
== root
&& sectedr
->objid
== objid
&& sectedr
->offset
== offset
)
1623 ptr
+= sizeof(UINT8
) + sectlen
;
1627 searchkey
.obj_id
= address
;
1628 searchkey
.obj_type
= TYPE_EXTENT_DATA_REF
;
1629 searchkey
.offset
= get_extent_data_ref_hash2(root
, objid
, offset
);
1631 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1632 if (!NT_SUCCESS(Status
)) {
1633 ERR("error - find_item returned %08x\n", Status
);
1637 if (!keycmp(searchkey
, tp
.item
->key
)) {
1638 if (tp
.item
->size
< sizeof(EXTENT_DATA_REF
))
1639 ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_DATA_REF
));
1641 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)tp
.item
->data
;
1650 UINT64
get_extent_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 size
, PIRP Irp
) {
1656 searchkey
.obj_id
= address
;
1657 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
1658 searchkey
.offset
= 0xffffffffffffffff;
1660 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1661 if (!NT_SUCCESS(Status
)) {
1662 ERR("error - find_item returned %08x\n", Status
);
1666 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
&& tp
.item
->key
.obj_id
== address
&&
1667 tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
&& tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
1668 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1670 return ei
->refcount
;
1673 if (tp
.item
->key
.obj_id
!= address
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
) {
1674 ERR("couldn't find (%llx,%x,%llx) in extent tree\n", address
, TYPE_EXTENT_ITEM
, size
);
1676 } else if (tp
.item
->key
.offset
!= size
) {
1677 ERR("extent %llx had size %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, size
);
1681 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1682 EXTENT_ITEM_V0
* eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1684 return eiv0
->refcount
;
1685 } else if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1686 ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
,
1687 tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
1691 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1693 return ei
->refcount
;
1696 BOOL
is_extent_unique(device_extension
* Vcb
, UINT64 address
, UINT64 size
, PIRP Irp
) {
1698 traverse_ptr tp
, next_tp
;
1700 UINT64 rc
, rcrun
, root
= 0, inode
= 0, offset
= 0;
1706 rc
= get_extent_refcount(Vcb
, address
, size
, Irp
);
1714 searchkey
.obj_id
= address
;
1715 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1716 searchkey
.offset
= size
;
1718 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1719 if (!NT_SUCCESS(Status
)) {
1720 WARN("error - find_item returned %08x\n", Status
);
1724 if (keycmp(tp
.item
->key
, searchkey
)) {
1725 WARN("could not find (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1729 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
))
1732 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1733 WARN("(%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
));
1737 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1739 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
1740 ptr
= (UINT8
*)&ei
[1];
1742 if (ei
->flags
& EXTENT_ITEM_TREE_BLOCK
) {
1743 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
)) {
1744 WARN("(%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
) + sizeof(EXTENT_ITEM2
));
1748 len
-= sizeof(EXTENT_ITEM2
);
1749 ptr
+= sizeof(EXTENT_ITEM2
);
1754 // Loop through inline extent entries
1757 UINT8 secttype
= *ptr
;
1758 ULONG sectlen
= get_extent_data_len(secttype
);
1759 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
1763 if (sectlen
> len
) {
1764 WARN("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
1769 WARN("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
1773 if (secttype
== TYPE_EXTENT_DATA_REF
) {
1774 EXTENT_DATA_REF
* sectedr
= (EXTENT_DATA_REF
*)(ptr
+ sizeof(UINT8
));
1776 if (root
== 0 && inode
== 0) {
1777 root
= sectedr
->root
;
1778 inode
= sectedr
->objid
;
1779 offset
= sectedr
->offset
;
1780 } else if (root
!= sectedr
->root
|| inode
!= sectedr
->objid
|| offset
!= sectedr
->offset
)
1786 ptr
+= sizeof(UINT8
) + sectlen
;
1793 // Loop through non-inlines if some refs still unaccounted for
1796 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
1798 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_EXTENT_DATA_REF
) {
1799 EXTENT_DATA_REF
* edr
= (EXTENT_DATA_REF
*)tp
.item
->data
;
1801 if (tp
.item
->size
< sizeof(EXTENT_DATA_REF
)) {
1802 WARN("(%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
,
1803 tp
.item
->size
, sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
));
1807 if (root
== 0 && inode
== 0) {
1810 offset
= edr
->offset
;
1811 } else if (root
!= edr
->root
|| inode
!= edr
->objid
|| offset
!= edr
->offset
)
1814 rcrun
+= edr
->count
;
1823 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
)
1828 // If we reach this point, there's still some refs unaccounted for somewhere.
1829 // Return FALSE in case we mess things up elsewhere.
1834 UINT64
get_extent_flags(device_extension
* Vcb
, UINT64 address
, PIRP Irp
) {
1840 searchkey
.obj_id
= address
;
1841 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
1842 searchkey
.offset
= 0xffffffffffffffff;
1844 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1845 if (!NT_SUCCESS(Status
)) {
1846 ERR("error - find_item returned %08x\n", Status
);
1850 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
&& tp
.item
->key
.obj_id
== address
&&
1851 tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
&& tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
1852 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1857 if (tp
.item
->key
.obj_id
!= address
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
) {
1858 ERR("couldn't find %llx in extent tree\n", address
);
1862 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
))
1864 else if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1865 ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
,
1866 tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
1870 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1875 void update_extent_flags(device_extension
* Vcb
, UINT64 address
, UINT64 flags
, PIRP Irp
) {
1881 searchkey
.obj_id
= address
;
1882 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
1883 searchkey
.offset
= 0xffffffffffffffff;
1885 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1886 if (!NT_SUCCESS(Status
)) {
1887 ERR("error - find_item returned %08x\n", Status
);
1891 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
&& tp
.item
->key
.obj_id
== address
&&
1892 tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
&& tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
1893 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1898 if (tp
.item
->key
.obj_id
!= address
|| tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
) {
1899 ERR("couldn't find %llx in extent tree\n", address
);
1903 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
))
1905 else if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1906 ERR("(%llx,%x,%llx) was %x bytes, expected at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
,
1907 tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM
));
1911 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1915 static changed_extent
* get_changed_extent_item(chunk
* c
, UINT64 address
, UINT64 size
, BOOL no_csum
) {
1919 le
= c
->changed_extents
.Flink
;
1920 while (le
!= &c
->changed_extents
) {
1921 ce
= CONTAINING_RECORD(le
, changed_extent
, list_entry
);
1923 if (ce
->address
== address
&& ce
->size
== size
)
1929 ce
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent
), ALLOC_TAG
);
1931 ERR("out of memory\n");
1935 ce
->address
= address
;
1937 ce
->old_size
= size
;
1940 ce
->no_csum
= no_csum
;
1941 ce
->superseded
= FALSE
;
1942 InitializeListHead(&ce
->refs
);
1943 InitializeListHead(&ce
->old_refs
);
1945 InsertTailList(&c
->changed_extents
, &ce
->list_entry
);
1950 NTSTATUS
update_changed_extent_ref(device_extension
* Vcb
, chunk
* c
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 objid
, UINT64 offset
, INT32 count
,
1951 BOOL no_csum
, BOOL superseded
, PIRP Irp
) {
1954 changed_extent_ref
* cer
;
1960 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
1962 ce
= get_changed_extent_item(c
, address
, size
, no_csum
);
1965 ERR("get_changed_extent_item failed\n");
1966 Status
= STATUS_INTERNAL_ERROR
;
1970 if (IsListEmpty(&ce
->refs
) && IsListEmpty(&ce
->old_refs
)) { // new entry
1971 searchkey
.obj_id
= address
;
1972 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1973 searchkey
.offset
= 0xffffffffffffffff;
1975 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1976 if (!NT_SUCCESS(Status
)) {
1977 ERR("error - find_item returned %08x\n", Status
);
1981 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1982 ERR("could not find address %llx in extent tree\n", address
);
1983 Status
= STATUS_INTERNAL_ERROR
;
1987 if (tp
.item
->key
.offset
!= size
) {
1988 ERR("extent %llx had size %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, size
);
1989 Status
= STATUS_INTERNAL_ERROR
;
1993 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1994 EXTENT_ITEM_V0
* eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1996 ce
->count
= ce
->old_count
= eiv0
->refcount
;
1997 } else if (tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
1998 EXTENT_ITEM
* ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2000 ce
->count
= ce
->old_count
= ei
->refcount
;
2002 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
));
2003 Status
= STATUS_INTERNAL_ERROR
;
2008 le
= ce
->refs
.Flink
;
2009 while (le
!= &ce
->refs
) {
2010 cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
2012 if (cer
->type
== TYPE_EXTENT_DATA_REF
&& cer
->edr
.root
== root
&& cer
->edr
.objid
== objid
&& cer
->edr
.offset
== offset
) {
2014 cer
->edr
.count
+= count
;
2015 Status
= STATUS_SUCCESS
;
2018 ce
->superseded
= TRUE
;
2026 old_count
= find_extent_data_refcount(Vcb
, address
, size
, root
, objid
, offset
, Irp
);
2028 if (old_count
> 0) {
2029 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
2032 ERR("out of memory\n");
2033 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2037 cer
->type
= TYPE_EXTENT_DATA_REF
;
2038 cer
->edr
.root
= root
;
2039 cer
->edr
.objid
= objid
;
2040 cer
->edr
.offset
= offset
;
2041 cer
->edr
.count
= old_count
;
2043 InsertTailList(&ce
->old_refs
, &cer
->list_entry
);
2046 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
2049 ERR("out of memory\n");
2050 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2054 cer
->type
= TYPE_EXTENT_DATA_REF
;
2055 cer
->edr
.root
= root
;
2056 cer
->edr
.objid
= objid
;
2057 cer
->edr
.offset
= offset
;
2058 cer
->edr
.count
= old_count
+ count
;
2060 InsertTailList(&ce
->refs
, &cer
->list_entry
);
2065 ce
->superseded
= TRUE
;
2067 Status
= STATUS_SUCCESS
;
2070 ExReleaseResourceLite(&c
->changed_extents_lock
);
2075 void add_changed_extent_ref(chunk
* c
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 objid
, UINT64 offset
, UINT32 count
, BOOL no_csum
) {
2077 changed_extent_ref
* cer
;
2080 ce
= get_changed_extent_item(c
, address
, size
, no_csum
);
2083 ERR("get_changed_extent_item failed\n");
2087 le
= ce
->refs
.Flink
;
2088 while (le
!= &ce
->refs
) {
2089 cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
2091 if (cer
->type
== TYPE_EXTENT_DATA_REF
&& cer
->edr
.root
== root
&& cer
->edr
.objid
== objid
&& cer
->edr
.offset
== offset
) {
2093 cer
->edr
.count
+= count
;
2100 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
2103 ERR("out of memory\n");
2107 cer
->type
= TYPE_EXTENT_DATA_REF
;
2108 cer
->edr
.root
= root
;
2109 cer
->edr
.objid
= objid
;
2110 cer
->edr
.offset
= offset
;
2111 cer
->edr
.count
= count
;
2113 InsertTailList(&ce
->refs
, &cer
->list_entry
);
2118 UINT64
find_extent_shared_tree_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 parent
, PIRP Irp
) {
2127 searchkey
.obj_id
= address
;
2128 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
2129 searchkey
.offset
= 0xffffffffffffffff;
2131 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2132 if (!NT_SUCCESS(Status
)) {
2133 ERR("error - find_item returned %08x\n", Status
);
2137 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| (tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
)) {
2138 TRACE("could not find address %llx in extent tree\n", address
);
2142 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& tp
.item
->key
.offset
!= Vcb
->superblock
.node_size
) {
2143 ERR("extent %llx had size %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, Vcb
->superblock
.node_size
);
2147 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
2148 ERR("(%llx,%x,%llx): size was %u, 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
));
2152 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2155 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
2156 ptr
= (UINT8
*)&ei
[1];
2158 if (searchkey
.obj_type
== TYPE_EXTENT_ITEM
&& ei
->flags
& EXTENT_ITEM_TREE_BLOCK
) {
2159 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
)) {
2160 ERR("(%llx,%x,%llx): size was %u, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
2161 tp
.item
->size
, sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
));
2165 len
-= sizeof(EXTENT_ITEM2
);
2166 ptr
+= sizeof(EXTENT_ITEM2
);
2170 UINT8 secttype
= *ptr
;
2171 ULONG sectlen
= get_extent_data_len(secttype
);
2172 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
2176 if (sectlen
> len
) {
2177 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
2182 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
2186 if (secttype
== TYPE_SHARED_BLOCK_REF
) {
2187 SHARED_BLOCK_REF
* sectsbr
= (SHARED_BLOCK_REF
*)(ptr
+ sizeof(UINT8
));
2189 if (sectsbr
->offset
== parent
)
2194 ptr
+= sizeof(UINT8
) + sectlen
;
2195 inline_rc
+= sectcount
;
2198 // FIXME - what if old?
2200 if (inline_rc
== ei
->refcount
)
2203 searchkey
.obj_id
= address
;
2204 searchkey
.obj_type
= TYPE_SHARED_BLOCK_REF
;
2205 searchkey
.offset
= parent
;
2207 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2208 if (!NT_SUCCESS(Status
)) {
2209 ERR("error - find_item returned %08x\n", Status
);
2213 if (!keycmp(searchkey
, tp
.item
->key
))
2219 UINT32
find_extent_shared_data_refcount(device_extension
* Vcb
, UINT64 address
, UINT64 parent
, PIRP Irp
) {
2228 searchkey
.obj_id
= address
;
2229 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
2230 searchkey
.offset
= 0xffffffffffffffff;
2232 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2233 if (!NT_SUCCESS(Status
)) {
2234 ERR("error - find_item returned %08x\n", Status
);
2238 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| (tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
)) {
2239 TRACE("could not find address %llx in extent tree\n", address
);
2243 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
2244 ERR("(%llx,%x,%llx): size was %u, 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
));
2248 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
2251 len
= tp
.item
->size
- sizeof(EXTENT_ITEM
);
2252 ptr
= (UINT8
*)&ei
[1];
2255 UINT8 secttype
= *ptr
;
2256 ULONG sectlen
= get_extent_data_len(secttype
);
2257 UINT64 sectcount
= get_extent_data_refcount(secttype
, ptr
+ sizeof(UINT8
));
2261 if (sectlen
> len
) {
2262 ERR("(%llx,%x,%llx): %x bytes left, expecting at least %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, len
, sectlen
);
2267 ERR("(%llx,%x,%llx): unrecognized extent type %x\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, secttype
);
2271 if (secttype
== TYPE_SHARED_DATA_REF
) {
2272 SHARED_DATA_REF
* sectsdr
= (SHARED_DATA_REF
*)(ptr
+ sizeof(UINT8
));
2274 if (sectsdr
->offset
== parent
)
2275 return sectsdr
->count
;
2279 ptr
+= sizeof(UINT8
) + sectlen
;
2280 inline_rc
+= sectcount
;
2283 // FIXME - what if old?
2285 if (inline_rc
== ei
->refcount
)
2288 searchkey
.obj_id
= address
;
2289 searchkey
.obj_type
= TYPE_SHARED_DATA_REF
;
2290 searchkey
.offset
= parent
;
2292 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2293 if (!NT_SUCCESS(Status
)) {
2294 ERR("error - find_item returned %08x\n", Status
);
2298 if (!keycmp(searchkey
, tp
.item
->key
)) {
2299 if (tp
.item
->size
< sizeof(UINT32
))
2300 ERR("(%llx,%x,%llx) has size %u, not %u as expected\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(UINT32
));
2302 UINT32
* count
= (UINT32
*)tp
.item
->data
;