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"
23 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
25 // #define DEBUG_WRITE_LOOPS
42 } EXTENT_ITEM_SKINNY_METADATA
;
44 static NTSTATUS
create_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
);
45 static NTSTATUS
update_tree_extents(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
);
47 #ifndef _MSC_VER // not in mingw yet
48 #define DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED 0x80000000
51 _Function_class_(IO_COMPLETION_ROUTINE
)
53 static NTSTATUS NTAPI
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
55 static NTSTATUS
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
57 write_context
* context
= conptr
;
61 context
->iosb
= Irp
->IoStatus
;
62 KeSetEvent(&context
->Event
, 0, FALSE
);
64 return STATUS_MORE_PROCESSING_REQUIRED
;
67 NTSTATUS
write_data_phys(_In_ PDEVICE_OBJECT device
, _In_ UINT64 address
, _In_reads_bytes_(length
) void* data
, _In_ UINT32 length
) {
71 PIO_STACK_LOCATION IrpSp
;
72 write_context context
;
74 TRACE("(%p, %llx, %p, %x)\n", device
, address
, data
, length
);
76 RtlZeroMemory(&context
, sizeof(write_context
));
78 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
80 offset
.QuadPart
= address
;
82 Irp
= IoAllocateIrp(device
->StackSize
, FALSE
);
85 ERR("IoAllocateIrp failed\n");
86 return STATUS_INSUFFICIENT_RESOURCES
;
89 IrpSp
= IoGetNextIrpStackLocation(Irp
);
90 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
92 if (device
->Flags
& DO_BUFFERED_IO
) {
93 Irp
->AssociatedIrp
.SystemBuffer
= data
;
95 Irp
->Flags
= IRP_BUFFERED_IO
;
96 } else if (device
->Flags
& DO_DIRECT_IO
) {
97 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
98 if (!Irp
->MdlAddress
) {
99 DbgPrint("IoAllocateMdl failed\n");
100 Status
= STATUS_INSUFFICIENT_RESOURCES
;
104 Status
= STATUS_SUCCESS
;
107 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoReadAccess
);
108 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
109 Status
= _SEH2_GetExceptionCode();
112 if (!NT_SUCCESS(Status
)) {
113 ERR("MmProbeAndLockPages threw exception %08x\n", Status
);
114 IoFreeMdl(Irp
->MdlAddress
);
118 Irp
->UserBuffer
= data
;
121 IrpSp
->Parameters
.Write
.Length
= length
;
122 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
124 Irp
->UserIosb
= &context
.iosb
;
126 Irp
->UserEvent
= &context
.Event
;
128 IoSetCompletionRoutine(Irp
, write_completion
, &context
, TRUE
, TRUE
, TRUE
);
130 Status
= IoCallDriver(device
, Irp
);
132 if (Status
== STATUS_PENDING
) {
133 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
134 Status
= context
.iosb
.Status
;
137 if (!NT_SUCCESS(Status
)) {
138 ERR("IoCallDriver returned %08x\n", Status
);
141 if (device
->Flags
& DO_DIRECT_IO
) {
142 MmUnlockPages(Irp
->MdlAddress
);
143 IoFreeMdl(Irp
->MdlAddress
);
152 static void add_trim_entry(device
* dev
, UINT64 address
, UINT64 size
) {
153 space
* s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
155 ERR("out of memory\n");
159 s
->address
= address
;
161 dev
->num_trim_entries
++;
163 InsertTailList(&dev
->trim_list
, &s
->list_entry
);
166 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
169 if (Vcb
->trim
&& !Vcb
->options
.no_trim
) {
170 if (c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
)
171 type
= BLOCK_FLAG_DUPLICATE
;
172 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
173 type
= BLOCK_FLAG_RAID0
;
174 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
)
175 type
= BLOCK_FLAG_DUPLICATE
;
176 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
177 type
= BLOCK_FLAG_RAID10
;
178 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
179 type
= BLOCK_FLAG_RAID5
;
180 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
181 type
= BLOCK_FLAG_RAID6
;
183 type
= BLOCK_FLAG_DUPLICATE
;
186 while (!IsListEmpty(&c
->deleting
)) {
187 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
189 if (Vcb
->trim
&& !Vcb
->options
.no_trim
&& (!Vcb
->options
.no_barrier
|| !(c
->chunk_item
->type
& BLOCK_FLAG_METADATA
))) {
190 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
192 if (type
== BLOCK_FLAG_DUPLICATE
) {
195 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
196 if (c
->devices
[i
] && c
->devices
[i
]->devobj
&& !c
->devices
[i
]->readonly
&& c
->devices
[i
]->trim
)
197 add_trim_entry(c
->devices
[i
], s
->address
- c
->offset
+ cis
[i
].offset
, s
->size
);
199 } else if (type
== BLOCK_FLAG_RAID0
) {
200 UINT64 startoff
, endoff
;
201 UINT16 startoffstripe
, endoffstripe
, i
;
203 get_raid0_offset(s
->address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &startoff
, &startoffstripe
);
204 get_raid0_offset(s
->address
- c
->offset
+ s
->size
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &endoff
, &endoffstripe
);
206 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
207 if (c
->devices
[i
] && c
->devices
[i
]->devobj
&& !c
->devices
[i
]->readonly
&& c
->devices
[i
]->trim
) {
208 UINT64 stripestart
, stripeend
;
210 if (startoffstripe
> i
)
211 stripestart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
212 else if (startoffstripe
== i
)
213 stripestart
= startoff
;
215 stripestart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
217 if (endoffstripe
> i
)
218 stripeend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
219 else if (endoffstripe
== i
)
220 stripeend
= endoff
+ 1;
222 stripeend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
224 if (stripestart
!= stripeend
)
225 add_trim_entry(c
->devices
[i
], stripestart
+ cis
[i
].offset
, stripeend
- stripestart
);
228 } else if (type
== BLOCK_FLAG_RAID10
) {
229 UINT64 startoff
, endoff
;
230 UINT16 sub_stripes
, startoffstripe
, endoffstripe
, i
;
232 sub_stripes
= max(1, c
->chunk_item
->sub_stripes
);
234 get_raid0_offset(s
->address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
235 get_raid0_offset(s
->address
- c
->offset
+ s
->size
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ sub_stripes
, &endoff
, &endoffstripe
);
237 startoffstripe
*= sub_stripes
;
238 endoffstripe
*= sub_stripes
;
240 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= sub_stripes
) {
242 UINT64 stripestart
, stripeend
;
244 if (startoffstripe
> i
)
245 stripestart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
246 else if (startoffstripe
== i
)
247 stripestart
= startoff
;
249 stripestart
= startoff
- (startoff
% c
->chunk_item
->stripe_length
);
251 if (endoffstripe
> i
)
252 stripeend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
253 else if (endoffstripe
== i
)
254 stripeend
= endoff
+ 1;
256 stripeend
= endoff
- (endoff
% c
->chunk_item
->stripe_length
);
258 if (stripestart
!= stripeend
) {
259 for (j
= 0; j
< sub_stripes
; j
++) {
260 if (c
->devices
[i
+j
] && c
->devices
[i
+j
]->devobj
&& !c
->devices
[i
+j
]->readonly
&& c
->devices
[i
+j
]->trim
)
261 add_trim_entry(c
->devices
[i
+j
], stripestart
+ cis
[i
+j
].offset
, stripeend
- stripestart
);
266 // FIXME - RAID5(?), RAID6(?)
269 RemoveEntryList(&s
->list_entry
);
275 DEVICE_MANAGE_DATA_SET_ATTRIBUTES
* dmdsa
;
276 ATA_PASS_THROUGH_EX apte
;
278 IO_STATUS_BLOCK iosb
;
279 } ioctl_context_stripe
;
284 ioctl_context_stripe
* stripes
;
287 _Function_class_(IO_COMPLETION_ROUTINE
)
289 static NTSTATUS NTAPI
ioctl_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
291 static NTSTATUS
ioctl_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
293 ioctl_context
* context
= (ioctl_context
*)conptr
;
294 LONG left2
= InterlockedDecrement(&context
->left
);
296 UNUSED(DeviceObject
);
300 KeSetEvent(&context
->Event
, 0, FALSE
);
302 return STATUS_MORE_PROCESSING_REQUIRED
;
305 static void clean_space_cache(device_extension
* Vcb
) {
310 TRACE("(%p)\n", Vcb
);
312 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
314 le
= Vcb
->chunks
.Flink
;
315 while (le
!= &Vcb
->chunks
) {
316 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
318 if (c
->space_changed
) {
319 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
321 if (c
->space_changed
)
322 clean_space_cache_chunk(Vcb
, c
);
324 c
->space_changed
= FALSE
;
326 ExReleaseResourceLite(&c
->lock
);
332 ExReleaseResourceLite(&Vcb
->chunk_lock
);
334 if (Vcb
->trim
&& !Vcb
->options
.no_trim
) {
335 ioctl_context context
;
340 le
= Vcb
->devices
.Flink
;
341 while (le
!= &Vcb
->devices
) {
342 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
344 if (dev
->devobj
&& !dev
->readonly
&& dev
->trim
&& dev
->num_trim_entries
> 0)
350 if (context
.left
== 0)
353 total_num
= context
.left
;
356 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
358 context
.stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ioctl_context_stripe
) * context
.left
, ALLOC_TAG
);
359 if (!context
.stripes
) {
360 ERR("out of memory\n");
364 RtlZeroMemory(context
.stripes
, sizeof(ioctl_context_stripe
) * context
.left
);
366 le
= Vcb
->devices
.Flink
;
367 while (le
!= &Vcb
->devices
) {
368 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
370 if (dev
->devobj
&& !dev
->readonly
&& dev
->trim
&& dev
->num_trim_entries
> 0) {
372 ioctl_context_stripe
* stripe
= &context
.stripes
[num
];
373 DEVICE_DATA_SET_RANGE
* ranges
;
374 ULONG datalen
= (ULONG
)sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), sizeof(UINT64
)) + (dev
->num_trim_entries
* sizeof(DEVICE_DATA_SET_RANGE
)), i
;
375 PIO_STACK_LOCATION IrpSp
;
377 stripe
->dmdsa
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
378 if (!stripe
->dmdsa
) {
379 ERR("out of memory\n");
383 stripe
->dmdsa
->Size
= sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
);
384 stripe
->dmdsa
->Action
= DeviceDsmAction_Trim
;
385 stripe
->dmdsa
->Flags
= DEVICE_DSM_FLAG_TRIM_NOT_FS_ALLOCATED
;
386 stripe
->dmdsa
->ParameterBlockOffset
= 0;
387 stripe
->dmdsa
->ParameterBlockLength
= 0;
388 stripe
->dmdsa
->DataSetRangesOffset
= (ULONG
)sector_align(sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES
), sizeof(UINT64
));
389 stripe
->dmdsa
->DataSetRangesLength
= dev
->num_trim_entries
* sizeof(DEVICE_DATA_SET_RANGE
);
391 ranges
= (DEVICE_DATA_SET_RANGE
*)((UINT8
*)stripe
->dmdsa
+ stripe
->dmdsa
->DataSetRangesOffset
);
395 le2
= dev
->trim_list
.Flink
;
396 while (le2
!= &dev
->trim_list
) {
397 space
* s
= CONTAINING_RECORD(le2
, space
, list_entry
);
399 ranges
[i
].StartingOffset
= s
->address
;
400 ranges
[i
].LengthInBytes
= s
->size
;
406 stripe
->Irp
= IoAllocateIrp(dev
->devobj
->StackSize
, FALSE
);
409 ERR("IoAllocateIrp failed\n");
413 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
414 IrpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
416 IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
;
417 IrpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= datalen
;
418 IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= 0;
420 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= stripe
->dmdsa
;
421 stripe
->Irp
->Flags
|= IRP_BUFFERED_IO
;
422 stripe
->Irp
->UserBuffer
= NULL
;
423 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
425 IoSetCompletionRoutine(stripe
->Irp
, ioctl_completion
, &context
, TRUE
, TRUE
, TRUE
);
427 IoCallDriver(dev
->devobj
, stripe
->Irp
);
430 while (!IsListEmpty(&dev
->trim_list
)) {
431 space
* s
= CONTAINING_RECORD(RemoveHeadList(&dev
->trim_list
), space
, list_entry
);
435 dev
->num_trim_entries
= 0;
443 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
445 for (num
= 0; num
< total_num
; num
++) {
446 if (context
.stripes
[num
].dmdsa
)
447 ExFreePool(context
.stripes
[num
].dmdsa
);
450 ExFreePool(context
.stripes
);
454 static BOOL
trees_consistent(device_extension
* Vcb
) {
455 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
458 le
= Vcb
->trees
.Flink
;
459 while (le
!= &Vcb
->trees
) {
460 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
463 if (t
->header
.num_items
== 0 && t
->parent
) {
464 #ifdef DEBUG_WRITE_LOOPS
465 ERR("empty tree found, looping again\n");
470 if (t
->size
> maxsize
) {
471 #ifdef DEBUG_WRITE_LOOPS
472 ERR("overlarge tree found (%u > %u), looping again\n", t
->size
, maxsize
);
477 if (!t
->has_new_address
) {
478 #ifdef DEBUG_WRITE_LOOPS
479 ERR("tree found without new address, looping again\n");
491 static NTSTATUS
add_parents(device_extension
* Vcb
, PIRP Irp
) {
495 for (level
= 0; level
<= 255; level
++) {
496 BOOL nothing_found
= TRUE
;
498 TRACE("level = %u\n", level
);
500 le
= Vcb
->trees
.Flink
;
501 while (le
!= &Vcb
->trees
) {
502 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
504 if (t
->write
&& t
->header
.level
== level
) {
505 TRACE("tree %p: root = %llx, level = %x, parent = %p\n", t
, t
->header
.tree_id
, t
->header
.level
, t
->parent
);
507 nothing_found
= FALSE
;
510 if (!t
->parent
->write
)
511 TRACE("adding tree %p (level %x)\n", t
->parent
, t
->header
.level
);
513 t
->parent
->write
= TRUE
;
514 } else if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
519 searchkey
.obj_id
= t
->root
->id
;
520 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
521 searchkey
.offset
= 0xffffffffffffffff;
523 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
524 if (!NT_SUCCESS(Status
)) {
525 ERR("error - find_item returned %08x\n", Status
);
529 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
530 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
531 return STATUS_INTERNAL_ERROR
;
534 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
535 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
538 ERR("out of memory\n");
539 return STATUS_INSUFFICIENT_RESOURCES
;
542 RtlCopyMemory(ri
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
544 Status
= delete_tree_item(Vcb
, &tp
);
545 if (!NT_SUCCESS(Status
)) {
546 ERR("delete_tree_item returned %08x\n", Status
);
551 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
);
552 if (!NT_SUCCESS(Status
)) {
553 ERR("insert_tree_item returned %08x\n", Status
);
568 return STATUS_SUCCESS
;
571 static void add_parents_to_cache(tree
* t
) {
578 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64 address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
580 EXTENT_ITEM_SKINNY_METADATA
* eism
;
581 traverse_ptr insert_tp
;
583 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
585 ERR("out of memory\n");
589 eism
->ei
.refcount
= 1;
590 eism
->ei
.generation
= Vcb
->superblock
.generation
;
591 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
592 eism
->type
= TYPE_TREE_BLOCK_REF
;
593 eism
->tbr
.offset
= root_id
;
595 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, Irp
);
596 if (!NT_SUCCESS(Status
)) {
597 ERR("insert_tree_item returned %08x\n", Status
);
602 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
604 space_list_subtract(c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
606 ExReleaseResourceLite(&c
->lock
);
608 add_parents_to_cache(insert_tp
.tree
);
613 BOOL
find_metadata_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64
* address
) {
617 TRACE("(%p, %llx, %p)\n", Vcb
, c
->offset
, address
);
619 if (Vcb
->superblock
.node_size
> c
->chunk_item
->size
- c
->used
)
622 if (!c
->cache_loaded
) {
623 NTSTATUS Status
= load_cache_chunk(Vcb
, c
, NULL
);
625 if (!NT_SUCCESS(Status
)) {
626 ERR("load_cache_chunk returned %08x\n", Status
);
631 if (IsListEmpty(&c
->space_size
))
634 if (!c
->last_alloc_set
) {
635 s
= CONTAINING_RECORD(c
->space
.Blink
, space
, list_entry
);
637 c
->last_alloc
= s
->address
;
638 c
->last_alloc_set
= TRUE
;
640 if (s
->size
>= Vcb
->superblock
.node_size
) {
641 *address
= s
->address
;
642 c
->last_alloc
+= Vcb
->superblock
.node_size
;
648 while (le
!= &c
->space
) {
649 s
= CONTAINING_RECORD(le
, space
, list_entry
);
651 if (s
->address
<= c
->last_alloc
&& s
->address
+ s
->size
>= c
->last_alloc
+ Vcb
->superblock
.node_size
) {
652 *address
= c
->last_alloc
;
653 c
->last_alloc
+= Vcb
->superblock
.node_size
;
660 le
= c
->space_size
.Flink
;
661 while (le
!= &c
->space_size
) {
662 s
= CONTAINING_RECORD(le
, space
, list_entry_size
);
664 if (s
->size
== Vcb
->superblock
.node_size
) {
665 *address
= s
->address
;
666 c
->last_alloc
= s
->address
+ Vcb
->superblock
.node_size
;
668 } else if (s
->size
< Vcb
->superblock
.node_size
) {
669 if (le
== c
->space_size
.Flink
)
672 s
= CONTAINING_RECORD(le
->Blink
, space
, list_entry_size
);
674 *address
= s
->address
;
675 c
->last_alloc
= s
->address
+ Vcb
->superblock
.node_size
;
683 s
= CONTAINING_RECORD(c
->space_size
.Blink
, space
, list_entry_size
);
685 if (s
->size
> Vcb
->superblock
.node_size
) {
686 *address
= s
->address
;
687 c
->last_alloc
= s
->address
+ Vcb
->superblock
.node_size
;
694 static BOOL
insert_tree_extent(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64
* new_address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
697 EXTENT_ITEM_TREE2
* eit2
;
698 traverse_ptr insert_tp
;
700 TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb
, level
, root_id
, c
, new_address
, rollback
);
702 if (!find_metadata_address_in_chunk(Vcb
, c
, &address
))
705 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
706 BOOL b
= insert_tree_extent_skinny(Vcb
, level
, root_id
, c
, address
, Irp
, rollback
);
709 *new_address
= address
;
714 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
716 ERR("out of memory\n");
720 eit2
->eit
.extent_item
.refcount
= 1;
721 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
722 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
723 eit2
->eit
.level
= level
;
724 eit2
->type
= TYPE_TREE_BLOCK_REF
;
725 eit2
->tbr
.offset
= root_id
;
727 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, Irp
);
728 if (!NT_SUCCESS(Status
)) {
729 ERR("insert_tree_item returned %08x\n", Status
);
734 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
736 space_list_subtract(c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
738 ExReleaseResourceLite(&c
->lock
);
740 add_parents_to_cache(insert_tp
.tree
);
742 *new_address
= address
;
747 NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
749 chunk
*origchunk
= NULL
, *c
;
753 if (t
->root
->id
== BTRFS_ROOT_CHUNK
)
754 flags
= Vcb
->system_flags
;
756 flags
= Vcb
->metadata_flags
;
758 if (t
->has_address
) {
759 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
761 if (origchunk
&& !origchunk
->readonly
&& !origchunk
->reloc
&& origchunk
->chunk_item
->type
== flags
&&
762 insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, origchunk
, &addr
, Irp
, rollback
)) {
763 t
->new_address
= addr
;
764 t
->has_new_address
= TRUE
;
765 return STATUS_SUCCESS
;
769 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
771 le
= Vcb
->chunks
.Flink
;
772 while (le
!= &Vcb
->chunks
) {
773 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
775 if (!c
->readonly
&& !c
->reloc
) {
776 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
778 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
779 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, c
, &addr
, Irp
, rollback
)) {
780 ExReleaseResourceLite(&c
->lock
);
781 ExReleaseResourceLite(&Vcb
->chunk_lock
);
782 t
->new_address
= addr
;
783 t
->has_new_address
= TRUE
;
784 return STATUS_SUCCESS
;
788 ExReleaseResourceLite(&c
->lock
);
794 // allocate new chunk if necessary
796 Status
= alloc_chunk(Vcb
, flags
, &c
, FALSE
);
798 if (!NT_SUCCESS(Status
)) {
799 ERR("alloc_chunk returned %08x\n", Status
);
800 ExReleaseResourceLite(&Vcb
->chunk_lock
);
804 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
806 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
807 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->root
->id
, c
, &addr
, Irp
, rollback
)) {
808 ExReleaseResourceLite(&c
->lock
);
809 ExReleaseResourceLite(&Vcb
->chunk_lock
);
810 t
->new_address
= addr
;
811 t
->has_new_address
= TRUE
;
812 return STATUS_SUCCESS
;
816 ExReleaseResourceLite(&c
->lock
);
818 ExReleaseResourceLite(&Vcb
->chunk_lock
);
820 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
822 return STATUS_DISK_FULL
;
825 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT64 parent_root
, UINT8 level
, PIRP Irp
, LIST_ENTRY
* rollback
) {
829 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
831 rc
= get_extent_refcount(Vcb
, address
, Vcb
->superblock
.node_size
, Irp
);
833 ERR("error - refcount for extent %llx was 0\n", address
);
834 return STATUS_INTERNAL_ERROR
;
840 root
= t
->header
.tree_id
;
842 Status
= decrease_extent_refcount_tree(Vcb
, address
, Vcb
->superblock
.node_size
, root
, level
, Irp
);
843 if (!NT_SUCCESS(Status
)) {
844 ERR("decrease_extent_refcount_tree returned %08x\n", Status
);
849 chunk
* c
= get_chunk_from_address(Vcb
, address
);
852 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
854 if (!c
->cache_loaded
) {
855 Status
= load_cache_chunk(Vcb
, c
, NULL
);
857 if (!NT_SUCCESS(Status
)) {
858 ERR("load_cache_chunk returned %08x\n", Status
);
859 ExReleaseResourceLite(&c
->lock
);
864 c
->used
-= Vcb
->superblock
.node_size
;
866 space_list_add(c
, address
, Vcb
->superblock
.node_size
, rollback
);
868 ExReleaseResourceLite(&c
->lock
);
870 ERR("could not find chunk for address %llx\n", address
);
873 return STATUS_SUCCESS
;
876 static NTSTATUS
add_changed_extent_ref_edr(changed_extent
* ce
, EXTENT_DATA_REF
* edr
, BOOL old
) {
877 LIST_ENTRY
*le2
, *list
;
878 changed_extent_ref
* cer
;
880 list
= old
? &ce
->old_refs
: &ce
->refs
;
883 while (le2
!= list
) {
884 cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
886 if (cer
->type
== TYPE_EXTENT_DATA_REF
&& cer
->edr
.root
== edr
->root
&& cer
->edr
.objid
== edr
->objid
&& cer
->edr
.offset
== edr
->offset
) {
887 cer
->edr
.count
+= edr
->count
;
894 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
896 ERR("out of memory\n");
897 return STATUS_INSUFFICIENT_RESOURCES
;
900 cer
->type
= TYPE_EXTENT_DATA_REF
;
901 RtlCopyMemory(&cer
->edr
, edr
, sizeof(EXTENT_DATA_REF
));
902 InsertTailList(list
, &cer
->list_entry
);
906 ce
->old_count
+= edr
->count
;
908 ce
->count
+= edr
->count
;
910 return STATUS_SUCCESS
;
913 static NTSTATUS
add_changed_extent_ref_sdr(changed_extent
* ce
, SHARED_DATA_REF
* sdr
, BOOL old
) {
914 LIST_ENTRY
*le2
, *list
;
915 changed_extent_ref
* cer
;
917 list
= old
? &ce
->old_refs
: &ce
->refs
;
920 while (le2
!= list
) {
921 cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
923 if (cer
->type
== TYPE_SHARED_DATA_REF
&& cer
->sdr
.offset
== sdr
->offset
) {
924 cer
->sdr
.count
+= sdr
->count
;
931 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
933 ERR("out of memory\n");
934 return STATUS_INSUFFICIENT_RESOURCES
;
937 cer
->type
= TYPE_SHARED_DATA_REF
;
938 RtlCopyMemory(&cer
->sdr
, sdr
, sizeof(SHARED_DATA_REF
));
939 InsertTailList(list
, &cer
->list_entry
);
943 ce
->old_count
+= sdr
->count
;
945 ce
->count
+= sdr
->count
;
947 return STATUS_SUCCESS
;
950 static BOOL
shared_tree_is_unique(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
955 if (!t
->updated_extents
&& t
->has_address
) {
956 Status
= update_tree_extents(Vcb
, t
, Irp
, rollback
);
957 if (!NT_SUCCESS(Status
)) {
958 ERR("update_tree_extents returned %08x\n", Status
);
963 searchkey
.obj_id
= t
->header
.address
;
964 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
965 searchkey
.offset
= 0xffffffffffffffff;
967 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
968 if (!NT_SUCCESS(Status
)) {
969 ERR("error - find_item returned %08x\n", Status
);
973 if (tp
.item
->key
.obj_id
== t
->header
.address
&& (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
|| tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
))
979 static NTSTATUS
update_tree_extents(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
981 UINT64 rc
= get_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, Irp
);
982 UINT64 flags
= get_extent_flags(Vcb
, t
->header
.address
, Irp
);
985 ERR("refcount for extent %llx was 0\n", t
->header
.address
);
986 return STATUS_INTERNAL_ERROR
;
989 if (flags
& EXTENT_ITEM_SHARED_BACKREFS
|| t
->header
.flags
& HEADER_FLAG_SHARED_BACKREF
|| !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
991 BOOL unique
= rc
> 1 ? FALSE
: (t
->parent
? shared_tree_is_unique(Vcb
, t
->parent
, Irp
, rollback
) : FALSE
);
993 if (t
->header
.level
== 0) {
996 le
= t
->itemlist
.Flink
;
997 while (le
!= &t
->itemlist
) {
998 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1000 if (!td
->inserted
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1001 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1003 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1004 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1006 if (ed2
->size
> 0) {
1007 EXTENT_DATA_REF edr
;
1008 changed_extent
* ce
= NULL
;
1009 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
1014 le2
= c
->changed_extents
.Flink
;
1015 while (le2
!= &c
->changed_extents
) {
1016 changed_extent
* ce2
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
1018 if (ce2
->address
== ed2
->address
) {
1027 edr
.root
= t
->root
->id
;
1028 edr
.objid
= td
->key
.obj_id
;
1029 edr
.offset
= td
->key
.offset
- ed2
->offset
;
1033 Status
= add_changed_extent_ref_edr(ce
, &edr
, TRUE
);
1034 if (!NT_SUCCESS(Status
)) {
1035 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1039 Status
= add_changed_extent_ref_edr(ce
, &edr
, FALSE
);
1040 if (!NT_SUCCESS(Status
)) {
1041 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1046 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, Irp
);
1047 if (!NT_SUCCESS(Status
)) {
1048 ERR("increase_extent_refcount returned %08x\n", Status
);
1052 if ((flags
& EXTENT_ITEM_SHARED_BACKREFS
&& unique
) || !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1053 UINT64 sdrrc
= find_extent_shared_data_refcount(Vcb
, ed2
->address
, t
->header
.address
, Irp
);
1056 SHARED_DATA_REF sdr
;
1058 sdr
.offset
= t
->header
.address
;
1061 Status
= decrease_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_SHARED_DATA_REF
, &sdr
, NULL
, 0,
1062 t
->header
.address
, ce
? ce
->superseded
: FALSE
, Irp
);
1063 if (!NT_SUCCESS(Status
)) {
1064 ERR("decrease_extent_refcount returned %08x\n", Status
);
1071 le2
= ce
->refs
.Flink
;
1072 while (le2
!= &ce
->refs
) {
1073 changed_extent_ref
* cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
1075 if (cer
->type
== TYPE_SHARED_DATA_REF
&& cer
->sdr
.offset
== sdr
.offset
) {
1084 le2
= ce
->old_refs
.Flink
;
1085 while (le2
!= &ce
->old_refs
) {
1086 changed_extent_ref
* cer
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
1088 if (cer
->type
== TYPE_SHARED_DATA_REF
&& cer
->sdr
.offset
== sdr
.offset
) {
1091 if (cer
->sdr
.count
> 1)
1094 RemoveEntryList(&cer
->list_entry
);
1107 // FIXME - clear shared flag if unique?
1117 le
= t
->itemlist
.Flink
;
1118 while (le
!= &t
->itemlist
) {
1119 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1121 if (!td
->inserted
) {
1122 tbr
.offset
= t
->root
->id
;
1124 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
,
1125 &tbr
, &td
->key
, t
->header
.level
- 1, Irp
);
1126 if (!NT_SUCCESS(Status
)) {
1127 ERR("increase_extent_refcount returned %08x\n", Status
);
1131 if (unique
|| !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1132 UINT64 sbrrc
= find_extent_shared_tree_refcount(Vcb
, td
->treeholder
.address
, t
->header
.address
, Irp
);
1135 SHARED_BLOCK_REF sbr
;
1137 sbr
.offset
= t
->header
.address
;
1139 Status
= decrease_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, NULL
, 0,
1140 t
->header
.address
, FALSE
, Irp
);
1141 if (!NT_SUCCESS(Status
)) {
1142 ERR("decrease_extent_refcount returned %08x\n", Status
);
1148 // FIXME - clear shared flag if unique?
1156 UINT64 sbrrc
= find_extent_shared_tree_refcount(Vcb
, t
->header
.address
, t
->parent
->header
.address
, Irp
);
1159 SHARED_BLOCK_REF sbr
;
1161 sbr
.offset
= t
->parent
->header
.address
;
1163 Status
= decrease_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, NULL
, 0,
1164 t
->parent
->header
.address
, FALSE
, Irp
);
1165 if (!NT_SUCCESS(Status
)) {
1166 ERR("decrease_extent_refcount returned %08x\n", Status
);
1173 tbr
.offset
= t
->parent
->header
.tree_id
;
1175 tbr
.offset
= t
->header
.tree_id
;
1177 Status
= increase_extent_refcount(Vcb
, t
->header
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
,
1178 t
->parent
? &t
->paritem
->key
: NULL
, t
->header
.level
, Irp
);
1179 if (!NT_SUCCESS(Status
)) {
1180 ERR("increase_extent_refcount returned %08x\n", Status
);
1184 // FIXME - clear shared flag if unique?
1186 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
1189 if (rc
> 1 || t
->header
.tree_id
== t
->root
->id
) {
1190 Status
= reduce_tree_extent(Vcb
, t
->header
.address
, t
, t
->parent
? t
->parent
->header
.tree_id
: t
->header
.tree_id
, t
->header
.level
, Irp
, rollback
);
1192 if (!NT_SUCCESS(Status
)) {
1193 ERR("reduce_tree_extent returned %08x\n", Status
);
1198 t
->has_address
= FALSE
;
1200 if ((rc
> 1 || t
->header
.tree_id
!= t
->root
->id
) && !(flags
& EXTENT_ITEM_SHARED_BACKREFS
)) {
1201 if (t
->header
.tree_id
== t
->root
->id
) {
1202 flags
|= EXTENT_ITEM_SHARED_BACKREFS
;
1203 update_extent_flags(Vcb
, t
->header
.address
, flags
, Irp
);
1206 if (t
->header
.level
> 0) {
1209 le
= t
->itemlist
.Flink
;
1210 while (le
!= &t
->itemlist
) {
1211 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1213 if (!td
->inserted
) {
1214 if (t
->header
.tree_id
== t
->root
->id
) {
1215 SHARED_BLOCK_REF sbr
;
1217 sbr
.offset
= t
->header
.address
;
1219 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_SHARED_BLOCK_REF
, &sbr
, &td
->key
, t
->header
.level
- 1, Irp
);
1223 tbr
.offset
= t
->root
->id
;
1225 Status
= increase_extent_refcount(Vcb
, td
->treeholder
.address
, Vcb
->superblock
.node_size
, TYPE_TREE_BLOCK_REF
, &tbr
, &td
->key
, t
->header
.level
- 1, Irp
);
1228 if (!NT_SUCCESS(Status
)) {
1229 ERR("increase_extent_refcount returned %08x\n", Status
);
1239 le
= t
->itemlist
.Flink
;
1240 while (le
!= &t
->itemlist
) {
1241 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1243 if (!td
->inserted
&& td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1244 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1246 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
1247 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1249 if (ed2
->size
> 0) {
1250 changed_extent
* ce
= NULL
;
1251 chunk
* c
= get_chunk_from_address(Vcb
, ed2
->address
);
1256 le2
= c
->changed_extents
.Flink
;
1257 while (le2
!= &c
->changed_extents
) {
1258 changed_extent
* ce2
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
1260 if (ce2
->address
== ed2
->address
) {
1269 if (t
->header
.tree_id
== t
->root
->id
) {
1270 SHARED_DATA_REF sdr
;
1272 sdr
.offset
= t
->header
.address
;
1276 Status
= add_changed_extent_ref_sdr(ce
, &sdr
, TRUE
);
1277 if (!NT_SUCCESS(Status
)) {
1278 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1282 Status
= add_changed_extent_ref_sdr(ce
, &sdr
, FALSE
);
1283 if (!NT_SUCCESS(Status
)) {
1284 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1289 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_SHARED_DATA_REF
, &sdr
, NULL
, 0, Irp
);
1291 EXTENT_DATA_REF edr
;
1293 edr
.root
= t
->root
->id
;
1294 edr
.objid
= td
->key
.obj_id
;
1295 edr
.offset
= td
->key
.offset
- ed2
->offset
;
1299 Status
= add_changed_extent_ref_edr(ce
, &edr
, TRUE
);
1300 if (!NT_SUCCESS(Status
)) {
1301 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1305 Status
= add_changed_extent_ref_edr(ce
, &edr
, FALSE
);
1306 if (!NT_SUCCESS(Status
)) {
1307 ERR("add_changed_extent_ref_edr returned %08x\n", Status
);
1312 Status
= increase_extent_refcount(Vcb
, ed2
->address
, ed2
->size
, TYPE_EXTENT_DATA_REF
, &edr
, NULL
, 0, Irp
);
1315 if (!NT_SUCCESS(Status
)) {
1316 ERR("increase_extent_refcount returned %08x\n", Status
);
1328 t
->updated_extents
= TRUE
;
1329 t
->header
.tree_id
= t
->root
->id
;
1331 return STATUS_SUCCESS
;
1334 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1337 BOOL changed
= FALSE
;
1338 UINT8 max_level
= 0, level
;
1340 TRACE("(%p)\n", Vcb
);
1342 le
= Vcb
->trees
.Flink
;
1343 while (le
!= &Vcb
->trees
) {
1344 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1346 if (t
->write
&& !t
->has_new_address
) {
1349 if (t
->has_address
) {
1350 c
= get_chunk_from_address(Vcb
, t
->header
.address
);
1353 if (!c
->cache_loaded
) {
1354 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1356 if (!c
->cache_loaded
) {
1357 Status
= load_cache_chunk(Vcb
, c
, NULL
);
1359 if (!NT_SUCCESS(Status
)) {
1360 ERR("load_cache_chunk returned %08x\n", Status
);
1361 ExReleaseResourceLite(&c
->lock
);
1366 ExReleaseResourceLite(&c
->lock
);
1371 Status
= get_tree_new_address(Vcb
, t
, Irp
, rollback
);
1372 if (!NT_SUCCESS(Status
)) {
1373 ERR("get_tree_new_address returned %08x\n", Status
);
1377 TRACE("allocated extent %llx\n", t
->new_address
);
1379 c
= get_chunk_from_address(Vcb
, t
->new_address
);
1382 c
->used
+= Vcb
->superblock
.node_size
;
1384 ERR("could not find chunk for address %llx\n", t
->new_address
);
1385 return STATUS_INTERNAL_ERROR
;
1390 if (t
->header
.level
> max_level
)
1391 max_level
= t
->header
.level
;
1398 return STATUS_SUCCESS
;
1402 le
= Vcb
->trees
.Flink
;
1403 while (le
!= &Vcb
->trees
) {
1404 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1406 if (t
->write
&& !t
->updated_extents
&& t
->has_address
&& t
->header
.level
== level
) {
1407 Status
= update_tree_extents(Vcb
, t
, Irp
, rollback
);
1408 if (!NT_SUCCESS(Status
)) {
1409 ERR("update_tree_extents returned %08x\n", Status
);
1423 return STATUS_SUCCESS
;
1426 static NTSTATUS
update_root_root(device_extension
* Vcb
, BOOL no_cache
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1430 TRACE("(%p)\n", Vcb
);
1432 le
= Vcb
->trees
.Flink
;
1433 while (le
!= &Vcb
->trees
) {
1434 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1436 if (t
->write
&& !t
->parent
) {
1437 if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
1441 searchkey
.obj_id
= t
->root
->id
;
1442 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1443 searchkey
.offset
= 0xffffffffffffffff;
1445 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
1446 if (!NT_SUCCESS(Status
)) {
1447 ERR("error - find_item returned %08x\n", Status
);
1451 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1452 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1453 return STATUS_INTERNAL_ERROR
;
1456 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, t
->new_address
);
1458 t
->root
->root_item
.block_number
= t
->new_address
;
1459 t
->root
->root_item
.root_level
= t
->header
.level
;
1460 t
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1461 t
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1463 // item is guaranteed to be at least sizeof(ROOT_ITEM), due to add_parents
1465 RtlCopyMemory(tp
.item
->data
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1468 t
->root
->treeholder
.address
= t
->new_address
;
1469 t
->root
->treeholder
.generation
= Vcb
->superblock
.generation
;
1475 if (!no_cache
&& !(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
)) {
1476 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
1477 Status
= update_chunk_caches(Vcb
, Irp
, rollback
);
1478 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1480 if (!NT_SUCCESS(Status
)) {
1481 ERR("update_chunk_caches returned %08x\n", Status
);
1486 return STATUS_SUCCESS
;
1489 NTSTATUS
do_tree_writes(device_extension
* Vcb
, LIST_ENTRY
* tree_writes
, BOOL no_free
) {
1495 write_data_context
* wtc
;
1497 BOOL raid56
= FALSE
;
1499 // merge together runs
1501 le
= tree_writes
->Flink
;
1502 while (le
!= tree_writes
) {
1503 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1505 if (!c
|| tw
->address
< c
->offset
|| tw
->address
>= c
->offset
+ c
->chunk_item
->size
)
1506 c
= get_chunk_from_address(Vcb
, tw
->address
);
1508 tree_write
* tw2
= CONTAINING_RECORD(le
->Blink
, tree_write
, list_entry
);
1510 if (tw
->address
== tw2
->address
+ tw2
->length
) {
1511 UINT8
* data
= ExAllocatePoolWithTag(NonPagedPool
, tw2
->length
+ tw
->length
, ALLOC_TAG
);
1514 ERR("out of memory\n");
1515 return STATUS_INSUFFICIENT_RESOURCES
;
1518 RtlCopyMemory(data
, tw2
->data
, tw2
->length
);
1519 RtlCopyMemory(&data
[tw2
->length
], tw
->data
, tw
->length
);
1522 ExFreePool(tw2
->data
);
1525 tw2
->length
+= tw
->length
;
1527 if (!no_free
) // FIXME - what if we allocated this just now?
1528 ExFreePool(tw
->data
);
1530 RemoveEntryList(&tw
->list_entry
);
1533 le
= tw2
->list_entry
.Flink
;
1540 if (c
->chunk_item
->type
& (BLOCK_FLAG_RAID5
| BLOCK_FLAG_RAID6
))
1548 le
= tree_writes
->Flink
;
1549 while (le
!= tree_writes
) {
1550 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1557 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_context
) * num_bits
, ALLOC_TAG
);
1559 ERR("out of memory\n");
1560 return STATUS_INSUFFICIENT_RESOURCES
;
1563 le
= tree_writes
->Flink
;
1565 while (le
!= tree_writes
) {
1566 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1568 TRACE("address: %llx, size: %x\n", tw
->address
, tw
->length
);
1570 KeInitializeEvent(&wtc
[bit_num
].Event
, NotificationEvent
, FALSE
);
1571 InitializeListHead(&wtc
[bit_num
].stripes
);
1572 wtc
[bit_num
].need_wait
= FALSE
;
1573 wtc
[bit_num
].stripes_left
= 0;
1574 wtc
[bit_num
].parity1
= wtc
[bit_num
].parity2
= wtc
[bit_num
].scratch
= NULL
;
1575 wtc
[bit_num
].mdl
= wtc
[bit_num
].parity1_mdl
= wtc
[bit_num
].parity2_mdl
= NULL
;
1577 Status
= write_data(Vcb
, tw
->address
, tw
->data
, tw
->length
, &wtc
[bit_num
], NULL
, NULL
, FALSE
, 0, HighPagePriority
);
1578 if (!NT_SUCCESS(Status
)) {
1579 ERR("write_data returned %08x\n", Status
);
1581 for (i
= 0; i
< num_bits
; i
++) {
1582 free_write_data_stripes(&wtc
[i
]);
1594 for (i
= 0; i
< num_bits
; i
++) {
1595 if (wtc
[i
].stripes
.Flink
!= &wtc
[i
].stripes
) {
1596 // launch writes and wait
1597 le
= wtc
[i
].stripes
.Flink
;
1598 while (le
!= &wtc
[i
].stripes
) {
1599 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1601 if (stripe
->status
!= WriteDataStatus_Ignore
) {
1602 wtc
[i
].need_wait
= TRUE
;
1603 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
1611 for (i
= 0; i
< num_bits
; i
++) {
1612 if (wtc
[i
].need_wait
)
1613 KeWaitForSingleObject(&wtc
[i
].Event
, Executive
, KernelMode
, FALSE
, NULL
);
1616 for (i
= 0; i
< num_bits
; i
++) {
1617 le
= wtc
[i
].stripes
.Flink
;
1618 while (le
!= &wtc
[i
].stripes
) {
1619 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1621 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
1622 Status
= stripe
->iosb
.Status
;
1623 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
1630 free_write_data_stripes(&wtc
[i
]);
1638 le
= tree_writes
->Flink
;
1639 while (le
!= tree_writes
) {
1640 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1645 ExAcquireResourceExclusiveLite(&c
->partial_stripes_lock
, TRUE
);
1647 while (!IsListEmpty(&c
->partial_stripes
)) {
1648 partial_stripe
* ps
= CONTAINING_RECORD(RemoveHeadList(&c
->partial_stripes
), partial_stripe
, list_entry
);
1650 Status
= flush_partial_stripe(Vcb
, c
, ps
);
1653 ExFreePool(ps
->bmparr
);
1657 if (!NT_SUCCESS(Status
)) {
1658 ERR("flush_partial_stripe returned %08x\n", Status
);
1659 ExReleaseResourceLite(&c
->partial_stripes_lock
);
1664 ExReleaseResourceLite(&c
->partial_stripes_lock
);
1671 return STATUS_SUCCESS
;
1674 static NTSTATUS
write_trees(device_extension
* Vcb
, PIRP Irp
) {
1680 LIST_ENTRY tree_writes
;
1683 TRACE("(%p)\n", Vcb
);
1685 InitializeListHead(&tree_writes
);
1687 for (level
= 0; level
<= 255; level
++) {
1688 BOOL nothing_found
= TRUE
;
1690 TRACE("level = %u\n", level
);
1692 le
= Vcb
->trees
.Flink
;
1693 while (le
!= &Vcb
->trees
) {
1694 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1696 if (t
->write
&& t
->header
.level
== level
) {
1697 KEY firstitem
, searchkey
;
1701 if (!t
->has_new_address
) {
1702 ERR("error - tried to write tree with no new address\n");
1703 return STATUS_INTERNAL_ERROR
;
1706 le2
= t
->itemlist
.Flink
;
1707 while (le2
!= &t
->itemlist
) {
1708 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1710 firstitem
= td
->key
;
1717 t
->paritem
->key
= firstitem
;
1718 t
->paritem
->treeholder
.address
= t
->new_address
;
1719 t
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
1722 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
1723 EXTENT_ITEM_TREE
* eit
;
1725 searchkey
.obj_id
= t
->new_address
;
1726 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1727 searchkey
.offset
= Vcb
->superblock
.node_size
;
1729 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1730 if (!NT_SUCCESS(Status
)) {
1731 ERR("error - find_item returned %08x\n", Status
);
1735 if (keycmp(searchkey
, tp
.item
->key
)) {
1736 ERR("could not find %llx,%x,%llx in extent_root (found %llx,%x,%llx instead)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1737 return STATUS_INTERNAL_ERROR
;
1740 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
1741 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM_TREE
));
1742 return STATUS_INTERNAL_ERROR
;
1745 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
1746 eit
->firstitem
= firstitem
;
1749 nothing_found
= FALSE
;
1759 TRACE("allocated tree extents\n");
1761 le
= Vcb
->trees
.Flink
;
1762 while (le
!= &Vcb
->trees
) {
1763 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1765 #ifdef DEBUG_PARANOID
1766 UINT32 num_items
= 0, size
= 0;
1771 #ifdef DEBUG_PARANOID
1775 le2
= t
->itemlist
.Flink
;
1776 while (le2
!= &t
->itemlist
) {
1777 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1782 if (keycmp(td
->key
, lastkey
) == 0) {
1783 ERR("(%llx,%x,%llx): duplicate key\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1785 } else if (keycmp(td
->key
, lastkey
) == -1) {
1786 ERR("(%llx,%x,%llx): key out of order\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
);
1794 if (t
->header
.level
== 0)
1800 if (t
->header
.level
== 0)
1801 size
+= num_items
* sizeof(leaf_node
);
1803 size
+= num_items
* sizeof(internal_node
);
1805 if (num_items
!= t
->header
.num_items
) {
1806 ERR("tree %llx, level %x: num_items was %x, expected %x\n", t
->root
->id
, t
->header
.level
, num_items
, t
->header
.num_items
);
1810 if (size
!= t
->size
) {
1811 ERR("tree %llx, level %x: size was %x, expected %x\n", t
->root
->id
, t
->header
.level
, size
, t
->size
);
1815 if (t
->header
.num_items
== 0 && t
->parent
) {
1816 ERR("tree %llx, level %x: tried to write empty tree with parent\n", t
->root
->id
, t
->header
.level
);
1820 if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
1821 ERR("tree %llx, level %x: tried to write overlarge tree (%x > %x)\n", t
->root
->id
, t
->header
.level
, t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
1826 ERR("tree %p\n", t
);
1827 le2
= t
->itemlist
.Flink
;
1828 while (le2
!= &t
->itemlist
) {
1829 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1831 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
1838 t
->header
.address
= t
->new_address
;
1839 t
->header
.generation
= Vcb
->superblock
.generation
;
1840 t
->header
.tree_id
= t
->root
->id
;
1841 t
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
1842 t
->header
.fs_uuid
= Vcb
->superblock
.uuid
;
1843 t
->has_address
= TRUE
;
1845 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
1847 ERR("out of memory\n");
1848 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1852 body
= data
+ sizeof(tree_header
);
1854 RtlCopyMemory(data
, &t
->header
, sizeof(tree_header
));
1855 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
1857 if (t
->header
.level
== 0) {
1858 leaf_node
* itemptr
= (leaf_node
*)body
;
1860 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
1862 le2
= t
->itemlist
.Flink
;
1863 while (le2
!= &t
->itemlist
) {
1864 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1866 dataptr
= dataptr
- td
->size
;
1868 itemptr
[i
].key
= td
->key
;
1869 itemptr
[i
].offset
= (UINT32
)((UINT8
*)dataptr
- (UINT8
*)body
);
1870 itemptr
[i
].size
= td
->size
;
1874 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
1880 internal_node
* itemptr
= (internal_node
*)body
;
1883 le2
= t
->itemlist
.Flink
;
1884 while (le2
!= &t
->itemlist
) {
1885 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1887 itemptr
[i
].key
= td
->key
;
1888 itemptr
[i
].address
= td
->treeholder
.address
;
1889 itemptr
[i
].generation
= td
->treeholder
.generation
;
1897 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
1899 *((UINT32
*)data
) = crc32
;
1900 TRACE("setting crc32 to %08x\n", crc32
);
1902 tw
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_write
), ALLOC_TAG
);
1904 ERR("out of memory\n");
1906 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1910 tw
->address
= t
->new_address
;
1911 tw
->length
= Vcb
->superblock
.node_size
;
1914 if (IsListEmpty(&tree_writes
))
1915 InsertTailList(&tree_writes
, &tw
->list_entry
);
1917 BOOL inserted
= FALSE
;
1919 le2
= tree_writes
.Flink
;
1920 while (le2
!= &tree_writes
) {
1921 tree_write
* tw2
= CONTAINING_RECORD(le2
, tree_write
, list_entry
);
1923 if (tw2
->address
> tw
->address
) {
1924 InsertHeadList(le2
->Blink
, &tw
->list_entry
);
1933 InsertTailList(&tree_writes
, &tw
->list_entry
);
1940 Status
= do_tree_writes(Vcb
, &tree_writes
, FALSE
);
1941 if (!NT_SUCCESS(Status
)) {
1942 ERR("do_tree_writes returned %08x\n", Status
);
1946 Status
= STATUS_SUCCESS
;
1949 while (!IsListEmpty(&tree_writes
)) {
1950 le
= RemoveHeadList(&tree_writes
);
1951 tw
= CONTAINING_RECORD(le
, tree_write
, list_entry
);
1954 ExFreePool(tw
->data
);
1962 static void update_backup_superblock(device_extension
* Vcb
, superblock_backup
* sb
, PIRP Irp
) {
1966 RtlZeroMemory(sb
, sizeof(superblock_backup
));
1968 sb
->root_tree_addr
= Vcb
->superblock
.root_tree_addr
;
1969 sb
->root_tree_generation
= Vcb
->superblock
.generation
;
1970 sb
->root_level
= Vcb
->superblock
.root_level
;
1972 sb
->chunk_tree_addr
= Vcb
->superblock
.chunk_tree_addr
;
1973 sb
->chunk_tree_generation
= Vcb
->superblock
.chunk_root_generation
;
1974 sb
->chunk_root_level
= Vcb
->superblock
.chunk_root_level
;
1976 searchkey
.obj_id
= BTRFS_ROOT_EXTENT
;
1977 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1978 searchkey
.offset
= 0xffffffffffffffff;
1980 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1981 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1982 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1984 sb
->extent_tree_addr
= ri
->block_number
;
1985 sb
->extent_tree_generation
= ri
->generation
;
1986 sb
->extent_root_level
= ri
->root_level
;
1990 searchkey
.obj_id
= BTRFS_ROOT_FSTREE
;
1992 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
1993 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
1994 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
1996 sb
->fs_tree_addr
= ri
->block_number
;
1997 sb
->fs_tree_generation
= ri
->generation
;
1998 sb
->fs_root_level
= ri
->root_level
;
2002 searchkey
.obj_id
= BTRFS_ROOT_DEVTREE
;
2004 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2005 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2006 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2008 sb
->dev_root_addr
= ri
->block_number
;
2009 sb
->dev_root_generation
= ri
->generation
;
2010 sb
->dev_root_level
= ri
->root_level
;
2014 searchkey
.obj_id
= BTRFS_ROOT_CHECKSUM
;
2016 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2017 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2018 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2020 sb
->csum_root_addr
= ri
->block_number
;
2021 sb
->csum_root_generation
= ri
->generation
;
2022 sb
->csum_root_level
= ri
->root_level
;
2026 sb
->total_bytes
= Vcb
->superblock
.total_bytes
;
2027 sb
->bytes_used
= Vcb
->superblock
.bytes_used
;
2028 sb
->num_devices
= Vcb
->superblock
.num_devices
;
2038 LIST_ENTRY list_entry
;
2039 } write_superblocks_stripe
;
2041 typedef struct _write_superblocks_context
{
2045 } write_superblocks_context
;
2047 _Function_class_(IO_COMPLETION_ROUTINE
)
2049 static NTSTATUS NTAPI
write_superblock_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2051 static NTSTATUS
write_superblock_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
2053 write_superblocks_stripe
* stripe
= conptr
;
2054 write_superblocks_context
* context
= stripe
->context
;
2056 UNUSED(DeviceObject
);
2058 stripe
->Status
= Irp
->IoStatus
.Status
;
2060 if (InterlockedDecrement(&context
->left
) == 0)
2061 KeSetEvent(&context
->Event
, 0, FALSE
);
2063 return STATUS_MORE_PROCESSING_REQUIRED
;
2066 static NTSTATUS
write_superblock(device_extension
* Vcb
, device
* device
, write_superblocks_context
* context
) {
2069 // All the documentation says that the Linux driver only writes one superblock
2070 // if it thinks a disk is an SSD, but this doesn't seem to be the case!
2072 while (superblock_addrs
[i
] > 0 && device
->devitem
.num_bytes
>= superblock_addrs
[i
] + sizeof(superblock
)) {
2073 ULONG sblen
= (ULONG
)sector_align(sizeof(superblock
), Vcb
->superblock
.sector_size
);
2076 write_superblocks_stripe
* stripe
;
2077 PIO_STACK_LOCATION IrpSp
;
2079 sb
= ExAllocatePoolWithTag(NonPagedPool
, sblen
, ALLOC_TAG
);
2081 ERR("out of memory\n");
2082 return STATUS_INSUFFICIENT_RESOURCES
;
2085 RtlCopyMemory(sb
, &Vcb
->superblock
, sizeof(superblock
));
2087 if (sblen
> sizeof(superblock
))
2088 RtlZeroMemory((UINT8
*)sb
+ sizeof(superblock
), sblen
- sizeof(superblock
));
2090 RtlCopyMemory(&sb
->dev_item
, &device
->devitem
, sizeof(DEV_ITEM
));
2091 sb
->sb_phys_addr
= superblock_addrs
[i
];
2093 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
2094 RtlCopyMemory(&sb
->checksum
, &crc32
, sizeof(UINT32
));
2096 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_superblocks_stripe
), ALLOC_TAG
);
2098 ERR("out of memory\n");
2100 return STATUS_INSUFFICIENT_RESOURCES
;
2103 stripe
->buf
= (UINT8
*)sb
;
2105 stripe
->Irp
= IoAllocateIrp(device
->devobj
->StackSize
, FALSE
);
2107 ERR("IoAllocateIrp failed\n");
2110 return STATUS_INSUFFICIENT_RESOURCES
;
2113 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
2114 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
2117 IrpSp
->Flags
|= SL_WRITE_THROUGH
;
2119 if (device
->devobj
->Flags
& DO_BUFFERED_IO
) {
2120 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= sb
;
2123 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
2124 } else if (device
->devobj
->Flags
& DO_DIRECT_IO
) {
2125 stripe
->mdl
= IoAllocateMdl(sb
, sblen
, FALSE
, FALSE
, NULL
);
2127 ERR("IoAllocateMdl failed\n");
2128 IoFreeIrp(stripe
->Irp
);
2131 return STATUS_INSUFFICIENT_RESOURCES
;
2134 stripe
->Irp
->MdlAddress
= stripe
->mdl
;
2136 MmBuildMdlForNonPagedPool(stripe
->mdl
);
2138 stripe
->Irp
->UserBuffer
= sb
;
2142 IrpSp
->Parameters
.Write
.Length
= sblen
;
2143 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= superblock_addrs
[i
];
2145 IoSetCompletionRoutine(stripe
->Irp
, write_superblock_completion
, stripe
, TRUE
, TRUE
, TRUE
);
2147 stripe
->context
= context
;
2148 stripe
->device
= device
;
2149 InsertTailList(&context
->stripes
, &stripe
->list_entry
);
2157 ERR("no superblocks written!\n");
2159 return STATUS_SUCCESS
;
2162 static NTSTATUS
write_superblocks(device_extension
* Vcb
, PIRP Irp
) {
2166 write_superblocks_context context
;
2168 TRACE("(%p)\n", Vcb
);
2170 le
= Vcb
->trees
.Flink
;
2171 while (le
!= &Vcb
->trees
) {
2172 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2174 if (t
->write
&& !t
->parent
) {
2175 if (t
->root
== Vcb
->root_root
) {
2176 Vcb
->superblock
.root_tree_addr
= t
->new_address
;
2177 Vcb
->superblock
.root_level
= t
->header
.level
;
2178 } else if (t
->root
== Vcb
->chunk_root
) {
2179 Vcb
->superblock
.chunk_tree_addr
= t
->new_address
;
2180 Vcb
->superblock
.chunk_root_generation
= t
->header
.generation
;
2181 Vcb
->superblock
.chunk_root_level
= t
->header
.level
;
2188 for (i
= 0; i
< BTRFS_NUM_BACKUP_ROOTS
- 1; i
++) {
2189 RtlCopyMemory(&Vcb
->superblock
.backup
[i
], &Vcb
->superblock
.backup
[i
+1], sizeof(superblock_backup
));
2192 update_backup_superblock(Vcb
, &Vcb
->superblock
.backup
[BTRFS_NUM_BACKUP_ROOTS
- 1], Irp
);
2194 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
2195 InitializeListHead(&context
.stripes
);
2198 le
= Vcb
->devices
.Flink
;
2199 while (le
!= &Vcb
->devices
) {
2200 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
2202 if (dev
->devobj
&& !dev
->readonly
) {
2203 Status
= write_superblock(Vcb
, dev
, &context
);
2204 if (!NT_SUCCESS(Status
)) {
2205 ERR("write_superblock returned %08x\n", Status
);
2213 if (IsListEmpty(&context
.stripes
)) {
2214 ERR("error - not writing any superblocks\n");
2215 Status
= STATUS_INTERNAL_ERROR
;
2219 le
= context
.stripes
.Flink
;
2220 while (le
!= &context
.stripes
) {
2221 write_superblocks_stripe
* stripe
= CONTAINING_RECORD(le
, write_superblocks_stripe
, list_entry
);
2223 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2228 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
2230 le
= context
.stripes
.Flink
;
2231 while (le
!= &context
.stripes
) {
2232 write_superblocks_stripe
* stripe
= CONTAINING_RECORD(le
, write_superblocks_stripe
, list_entry
);
2234 if (!NT_SUCCESS(stripe
->Status
)) {
2235 ERR("device %llx returned %08x\n", stripe
->device
->devitem
.dev_id
, stripe
->Status
);
2236 log_device_error(Vcb
, stripe
->device
, BTRFS_DEV_STAT_WRITE_ERRORS
);
2237 Status
= stripe
->Status
;
2244 Status
= STATUS_SUCCESS
;
2247 while (!IsListEmpty(&context
.stripes
)) {
2248 write_superblocks_stripe
* stripe
= CONTAINING_RECORD(RemoveHeadList(&context
.stripes
), write_superblocks_stripe
, list_entry
);
2251 if (stripe
->mdl
->MdlFlags
& MDL_PAGES_LOCKED
)
2252 MmUnlockPages(stripe
->mdl
);
2254 IoFreeMdl(stripe
->mdl
);
2258 IoFreeIrp(stripe
->Irp
);
2261 ExFreePool(stripe
->buf
);
2269 static NTSTATUS
flush_changed_extent(device_extension
* Vcb
, chunk
* c
, changed_extent
* ce
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2270 LIST_ENTRY
*le
, *le2
;
2274 if (ce
->count
== 0 && ce
->old_count
== 0) {
2275 while (!IsListEmpty(&ce
->refs
)) {
2276 changed_extent_ref
* cer
= CONTAINING_RECORD(RemoveHeadList(&ce
->refs
), changed_extent_ref
, list_entry
);
2280 while (!IsListEmpty(&ce
->old_refs
)) {
2281 changed_extent_ref
* cer
= CONTAINING_RECORD(RemoveHeadList(&ce
->old_refs
), changed_extent_ref
, list_entry
);
2288 le
= ce
->refs
.Flink
;
2289 while (le
!= &ce
->refs
) {
2290 changed_extent_ref
* cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
2291 UINT32 old_count
= 0;
2293 if (cer
->type
== TYPE_EXTENT_DATA_REF
) {
2294 le2
= ce
->old_refs
.Flink
;
2295 while (le2
!= &ce
->old_refs
) {
2296 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
2298 if (cer2
->type
== TYPE_EXTENT_DATA_REF
&& cer2
->edr
.root
== cer
->edr
.root
&& cer2
->edr
.objid
== cer
->edr
.objid
&& cer2
->edr
.offset
== cer
->edr
.offset
) {
2299 old_count
= cer2
->edr
.count
;
2306 old_size
= ce
->old_count
> 0 ? ce
->old_size
: ce
->size
;
2308 if (cer
->edr
.count
> old_count
) {
2309 Status
= increase_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
, cer
->edr
.count
- old_count
, Irp
);
2311 if (!NT_SUCCESS(Status
)) {
2312 ERR("increase_extent_refcount_data returned %08x\n", Status
);
2316 } else if (cer
->type
== TYPE_SHARED_DATA_REF
) {
2317 le2
= ce
->old_refs
.Flink
;
2318 while (le2
!= &ce
->old_refs
) {
2319 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
2321 if (cer2
->type
== TYPE_SHARED_DATA_REF
&& cer2
->sdr
.offset
== cer
->sdr
.offset
) {
2322 RemoveEntryList(&cer2
->list_entry
);
2334 le
= ce
->refs
.Flink
;
2335 while (le
!= &ce
->refs
) {
2336 changed_extent_ref
* cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
2337 LIST_ENTRY
* le3
= le
->Flink
;
2338 UINT32 old_count
= 0;
2340 if (cer
->type
== TYPE_EXTENT_DATA_REF
) {
2341 le2
= ce
->old_refs
.Flink
;
2342 while (le2
!= &ce
->old_refs
) {
2343 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
2345 if (cer2
->type
== TYPE_EXTENT_DATA_REF
&& cer2
->edr
.root
== cer
->edr
.root
&& cer2
->edr
.objid
== cer
->edr
.objid
&& cer2
->edr
.offset
== cer
->edr
.offset
) {
2346 old_count
= cer2
->edr
.count
;
2348 RemoveEntryList(&cer2
->list_entry
);
2356 old_size
= ce
->old_count
> 0 ? ce
->old_size
: ce
->size
;
2358 if (cer
->edr
.count
< old_count
) {
2359 Status
= decrease_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
,
2360 old_count
- cer
->edr
.count
, ce
->superseded
, Irp
);
2362 if (!NT_SUCCESS(Status
)) {
2363 ERR("decrease_extent_refcount_data returned %08x\n", Status
);
2368 if (ce
->size
!= ce
->old_size
&& ce
->old_count
> 0) {
2373 searchkey
.obj_id
= ce
->address
;
2374 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2375 searchkey
.offset
= ce
->old_size
;
2377 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2378 if (!NT_SUCCESS(Status
)) {
2379 ERR("error - find_item returned %08x\n", Status
);
2383 if (keycmp(searchkey
, tp
.item
->key
)) {
2384 ERR("could not find (%llx,%x,%llx) in extent tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2385 return STATUS_INTERNAL_ERROR
;
2388 if (tp
.item
->size
> 0) {
2389 data
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2392 ERR("out of memory\n");
2393 return STATUS_INSUFFICIENT_RESOURCES
;
2396 RtlCopyMemory(data
, tp
.item
->data
, tp
.item
->size
);
2400 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, ce
->address
, TYPE_EXTENT_ITEM
, ce
->size
, data
, tp
.item
->size
, NULL
, Irp
);
2401 if (!NT_SUCCESS(Status
)) {
2402 ERR("insert_tree_item returned %08x\n", Status
);
2403 if (data
) ExFreePool(data
);
2407 Status
= delete_tree_item(Vcb
, &tp
);
2408 if (!NT_SUCCESS(Status
)) {
2409 ERR("delete_tree_item returned %08x\n", Status
);
2415 RemoveEntryList(&cer
->list_entry
);
2421 #ifdef DEBUG_PARANOID
2422 if (!IsListEmpty(&ce
->old_refs
))
2423 WARN("old_refs not empty\n");
2427 if (ce
->count
== 0 && !ce
->superseded
) {
2428 c
->used
-= ce
->size
;
2429 space_list_add(c
, ce
->address
, ce
->size
, rollback
);
2432 RemoveEntryList(&ce
->list_entry
);
2435 return STATUS_SUCCESS
;
2438 void add_checksum_entry(device_extension
* Vcb
, UINT64 address
, ULONG length
, UINT32
* csum
, PIRP Irp
) {
2440 traverse_ptr tp
, next_tp
;
2442 UINT64 startaddr
, endaddr
;
2447 ULONG runlength
, index
;
2449 searchkey
.obj_id
= EXTENT_CSUM_ID
;
2450 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
2451 searchkey
.offset
= address
;
2453 // FIXME - create checksum_root if it doesn't exist at all
2455 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
2456 if (Status
== STATUS_NOT_FOUND
) { // tree is completely empty
2457 if (csum
) { // not deleted
2458 ULONG length2
= length
;
2459 UINT64 off
= address
;
2460 UINT32
* data
= csum
;
2463 UINT16 il
= (UINT16
)min(length2
, MAX_CSUM_SIZE
/ sizeof(UINT32
));
2465 checksums
= ExAllocatePoolWithTag(PagedPool
, il
* sizeof(UINT32
), ALLOC_TAG
);
2467 ERR("out of memory\n");
2471 RtlCopyMemory(checksums
, data
, il
* sizeof(UINT32
));
2473 Status
= insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, off
, checksums
,
2474 il
* sizeof(UINT32
), NULL
, Irp
);
2475 if (!NT_SUCCESS(Status
)) {
2476 ERR("insert_tree_item returned %08x\n", Status
);
2477 ExFreePool(checksums
);
2484 off
+= il
* Vcb
->superblock
.sector_size
;
2487 } while (length2
> 0);
2489 } else if (!NT_SUCCESS(Status
)) {
2490 ERR("find_item returned %08x\n", Status
);
2495 // FIXME - check entry is TYPE_EXTENT_CSUM?
2497 if (tp
.item
->key
.offset
< address
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= address
)
2498 startaddr
= tp
.item
->key
.offset
;
2500 startaddr
= address
;
2502 searchkey
.obj_id
= EXTENT_CSUM_ID
;
2503 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
2504 searchkey
.offset
= address
+ (length
* Vcb
->superblock
.sector_size
);
2506 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
2507 if (!NT_SUCCESS(Status
)) {
2508 ERR("find_item returned %08x\n", Status
);
2512 tplen
= tp
.item
->size
/ sizeof(UINT32
);
2514 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= address
+ (length
* Vcb
->superblock
.sector_size
))
2515 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
2517 endaddr
= address
+ (length
* Vcb
->superblock
.sector_size
);
2519 TRACE("cs starts at %llx (%x sectors)\n", address
, length
);
2520 TRACE("startaddr = %llx\n", startaddr
);
2521 TRACE("endaddr = %llx\n", endaddr
);
2523 len
= (ULONG
)((endaddr
- startaddr
) / Vcb
->superblock
.sector_size
);
2525 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
2527 ERR("out of memory\n");
2531 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
2533 ERR("out of memory\n");
2534 ExFreePool(checksums
);
2538 RtlInitializeBitMap(&bmp
, bmparr
, len
);
2539 RtlSetAllBits(&bmp
);
2541 searchkey
.obj_id
= EXTENT_CSUM_ID
;
2542 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
2543 searchkey
.offset
= address
;
2545 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
2546 if (!NT_SUCCESS(Status
)) {
2547 ERR("find_item returned %08x\n", Status
);
2548 ExFreePool(checksums
);
2553 // set bit = free space, cleared bit = allocated sector
2555 while (tp
.item
->key
.offset
< endaddr
) {
2556 if (tp
.item
->key
.offset
>= startaddr
) {
2557 if (tp
.item
->size
> 0) {
2558 ULONG itemlen
= (ULONG
)min((len
- (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
) * sizeof(UINT32
), tp
.item
->size
);
2560 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, itemlen
);
2561 RtlClearBits(&bmp
, (ULONG
)((tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
), itemlen
/ sizeof(UINT32
));
2564 Status
= delete_tree_item(Vcb
, &tp
);
2565 if (!NT_SUCCESS(Status
)) {
2566 ERR("delete_tree_item returned %08x\n", Status
);
2567 ExFreePool(checksums
);
2573 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
2579 if (!csum
) { // deleted
2580 RtlSetBits(&bmp
, (ULONG
)((address
- startaddr
) / Vcb
->superblock
.sector_size
), length
);
2582 RtlCopyMemory(&checksums
[(address
- startaddr
) / Vcb
->superblock
.sector_size
], csum
, length
* sizeof(UINT32
));
2583 RtlClearBits(&bmp
, (ULONG
)((address
- startaddr
) / Vcb
->superblock
.sector_size
), length
);
2586 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
2588 while (runlength
!= 0) {
2594 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
2595 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
2597 rl
= (UINT16
)runlength
;
2599 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
2601 ERR("out of memory\n");
2603 ExFreePool(checksums
);
2607 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
2609 off
= startaddr
+ UInt32x32To64(index
, Vcb
->superblock
.sector_size
);
2611 Status
= insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, off
, data
, sizeof(UINT32
) * rl
, NULL
, Irp
);
2612 if (!NT_SUCCESS(Status
)) {
2613 ERR("insert_tree_item returned %08x\n", Status
);
2616 ExFreePool(checksums
);
2622 } while (runlength
> 0);
2624 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
2628 ExFreePool(checksums
);
2632 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2633 LIST_ENTRY
*le
= Vcb
->chunks
.Flink
, *le2
;
2637 BLOCK_GROUP_ITEM
* bgi
;
2640 TRACE("(%p)\n", Vcb
);
2642 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
2644 while (le
!= &Vcb
->chunks
) {
2645 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2647 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2649 if (!c
->cache_loaded
&& (!IsListEmpty(&c
->changed_extents
) || c
->used
!= c
->oldused
)) {
2650 Status
= load_cache_chunk(Vcb
, c
, NULL
);
2652 if (!NT_SUCCESS(Status
)) {
2653 ERR("load_cache_chunk returned %08x\n", Status
);
2654 ExReleaseResourceLite(&c
->lock
);
2659 le2
= c
->changed_extents
.Flink
;
2660 while (le2
!= &c
->changed_extents
) {
2661 LIST_ENTRY
* le3
= le2
->Flink
;
2662 changed_extent
* ce
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
2664 Status
= flush_changed_extent(Vcb
, c
, ce
, Irp
, rollback
);
2665 if (!NT_SUCCESS(Status
)) {
2666 ERR("flush_changed_extent returned %08x\n", Status
);
2667 ExReleaseResourceLite(&c
->lock
);
2674 // This is usually done by update_chunks, but we have to check again in case any new chunks
2675 // have been allocated since.
2677 Status
= create_chunk(Vcb
, c
, Irp
);
2678 if (!NT_SUCCESS(Status
)) {
2679 ERR("create_chunk returned %08x\n", Status
);
2680 ExReleaseResourceLite(&c
->lock
);
2686 if (c
->old_cache
->dirty
) {
2687 LIST_ENTRY batchlist
;
2689 InitializeListHead(&batchlist
);
2691 Status
= flush_fcb(c
->old_cache
, FALSE
, &batchlist
, Irp
);
2692 if (!NT_SUCCESS(Status
)) {
2693 ERR("flush_fcb returned %08x\n", Status
);
2694 ExReleaseResourceLite(&c
->lock
);
2695 clear_batch_list(Vcb
, &batchlist
);
2699 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
2700 if (!NT_SUCCESS(Status
)) {
2701 ERR("commit_batch_list returned %08x\n", Status
);
2702 ExReleaseResourceLite(&c
->lock
);
2707 free_fcb(Vcb
, c
->old_cache
);
2708 c
->old_cache
= NULL
;
2711 if (c
->used
!= c
->oldused
) {
2712 searchkey
.obj_id
= c
->offset
;
2713 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2714 searchkey
.offset
= c
->chunk_item
->size
;
2716 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2717 if (!NT_SUCCESS(Status
)) {
2718 ERR("error - find_item returned %08x\n", Status
);
2719 ExReleaseResourceLite(&c
->lock
);
2723 if (keycmp(searchkey
, tp
.item
->key
)) {
2724 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2725 Status
= STATUS_INTERNAL_ERROR
;
2726 ExReleaseResourceLite(&c
->lock
);
2730 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2731 ERR("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(BLOCK_GROUP_ITEM
));
2732 Status
= STATUS_INTERNAL_ERROR
;
2733 ExReleaseResourceLite(&c
->lock
);
2737 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2739 ERR("out of memory\n");
2740 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2741 ExReleaseResourceLite(&c
->lock
);
2745 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2746 bgi
->used
= c
->used
;
2748 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2750 Status
= delete_tree_item(Vcb
, &tp
);
2751 if (!NT_SUCCESS(Status
)) {
2752 ERR("delete_tree_item returned %08x\n", Status
);
2754 ExReleaseResourceLite(&c
->lock
);
2758 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, Irp
);
2759 if (!NT_SUCCESS(Status
)) {
2760 ERR("insert_tree_item returned %08x\n", Status
);
2762 ExReleaseResourceLite(&c
->lock
);
2766 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2768 Vcb
->superblock
.bytes_used
+= c
->used
- c
->oldused
;
2770 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2772 c
->oldused
= c
->used
;
2775 ExReleaseResourceLite(&c
->lock
);
2780 Status
= STATUS_SUCCESS
;
2783 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2788 static void get_first_item(tree
* t
, KEY
* key
) {
2791 le
= t
->itemlist
.Flink
;
2792 while (le
!= &t
->itemlist
) {
2793 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2800 static NTSTATUS
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2803 tree_data
* oldlastitem
;
2805 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2807 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2809 ERR("out of memory\n");
2810 return STATUS_INSUFFICIENT_RESOURCES
;
2813 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2814 nt
->header
.address
= 0;
2815 nt
->header
.generation
= Vcb
->superblock
.generation
;
2816 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2817 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| HEADER_FLAG_WRITTEN
;
2819 nt
->has_address
= FALSE
;
2821 nt
->parent
= t
->parent
;
2823 #ifdef DEBUG_PARANOID
2824 if (nt
->parent
&& nt
->parent
->header
.level
<= nt
->header
.level
) int3
;
2828 nt
->new_address
= 0;
2829 nt
->has_new_address
= FALSE
;
2830 nt
->updated_extents
= FALSE
;
2831 nt
->uniqueness_determined
= TRUE
;
2832 nt
->is_unique
= TRUE
;
2833 nt
->list_entry_hash
.Flink
= NULL
;
2835 InitializeListHead(&nt
->itemlist
);
2837 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2839 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2840 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2841 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2842 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2844 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2845 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2847 nt
->size
= t
->size
- size
;
2849 t
->header
.num_items
= numitems
;
2852 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2854 if (nt
->header
.level
> 0) {
2855 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2857 while (le
!= &nt
->itemlist
) {
2858 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2860 if (td2
->treeholder
.tree
) {
2861 td2
->treeholder
.tree
->parent
= nt
;
2862 #ifdef DEBUG_PARANOID
2863 if (td2
->treeholder
.tree
->parent
&& td2
->treeholder
.tree
->parent
->header
.level
<= td2
->treeholder
.tree
->header
.level
) int3
;
2870 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2872 while (le
!= &nt
->itemlist
) {
2873 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2875 if (!td2
->inserted
&& td2
->data
) {
2876 UINT8
* data
= ExAllocatePoolWithTag(PagedPool
, td2
->size
, ALLOC_TAG
);
2879 ERR("out of memory\n");
2880 return STATUS_INSUFFICIENT_RESOURCES
;
2883 RtlCopyMemory(data
, td2
->data
, td2
->size
);
2885 td2
->inserted
= TRUE
;
2893 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2895 ERR("out of memory\n");
2896 return STATUS_INSUFFICIENT_RESOURCES
;
2899 td
->key
= newfirstitem
->key
;
2901 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2904 td
->inserted
= TRUE
;
2905 td
->treeholder
.tree
= nt
;
2908 nt
->parent
->header
.num_items
++;
2909 nt
->parent
->size
+= sizeof(internal_node
);
2914 TRACE("adding new tree parent\n");
2916 if (nt
->header
.level
== 255) {
2917 ERR("cannot add parent to tree at level 255\n");
2918 return STATUS_INTERNAL_ERROR
;
2921 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2923 ERR("out of memory\n");
2924 return STATUS_INSUFFICIENT_RESOURCES
;
2927 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2928 pt
->header
.address
= 0;
2929 pt
->header
.num_items
= 2;
2930 pt
->header
.level
= nt
->header
.level
+ 1;
2931 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| HEADER_FLAG_WRITTEN
;
2933 pt
->has_address
= FALSE
;
2938 pt
->new_address
= 0;
2939 pt
->has_new_address
= FALSE
;
2940 pt
->updated_extents
= FALSE
;
2941 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2942 pt
->uniqueness_determined
= TRUE
;
2943 pt
->is_unique
= TRUE
;
2944 pt
->list_entry_hash
.Flink
= NULL
;
2946 InitializeListHead(&pt
->itemlist
);
2948 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2950 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2952 ERR("out of memory\n");
2953 return STATUS_INSUFFICIENT_RESOURCES
;
2956 get_first_item(t
, &td
->key
);
2958 td
->inserted
= FALSE
;
2959 td
->treeholder
.address
= 0;
2960 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2961 td
->treeholder
.tree
= t
;
2962 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2965 td
= ExAllocateFromPagedLookasideList(&Vcb
->tree_data_lookaside
);
2967 ERR("out of memory\n");
2968 return STATUS_INSUFFICIENT_RESOURCES
;
2971 td
->key
= newfirstitem
->key
;
2973 td
->inserted
= FALSE
;
2974 td
->treeholder
.address
= 0;
2975 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2976 td
->treeholder
.tree
= nt
;
2977 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2982 t
->root
->treeholder
.tree
= pt
;
2987 #ifdef DEBUG_PARANOID
2988 if (t
->parent
&& t
->parent
->header
.level
<= t
->header
.level
) int3
;
2989 if (nt
->parent
&& nt
->parent
->header
.level
<= nt
->header
.level
) int3
;
2993 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2995 return STATUS_SUCCESS
;
2998 static NTSTATUS
split_tree(device_extension
* Vcb
, tree
* t
) {
3000 UINT32 size
, ds
, numitems
;
3005 // FIXME - naïve implementation: maximizes number of filled trees
3007 le
= t
->itemlist
.Flink
;
3008 while (le
!= &t
->itemlist
) {
3009 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3012 if (t
->header
.level
== 0)
3013 ds
= sizeof(leaf_node
) + td
->size
;
3015 ds
= sizeof(internal_node
);
3017 if (numitems
== 0 && ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3018 ERR("(%llx,%x,%llx) in tree %llx is too large (%x > %x)\n",
3019 td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, t
->root
->id
,
3020 ds
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3021 return STATUS_INTERNAL_ERROR
;
3024 // FIXME - move back if previous item was deleted item with same key
3025 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
3026 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
3035 return STATUS_SUCCESS
;
3038 BOOL
is_tree_unique(device_extension
* Vcb
, tree
* t
, PIRP Irp
) {
3046 if (t
->uniqueness_determined
)
3047 return t
->is_unique
;
3049 if (t
->parent
&& !is_tree_unique(Vcb
, t
->parent
, Irp
))
3052 if (t
->has_address
) {
3053 searchkey
.obj_id
= t
->header
.address
;
3054 searchkey
.obj_type
= Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
? TYPE_METADATA_ITEM
: TYPE_EXTENT_ITEM
;
3055 searchkey
.offset
= 0xffffffffffffffff;
3057 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3058 if (!NT_SUCCESS(Status
)) {
3059 ERR("error - find_item returned %08x\n", Status
);
3063 if (tp
.item
->key
.obj_id
!= t
->header
.address
|| (tp
.item
->key
.obj_type
!= TYPE_METADATA_ITEM
&& tp
.item
->key
.obj_type
!= TYPE_EXTENT_ITEM
))
3066 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& tp
.item
->size
== sizeof(EXTENT_ITEM_V0
))
3069 if (tp
.item
->size
< sizeof(EXTENT_ITEM
))
3072 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
3074 if (ei
->refcount
> 1)
3077 if (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
&& ei
->flags
& EXTENT_ITEM_TREE_BLOCK
) {
3080 if (tp
.item
->size
< sizeof(EXTENT_ITEM
) + sizeof(EXTENT_ITEM2
))
3083 ei2
= (EXTENT_ITEM2
*)&ei
[1];
3084 type
= (UINT8
*)&ei2
[1];
3086 type
= (UINT8
*)&ei
[1];
3088 if (type
>= tp
.item
->data
+ tp
.item
->size
|| *type
!= TYPE_TREE_BLOCK_REF
)
3096 t
->uniqueness_determined
= TRUE
;
3101 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, BOOL
* done
, BOOL
* done_deletions
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3103 tree_data
* nextparitem
= NULL
;
3105 tree
*next_tree
, *par
;
3110 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
3112 // FIXME - doesn't capture everything, as it doesn't ascend
3113 le
= t
->paritem
->list_entry
.Flink
;
3114 while (le
!= &t
->parent
->itemlist
) {
3115 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3126 return STATUS_SUCCESS
;
3128 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
3130 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
, NULL
);
3131 if (!NT_SUCCESS(Status
)) {
3132 ERR("do_load_tree returned %08x\n", Status
);
3136 if (!is_tree_unique(Vcb
, nextparitem
->treeholder
.tree
, Irp
))
3137 return STATUS_SUCCESS
;
3139 next_tree
= nextparitem
->treeholder
.tree
;
3141 if (!next_tree
->updated_extents
&& next_tree
->has_address
) {
3142 Status
= update_tree_extents(Vcb
, next_tree
, Irp
, rollback
);
3143 if (!NT_SUCCESS(Status
)) {
3144 ERR("update_tree_extents returned %08x\n", Status
);
3149 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3150 // merge two trees into one
3152 t
->header
.num_items
+= next_tree
->header
.num_items
;
3153 t
->size
+= next_tree
->size
;
3155 if (next_tree
->header
.level
> 0) {
3156 le
= next_tree
->itemlist
.Flink
;
3158 while (le
!= &next_tree
->itemlist
) {
3159 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3161 if (td2
->treeholder
.tree
) {
3162 td2
->treeholder
.tree
->parent
= t
;
3163 #ifdef DEBUG_PARANOID
3164 if (td2
->treeholder
.tree
->parent
&& td2
->treeholder
.tree
->parent
->header
.level
<= td2
->treeholder
.tree
->header
.level
) int3
;
3168 td2
->inserted
= TRUE
;
3172 le
= next_tree
->itemlist
.Flink
;
3174 while (le
!= &next_tree
->itemlist
) {
3175 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3177 if (!td2
->inserted
&& td2
->data
) {
3178 UINT8
* data
= ExAllocatePoolWithTag(PagedPool
, td2
->size
, ALLOC_TAG
);
3181 ERR("out of memory\n");
3182 return STATUS_INSUFFICIENT_RESOURCES
;
3185 RtlCopyMemory(data
, td2
->data
, td2
->size
);
3187 td2
->inserted
= TRUE
;
3194 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
3195 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
3196 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
3197 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
3199 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
3201 next_tree
->header
.num_items
= 0;
3202 next_tree
->size
= 0;
3204 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
3205 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, next_tree
->parent
->header
.tree_id
, next_tree
->header
.level
, Irp
, rollback
);
3207 if (!NT_SUCCESS(Status
)) {
3208 ERR("reduce_tree_extent returned %08x\n", Status
);
3211 } else if (next_tree
->has_address
) {
3212 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, next_tree
->parent
->header
.tree_id
, next_tree
->header
.level
, Irp
, rollback
);
3214 if (!NT_SUCCESS(Status
)) {
3215 ERR("reduce_tree_extent returned %08x\n", Status
);
3220 if (!nextparitem
->ignore
) {
3221 nextparitem
->ignore
= TRUE
;
3222 next_tree
->parent
->header
.num_items
--;
3223 next_tree
->parent
->size
-= sizeof(internal_node
);
3225 *done_deletions
= TRUE
;
3228 par
= next_tree
->parent
;
3234 RemoveEntryList(&nextparitem
->list_entry
);
3235 ExFreePool(next_tree
->paritem
);
3236 next_tree
->paritem
= NULL
;
3238 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3240 free_tree(next_tree
);
3244 // rebalance by moving items from second tree into first
3245 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
3246 KEY firstitem
= {0, 0, 0};
3247 BOOL changed
= FALSE
;
3249 TRACE("attempting rebalance\n");
3251 le
= next_tree
->itemlist
.Flink
;
3252 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
3253 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3257 if (next_tree
->header
.level
== 0)
3258 size
= sizeof(leaf_node
) + td
->size
;
3260 size
= sizeof(internal_node
);
3264 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3265 RemoveEntryList(&td
->list_entry
);
3266 InsertTailList(&t
->itemlist
, &td
->list_entry
);
3268 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
) {
3269 td
->treeholder
.tree
->parent
= t
;
3270 #ifdef DEBUG_PARANOID
3271 if (td
->treeholder
.tree
->parent
&& td
->treeholder
.tree
->parent
->header
.level
<= td
->treeholder
.tree
->header
.level
) int3
;
3273 } else if (next_tree
->header
.level
== 0 && !td
->inserted
&& td
->size
> 0) {
3274 UINT8
* data
= ExAllocatePoolWithTag(PagedPool
, td
->size
, ALLOC_TAG
);
3277 ERR("out of memory\n");
3278 return STATUS_INSUFFICIENT_RESOURCES
;
3281 RtlCopyMemory(data
, td
->data
, td
->size
);
3285 td
->inserted
= TRUE
;
3288 next_tree
->size
-= size
;
3290 next_tree
->header
.num_items
--;
3291 t
->header
.num_items
++;
3298 le
= next_tree
->itemlist
.Flink
;
3301 le
= next_tree
->itemlist
.Flink
;
3302 while (le
!= &next_tree
->itemlist
) {
3303 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3306 firstitem
= td
->key
;
3313 // FIXME - once ascension is working, make this work with parent's parent, etc.
3314 if (next_tree
->paritem
)
3315 next_tree
->paritem
->key
= firstitem
;
3327 return STATUS_SUCCESS
;
3330 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, PIRP Irp
) {
3335 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
3336 searchkey
.obj_id
= address
;
3337 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
3338 searchkey
.offset
= t
->header
.level
;
3340 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3341 if (!NT_SUCCESS(Status
)) {
3342 ERR("error - find_item returned %08x\n", Status
);
3346 if (!keycmp(tp
.item
->key
, searchkey
)) {
3347 EXTENT_ITEM_SKINNY_METADATA
* eism
;
3349 if (tp
.item
->size
> 0) {
3350 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3353 ERR("out of memory\n");
3354 return STATUS_INSUFFICIENT_RESOURCES
;
3357 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
3361 Status
= delete_tree_item(Vcb
, &tp
);
3362 if (!NT_SUCCESS(Status
)) {
3363 ERR("delete_tree_item returned %08x\n", Status
);
3364 if (eism
) ExFreePool(eism
);
3368 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, Irp
);
3369 if (!NT_SUCCESS(Status
)) {
3370 ERR("insert_tree_item returned %08x\n", Status
);
3371 if (eism
) ExFreePool(eism
);
3375 return STATUS_SUCCESS
;
3379 searchkey
.obj_id
= address
;
3380 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3381 searchkey
.offset
= 0xffffffffffffffff;
3383 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3384 if (!NT_SUCCESS(Status
)) {
3385 ERR("error - find_item returned %08x\n", Status
);
3389 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3390 EXTENT_ITEM_TREE
* eit
;
3392 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
3393 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(EXTENT_ITEM_TREE
));
3394 return STATUS_INTERNAL_ERROR
;
3397 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3400 ERR("out of memory\n");
3401 return STATUS_INSUFFICIENT_RESOURCES
;
3404 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
3406 Status
= delete_tree_item(Vcb
, &tp
);
3407 if (!NT_SUCCESS(Status
)) {
3408 ERR("delete_tree_item returned %08x\n", Status
);
3415 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, eit
, tp
.item
->size
, NULL
, Irp
);
3416 if (!NT_SUCCESS(Status
)) {
3417 ERR("insert_tree_item returned %08x\n", Status
);
3422 return STATUS_SUCCESS
;
3425 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
3427 return STATUS_INTERNAL_ERROR
;
3430 static NTSTATUS
update_tree_extents_recursive(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3433 if (t
->parent
&& !t
->parent
->updated_extents
&& t
->parent
->has_address
) {
3434 Status
= update_tree_extents_recursive(Vcb
, t
->parent
, Irp
, rollback
);
3435 if (!NT_SUCCESS(Status
))
3439 Status
= update_tree_extents(Vcb
, t
, Irp
, rollback
);
3440 if (!NT_SUCCESS(Status
)) {
3441 ERR("update_tree_extents returned %08x\n", Status
);
3445 return STATUS_SUCCESS
;
3448 static NTSTATUS
do_splits(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3449 ULONG level
, max_level
;
3451 BOOL empty
, done_deletions
= FALSE
;
3455 TRACE("(%p)\n", Vcb
);
3459 for (level
= 0; level
<= 255; level
++) {
3460 LIST_ENTRY
*le
, *nextle
;
3464 TRACE("doing level %u\n", level
);
3466 le
= Vcb
->trees
.Flink
;
3468 while (le
!= &Vcb
->trees
) {
3469 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3473 if (t
->write
&& t
->header
.level
== level
) {
3476 if (t
->header
.num_items
== 0) {
3478 done_deletions
= TRUE
;
3480 TRACE("deleting tree in root %llx\n", t
->root
->id
);
3482 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3484 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3485 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, t
->parent
->header
.tree_id
, t
->header
.level
, Irp
, rollback
);
3487 if (!NT_SUCCESS(Status
)) {
3488 ERR("reduce_tree_extent returned %08x\n", Status
);
3492 t
->has_new_address
= FALSE
;
3493 } else if (t
->has_address
) {
3494 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, t
->parent
->header
.tree_id
, t
->header
.level
, Irp
, rollback
);
3496 if (!NT_SUCCESS(Status
)) {
3497 ERR("reduce_tree_extent returned %08x\n", Status
);
3501 t
->has_address
= FALSE
;
3504 if (!t
->paritem
->ignore
) {
3505 t
->paritem
->ignore
= TRUE
;
3506 t
->parent
->header
.num_items
--;
3507 t
->parent
->size
-= sizeof(internal_node
);
3510 RemoveEntryList(&t
->paritem
->list_entry
);
3511 ExFreePool(t
->paritem
);
3515 } else if (t
->header
.level
!= 0) {
3516 if (t
->has_new_address
) {
3517 Status
= update_extent_level(Vcb
, t
->new_address
, t
, 0, Irp
);
3519 if (!NT_SUCCESS(Status
)) {
3520 ERR("update_extent_level returned %08x\n", Status
);
3525 t
->header
.level
= 0;
3527 } else if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3528 TRACE("splitting overlarge tree (%x > %x)\n", t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3530 if (!t
->updated_extents
&& t
->has_address
) {
3531 Status
= update_tree_extents_recursive(Vcb
, t
, Irp
, rollback
);
3532 if (!NT_SUCCESS(Status
)) {
3533 ERR("update_tree_extents_recursive returned %08x\n", Status
);
3538 Status
= split_tree(Vcb
, t
);
3540 if (!NT_SUCCESS(Status
)) {
3541 ERR("split_tree returned %08x\n", Status
);
3553 TRACE("nothing found for level %u\n", level
);
3558 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
3560 for (level
= 0; level
<= max_level
; level
++) {
3563 le
= Vcb
->trees
.Flink
;
3565 while (le
!= &Vcb
->trees
) {
3566 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3568 if (t
->write
&& t
->header
.level
== level
&& t
->header
.num_items
> 0 && t
->parent
&& t
->size
< min_size
&&
3569 t
->root
->id
!= BTRFS_ROOT_FREE_SPACE
&& is_tree_unique(Vcb
, t
, Irp
)) {
3573 Status
= try_tree_amalgamate(Vcb
, t
, &done
, &done_deletions
, Irp
, rollback
);
3574 if (!NT_SUCCESS(Status
)) {
3575 ERR("try_tree_amalgamate returned %08x\n", Status
);
3578 } while (done
&& t
->size
< min_size
);
3585 // simplify trees if top tree only has one entry
3587 if (done_deletions
) {
3588 for (level
= max_level
; level
> 0; level
--) {
3589 LIST_ENTRY
*le
, *nextle
;
3591 le
= Vcb
->trees
.Flink
;
3592 while (le
!= &Vcb
->trees
) {
3594 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3596 if (t
->write
&& t
->header
.level
== level
) {
3597 if (!t
->parent
&& t
->header
.num_items
== 1) {
3598 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
3599 tree_data
* td
= NULL
;
3600 tree
* child_tree
= NULL
;
3602 while (le2
!= &t
->itemlist
) {
3603 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3609 TRACE("deleting top-level tree in root %llx with one item\n", t
->root
->id
);
3611 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3612 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, t
->header
.tree_id
, t
->header
.level
, Irp
, rollback
);
3614 if (!NT_SUCCESS(Status
)) {
3615 ERR("reduce_tree_extent returned %08x\n", Status
);
3619 t
->has_new_address
= FALSE
;
3620 } else if (t
->has_address
) {
3621 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, t
->header
.tree_id
, t
->header
.level
, Irp
, rollback
);
3623 if (!NT_SUCCESS(Status
)) {
3624 ERR("reduce_tree_extent returned %08x\n", Status
);
3628 t
->has_address
= FALSE
;
3631 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3632 KEY searchkey
= {0,0,0};
3635 Status
= find_item(Vcb
, t
->root
, &tp
, &searchkey
, FALSE
, Irp
);
3636 if (!NT_SUCCESS(Status
)) {
3637 ERR("error - find_item returned %08x\n", Status
);
3642 child_tree
= td
->treeholder
.tree
;
3645 child_tree
->parent
= NULL
;
3646 child_tree
->paritem
= NULL
;
3649 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3654 child_tree
->root
->treeholder
.tree
= child_tree
;
3663 return STATUS_SUCCESS
;
3666 static NTSTATUS
remove_root_extents(device_extension
* Vcb
, root
* r
, tree_holder
* th
, UINT8 level
, tree
* parent
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3670 Status
= load_tree(Vcb
, th
->address
, r
, &th
->tree
, th
->generation
, NULL
);
3672 if (!NT_SUCCESS(Status
)) {
3673 ERR("load_tree(%llx) returned %08x\n", th
->address
, Status
);
3679 LIST_ENTRY
* le
= th
->tree
->itemlist
.Flink
;
3681 while (le
!= &th
->tree
->itemlist
) {
3682 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3685 Status
= remove_root_extents(Vcb
, r
, &td
->treeholder
, th
->tree
->header
.level
- 1, th
->tree
, Irp
, rollback
);
3687 if (!NT_SUCCESS(Status
)) {
3688 ERR("remove_root_extents returned %08x\n", Status
);
3697 if (th
->tree
&& !th
->tree
->updated_extents
&& th
->tree
->has_address
) {
3698 Status
= update_tree_extents(Vcb
, th
->tree
, Irp
, rollback
);
3699 if (!NT_SUCCESS(Status
)) {
3700 ERR("update_tree_extents returned %08x\n", Status
);
3705 if (!th
->tree
|| th
->tree
->has_address
) {
3706 Status
= reduce_tree_extent(Vcb
, th
->address
, NULL
, parent
? parent
->header
.tree_id
: r
->id
, level
, Irp
, rollback
);
3708 if (!NT_SUCCESS(Status
)) {
3709 ERR("reduce_tree_extent(%llx) returned %08x\n", th
->address
, Status
);
3714 return STATUS_SUCCESS
;
3717 static NTSTATUS
drop_root(device_extension
* Vcb
, root
* r
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3722 Status
= remove_root_extents(Vcb
, r
, &r
->treeholder
, r
->root_item
.root_level
, NULL
, Irp
, rollback
);
3723 if (!NT_SUCCESS(Status
)) {
3724 ERR("remove_root_extents returned %08x\n", Status
);
3728 // remove entries in uuid root (tree 9)
3729 if (Vcb
->uuid_root
) {
3730 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
.uuid
[0], sizeof(UINT64
));
3731 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
3732 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
3734 if (searchkey
.obj_id
!= 0 || searchkey
.offset
!= 0) {
3735 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
3736 if (!NT_SUCCESS(Status
)) {
3737 WARN("find_item returned %08x\n", Status
);
3739 if (!keycmp(tp
.item
->key
, searchkey
)) {
3740 Status
= delete_tree_item(Vcb
, &tp
);
3741 if (!NT_SUCCESS(Status
)) {
3742 ERR("delete_tree_item returned %08x\n", Status
);
3746 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3750 if (r
->root_item
.rtransid
> 0) {
3751 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.received_uuid
.uuid
[0], sizeof(UINT64
));
3752 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
3753 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.received_uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
3755 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
3756 if (!NT_SUCCESS(Status
))
3757 WARN("find_item returned %08x\n", Status
);
3759 if (!keycmp(tp
.item
->key
, searchkey
)) {
3760 if (tp
.item
->size
== sizeof(UINT64
)) {
3761 UINT64
* id
= (UINT64
*)tp
.item
->data
;
3764 Status
= delete_tree_item(Vcb
, &tp
);
3765 if (!NT_SUCCESS(Status
)) {
3766 ERR("delete_tree_item returned %08x\n", Status
);
3770 } else if (tp
.item
->size
> sizeof(UINT64
)) {
3772 UINT64
* ids
= (UINT64
*)tp
.item
->data
;
3774 for (i
= 0; i
< tp
.item
->size
/ sizeof(UINT64
); i
++) {
3775 if (ids
[i
] == r
->id
) {
3778 ne
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
- sizeof(UINT64
), ALLOC_TAG
);
3780 ERR("out of memory\n");
3781 return STATUS_INSUFFICIENT_RESOURCES
;
3785 RtlCopyMemory(ne
, ids
, sizeof(UINT64
) * i
);
3787 if ((i
+ 1) * sizeof(UINT64
) < tp
.item
->size
)
3788 RtlCopyMemory(&ne
[i
], &ids
[i
+ 1], tp
.item
->size
- ((i
+ 1) * sizeof(UINT64
)));
3790 Status
= delete_tree_item(Vcb
, &tp
);
3791 if (!NT_SUCCESS(Status
)) {
3792 ERR("delete_tree_item returned %08x\n", Status
);
3797 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
,
3798 ne
, tp
.item
->size
- sizeof(UINT64
), NULL
, Irp
);
3799 if (!NT_SUCCESS(Status
)) {
3800 ERR("insert_tree_item returned %08x\n", Status
);
3810 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3817 searchkey
.obj_id
= r
->id
;
3818 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
3819 searchkey
.offset
= 0xffffffffffffffff;
3821 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3822 if (!NT_SUCCESS(Status
)) {
3823 ERR("find_item returned %08x\n", Status
);
3827 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3828 Status
= delete_tree_item(Vcb
, &tp
);
3830 if (!NT_SUCCESS(Status
)) {
3831 ERR("delete_tree_item returned %08x\n", Status
);
3835 WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3837 // delete items in tree cache
3839 free_trees_root(Vcb
, r
);
3841 return STATUS_SUCCESS
;
3844 static NTSTATUS
drop_roots(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3845 LIST_ENTRY
*le
= Vcb
->drop_roots
.Flink
, *le2
;
3848 while (le
!= &Vcb
->drop_roots
) {
3849 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3853 Status
= drop_root(Vcb
, r
, Irp
, rollback
);
3854 if (!NT_SUCCESS(Status
)) {
3855 ERR("drop_root(%llx) returned %08x\n", r
->id
, Status
);
3862 return STATUS_SUCCESS
;
3865 NTSTATUS
update_dev_item(device_extension
* Vcb
, device
* device
, PIRP Irp
) {
3871 searchkey
.obj_id
= 1;
3872 searchkey
.obj_type
= TYPE_DEV_ITEM
;
3873 searchkey
.offset
= device
->devitem
.dev_id
;
3875 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3876 if (!NT_SUCCESS(Status
)) {
3877 ERR("error - find_item returned %08x\n", Status
);
3881 if (keycmp(tp
.item
->key
, searchkey
)) {
3882 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
3883 return STATUS_INTERNAL_ERROR
;
3886 Status
= delete_tree_item(Vcb
, &tp
);
3887 if (!NT_SUCCESS(Status
)) {
3888 ERR("delete_tree_item returned %08x\n", Status
);
3892 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
3894 ERR("out of memory\n");
3895 return STATUS_INSUFFICIENT_RESOURCES
;
3898 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
3900 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
3901 if (!NT_SUCCESS(Status
)) {
3902 ERR("insert_tree_item returned %08x\n", Status
);
3907 return STATUS_SUCCESS
;
3910 static void regen_bootstrap(device_extension
* Vcb
) {
3916 le
= Vcb
->sys_chunks
.Flink
;
3917 while (le
!= &Vcb
->sys_chunks
) {
3918 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3920 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
3922 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
3925 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
3932 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, UINT16 size
) {
3936 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
3937 ERR("error - bootstrap is full\n");
3938 return STATUS_INTERNAL_ERROR
;
3941 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3943 ERR("out of memory\n");
3944 return STATUS_INSUFFICIENT_RESOURCES
;
3947 sc
->key
.obj_id
= obj_id
;
3948 sc
->key
.obj_type
= obj_type
;
3949 sc
->key
.offset
= offset
;
3951 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3953 ERR("out of memory\n");
3955 return STATUS_INSUFFICIENT_RESOURCES
;
3958 RtlCopyMemory(sc
->data
, data
, sc
->size
);
3960 le
= Vcb
->sys_chunks
.Flink
;
3961 while (le
!= &Vcb
->sys_chunks
) {
3962 sys_chunk
* sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3964 if (keycmp(sc2
->key
, sc
->key
) == 1)
3969 InsertTailList(le
, &sc
->list_entry
);
3971 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
3973 regen_bootstrap(Vcb
);
3975 return STATUS_SUCCESS
;
3978 static NTSTATUS
create_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
) {
3980 CHUNK_ITEM_STRIPE
* cis
;
3981 BLOCK_GROUP_ITEM
* bgi
;
3985 ci
= ExAllocatePoolWithTag(PagedPool
, c
->size
, ALLOC_TAG
);
3987 ERR("out of memory\n");
3988 return STATUS_INSUFFICIENT_RESOURCES
;
3991 RtlCopyMemory(ci
, c
->chunk_item
, c
->size
);
3993 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
, NULL
, Irp
);
3994 if (!NT_SUCCESS(Status
)) {
3995 ERR("insert_tree_item failed\n");
4000 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
) {
4001 Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
);
4002 if (!NT_SUCCESS(Status
)) {
4003 ERR("add_to_bootstrap returned %08x\n", Status
);
4008 // add BLOCK_GROUP_ITEM to tree 2
4010 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
4012 ERR("out of memory\n");
4013 return STATUS_INSUFFICIENT_RESOURCES
;
4016 bgi
->used
= c
->used
;
4017 bgi
->chunk_tree
= 0x100;
4018 bgi
->flags
= c
->chunk_item
->type
;
4020 Status
= insert_tree_item(Vcb
, Vcb
->extent_root
, c
->offset
, TYPE_BLOCK_GROUP_ITEM
, c
->chunk_item
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, Irp
);
4021 if (!NT_SUCCESS(Status
)) {
4022 ERR("insert_tree_item failed\n");
4027 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
4028 factor
= c
->chunk_item
->num_stripes
;
4029 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
4030 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
4031 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
4032 factor
= c
->chunk_item
->num_stripes
- 1;
4033 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
4034 factor
= c
->chunk_item
->num_stripes
- 2;
4035 else // SINGLE, DUPLICATE, RAID1
4038 // add DEV_EXTENTs to tree 4
4040 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
4042 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
4045 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
4047 ERR("out of memory\n");
4048 return STATUS_INSUFFICIENT_RESOURCES
;
4051 de
->chunktree
= Vcb
->chunk_root
->id
;
4053 de
->address
= c
->offset
;
4054 de
->length
= c
->chunk_item
->size
/ factor
;
4055 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
4057 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, c
->devices
[i
]->devitem
.dev_id
, TYPE_DEV_EXTENT
, cis
[i
].offset
, de
, sizeof(DEV_EXTENT
), NULL
, Irp
);
4058 if (!NT_SUCCESS(Status
)) {
4059 ERR("insert_tree_item returned %08x\n", Status
);
4064 // FIXME - no point in calling this twice for the same device
4065 Status
= update_dev_item(Vcb
, c
->devices
[i
], Irp
);
4066 if (!NT_SUCCESS(Status
)) {
4067 ERR("update_dev_item returned %08x\n", Status
);
4074 return STATUS_SUCCESS
;
4077 static void remove_from_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
) {
4081 le
= Vcb
->sys_chunks
.Flink
;
4082 while (le
!= &Vcb
->sys_chunks
) {
4083 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
4085 if (sc2
->key
.obj_id
== obj_id
&& sc2
->key
.obj_type
== obj_type
&& sc2
->key
.offset
== offset
) {
4086 RemoveEntryList(&sc2
->list_entry
);
4088 Vcb
->superblock
.n
-= sizeof(KEY
) + sc2
->size
;
4090 ExFreePool(sc2
->data
);
4092 regen_bootstrap(Vcb
);
4100 static NTSTATUS
set_xattr(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, root
* subvol
, UINT64 inode
, char* name
, UINT16 namelen
,
4101 UINT32 crc32
, UINT8
* data
, UINT16 datalen
) {
4106 TRACE("(%p, %llx, %llx, %.*s, %08x, %p, %u)\n", Vcb
, subvol
->id
, inode
, namelen
, name
, crc32
, data
, datalen
);
4108 xasize
= (UINT16
)offsetof(DIR_ITEM
, name
[0]) + namelen
+ datalen
;
4110 xa
= ExAllocatePoolWithTag(PagedPool
, xasize
, ALLOC_TAG
);
4112 ERR("out of memory\n");
4113 return STATUS_INSUFFICIENT_RESOURCES
;
4117 xa
->key
.obj_type
= 0;
4119 xa
->transid
= Vcb
->superblock
.generation
;
4122 xa
->type
= BTRFS_TYPE_EA
;
4123 RtlCopyMemory(xa
->name
, name
, namelen
);
4124 RtlCopyMemory(xa
->name
+ namelen
, data
, datalen
);
4126 Status
= insert_tree_item_batch(batchlist
, Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, xa
, xasize
, Batch_SetXattr
);
4127 if (!NT_SUCCESS(Status
)) {
4128 ERR("insert_tree_item_batch returned %08x\n", Status
);
4133 return STATUS_SUCCESS
;
4136 static NTSTATUS
delete_xattr(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, root
* subvol
, UINT64 inode
, char* name
,
4137 UINT16 namelen
, UINT32 crc32
) {
4142 TRACE("(%p, %llx, %llx, %.*s, %08x)\n", Vcb
, subvol
->id
, inode
, namelen
, name
, crc32
);
4144 xasize
= (UINT16
)offsetof(DIR_ITEM
, name
[0]) + namelen
;
4146 xa
= ExAllocatePoolWithTag(PagedPool
, xasize
, ALLOC_TAG
);
4148 ERR("out of memory\n");
4149 return STATUS_INSUFFICIENT_RESOURCES
;
4153 xa
->key
.obj_type
= 0;
4155 xa
->transid
= Vcb
->superblock
.generation
;
4158 xa
->type
= BTRFS_TYPE_EA
;
4159 RtlCopyMemory(xa
->name
, name
, namelen
);
4161 Status
= insert_tree_item_batch(batchlist
, Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, xa
, xasize
, Batch_DeleteXattr
);
4162 if (!NT_SUCCESS(Status
)) {
4163 ERR("insert_tree_item_batch returned %08x\n", Status
);
4168 return STATUS_SUCCESS
;
4171 static NTSTATUS
insert_sparse_extent(fcb
* fcb
, LIST_ENTRY
* batchlist
, UINT64 start
, UINT64 length
) {
4176 TRACE("((%llx, %llx), %llx, %llx)\n", fcb
->subvol
->id
, fcb
->inode
, start
, length
);
4178 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4180 ERR("out of memory\n");
4181 return STATUS_INSUFFICIENT_RESOURCES
;
4184 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
4185 ed
->decoded_size
= length
;
4186 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4187 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4188 ed
->encoding
= BTRFS_ENCODING_NONE
;
4189 ed
->type
= EXTENT_TYPE_REGULAR
;
4191 ed2
= (EXTENT_DATA2
*)ed
->data
;
4195 ed2
->num_bytes
= length
;
4197 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), Batch_Insert
);
4198 if (!NT_SUCCESS(Status
)) {
4199 ERR("insert_tree_item_batch returned %08x\n", Status
);
4204 return STATUS_SUCCESS
;
4208 #pragma warning(push)
4209 #pragma warning(suppress: 28194)
4211 NTSTATUS
insert_tree_item_batch(LIST_ENTRY
* batchlist
, device_extension
* Vcb
, root
* r
, UINT64 objid
, UINT8 objtype
, UINT64 offset
,
4212 _In_opt_
_When_(return >= 0, __drv_aliasesMem
) void* data
, UINT16 datalen
, enum batch_operation operation
) {
4214 batch_root
* br
= NULL
;
4217 le
= batchlist
->Flink
;
4218 while (le
!= batchlist
) {
4219 batch_root
* br2
= CONTAINING_RECORD(le
, batch_root
, list_entry
);
4230 br
= ExAllocatePoolWithTag(PagedPool
, sizeof(batch_root
), ALLOC_TAG
);
4232 ERR("out of memory\n");
4233 return STATUS_INSUFFICIENT_RESOURCES
;
4237 InitializeListHead(&br
->items
);
4238 InsertTailList(batchlist
, &br
->list_entry
);
4241 bi
= ExAllocateFromPagedLookasideList(&Vcb
->batch_item_lookaside
);
4243 ERR("out of memory\n");
4244 return STATUS_INSUFFICIENT_RESOURCES
;
4247 bi
->key
.obj_id
= objid
;
4248 bi
->key
.obj_type
= objtype
;
4249 bi
->key
.offset
= offset
;
4251 bi
->datalen
= datalen
;
4252 bi
->operation
= operation
;
4254 le
= br
->items
.Blink
;
4255 while (le
!= &br
->items
) {
4256 batch_item
* bi2
= CONTAINING_RECORD(le
, batch_item
, list_entry
);
4258 if (keycmp(bi2
->key
, bi
->key
) != 1) {
4259 InsertHeadList(&bi2
->list_entry
, &bi
->list_entry
);
4260 return STATUS_SUCCESS
;
4266 InsertHeadList(&br
->items
, &bi
->list_entry
);
4268 return STATUS_SUCCESS
;
4271 #pragma warning(pop)
4282 LIST_ENTRY list_entry
;
4285 static void rationalize_extents(fcb
* fcb
, PIRP Irp
) {
4287 LIST_ENTRY extent_ranges
;
4289 BOOL changed
= FALSE
, truncating
= FALSE
;
4290 UINT32 num_extents
= 0;
4292 InitializeListHead(&extent_ranges
);
4294 le
= fcb
->extents
.Flink
;
4295 while (le
!= &fcb
->extents
) {
4296 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4298 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4299 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4301 if (ed2
->size
!= 0) {
4304 le2
= extent_ranges
.Flink
;
4305 while (le2
!= &extent_ranges
) {
4306 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4308 if (er2
->address
== ed2
->address
) {
4309 er2
->skip_start
= min(er2
->skip_start
, ed2
->offset
);
4310 er2
->skip_end
= min(er2
->skip_end
, ed2
->size
- ed2
->offset
- ed2
->num_bytes
);
4312 } else if (er2
->address
> ed2
->address
)
4318 er
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_range
), ALLOC_TAG
); // FIXME - should be from lookaside?
4320 ERR("out of memory\n");
4324 er
->address
= ed2
->address
;
4325 er
->length
= ed2
->size
;
4326 er
->offset
= ext
->offset
- ed2
->offset
;
4327 er
->changed
= FALSE
;
4329 er
->skip_start
= ed2
->offset
;
4330 er
->skip_end
= ed2
->size
- ed2
->offset
- ed2
->num_bytes
;
4332 if (er
->skip_start
!= 0 || er
->skip_end
!= 0)
4335 InsertHeadList(le2
->Blink
, &er
->list_entry
);
4344 if (num_extents
== 0 || (num_extents
== 1 && !truncating
))
4347 le
= extent_ranges
.Flink
;
4348 while (le
!= &extent_ranges
) {
4349 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4354 er
->chunk
= get_chunk_from_address(fcb
->Vcb
, er
->address
);
4357 ERR("get_chunk_from_address(%llx) failed\n", er
->address
);
4362 while (le2
!= &extent_ranges
) {
4363 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4365 if (!er2
->chunk
&& er2
->address
>= er
->chunk
->offset
&& er2
->address
< er
->chunk
->offset
+ er
->chunk
->chunk_item
->size
)
4366 er2
->chunk
= er
->chunk
;
4376 // truncate beginning or end of extent if unused
4378 le
= extent_ranges
.Flink
;
4379 while (le
!= &extent_ranges
) {
4380 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4382 if (er
->skip_start
> 0) {
4383 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
4384 while (le2
!= &fcb
->extents
) {
4385 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
4387 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4388 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4390 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
4393 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4394 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4395 if (!NT_SUCCESS(Status
)) {
4396 ERR("update_changed_extent_ref returned %08x\n", Status
);
4400 ext
->extent_data
.decoded_size
-= er
->skip_start
;
4401 ed2
->size
-= er
->skip_start
;
4402 ed2
->address
+= er
->skip_start
;
4403 ed2
->offset
-= er
->skip_start
;
4405 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4406 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4413 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
))
4414 add_checksum_entry(fcb
->Vcb
, er
->address
, (ULONG
)(er
->skip_start
/ fcb
->Vcb
->superblock
.sector_size
), NULL
, NULL
);
4416 ExAcquireResourceExclusiveLite(&er
->chunk
->lock
, TRUE
);
4418 if (!er
->chunk
->cache_loaded
) {
4419 NTSTATUS Status
= load_cache_chunk(fcb
->Vcb
, er
->chunk
, NULL
);
4421 if (!NT_SUCCESS(Status
)) {
4422 ERR("load_cache_chunk returned %08x\n", Status
);
4423 ExReleaseResourceLite(&er
->chunk
->lock
);
4428 er
->chunk
->used
-= er
->skip_start
;
4430 space_list_add(er
->chunk
, er
->address
, er
->skip_start
, NULL
);
4432 ExReleaseResourceLite(&er
->chunk
->lock
);
4434 er
->address
+= er
->skip_start
;
4435 er
->length
-= er
->skip_start
;
4438 if (er
->skip_end
> 0) {
4439 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
4440 while (le2
!= &fcb
->extents
) {
4441 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
4443 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4444 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4446 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
4449 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4450 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4451 if (!NT_SUCCESS(Status
)) {
4452 ERR("update_changed_extent_ref returned %08x\n", Status
);
4456 ext
->extent_data
.decoded_size
-= er
->skip_end
;
4457 ed2
->size
-= er
->skip_end
;
4459 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4460 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4467 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
))
4468 add_checksum_entry(fcb
->Vcb
, er
->address
+ er
->length
- er
->skip_end
, (ULONG
)(er
->skip_end
/ fcb
->Vcb
->superblock
.sector_size
), NULL
, NULL
);
4470 ExAcquireResourceExclusiveLite(&er
->chunk
->lock
, TRUE
);
4472 if (!er
->chunk
->cache_loaded
) {
4473 NTSTATUS Status
= load_cache_chunk(fcb
->Vcb
, er
->chunk
, NULL
);
4475 if (!NT_SUCCESS(Status
)) {
4476 ERR("load_cache_chunk returned %08x\n", Status
);
4477 ExReleaseResourceLite(&er
->chunk
->lock
);
4482 er
->chunk
->used
-= er
->skip_end
;
4484 space_list_add(er
->chunk
, er
->address
+ er
->length
- er
->skip_end
, er
->skip_end
, NULL
);
4486 ExReleaseResourceLite(&er
->chunk
->lock
);
4488 er
->length
-= er
->skip_end
;
4495 if (num_extents
< 2)
4498 // merge together adjacent extents
4499 le
= extent_ranges
.Flink
;
4500 while (le
!= &extent_ranges
) {
4501 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4503 if (le
->Flink
!= &extent_ranges
&& er
->length
< MAX_EXTENT_SIZE
) {
4504 extent_range
* er2
= CONTAINING_RECORD(le
->Flink
, extent_range
, list_entry
);
4506 if (er
->chunk
== er2
->chunk
) {
4507 if (er2
->address
== er
->address
+ er
->length
&& er2
->offset
>= er
->offset
+ er
->length
) {
4508 if (er
->length
+ er2
->length
<= MAX_EXTENT_SIZE
) {
4509 er
->length
+= er2
->length
;
4512 RemoveEntryList(&er2
->list_entry
);
4528 le
= fcb
->extents
.Flink
;
4529 while (le
!= &fcb
->extents
) {
4530 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4532 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4533 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4535 if (ed2
->size
!= 0) {
4538 le2
= extent_ranges
.Flink
;
4539 while (le2
!= &extent_ranges
) {
4540 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4542 if (ed2
->address
>= er2
->address
&& ed2
->address
+ ed2
->size
<= er2
->address
+ er2
->length
&& er2
->changed
) {
4545 Status
= update_changed_extent_ref(fcb
->Vcb
, er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4546 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4547 if (!NT_SUCCESS(Status
)) {
4548 ERR("update_changed_extent_ref returned %08x\n", Status
);
4552 ed2
->offset
+= ed2
->address
- er2
->address
;
4553 ed2
->address
= er2
->address
;
4554 ed2
->size
= er2
->length
;
4555 ext
->extent_data
.decoded_size
= ed2
->size
;
4557 add_changed_extent_ref(er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4558 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4572 while (!IsListEmpty(&extent_ranges
)) {
4573 le
= RemoveHeadList(&extent_ranges
);
4574 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4580 NTSTATUS
flush_fcb(fcb
* fcb
, BOOL cache
, LIST_ENTRY
* batchlist
, PIRP Irp
) {
4586 #ifdef DEBUG_PARANOID
4587 UINT64 old_size
= 0;
4588 BOOL extents_changed
;
4593 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
, fcb
->adshash
);
4594 if (!NT_SUCCESS(Status
)) {
4595 ERR("delete_xattr returned %08x\n", Status
);
4599 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
,
4600 fcb
->adshash
, (UINT8
*)fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
4601 if (!NT_SUCCESS(Status
)) {
4602 ERR("set_xattr returned %08x\n", Status
);
4607 Status
= STATUS_SUCCESS
;
4612 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0xffffffffffffffff, NULL
, 0, Batch_DeleteInode
);
4613 if (!NT_SUCCESS(Status
)) {
4614 ERR("insert_tree_item_batch returned %08x\n", Status
);
4618 Status
= STATUS_SUCCESS
;
4622 #ifdef DEBUG_PARANOID
4623 extents_changed
= fcb
->extents_changed
;
4626 if (fcb
->extents_changed
) {
4628 BOOL prealloc
= FALSE
, extents_inline
= FALSE
;
4631 // delete ignored extent items
4632 le
= fcb
->extents
.Flink
;
4633 while (le
!= &fcb
->extents
) {
4634 LIST_ENTRY
* le2
= le
->Flink
;
4635 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4638 RemoveEntryList(&ext
->list_entry
);
4641 ExFreePool(ext
->csum
);
4649 le
= fcb
->extents
.Flink
;
4650 while (le
!= &fcb
->extents
) {
4651 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4653 if (ext
->inserted
&& ext
->csum
&& ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
4654 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4656 if (ed2
->size
> 0) { // not sparse
4657 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
)
4658 add_checksum_entry(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (ULONG
)(ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
), ext
->csum
, Irp
);
4660 add_checksum_entry(fcb
->Vcb
, ed2
->address
, (ULONG
)(ed2
->size
/ fcb
->Vcb
->superblock
.sector_size
), ext
->csum
, Irp
);
4667 if (!IsListEmpty(&fcb
->extents
)) {
4668 rationalize_extents(fcb
, Irp
);
4670 // merge together adjacent EXTENT_DATAs pointing to same extent
4672 le
= fcb
->extents
.Flink
;
4673 while (le
!= &fcb
->extents
) {
4674 LIST_ENTRY
* le2
= le
->Flink
;
4675 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4677 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && le
->Flink
!= &fcb
->extents
) {
4678 extent
* nextext
= CONTAINING_RECORD(le
->Flink
, extent
, list_entry
);
4680 if (ext
->extent_data
.type
== nextext
->extent_data
.type
) {
4681 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4682 EXTENT_DATA2
* ned2
= (EXTENT_DATA2
*)nextext
->extent_data
.data
;
4684 if (ed2
->size
!= 0 && ed2
->address
== ned2
->address
&& ed2
->size
== ned2
->size
&&
4685 nextext
->offset
== ext
->offset
+ ed2
->num_bytes
&& ned2
->offset
== ed2
->offset
+ ed2
->num_bytes
) {
4688 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->csum
) {
4689 ULONG len
= (ULONG
)((ed2
->num_bytes
+ ned2
->num_bytes
) / fcb
->Vcb
->superblock
.sector_size
);
4692 csum
= ExAllocatePoolWithTag(NonPagedPool
, len
* sizeof(UINT32
), ALLOC_TAG
);
4694 ERR("out of memory\n");
4695 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4699 RtlCopyMemory(csum
, ext
->csum
, (ULONG
)(ed2
->num_bytes
* sizeof(UINT32
) / fcb
->Vcb
->superblock
.sector_size
));
4700 RtlCopyMemory(&csum
[ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
], nextext
->csum
,
4701 (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / fcb
->Vcb
->superblock
.sector_size
));
4703 ExFreePool(ext
->csum
);
4707 ext
->extent_data
.generation
= fcb
->Vcb
->superblock
.generation
;
4708 ed2
->num_bytes
+= ned2
->num_bytes
;
4710 RemoveEntryList(&nextext
->list_entry
);
4713 ExFreePool(nextext
->csum
);
4715 ExFreePool(nextext
);
4717 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
4720 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
4722 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
4723 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
4724 if (!NT_SUCCESS(Status
)) {
4725 ERR("update_changed_extent_ref returned %08x\n", Status
);
4739 if (!fcb
->created
) {
4740 // delete existing EXTENT_DATA items
4742 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, NULL
, 0, Batch_DeleteExtentData
);
4743 if (!NT_SUCCESS(Status
)) {
4744 ERR("insert_tree_item_batch returned %08x\n", Status
);
4749 // add new EXTENT_DATAs
4753 le
= fcb
->extents
.Flink
;
4754 while (le
!= &fcb
->extents
) {
4755 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4758 ext
->inserted
= FALSE
;
4760 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && ext
->offset
> last_end
) {
4761 Status
= insert_sparse_extent(fcb
, batchlist
, last_end
, ext
->offset
- last_end
);
4762 if (!NT_SUCCESS(Status
)) {
4763 ERR("insert_sparse_extent returned %08x\n", Status
);
4768 ed
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
4770 ERR("out of memory\n");
4771 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4775 RtlCopyMemory(ed
, &ext
->extent_data
, ext
->datalen
);
4777 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, ext
->offset
,
4778 ed
, ext
->datalen
, Batch_Insert
);
4779 if (!NT_SUCCESS(Status
)) {
4780 ERR("insert_tree_item_batch returned %08x\n", Status
);
4784 if (ed
->type
== EXTENT_TYPE_PREALLOC
)
4787 if (ed
->type
== EXTENT_TYPE_INLINE
)
4788 extents_inline
= TRUE
;
4790 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
)) {
4791 if (ed
->type
== EXTENT_TYPE_INLINE
)
4792 last_end
= ext
->offset
+ ed
->decoded_size
;
4794 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4796 last_end
= ext
->offset
+ ed2
->num_bytes
;
4803 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && !extents_inline
&&
4804 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
) {
4805 Status
= insert_sparse_extent(fcb
, batchlist
, last_end
, sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
);
4806 if (!NT_SUCCESS(Status
)) {
4807 ERR("insert_sparse_extent returned %08x\n", Status
);
4812 // update prealloc flag in INODE_ITEM
4815 fcb
->inode_item
.flags
&= ~BTRFS_INODE_PREALLOC
;
4817 fcb
->inode_item
.flags
|= BTRFS_INODE_PREALLOC
;
4819 fcb
->inode_item_changed
= TRUE
;
4821 fcb
->extents_changed
= FALSE
;
4824 if ((!fcb
->created
&& fcb
->inode_item_changed
) || cache
) {
4825 searchkey
.obj_id
= fcb
->inode
;
4826 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4827 searchkey
.offset
= 0xffffffffffffffff;
4829 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4830 if (!NT_SUCCESS(Status
)) {
4831 ERR("error - find_item returned %08x\n", Status
);
4835 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4837 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4839 ERR("out of memory\n");
4840 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4844 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4846 Status
= insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
4847 if (!NT_SUCCESS(Status
)) {
4848 ERR("insert_tree_item returned %08x\n", Status
);
4854 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4855 Status
= STATUS_INTERNAL_ERROR
;
4859 #ifdef DEBUG_PARANOID
4860 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp
.item
->data
;
4862 old_size
= ii2
->st_size
;
4865 ii_offset
= tp
.item
->key
.offset
;
4869 Status
= delete_tree_item(fcb
->Vcb
, &tp
);
4870 if (!NT_SUCCESS(Status
)) {
4871 ERR("delete_tree_item returned %08x\n", Status
);
4875 searchkey
.obj_id
= fcb
->inode
;
4876 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4877 searchkey
.offset
= ii_offset
;
4879 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4880 if (!NT_SUCCESS(Status
)) {
4881 ERR("error - find_item returned %08x\n", Status
);
4885 if (keycmp(tp
.item
->key
, searchkey
)) {
4886 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4887 Status
= STATUS_INTERNAL_ERROR
;
4890 RtlCopyMemory(tp
.item
->data
, &fcb
->inode_item
, min(tp
.item
->size
, sizeof(INODE_ITEM
)));
4893 #ifdef DEBUG_PARANOID
4894 if (!extents_changed
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& old_size
!= fcb
->inode_item
.st_size
) {
4895 ERR("error - size has changed but extents not marked as changed\n");
4902 fcb
->created
= FALSE
;
4904 if (!cache
&& fcb
->inode_item_changed
) {
4905 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4907 ERR("out of memory\n");
4908 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4912 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4914 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, ii_offset
, ii
, sizeof(INODE_ITEM
),
4916 if (!NT_SUCCESS(Status
)) {
4917 ERR("insert_tree_item_batch returned %08x\n", Status
);
4921 fcb
->inode_item_changed
= FALSE
;
4924 if (fcb
->sd_dirty
) {
4925 if (!fcb
->sd_deleted
) {
4926 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, (UINT16
)strlen(EA_NTACL
),
4927 EA_NTACL_HASH
, (UINT8
*)fcb
->sd
, (UINT16
)RtlLengthSecurityDescriptor(fcb
->sd
));
4928 if (!NT_SUCCESS(Status
)) {
4929 ERR("set_xattr returned %08x\n", Status
);
4933 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, (UINT16
)strlen(EA_NTACL
), EA_NTACL_HASH
);
4934 if (!NT_SUCCESS(Status
)) {
4935 ERR("delete_xattr returned %08x\n", Status
);
4940 fcb
->sd_deleted
= FALSE
;
4941 fcb
->sd_dirty
= FALSE
;
4944 if (fcb
->atts_changed
) {
4945 if (!fcb
->atts_deleted
) {
4946 UINT8 val
[16], *val2
;
4947 ULONG atts
= fcb
->atts
;
4949 TRACE("inserting new DOSATTRIB xattr\n");
4951 if (fcb
->inode
== SUBVOL_ROOT_INODE
)
4952 atts
&= ~FILE_ATTRIBUTE_READONLY
;
4954 val2
= &val
[sizeof(val
) - 1];
4957 UINT8 c
= atts
% 16;
4958 *val2
= c
<= 9 ? (c
+ '0') : (c
- 0xa + 'a');
4962 } while (atts
!= 0);
4968 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, (UINT16
)strlen(EA_DOSATTRIB
),
4969 EA_DOSATTRIB_HASH
, val2
, (UINT16
)(val
+ sizeof(val
) - val2
));
4970 if (!NT_SUCCESS(Status
)) {
4971 ERR("set_xattr returned %08x\n", Status
);
4975 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, (UINT16
)strlen(EA_DOSATTRIB
), EA_DOSATTRIB_HASH
);
4976 if (!NT_SUCCESS(Status
)) {
4977 ERR("delete_xattr returned %08x\n", Status
);
4982 fcb
->atts_changed
= FALSE
;
4983 fcb
->atts_deleted
= FALSE
;
4986 if (fcb
->reparse_xattr_changed
) {
4987 if (fcb
->reparse_xattr
.Buffer
&& fcb
->reparse_xattr
.Length
> 0) {
4988 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, (UINT16
)strlen(EA_REPARSE
),
4989 EA_REPARSE_HASH
, (UINT8
*)fcb
->reparse_xattr
.Buffer
, (UINT16
)fcb
->reparse_xattr
.Length
);
4990 if (!NT_SUCCESS(Status
)) {
4991 ERR("set_xattr returned %08x\n", Status
);
4995 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, (UINT16
)strlen(EA_REPARSE
), EA_REPARSE_HASH
);
4996 if (!NT_SUCCESS(Status
)) {
4997 ERR("delete_xattr returned %08x\n", Status
);
5002 fcb
->reparse_xattr_changed
= FALSE
;
5005 if (fcb
->ea_changed
) {
5006 if (fcb
->ea_xattr
.Buffer
&& fcb
->ea_xattr
.Length
> 0) {
5007 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_EA
, (UINT16
)strlen(EA_EA
),
5008 EA_EA_HASH
, (UINT8
*)fcb
->ea_xattr
.Buffer
, (UINT16
)fcb
->ea_xattr
.Length
);
5009 if (!NT_SUCCESS(Status
)) {
5010 ERR("set_xattr returned %08x\n", Status
);
5014 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_EA
, (UINT16
)strlen(EA_EA
), EA_EA_HASH
);
5015 if (!NT_SUCCESS(Status
)) {
5016 ERR("delete_xattr returned %08x\n", Status
);
5021 fcb
->ea_changed
= FALSE
;
5024 if (fcb
->prop_compression_changed
) {
5025 if (fcb
->prop_compression
== PropCompression_None
) {
5026 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
), EA_PROP_COMPRESSION_HASH
);
5027 if (!NT_SUCCESS(Status
)) {
5028 ERR("delete_xattr returned %08x\n", Status
);
5031 } else if (fcb
->prop_compression
== PropCompression_Zlib
) {
5032 const char zlib
[] = "zlib";
5034 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
),
5035 EA_PROP_COMPRESSION_HASH
, (UINT8
*)zlib
, (UINT16
)strlen(zlib
));
5036 if (!NT_SUCCESS(Status
)) {
5037 ERR("set_xattr returned %08x\n", Status
);
5040 } else if (fcb
->prop_compression
== PropCompression_LZO
) {
5041 const char lzo
[] = "lzo";
5043 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
),
5044 EA_PROP_COMPRESSION_HASH
, (UINT8
*)lzo
, (UINT16
)strlen(lzo
));
5045 if (!NT_SUCCESS(Status
)) {
5046 ERR("set_xattr returned %08x\n", Status
);
5051 fcb
->prop_compression_changed
= FALSE
;
5054 if (fcb
->xattrs_changed
) {
5057 le
= fcb
->xattrs
.Flink
;
5058 while (le
!= &fcb
->xattrs
) {
5059 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
5060 LIST_ENTRY
* le2
= le
->Flink
;
5063 UINT32 hash
= calc_crc32c(0xfffffffe, (UINT8
*)xa
->data
, xa
->namelen
);
5065 if (xa
->valuelen
== 0) {
5066 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, xa
->data
, xa
->namelen
, hash
);
5067 if (!NT_SUCCESS(Status
)) {
5068 ERR("delete_xattr returned %08x\n", Status
);
5072 RemoveEntryList(&xa
->list_entry
);
5075 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, xa
->data
, xa
->namelen
,
5076 hash
, (UINT8
*)&xa
->data
[xa
->namelen
], xa
->valuelen
);
5077 if (!NT_SUCCESS(Status
)) {
5078 ERR("set_xattr returned %08x\n", Status
);
5089 fcb
->xattrs_changed
= FALSE
;
5092 Status
= STATUS_SUCCESS
;
5100 if (!ExIsResourceAcquiredExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
)) {
5101 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, TRUE
);
5105 RemoveEntryList(&fcb
->list_entry_dirty
);
5108 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
5114 void add_trim_entry_avoid_sb(device_extension
* Vcb
, device
* dev
, UINT64 address
, UINT64 size
) {
5116 ULONG sblen
= (ULONG
)sector_align(sizeof(superblock
), Vcb
->superblock
.sector_size
);
5119 while (superblock_addrs
[i
] != 0) {
5120 if (superblock_addrs
[i
] + sblen
>= address
&& superblock_addrs
[i
] < address
+ size
) {
5121 if (superblock_addrs
[i
] > address
)
5122 add_trim_entry(dev
, address
, superblock_addrs
[i
] - address
);
5124 if (size
<= superblock_addrs
[i
] + sblen
- address
)
5127 size
-= superblock_addrs
[i
] + sblen
- address
;
5128 address
= superblock_addrs
[i
] + sblen
;
5129 } else if (superblock_addrs
[i
] > address
+ size
)
5135 add_trim_entry(dev
, address
, size
);
5138 static NTSTATUS
drop_chunk(device_extension
* Vcb
, chunk
* c
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5143 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];;
5145 TRACE("dropping chunk %llx\n", c
->offset
);
5147 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
5148 factor
= c
->chunk_item
->num_stripes
;
5149 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
5150 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
5151 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
5152 factor
= c
->chunk_item
->num_stripes
- 1;
5153 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
5154 factor
= c
->chunk_item
->num_stripes
- 2;
5155 else // SINGLE, DUPLICATE, RAID1
5159 if (Vcb
->trim
&& !Vcb
->options
.no_trim
) {
5160 UINT64 len
= c
->chunk_item
->size
/ factor
;
5162 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5163 if (c
->devices
[i
] && c
->devices
[i
]->devobj
&& !c
->devices
[i
]->readonly
&& c
->devices
[i
]->trim
)
5164 add_trim_entry_avoid_sb(Vcb
, c
->devices
[i
], cis
[i
].offset
, len
);
5169 Status
= load_stored_free_space_cache(Vcb
, c
, TRUE
, Irp
);
5171 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
5172 WARN("load_stored_free_space_cache returned %08x\n", Status
);
5175 // remove free space cache
5177 c
->cache
->deleted
= TRUE
;
5179 Status
= excise_extents(Vcb
, c
->cache
, 0, c
->cache
->inode_item
.st_size
, Irp
, rollback
);
5180 if (!NT_SUCCESS(Status
)) {
5181 ERR("excise_extents returned %08x\n", Status
);
5185 Status
= flush_fcb(c
->cache
, TRUE
, batchlist
, Irp
);
5187 free_fcb(Vcb
, c
->cache
);
5189 if (!NT_SUCCESS(Status
)) {
5190 ERR("flush_fcb returned %08x\n", Status
);
5194 searchkey
.obj_id
= FREE_SPACE_CACHE_ID
;
5195 searchkey
.obj_type
= 0;
5196 searchkey
.offset
= c
->offset
;
5198 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5199 if (!NT_SUCCESS(Status
)) {
5200 ERR("error - find_item returned %08x\n", Status
);
5204 if (!keycmp(tp
.item
->key
, searchkey
)) {
5205 Status
= delete_tree_item(Vcb
, &tp
);
5206 if (!NT_SUCCESS(Status
)) {
5207 ERR("delete_tree_item returned %08x\n", Status
);
5213 if (Vcb
->space_root
) {
5214 Status
= insert_tree_item_batch(batchlist
, Vcb
, Vcb
->space_root
, c
->offset
, TYPE_FREE_SPACE_INFO
, c
->chunk_item
->size
,
5215 NULL
, 0, Batch_DeleteFreeSpace
);
5216 if (!NT_SUCCESS(Status
)) {
5217 ERR("insert_tree_item_batch returned %08x\n", Status
);
5222 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5224 // remove DEV_EXTENTs from tree 4
5225 searchkey
.obj_id
= cis
[i
].dev_id
;
5226 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
5227 searchkey
.offset
= cis
[i
].offset
;
5229 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
5230 if (!NT_SUCCESS(Status
)) {
5231 ERR("error - find_item returned %08x\n", Status
);
5235 if (!keycmp(tp
.item
->key
, searchkey
)) {
5236 Status
= delete_tree_item(Vcb
, &tp
);
5237 if (!NT_SUCCESS(Status
)) {
5238 ERR("delete_tree_item returned %08x\n", Status
);
5242 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
5243 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
5245 c
->devices
[i
]->devitem
.bytes_used
-= de
->length
;
5247 if (Vcb
->balance
.thread
&& Vcb
->balance
.shrinking
&& Vcb
->balance
.opts
[0].devid
== c
->devices
[i
]->devitem
.dev_id
) {
5248 if (cis
[i
].offset
< Vcb
->balance
.opts
[0].drange_start
&& cis
[i
].offset
+ de
->length
> Vcb
->balance
.opts
[0].drange_start
)
5249 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, Vcb
->balance
.opts
[0].drange_start
- cis
[i
].offset
, NULL
, rollback
);
5251 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, de
->length
, NULL
, rollback
);
5254 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
5256 UINT64 len
= c
->chunk_item
->size
/ factor
;
5258 c
->devices
[i
]->devitem
.bytes_used
-= len
;
5260 if (Vcb
->balance
.thread
&& Vcb
->balance
.shrinking
&& Vcb
->balance
.opts
[0].devid
== c
->devices
[i
]->devitem
.dev_id
) {
5261 if (cis
[i
].offset
< Vcb
->balance
.opts
[0].drange_start
&& cis
[i
].offset
+ len
> Vcb
->balance
.opts
[0].drange_start
)
5262 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, Vcb
->balance
.opts
[0].drange_start
- cis
[i
].offset
, NULL
, rollback
);
5264 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, len
, NULL
, rollback
);
5268 // modify DEV_ITEMs in chunk tree
5269 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5270 if (c
->devices
[i
]) {
5274 searchkey
.obj_id
= 1;
5275 searchkey
.obj_type
= TYPE_DEV_ITEM
;
5276 searchkey
.offset
= c
->devices
[i
]->devitem
.dev_id
;
5278 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
5279 if (!NT_SUCCESS(Status
)) {
5280 ERR("error - find_item returned %08x\n", Status
);
5284 if (keycmp(tp
.item
->key
, searchkey
)) {
5285 ERR("error - could not find DEV_ITEM for device %llx\n", searchkey
.offset
);
5286 return STATUS_INTERNAL_ERROR
;
5289 Status
= delete_tree_item(Vcb
, &tp
);
5290 if (!NT_SUCCESS(Status
)) {
5291 ERR("delete_tree_item returned %08x\n", Status
);
5295 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
5297 ERR("out of memory\n");
5298 return STATUS_INSUFFICIENT_RESOURCES
;
5301 RtlCopyMemory(di
, &c
->devices
[i
]->devitem
, sizeof(DEV_ITEM
));
5303 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, c
->devices
[i
]->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
5304 if (!NT_SUCCESS(Status
)) {
5305 ERR("insert_tree_item returned %08x\n", Status
);
5309 for (j
= i
+ 1; j
< c
->chunk_item
->num_stripes
; j
++) {
5310 if (c
->devices
[j
] == c
->devices
[i
])
5311 c
->devices
[j
] = NULL
;
5317 // remove CHUNK_ITEM from chunk tree
5318 searchkey
.obj_id
= 0x100;
5319 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
5320 searchkey
.offset
= c
->offset
;
5322 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
5323 if (!NT_SUCCESS(Status
)) {
5324 ERR("error - find_item returned %08x\n", Status
);
5328 if (!keycmp(tp
.item
->key
, searchkey
)) {
5329 Status
= delete_tree_item(Vcb
, &tp
);
5331 if (!NT_SUCCESS(Status
)) {
5332 ERR("delete_tree_item returned %08x\n", Status
);
5336 WARN("could not find CHUNK_ITEM for chunk %llx\n", c
->offset
);
5338 // remove BLOCK_GROUP_ITEM from extent tree
5339 searchkey
.obj_id
= c
->offset
;
5340 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
5341 searchkey
.offset
= 0xffffffffffffffff;
5343 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5344 if (!NT_SUCCESS(Status
)) {
5345 ERR("error - find_item returned %08x\n", Status
);
5349 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
5350 Status
= delete_tree_item(Vcb
, &tp
);
5352 if (!NT_SUCCESS(Status
)) {
5353 ERR("delete_tree_item returned %08x\n", Status
);
5357 WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c
->offset
);
5360 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
)
5361 remove_from_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
);
5363 RemoveEntryList(&c
->list_entry
);
5365 // clear raid56 incompat flag if dropping last RAID5/6 chunk
5367 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
5369 BOOL clear_flag
= TRUE
;
5371 le
= Vcb
->chunks
.Flink
;
5372 while (le
!= &Vcb
->chunks
) {
5373 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5375 if (c2
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c2
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
5384 Vcb
->superblock
.incompat_flags
&= ~BTRFS_INCOMPAT_FLAGS_RAID56
;
5387 Vcb
->superblock
.bytes_used
-= c
->oldused
;
5389 ExFreePool(c
->chunk_item
);
5390 ExFreePool(c
->devices
);
5392 while (!IsListEmpty(&c
->space
)) {
5393 space
* s
= CONTAINING_RECORD(c
->space
.Flink
, space
, list_entry
);
5395 RemoveEntryList(&s
->list_entry
);
5399 while (!IsListEmpty(&c
->deleting
)) {
5400 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
5402 RemoveEntryList(&s
->list_entry
);
5406 ExDeleteResourceLite(&c
->partial_stripes_lock
);
5407 ExDeleteResourceLite(&c
->range_locks_lock
);
5408 ExDeleteResourceLite(&c
->lock
);
5409 ExDeleteResourceLite(&c
->changed_extents_lock
);
5413 return STATUS_SUCCESS
;
5416 static NTSTATUS
partial_stripe_read(device_extension
* Vcb
, chunk
* c
, partial_stripe
* ps
, UINT64 startoff
, UINT16 parity
, ULONG offset
, ULONG len
) {
5418 ULONG sl
= (ULONG
)(c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
);
5419 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
5422 ULONG readlen
= min(offset
+ len
, offset
+ (sl
- (offset
% sl
))) - offset
;
5425 stripe
= (parity
+ (offset
/ sl
) + 1) % c
->chunk_item
->num_stripes
;
5427 if (c
->devices
[stripe
]->devobj
) {
5428 Status
= sync_read_phys(c
->devices
[stripe
]->devobj
, cis
[stripe
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5429 readlen
* Vcb
->superblock
.sector_size
, ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), FALSE
);
5430 if (!NT_SUCCESS(Status
)) {
5431 ERR("sync_read_phys returned %08x\n", Status
);
5434 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
5438 scratch
= ExAllocatePoolWithTag(NonPagedPool
, readlen
* Vcb
->superblock
.sector_size
, ALLOC_TAG
);
5440 ERR("out of memory\n");
5441 return STATUS_INSUFFICIENT_RESOURCES
;
5444 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5446 if (!c
->devices
[i
]->devobj
) {
5447 ExFreePool(scratch
);
5448 return STATUS_UNEXPECTED_IO_ERROR
;
5451 if (i
== 0 || (stripe
== 0 && i
== 1)) {
5452 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5453 readlen
* Vcb
->superblock
.sector_size
, ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), FALSE
);
5454 if (!NT_SUCCESS(Status
)) {
5455 ERR("sync_read_phys returned %08x\n", Status
);
5456 ExFreePool(scratch
);
5460 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5461 readlen
* Vcb
->superblock
.sector_size
, scratch
, FALSE
);
5462 if (!NT_SUCCESS(Status
)) {
5463 ERR("sync_read_phys returned %08x\n", Status
);
5464 ExFreePool(scratch
);
5468 do_xor(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
, readlen
* Vcb
->superblock
.sector_size
);
5473 ExFreePool(scratch
);
5476 UINT16 k
, i
, logstripe
, error_stripe
, num_errors
= 0;
5478 scratch
= ExAllocatePoolWithTag(NonPagedPool
, (c
->chunk_item
->num_stripes
+ 2) * readlen
* Vcb
->superblock
.sector_size
, ALLOC_TAG
);
5480 ERR("out of memory\n");
5481 return STATUS_INSUFFICIENT_RESOURCES
;
5484 i
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
5485 for (k
= 0; k
< c
->chunk_item
->num_stripes
; k
++) {
5487 if (c
->devices
[i
]->devobj
) {
5488 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5489 readlen
* Vcb
->superblock
.sector_size
, scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
), FALSE
);
5490 if (!NT_SUCCESS(Status
)) {
5491 ERR("sync_read_phys returned %08x\n", Status
);
5500 if (num_errors
> 1) {
5501 ExFreePool(scratch
);
5502 return STATUS_UNEXPECTED_IO_ERROR
;
5507 i
= (i
+ 1) % c
->chunk_item
->num_stripes
;
5510 if (num_errors
== 0 || error_stripe
== c
->chunk_item
->num_stripes
- 1) {
5511 for (k
= 0; k
< c
->chunk_item
->num_stripes
- 1; k
++) {
5512 if (k
!= logstripe
) {
5513 if (k
== 0 || (k
== 1 && logstripe
== 0)) {
5514 RtlCopyMemory(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
),
5515 readlen
* Vcb
->superblock
.sector_size
);
5517 do_xor(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
),
5518 readlen
* Vcb
->superblock
.sector_size
);
5523 raid6_recover2(scratch
, c
->chunk_item
->num_stripes
, readlen
* Vcb
->superblock
.sector_size
, logstripe
,
5524 error_stripe
, scratch
+ (c
->chunk_item
->num_stripes
* readlen
* Vcb
->superblock
.sector_size
));
5526 RtlCopyMemory(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (c
->chunk_item
->num_stripes
* readlen
* Vcb
->superblock
.sector_size
),
5527 readlen
* Vcb
->superblock
.sector_size
);
5530 ExFreePool(scratch
);
5537 return STATUS_SUCCESS
;
5540 NTSTATUS
flush_partial_stripe(device_extension
* Vcb
, chunk
* c
, partial_stripe
* ps
) {
5542 UINT16 parity2
, stripe
, startoffstripe
;
5545 ULONG runlength
, index
, last1
;
5546 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
5548 UINT16 k
, num_data_stripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
5549 UINT64 ps_length
= num_data_stripes
* c
->chunk_item
->stripe_length
;
5550 ULONG stripe_length
= (ULONG
)c
->chunk_item
->stripe_length
;
5552 // FIXME - do writes asynchronously?
5554 get_raid0_offset(ps
->address
- c
->offset
, stripe_length
, num_data_stripes
, &startoff
, &startoffstripe
);
5556 parity2
= (((ps
->address
- c
->offset
) / ps_length
) + c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
5558 // read data (or reconstruct if degraded)
5560 runlength
= RtlFindFirstRunClear(&ps
->bmp
, &index
);
5563 while (runlength
!= 0) {
5564 if (index
> last1
) {
5565 Status
= partial_stripe_read(Vcb
, c
, ps
, startoff
, parity2
, last1
, index
- last1
);
5566 if (!NT_SUCCESS(Status
)) {
5567 ERR("partial_stripe_read returned %08x\n", Status
);
5572 last1
= index
+ runlength
;
5574 runlength
= RtlFindNextForwardRunClear(&ps
->bmp
, index
+ runlength
, &index
);
5577 if (last1
< ps_length
/ Vcb
->superblock
.sector_size
) {
5578 Status
= partial_stripe_read(Vcb
, c
, ps
, startoff
, parity2
, last1
, (ULONG
)((ps_length
/ Vcb
->superblock
.sector_size
) - last1
));
5579 if (!NT_SUCCESS(Status
)) {
5580 ERR("partial_stripe_read returned %08x\n", Status
);
5585 // set unallocated data to 0
5586 le
= c
->space
.Flink
;
5587 while (le
!= &c
->space
) {
5588 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
5590 if (s
->address
+ s
->size
> ps
->address
&& s
->address
< ps
->address
+ ps_length
) {
5591 UINT64 start
= max(ps
->address
, s
->address
);
5592 UINT64 end
= min(ps
->address
+ ps_length
, s
->address
+ s
->size
);
5594 RtlZeroMemory(ps
->data
+ start
- ps
->address
, (ULONG
)(end
- start
));
5595 } else if (s
->address
>= ps
->address
+ ps_length
)
5601 le
= c
->deleting
.Flink
;
5602 while (le
!= &c
->deleting
) {
5603 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
5605 if (s
->address
+ s
->size
> ps
->address
&& s
->address
< ps
->address
+ ps_length
) {
5606 UINT64 start
= max(ps
->address
, s
->address
);
5607 UINT64 end
= min(ps
->address
+ ps_length
, s
->address
+ s
->size
);
5609 RtlZeroMemory(ps
->data
+ start
- ps
->address
, (ULONG
)(end
- start
));
5610 } else if (s
->address
>= ps
->address
+ ps_length
)
5616 stripe
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
5619 for (k
= 0; k
< num_data_stripes
; k
++) {
5620 if (c
->devices
[stripe
]->devobj
) {
5621 Status
= write_data_phys(c
->devices
[stripe
]->devobj
, cis
[stripe
].offset
+ startoff
, data
, stripe_length
);
5622 if (!NT_SUCCESS(Status
)) {
5623 ERR("write_data_phys returned %08x\n", Status
);
5628 data
+= stripe_length
;
5629 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
5633 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
5634 if (c
->devices
[parity2
]->devobj
) {
5637 for (i
= 1; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
5638 do_xor(ps
->data
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5641 Status
= write_data_phys(c
->devices
[parity2
]->devobj
, cis
[parity2
].offset
+ startoff
, ps
->data
, stripe_length
);
5642 if (!NT_SUCCESS(Status
)) {
5643 ERR("write_data_phys returned %08x\n", Status
);
5648 UINT16 parity1
= (parity2
+ c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
5650 if (c
->devices
[parity1
]->devobj
|| c
->devices
[parity2
]->devobj
) {
5654 scratch
= ExAllocatePoolWithTag(NonPagedPool
, stripe_length
* 2, ALLOC_TAG
);
5656 ERR("out of memory\n");
5657 return STATUS_INSUFFICIENT_RESOURCES
;
5660 i
= c
->chunk_item
->num_stripes
- 3;
5663 if (i
== c
->chunk_item
->num_stripes
- 3) {
5664 RtlCopyMemory(scratch
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5665 RtlCopyMemory(scratch
+ stripe_length
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5667 do_xor(scratch
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5669 galois_double(scratch
+ stripe_length
, stripe_length
);
5670 do_xor(scratch
+ stripe_length
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5679 if (c
->devices
[parity1
]->devobj
) {
5680 Status
= write_data_phys(c
->devices
[parity1
]->devobj
, cis
[parity1
].offset
+ startoff
, scratch
, stripe_length
);
5681 if (!NT_SUCCESS(Status
)) {
5682 ERR("write_data_phys returned %08x\n", Status
);
5683 ExFreePool(scratch
);
5688 if (c
->devices
[parity2
]->devobj
) {
5689 Status
= write_data_phys(c
->devices
[parity2
]->devobj
, cis
[parity2
].offset
+ startoff
, scratch
+ stripe_length
, stripe_length
);
5690 if (!NT_SUCCESS(Status
)) {
5691 ERR("write_data_phys returned %08x\n", Status
);
5692 ExFreePool(scratch
);
5697 ExFreePool(scratch
);
5701 return STATUS_SUCCESS
;
5704 static NTSTATUS
update_chunks(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5705 LIST_ENTRY
*le
, *le2
;
5707 UINT64 used_minus_cache
;
5709 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
5711 // FIXME - do tree chunks before data chunks
5713 le
= Vcb
->chunks
.Flink
;
5714 while (le
!= &Vcb
->chunks
) {
5715 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5720 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
5722 // flush partial stripes
5723 if (!Vcb
->readonly
&& (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)) {
5724 ExAcquireResourceExclusiveLite(&c
->partial_stripes_lock
, TRUE
);
5726 while (!IsListEmpty(&c
->partial_stripes
)) {
5727 partial_stripe
* ps
= CONTAINING_RECORD(RemoveHeadList(&c
->partial_stripes
), partial_stripe
, list_entry
);
5729 Status
= flush_partial_stripe(Vcb
, c
, ps
);
5732 ExFreePool(ps
->bmparr
);
5736 if (!NT_SUCCESS(Status
)) {
5737 ERR("flush_partial_stripe returned %08x\n", Status
);
5738 ExReleaseResourceLite(&c
->partial_stripes_lock
);
5739 ExReleaseResourceLite(&c
->lock
);
5740 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5745 ExReleaseResourceLite(&c
->partial_stripes_lock
);
5748 if (c
->list_entry_balance
.Flink
) {
5749 ExReleaseResourceLite(&c
->lock
);
5754 if (c
->space_changed
|| c
->created
) {
5755 used_minus_cache
= c
->used
;
5757 // subtract self-hosted cache
5758 if (used_minus_cache
> 0 && c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->cache
&& c
->cache
->inode_item
.st_size
== c
->used
) {
5761 le3
= c
->cache
->extents
.Flink
;
5762 while (le3
!= &c
->cache
->extents
) {
5763 extent
* ext
= CONTAINING_RECORD(le3
, extent
, list_entry
);
5764 EXTENT_DATA
* ed
= &ext
->extent_data
;
5767 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
5768 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5770 if (ed2
->size
!= 0 && ed2
->address
>= c
->offset
&& ed2
->address
+ ed2
->size
<= c
->offset
+ c
->chunk_item
->size
)
5771 used_minus_cache
-= ed2
->size
;
5779 if (used_minus_cache
== 0) {
5780 Status
= drop_chunk(Vcb
, c
, batchlist
, Irp
, rollback
);
5781 if (!NT_SUCCESS(Status
)) {
5782 ERR("drop_chunk returned %08x\n", Status
);
5783 ExReleaseResourceLite(&c
->lock
);
5784 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5787 } else if (c
->created
) {
5788 Status
= create_chunk(Vcb
, c
, Irp
);
5789 if (!NT_SUCCESS(Status
)) {
5790 ERR("create_chunk returned %08x\n", Status
);
5791 ExReleaseResourceLite(&c
->lock
);
5792 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5797 if (used_minus_cache
> 0)
5798 ExReleaseResourceLite(&c
->lock
);
5805 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5807 return STATUS_SUCCESS
;
5810 static NTSTATUS
delete_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, UINT64 parinode
, PANSI_STRING utf8
, PIRP Irp
) {
5815 searchkey
.obj_id
= parsubvolid
;
5816 searchkey
.obj_type
= TYPE_ROOT_REF
;
5817 searchkey
.offset
= subvolid
;
5819 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5820 if (!NT_SUCCESS(Status
)) {
5821 ERR("error - find_item returned %08x\n", Status
);
5825 if (!keycmp(searchkey
, tp
.item
->key
)) {
5826 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
5827 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(ROOT_REF
));
5828 return STATUS_INTERNAL_ERROR
;
5833 rr
= (ROOT_REF
*)tp
.item
->data
;
5834 len
= tp
.item
->size
;
5839 if (len
< sizeof(ROOT_REF
) || len
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
5840 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5844 itemlen
= (UINT16
)offsetof(ROOT_REF
, name
[0]) + rr
->n
;
5846 if (rr
->dir
== parinode
&& rr
->n
== utf8
->Length
&& RtlCompareMemory(rr
->name
, utf8
->Buffer
, rr
->n
) == rr
->n
) {
5847 UINT16 newlen
= tp
.item
->size
- itemlen
;
5849 Status
= delete_tree_item(Vcb
, &tp
);
5850 if (!NT_SUCCESS(Status
)) {
5851 ERR("delete_tree_item returned %08x\n", Status
);
5856 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5858 UINT8
*newrr
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *rroff
;
5861 ERR("out of memory\n");
5862 return STATUS_INSUFFICIENT_RESOURCES
;
5865 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5867 if ((UINT8
*)rr
> tp
.item
->data
) {
5868 RtlCopyMemory(newrr
, tp
.item
->data
, (UINT8
*)rr
- tp
.item
->data
);
5869 rroff
= newrr
+ ((UINT8
*)rr
- tp
.item
->data
);
5874 if ((UINT8
*)&rr
->name
[rr
->n
] < tp
.item
->data
+ tp
.item
->size
)
5875 RtlCopyMemory(rroff
, &rr
->name
[rr
->n
], tp
.item
->size
- ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
));
5877 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newrr
, newlen
, NULL
, Irp
);
5878 if (!NT_SUCCESS(Status
)) {
5879 ERR("insert_tree_item returned %08x\n", Status
);
5888 if (len
> itemlen
) {
5890 rr
= (ROOT_REF
*)&rr
->name
[rr
->n
];
5896 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey
.offset
, searchkey
.obj_id
);
5897 return STATUS_NOT_FOUND
;
5900 return STATUS_SUCCESS
;
5904 #pragma warning(push)
5905 #pragma warning(suppress: 28194)
5907 static NTSTATUS
add_root_ref(_In_ device_extension
* Vcb
, _In_ UINT64 subvolid
, _In_ UINT64 parsubvolid
, _In_ __drv_aliasesMem ROOT_REF
* rr
, _In_opt_ PIRP Irp
) {
5912 searchkey
.obj_id
= parsubvolid
;
5913 searchkey
.obj_type
= TYPE_ROOT_REF
;
5914 searchkey
.offset
= subvolid
;
5916 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5917 if (!NT_SUCCESS(Status
)) {
5918 ERR("error - find_item returned %08x\n", Status
);
5922 if (!keycmp(searchkey
, tp
.item
->key
)) {
5923 UINT16 rrsize
= tp
.item
->size
+ (UINT16
)offsetof(ROOT_REF
, name
[0]) + rr
->n
;
5926 rr2
= ExAllocatePoolWithTag(PagedPool
, rrsize
, ALLOC_TAG
);
5928 ERR("out of memory\n");
5929 return STATUS_INSUFFICIENT_RESOURCES
;
5932 if (tp
.item
->size
> 0)
5933 RtlCopyMemory(rr2
, tp
.item
->data
, tp
.item
->size
);
5935 RtlCopyMemory(rr2
+ tp
.item
->size
, rr
, offsetof(ROOT_REF
, name
[0]) + rr
->n
);
5938 Status
= delete_tree_item(Vcb
, &tp
);
5939 if (!NT_SUCCESS(Status
)) {
5940 ERR("delete_tree_item returned %08x\n", Status
);
5945 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr2
, rrsize
, NULL
, Irp
);
5946 if (!NT_SUCCESS(Status
)) {
5947 ERR("insert_tree_item returned %08x\n", Status
);
5952 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr
, (UINT16
)offsetof(ROOT_REF
, name
[0]) + rr
->n
, NULL
, Irp
);
5953 if (!NT_SUCCESS(Status
)) {
5954 ERR("insert_tree_item returned %08x\n", Status
);
5960 return STATUS_SUCCESS
;
5963 #pragma warning(pop)
5966 static NTSTATUS
update_root_backref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, PIRP Irp
) {
5973 searchkey
.obj_id
= parsubvolid
;
5974 searchkey
.obj_type
= TYPE_ROOT_REF
;
5975 searchkey
.offset
= subvolid
;
5977 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5978 if (!NT_SUCCESS(Status
)) {
5979 ERR("error - find_item returned %08x\n", Status
);
5983 if (!keycmp(tp
.item
->key
, searchkey
) && tp
.item
->size
> 0) {
5984 datalen
= tp
.item
->size
;
5986 data
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
5988 ERR("out of memory\n");
5989 return STATUS_INSUFFICIENT_RESOURCES
;
5992 RtlCopyMemory(data
, tp
.item
->data
, datalen
);
5998 searchkey
.obj_id
= subvolid
;
5999 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
6000 searchkey
.offset
= parsubvolid
;
6002 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6003 if (!NT_SUCCESS(Status
)) {
6004 ERR("error - find_item returned %08x\n", Status
);
6012 if (!keycmp(tp
.item
->key
, searchkey
)) {
6013 Status
= delete_tree_item(Vcb
, &tp
);
6014 if (!NT_SUCCESS(Status
)) {
6015 ERR("delete_tree_item returned %08x\n", Status
);
6025 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, subvolid
, TYPE_ROOT_BACKREF
, parsubvolid
, data
, datalen
, NULL
, Irp
);
6026 if (!NT_SUCCESS(Status
)) {
6027 ERR("insert_tree_item returned %08x\n", Status
);
6033 return STATUS_SUCCESS
;
6036 static NTSTATUS
add_root_item_to_cache(device_extension
* Vcb
, UINT64 root
, PIRP Irp
) {
6041 searchkey
.obj_id
= root
;
6042 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
6043 searchkey
.offset
= 0xffffffffffffffff;
6045 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6046 if (!NT_SUCCESS(Status
)) {
6047 ERR("error - find_item returned %08x\n", Status
);
6051 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6052 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
6053 return STATUS_INTERNAL_ERROR
;
6056 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
6057 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
6059 ERR("out of memory\n");
6060 return STATUS_INSUFFICIENT_RESOURCES
;
6063 if (tp
.item
->size
> 0)
6064 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
6066 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
6068 Status
= delete_tree_item(Vcb
, &tp
);
6069 if (!NT_SUCCESS(Status
)) {
6070 ERR("delete_tree_item returned %08x\n", Status
);
6075 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
);
6076 if (!NT_SUCCESS(Status
)) {
6077 ERR("insert_tree_item returned %08x\n", Status
);
6082 tp
.tree
->write
= TRUE
;
6085 return STATUS_SUCCESS
;
6088 static NTSTATUS
flush_fileref(file_ref
* fileref
, LIST_ENTRY
* batchlist
, PIRP Irp
) {
6091 // if fileref created and then immediately deleted, do nothing
6092 if (fileref
->created
&& fileref
->deleted
) {
6093 fileref
->dirty
= FALSE
;
6094 return STATUS_SUCCESS
;
6097 if (fileref
->fcb
->ads
) {
6098 fileref
->dirty
= FALSE
;
6099 return STATUS_SUCCESS
;
6102 if (fileref
->created
) {
6107 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6109 disize
= (UINT16
)(offsetof(DIR_ITEM
, name
[0]) + fileref
->dc
->utf8
.Length
);
6110 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6112 ERR("out of memory\n");
6113 return STATUS_INSUFFICIENT_RESOURCES
;
6116 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6117 di
->key
.obj_id
= fileref
->fcb
->inode
;
6118 di
->key
.obj_type
= TYPE_INODE_ITEM
;
6120 } else { // subvolume
6121 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
6122 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
6123 di
->key
.offset
= 0xffffffffffffffff;
6126 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
6128 di
->n
= (UINT16
)fileref
->dc
->utf8
.Length
;
6129 di
->type
= fileref
->fcb
->type
;
6130 RtlCopyMemory(di
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6132 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6134 ERR("out of memory\n");
6135 return STATUS_INSUFFICIENT_RESOURCES
;
6138 RtlCopyMemory(di2
, di
, disize
);
6140 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6141 fileref
->dc
->index
, di
, disize
, Batch_Insert
);
6142 if (!NT_SUCCESS(Status
)) {
6143 ERR("insert_tree_item_batch returned %08x\n", Status
);
6147 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
, crc32
,
6148 di2
, disize
, Batch_DirItem
);
6149 if (!NT_SUCCESS(Status
)) {
6150 ERR("insert_tree_item_batch returned %08x\n", Status
);
6154 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6157 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
6159 ERR("out of memory\n");
6160 return STATUS_INSUFFICIENT_RESOURCES
;
6163 ir
->index
= fileref
->dc
->index
;
6164 ir
->n
= fileref
->dc
->utf8
.Length
;
6165 RtlCopyMemory(ir
->name
, fileref
->dc
->utf8
.Buffer
, ir
->n
);
6167 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6168 ir
, sizeof(INODE_REF
) - 1 + ir
->n
, Batch_InodeRef
);
6169 if (!NT_SUCCESS(Status
)) {
6170 ERR("insert_tree_item_batch returned %08x\n", Status
);
6173 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) {
6177 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->dc
->utf8
.Length
;
6179 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
6181 ERR("out of memory\n");
6182 return STATUS_INSUFFICIENT_RESOURCES
;
6185 rr
->dir
= fileref
->parent
->fcb
->inode
;
6186 rr
->index
= fileref
->dc
->index
;
6187 rr
->n
= fileref
->dc
->utf8
.Length
;
6188 RtlCopyMemory(rr
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6190 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
);
6191 if (!NT_SUCCESS(Status
)) {
6192 ERR("add_root_ref returned %08x\n", Status
);
6196 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6197 if (!NT_SUCCESS(Status
)) {
6198 ERR("update_root_backref returned %08x\n", Status
);
6203 fileref
->created
= FALSE
;
6204 } else if (fileref
->deleted
) {
6209 name
= &fileref
->oldutf8
;
6211 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)name
->Buffer
, name
->Length
);
6213 TRACE("deleting %.*S\n", file_desc_fileref(fileref
));
6215 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + name
->Length
, ALLOC_TAG
);
6217 ERR("out of memory\n");
6218 return STATUS_INSUFFICIENT_RESOURCES
;
6222 di
->n
= name
->Length
;
6223 RtlCopyMemory(di
->name
, name
->Buffer
, name
->Length
);
6225 // delete DIR_ITEM (0x54)
6227 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
,
6228 crc32
, di
, sizeof(DIR_ITEM
) - 1 + name
->Length
, Batch_DeleteDirItem
);
6229 if (!NT_SUCCESS(Status
)) {
6230 ERR("insert_tree_item_batch returned %08x\n", Status
);
6234 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6237 // delete INODE_REF (0xc)
6239 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + name
->Length
, ALLOC_TAG
);
6241 ERR("out of memory\n");
6242 return STATUS_INSUFFICIENT_RESOURCES
;
6245 ir
->index
= fileref
->oldindex
;
6246 ir
->n
= name
->Length
;
6247 RtlCopyMemory(ir
->name
, name
->Buffer
, name
->Length
);
6249 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
,
6250 fileref
->parent
->fcb
->inode
, ir
, sizeof(INODE_REF
) - 1 + name
->Length
, Batch_DeleteInodeRef
);
6251 if (!NT_SUCCESS(Status
)) {
6252 ERR("insert_tree_item_batch returned %08x\n", Status
);
6255 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) { // subvolume
6256 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, name
, Irp
);
6257 if (!NT_SUCCESS(Status
)) {
6258 ERR("delete_root_ref returned %08x\n", Status
);
6262 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6263 if (!NT_SUCCESS(Status
)) {
6264 ERR("update_root_backref returned %08x\n", Status
);
6269 // delete DIR_INDEX (0x60)
6271 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6272 fileref
->oldindex
, NULL
, 0, Batch_Delete
);
6273 if (!NT_SUCCESS(Status
)) {
6274 ERR("insert_tree_item_batch returned %08x\n", Status
);
6278 if (fileref
->oldutf8
.Buffer
) {
6279 ExFreePool(fileref
->oldutf8
.Buffer
);
6280 fileref
->oldutf8
.Buffer
= NULL
;
6282 } else { // rename or change type
6283 PANSI_STRING oldutf8
= fileref
->oldutf8
.Buffer
? &fileref
->oldutf8
: &fileref
->dc
->utf8
;
6284 UINT32 crc32
, oldcrc32
;
6286 DIR_ITEM
*olddi
, *di
, *di2
;
6288 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6290 if (!fileref
->oldutf8
.Buffer
)
6293 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->oldutf8
.Buffer
, fileref
->oldutf8
.Length
);
6295 olddi
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + oldutf8
->Length
, ALLOC_TAG
);
6297 ERR("out of memory\n");
6298 return STATUS_INSUFFICIENT_RESOURCES
;
6302 olddi
->n
= (UINT16
)oldutf8
->Length
;
6303 RtlCopyMemory(olddi
->name
, oldutf8
->Buffer
, oldutf8
->Length
);
6305 // delete DIR_ITEM (0x54)
6307 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
,
6308 oldcrc32
, olddi
, sizeof(DIR_ITEM
) - 1 + oldutf8
->Length
, Batch_DeleteDirItem
);
6309 if (!NT_SUCCESS(Status
)) {
6310 ERR("insert_tree_item_batch returned %08x\n", Status
);
6315 // add DIR_ITEM (0x54)
6317 disize
= (UINT16
)(offsetof(DIR_ITEM
, name
[0]) + fileref
->dc
->utf8
.Length
);
6318 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6320 ERR("out of memory\n");
6321 return STATUS_INSUFFICIENT_RESOURCES
;
6324 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6326 ERR("out of memory\n");
6328 return STATUS_INSUFFICIENT_RESOURCES
;
6332 di
->key
= fileref
->dc
->key
;
6333 else if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6334 di
->key
.obj_id
= fileref
->fcb
->inode
;
6335 di
->key
.obj_type
= TYPE_INODE_ITEM
;
6337 } else { // subvolume
6338 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
6339 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
6340 di
->key
.offset
= 0xffffffffffffffff;
6343 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
6345 di
->n
= (UINT16
)fileref
->dc
->utf8
.Length
;
6346 di
->type
= fileref
->fcb
->type
;
6347 RtlCopyMemory(di
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6349 RtlCopyMemory(di2
, di
, disize
);
6351 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
, crc32
,
6352 di
, disize
, Batch_DirItem
);
6353 if (!NT_SUCCESS(Status
)) {
6354 ERR("insert_tree_item_batch returned %08x\n", Status
);
6360 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6361 INODE_REF
*ir
, *ir2
;
6363 // delete INODE_REF (0xc)
6365 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + oldutf8
->Length
, ALLOC_TAG
);
6367 ERR("out of memory\n");
6369 return STATUS_INSUFFICIENT_RESOURCES
;
6372 ir
->index
= fileref
->dc
->index
;
6373 ir
->n
= oldutf8
->Length
;
6374 RtlCopyMemory(ir
->name
, oldutf8
->Buffer
, ir
->n
);
6376 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6377 ir
, sizeof(INODE_REF
) - 1 + ir
->n
, Batch_DeleteInodeRef
);
6378 if (!NT_SUCCESS(Status
)) {
6379 ERR("insert_tree_item_batch returned %08x\n", Status
);
6385 // add INODE_REF (0xc)
6387 ir2
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
6389 ERR("out of memory\n");
6391 return STATUS_INSUFFICIENT_RESOURCES
;
6394 ir2
->index
= fileref
->dc
->index
;
6395 ir2
->n
= fileref
->dc
->utf8
.Length
;
6396 RtlCopyMemory(ir2
->name
, fileref
->dc
->utf8
.Buffer
, ir2
->n
);
6398 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6399 ir2
, sizeof(INODE_REF
) - 1 + ir2
->n
, Batch_InodeRef
);
6400 if (!NT_SUCCESS(Status
)) {
6401 ERR("insert_tree_item_batch returned %08x\n", Status
);
6406 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) { // subvolume
6410 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
);
6411 if (!NT_SUCCESS(Status
)) {
6412 ERR("delete_root_ref returned %08x\n", Status
);
6417 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->dc
->utf8
.Length
;
6419 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
6421 ERR("out of memory\n");
6423 return STATUS_INSUFFICIENT_RESOURCES
;
6426 rr
->dir
= fileref
->parent
->fcb
->inode
;
6427 rr
->index
= fileref
->dc
->index
;
6428 rr
->n
= fileref
->dc
->utf8
.Length
;
6429 RtlCopyMemory(rr
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6431 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
);
6432 if (!NT_SUCCESS(Status
)) {
6433 ERR("add_root_ref returned %08x\n", Status
);
6438 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6439 if (!NT_SUCCESS(Status
)) {
6440 ERR("update_root_backref returned %08x\n", Status
);
6446 // delete DIR_INDEX (0x60)
6448 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6449 fileref
->dc
->index
, NULL
, 0, Batch_Delete
);
6450 if (!NT_SUCCESS(Status
)) {
6451 ERR("insert_tree_item_batch returned %08x\n", Status
);
6456 // add DIR_INDEX (0x60)
6458 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6459 fileref
->dc
->index
, di2
, disize
, Batch_Insert
);
6460 if (!NT_SUCCESS(Status
)) {
6461 ERR("insert_tree_item_batch returned %08x\n", Status
);
6466 if (fileref
->oldutf8
.Buffer
) {
6467 ExFreePool(fileref
->oldutf8
.Buffer
);
6468 fileref
->oldutf8
.Buffer
= NULL
;
6472 fileref
->dirty
= FALSE
;
6474 return STATUS_SUCCESS
;
6477 static void flush_disk_caches(device_extension
* Vcb
) {
6479 ioctl_context context
;
6484 le
= Vcb
->devices
.Flink
;
6486 while (le
!= &Vcb
->devices
) {
6487 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6489 if (dev
->devobj
&& !dev
->readonly
&& dev
->can_flush
)
6495 if (context
.left
== 0)
6500 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
6502 context
.stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ioctl_context_stripe
) * context
.left
, ALLOC_TAG
);
6503 if (!context
.stripes
) {
6504 ERR("out of memory\n");
6508 RtlZeroMemory(context
.stripes
, sizeof(ioctl_context_stripe
) * context
.left
);
6510 le
= Vcb
->devices
.Flink
;
6512 while (le
!= &Vcb
->devices
) {
6513 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6515 if (dev
->devobj
&& !dev
->readonly
&& dev
->can_flush
) {
6516 PIO_STACK_LOCATION IrpSp
;
6517 ioctl_context_stripe
* stripe
= &context
.stripes
[num
];
6519 RtlZeroMemory(&stripe
->apte
, sizeof(ATA_PASS_THROUGH_EX
));
6521 stripe
->apte
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
6522 stripe
->apte
.TimeOutValue
= 5;
6523 stripe
->apte
.CurrentTaskFile
[6] = IDE_COMMAND_FLUSH_CACHE
;
6525 stripe
->Irp
= IoAllocateIrp(dev
->devobj
->StackSize
, FALSE
);
6528 ERR("IoAllocateIrp failed\n");
6532 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
6533 IrpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
6535 IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_ATA_PASS_THROUGH
;
6536 IrpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= sizeof(ATA_PASS_THROUGH_EX
);
6537 IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= sizeof(ATA_PASS_THROUGH_EX
);
6539 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= &stripe
->apte
;
6540 stripe
->Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_INPUT_OPERATION
;
6541 stripe
->Irp
->UserBuffer
= &stripe
->apte
;
6542 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
6544 IoSetCompletionRoutine(stripe
->Irp
, ioctl_completion
, &context
, TRUE
, TRUE
, TRUE
);
6546 IoCallDriver(dev
->devobj
, stripe
->Irp
);
6555 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
6557 ExFreePool(context
.stripes
);
6560 static NTSTATUS
flush_changed_dev_stats(device_extension
* Vcb
, device
* dev
, PIRP Irp
) {
6567 searchkey
.obj_id
= 0;
6568 searchkey
.obj_type
= TYPE_DEV_STATS
;
6569 searchkey
.offset
= dev
->devitem
.dev_id
;
6571 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
6572 if (!NT_SUCCESS(Status
)) {
6573 ERR("find_item returned %08x\n", Status
);
6577 if (!keycmp(tp
.item
->key
, searchkey
)) {
6578 Status
= delete_tree_item(Vcb
, &tp
);
6579 if (!NT_SUCCESS(Status
)) {
6580 ERR("delete_tree_item returned %08x\n", Status
);
6585 statslen
= sizeof(UINT64
) * 5;
6586 stats
= ExAllocatePoolWithTag(PagedPool
, statslen
, ALLOC_TAG
);
6588 ERR("out of memory\n");
6589 return STATUS_INSUFFICIENT_RESOURCES
;
6592 RtlCopyMemory(stats
, dev
->stats
, statslen
);
6594 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, dev
->devitem
.dev_id
, stats
, statslen
, NULL
, Irp
);
6595 if (!NT_SUCCESS(Status
)) {
6596 ERR("insert_tree_item returned %08x\n", Status
);
6601 return STATUS_SUCCESS
;
6604 static NTSTATUS
flush_subvol(device_extension
* Vcb
, root
* r
, PIRP Irp
) {
6607 if (r
!= Vcb
->root_root
&& r
!= Vcb
->chunk_root
) {
6612 searchkey
.obj_id
= r
->id
;
6613 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
6614 searchkey
.offset
= 0xffffffffffffffff;
6616 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6617 if (!NT_SUCCESS(Status
)) {
6618 ERR("error - find_item returned %08x\n", Status
);
6622 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6623 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
6624 return STATUS_INTERNAL_ERROR
;
6627 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
6629 ERR("out of memory\n");
6630 return STATUS_INSUFFICIENT_RESOURCES
;
6633 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
6635 Status
= delete_tree_item(Vcb
, &tp
);
6636 if (!NT_SUCCESS(Status
)) {
6637 ERR("delete_tree_item returned %08x\n", Status
);
6641 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
);
6642 if (!NT_SUCCESS(Status
)) {
6643 ERR("insert_tree_item returned %08x\n", Status
);
6652 if (!Vcb
->uuid_root
) {
6655 TRACE("uuid root doesn't exist, creating it\n");
6657 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
6659 if (!NT_SUCCESS(Status
)) {
6660 ERR("create_root returned %08x\n", Status
);
6664 Vcb
->uuid_root
= uuid_root
;
6667 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.received_uuid
, sizeof(UINT64
));
6668 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
6669 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.received_uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
6671 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
6672 if (!NT_SUCCESS(Status
)) {
6673 ERR("find_item returned %08x\n", Status
);
6677 if (!keycmp(tp
.item
->key
, searchkey
)) {
6678 if (tp
.item
->size
+ sizeof(UINT64
) <= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
)) {
6681 ids
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ sizeof(UINT64
), ALLOC_TAG
);
6683 ERR("out of memory\n");
6684 return STATUS_INSUFFICIENT_RESOURCES
;
6687 RtlCopyMemory(ids
, tp
.item
->data
, tp
.item
->size
);
6688 RtlCopyMemory((UINT8
*)ids
+ tp
.item
->size
, &r
->id
, sizeof(UINT64
));
6690 Status
= delete_tree_item(Vcb
, &tp
);
6691 if (!NT_SUCCESS(Status
)) {
6692 ERR("delete_tree_item returned %08x\n", Status
);
6697 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ids
, tp
.item
->size
+ sizeof(UINT64
), NULL
, Irp
);
6698 if (!NT_SUCCESS(Status
)) {
6699 ERR("insert_tree_item returned %08x\n", Status
);
6707 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
6709 ERR("out of memory\n");
6710 return STATUS_INSUFFICIENT_RESOURCES
;
6715 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
6716 if (!NT_SUCCESS(Status
)) {
6717 ERR("insert_tree_item returned %08x\n", Status
);
6718 ExFreePool(root_num
);
6723 r
->received
= FALSE
;
6728 return STATUS_SUCCESS
;
6731 static NTSTATUS
test_not_full(device_extension
* Vcb
) {
6732 UINT64 reserve
, could_alloc
, free_space
;
6735 // This function ensures we drop into readonly mode if we're about to leave very little
6736 // space for metadata - this is similar to the "global reserve" of the Linux driver.
6737 // Otherwise we might completely fill our space, at which point due to COW we can't
6738 // delete anything in order to fix this.
6740 reserve
= Vcb
->extent_root
->root_item
.bytes_used
;
6741 reserve
+= Vcb
->root_root
->root_item
.bytes_used
;
6742 if (Vcb
->checksum_root
) reserve
+= Vcb
->checksum_root
->root_item
.bytes_used
;
6744 reserve
= max(reserve
, 0x1000000); // 16 M
6745 reserve
= min(reserve
, 0x20000000); // 512 M
6747 // Find out how much space would be available for new metadata chunks
6751 if (Vcb
->metadata_flags
& BLOCK_FLAG_RAID5
) {
6752 UINT64 s1
= 0, s2
= 0, s3
= 0;
6754 le
= Vcb
->devices
.Flink
;
6755 while (le
!= &Vcb
->devices
) {
6756 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6758 if (!dev
->readonly
) {
6759 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6765 } else if (space
>= s2
) {
6768 } else if (space
>= s3
)
6775 could_alloc
= s3
* 2;
6776 } else if (Vcb
->metadata_flags
& (BLOCK_FLAG_RAID10
| BLOCK_FLAG_RAID6
)) {
6777 UINT64 s1
= 0, s2
= 0, s3
= 0, s4
= 0;
6779 le
= Vcb
->devices
.Flink
;
6780 while (le
!= &Vcb
->devices
) {
6781 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6783 if (!dev
->readonly
) {
6784 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6791 } else if (space
>= s2
) {
6795 } else if (space
>= s3
) {
6798 } else if (space
>= s4
)
6805 could_alloc
= s4
* 2;
6806 } else if (Vcb
->metadata_flags
& (BLOCK_FLAG_RAID0
| BLOCK_FLAG_RAID1
)) {
6807 UINT64 s1
= 0, s2
= 0;
6809 le
= Vcb
->devices
.Flink
;
6810 while (le
!= &Vcb
->devices
) {
6811 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6813 if (!dev
->readonly
) {
6814 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6819 } else if (space
>= s2
)
6826 if (Vcb
->metadata_flags
& BLOCK_FLAG_RAID1
)
6829 could_alloc
= s2
* 2;
6830 } else if (Vcb
->metadata_flags
& BLOCK_FLAG_DUPLICATE
) {
6831 le
= Vcb
->devices
.Flink
;
6832 while (le
!= &Vcb
->devices
) {
6833 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6835 if (!dev
->readonly
) {
6836 UINT64 space
= (dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
) / 2;
6838 could_alloc
= max(could_alloc
, space
);
6844 le
= Vcb
->devices
.Flink
;
6845 while (le
!= &Vcb
->devices
) {
6846 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6848 if (!dev
->readonly
) {
6849 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6851 could_alloc
= max(could_alloc
, space
);
6858 if (could_alloc
>= reserve
)
6859 return STATUS_SUCCESS
;
6863 le
= Vcb
->chunks
.Flink
;
6864 while (le
!= &Vcb
->chunks
) {
6865 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
6867 if (!c
->reloc
&& !c
->readonly
&& c
->chunk_item
->type
& BLOCK_FLAG_METADATA
) {
6868 free_space
+= c
->chunk_item
->size
- c
->used
;
6870 if (free_space
+ could_alloc
>= reserve
)
6871 return STATUS_SUCCESS
;
6877 return STATUS_DISK_FULL
;
6880 static NTSTATUS
do_write2(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6882 LIST_ENTRY
*le
, batchlist
;
6883 BOOL cache_changed
= FALSE
;
6884 volume_device_extension
* vde
;
6885 BOOL no_cache
= FALSE
;
6886 #ifdef DEBUG_FLUSH_TIMES
6887 UINT64 filerefs
= 0, fcbs
= 0;
6888 LARGE_INTEGER freq
, time1
, time2
;
6890 #ifdef DEBUG_WRITE_LOOPS
6894 TRACE("(%p)\n", Vcb
);
6896 InitializeListHead(&batchlist
);
6898 #ifdef DEBUG_FLUSH_TIMES
6899 time1
= KeQueryPerformanceCounter(&freq
);
6902 ExAcquireResourceExclusiveLite(&Vcb
->dirty_filerefs_lock
, TRUE
);
6904 while (!IsListEmpty(&Vcb
->dirty_filerefs
)) {
6905 file_ref
* fr
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->dirty_filerefs
), file_ref
, list_entry_dirty
);
6907 flush_fileref(fr
, &batchlist
, Irp
);
6908 free_fileref(Vcb
, fr
);
6910 #ifdef DEBUG_FLUSH_TIMES
6915 ExReleaseResourceLite(&Vcb
->dirty_filerefs_lock
);
6917 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
6918 if (!NT_SUCCESS(Status
)) {
6919 ERR("commit_batch_list returned %08x\n", Status
);
6923 #ifdef DEBUG_FLUSH_TIMES
6924 time2
= KeQueryPerformanceCounter(NULL
);
6926 ERR("flushed %llu filerefs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
6928 time1
= KeQueryPerformanceCounter(&freq
);
6931 // We process deleted streams first, so we don't run over our xattr
6932 // limit unless we absolutely have to.
6933 // We also process deleted normal files, to avoid any problems
6934 // caused by inode collisions.
6936 ExAcquireResourceExclusiveLite(&Vcb
->dirty_fcbs_lock
, TRUE
);
6938 le
= Vcb
->dirty_fcbs
.Flink
;
6939 while (le
!= &Vcb
->dirty_fcbs
) {
6940 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_dirty
);
6941 LIST_ENTRY
* le2
= le
->Flink
;
6944 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
6945 Status
= flush_fcb(fcb
, FALSE
, &batchlist
, Irp
);
6946 ExReleaseResourceLite(fcb
->Header
.Resource
);
6950 if (!NT_SUCCESS(Status
)) {
6951 ERR("flush_fcb returned %08x\n", Status
);
6952 clear_batch_list(Vcb
, &batchlist
);
6953 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6957 #ifdef DEBUG_FLUSH_TIMES
6965 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
6966 if (!NT_SUCCESS(Status
)) {
6967 ERR("commit_batch_list returned %08x\n", Status
);
6968 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6972 le
= Vcb
->dirty_fcbs
.Flink
;
6973 while (le
!= &Vcb
->dirty_fcbs
) {
6974 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_dirty
);
6975 LIST_ENTRY
* le2
= le
->Flink
;
6977 if (fcb
->subvol
!= Vcb
->root_root
) {
6978 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
6979 Status
= flush_fcb(fcb
, FALSE
, &batchlist
, Irp
);
6980 ExReleaseResourceLite(fcb
->Header
.Resource
);
6983 if (!NT_SUCCESS(Status
)) {
6984 ERR("flush_fcb returned %08x\n", Status
);
6985 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6989 #ifdef DEBUG_FLUSH_TIMES
6997 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6999 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
7000 if (!NT_SUCCESS(Status
)) {
7001 ERR("commit_batch_list returned %08x\n", Status
);
7005 #ifdef DEBUG_FLUSH_TIMES
7006 time2
= KeQueryPerformanceCounter(NULL
);
7008 ERR("flushed %llu fcbs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
7011 // no need to get dirty_subvols_lock here, as we have tree_lock exclusively
7012 while (!IsListEmpty(&Vcb
->dirty_subvols
)) {
7013 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->dirty_subvols
), root
, list_entry_dirty
);
7015 Status
= flush_subvol(Vcb
, r
, Irp
);
7016 if (!NT_SUCCESS(Status
)) {
7017 ERR("flush_subvol returned %08x\n", Status
);
7022 if (!IsListEmpty(&Vcb
->drop_roots
)) {
7023 Status
= drop_roots(Vcb
, Irp
, rollback
);
7025 if (!NT_SUCCESS(Status
)) {
7026 ERR("drop_roots returned %08x\n", Status
);
7031 Status
= update_chunks(Vcb
, &batchlist
, Irp
, rollback
);
7033 if (!NT_SUCCESS(Status
)) {
7034 ERR("update_chunks returned %08x\n", Status
);
7038 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
7040 // If only changing superblock, e.g. changing label, we still need to rewrite
7041 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
7042 if (!Vcb
->root_root
->treeholder
.tree
|| !Vcb
->root_root
->treeholder
.tree
->write
) {
7047 searchkey
.obj_id
= 0;
7048 searchkey
.obj_type
= 0;
7049 searchkey
.offset
= 0;
7051 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
7052 if (!NT_SUCCESS(Status
)) {
7053 ERR("error - find_item returned %08x\n", Status
);
7057 Vcb
->root_root
->treeholder
.tree
->write
= TRUE
;
7060 // make sure we always update the extent tree
7061 Status
= add_root_item_to_cache(Vcb
, BTRFS_ROOT_EXTENT
, Irp
);
7062 if (!NT_SUCCESS(Status
)) {
7063 ERR("add_root_item_to_cache returned %08x\n", Status
);
7067 if (Vcb
->stats_changed
) {
7068 le
= Vcb
->devices
.Flink
;
7069 while (le
!= &Vcb
->devices
) {
7070 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
7072 if (dev
->stats_changed
) {
7073 Status
= flush_changed_dev_stats(Vcb
, dev
, Irp
);
7074 if (!NT_SUCCESS(Status
)) {
7075 ERR("flush_changed_dev_stats returned %08x\n", Status
);
7078 dev
->stats_changed
= FALSE
;
7084 Vcb
->stats_changed
= FALSE
;
7088 Status
= add_parents(Vcb
, Irp
);
7089 if (!NT_SUCCESS(Status
)) {
7090 ERR("add_parents returned %08x\n", Status
);
7094 Status
= allocate_tree_extents(Vcb
, Irp
, rollback
);
7095 if (!NT_SUCCESS(Status
)) {
7096 ERR("allocate_tree_extents returned %08x\n", Status
);
7100 Status
= do_splits(Vcb
, Irp
, rollback
);
7101 if (!NT_SUCCESS(Status
)) {
7102 ERR("do_splits returned %08x\n", Status
);
7106 Status
= update_chunk_usage(Vcb
, Irp
, rollback
);
7107 if (!NT_SUCCESS(Status
)) {
7108 ERR("update_chunk_usage returned %08x\n", Status
);
7112 if (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
)) {
7114 Status
= allocate_cache(Vcb
, &cache_changed
, Irp
, rollback
);
7115 if (!NT_SUCCESS(Status
)) {
7116 WARN("allocate_cache returned %08x\n", Status
);
7118 cache_changed
= FALSE
;
7122 Status
= update_chunk_caches_tree(Vcb
, Irp
);
7123 if (!NT_SUCCESS(Status
)) {
7124 ERR("update_chunk_caches_tree returned %08x\n", Status
);
7129 #ifdef DEBUG_WRITE_LOOPS
7133 ERR("cache has changed, looping again\n");
7135 } while (cache_changed
|| !trees_consistent(Vcb
));
7137 #ifdef DEBUG_WRITE_LOOPS
7138 ERR("%u loops\n", loops
);
7141 TRACE("trees consistent\n");
7143 Status
= update_root_root(Vcb
, no_cache
, Irp
, rollback
);
7144 if (!NT_SUCCESS(Status
)) {
7145 ERR("update_root_root returned %08x\n", Status
);
7149 Status
= write_trees(Vcb
, Irp
);
7150 if (!NT_SUCCESS(Status
)) {
7151 ERR("write_trees returned %08x\n", Status
);
7155 Status
= test_not_full(Vcb
);
7156 if (!NT_SUCCESS(Status
)) {
7157 ERR("test_not_full returned %08x\n", Status
);
7161 #ifdef DEBUG_PARANOID
7162 le
= Vcb
->trees
.Flink
;
7163 while (le
!= &Vcb
->trees
) {
7164 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
7168 searchkey
.obj_id
= t
->header
.address
;
7169 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
7170 searchkey
.offset
= 0xffffffffffffffff;
7172 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
7173 if (!NT_SUCCESS(Status
)) {
7174 ERR("error - find_item returned %08x\n", Status
);
7178 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
7179 searchkey
.obj_id
= t
->header
.address
;
7180 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
7181 searchkey
.offset
= 0xffffffffffffffff;
7183 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
7184 if (!NT_SUCCESS(Status
)) {
7185 ERR("error - find_item returned %08x\n", Status
);
7189 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
7190 ERR("error - could not find entry in extent tree for tree at %llx\n", t
->header
.address
);
7191 Status
= STATUS_INTERNAL_ERROR
;
7200 Vcb
->superblock
.cache_generation
= Vcb
->superblock
.generation
;
7202 if (!Vcb
->options
.no_barrier
)
7203 flush_disk_caches(Vcb
);
7205 Status
= write_superblocks(Vcb
, Irp
);
7206 if (!NT_SUCCESS(Status
)) {
7207 ERR("write_superblocks returned %08x\n", Status
);
7214 pdo_device_extension
* pdode
= vde
->pdode
;
7216 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
7218 le
= pdode
->children
.Flink
;
7220 while (le
!= &pdode
->children
) {
7221 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
7223 vc
->generation
= Vcb
->superblock
.generation
;
7227 ExReleaseResourceLite(&pdode
->child_lock
);
7230 clean_space_cache(Vcb
);
7232 le
= Vcb
->chunks
.Flink
;
7233 while (le
!= &Vcb
->chunks
) {
7234 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
7237 c
->space_changed
= FALSE
;
7242 Vcb
->superblock
.generation
++;
7244 Status
= STATUS_SUCCESS
;
7246 le
= Vcb
->trees
.Flink
;
7247 while (le
!= &Vcb
->trees
) {
7248 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
7255 Vcb
->need_write
= FALSE
;
7257 while (!IsListEmpty(&Vcb
->drop_roots
)) {
7258 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->drop_roots
), root
, list_entry
);
7260 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
7261 ExFreePool(r
->nonpaged
);
7266 TRACE("do_write returning %08x\n", Status
);
7271 NTSTATUS
do_write(device_extension
* Vcb
, PIRP Irp
) {
7272 LIST_ENTRY rollback
;
7275 InitializeListHead(&rollback
);
7277 Status
= do_write2(Vcb
, Irp
, &rollback
);
7279 if (!NT_SUCCESS(Status
)) {
7280 ERR("do_write2 returned %08x, dropping into readonly mode\n", Status
);
7281 Vcb
->readonly
= TRUE
;
7282 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_FORCED_CLOSED
);
7283 do_rollback(Vcb
, &rollback
);
7285 clear_rollback(&rollback
);
7291 static void print_stats(device_extension
* Vcb
) {
7292 ERR("READ STATS:\n");
7293 ERR("number of reads: %llu\n", Vcb
->stats
.num_reads
);
7294 ERR("data read: %llu bytes\n", Vcb
->stats
.data_read
);
7295 ERR("total time taken: %llu\n", Vcb
->stats
.read_total_time
);
7296 ERR("csum time taken: %llu\n", Vcb
->stats
.read_csum_time
);
7297 ERR("disk time taken: %llu\n", Vcb
->stats
.read_disk_time
);
7298 ERR("other time taken: %llu\n", Vcb
->stats
.read_total_time
- Vcb
->stats
.read_csum_time
- Vcb
->stats
.read_disk_time
);
7300 ERR("OPEN STATS:\n");
7301 ERR("number of opens: %llu\n", Vcb
->stats
.num_opens
);
7302 ERR("total time taken: %llu\n", Vcb
->stats
.open_total_time
);
7303 ERR("number of overwrites: %llu\n", Vcb
->stats
.num_overwrites
);
7304 ERR("total time taken: %llu\n", Vcb
->stats
.overwrite_total_time
);
7305 ERR("number of creates: %llu\n", Vcb
->stats
.num_creates
);
7306 ERR("calls to open_fcb: %llu\n", Vcb
->stats
.open_fcb_calls
);
7307 ERR("time spent in open_fcb: %llu\n", Vcb
->stats
.open_fcb_time
);
7308 ERR("total time taken: %llu\n", Vcb
->stats
.create_total_time
);
7310 RtlZeroMemory(&Vcb
->stats
, sizeof(debug_stats
));
7314 static void do_flush(device_extension
* Vcb
) {
7317 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
7323 if (Vcb
->need_write
&& !Vcb
->readonly
)
7324 Status
= do_write(Vcb
, NULL
);
7326 Status
= STATUS_SUCCESS
;
7330 if (!NT_SUCCESS(Status
))
7331 ERR("do_write returned %08x\n", Status
);
7333 ExReleaseResourceLite(&Vcb
->tree_lock
);
7336 _Function_class_(KSTART_ROUTINE
)
7338 void NTAPI
flush_thread(void* context
) {
7340 void flush_thread(void* context
) {
7342 DEVICE_OBJECT
* devobj
= context
;
7343 device_extension
* Vcb
= devobj
->DeviceExtension
;
7344 LARGE_INTEGER due_time
;
7346 ObReferenceObject(devobj
);
7348 KeInitializeTimer(&Vcb
->flush_thread_timer
);
7350 due_time
.QuadPart
= (UINT64
)Vcb
->options
.flush_interval
* -10000000;
7352 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
7355 KeWaitForSingleObject(&Vcb
->flush_thread_timer
, Executive
, KernelMode
, FALSE
, NULL
);
7357 if (!(devobj
->Vpb
->Flags
& VPB_MOUNTED
) || Vcb
->removing
)
7363 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
7366 ObDereferenceObject(devobj
);
7367 KeCancelTimer(&Vcb
->flush_thread_timer
);
7369 KeSetEvent(&Vcb
->flush_thread_finished
, 0, FALSE
);
7371 PsTerminateSystemThread(STATUS_SUCCESS
);