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
);
4257 int cmp
= keycmp(bi2
->key
, bi
->key
);
4259 if (cmp
== -1 || (cmp
== 0 && bi
->operation
>= bi2
->operation
)) {
4260 InsertHeadList(&bi2
->list_entry
, &bi
->list_entry
);
4261 return STATUS_SUCCESS
;
4267 InsertHeadList(&br
->items
, &bi
->list_entry
);
4269 return STATUS_SUCCESS
;
4272 #pragma warning(pop)
4283 LIST_ENTRY list_entry
;
4286 static void rationalize_extents(fcb
* fcb
, PIRP Irp
) {
4288 LIST_ENTRY extent_ranges
;
4290 BOOL changed
= FALSE
, truncating
= FALSE
;
4291 UINT32 num_extents
= 0;
4293 InitializeListHead(&extent_ranges
);
4295 le
= fcb
->extents
.Flink
;
4296 while (le
!= &fcb
->extents
) {
4297 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4299 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4300 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4302 if (ed2
->size
!= 0) {
4305 le2
= extent_ranges
.Flink
;
4306 while (le2
!= &extent_ranges
) {
4307 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4309 if (er2
->address
== ed2
->address
) {
4310 er2
->skip_start
= min(er2
->skip_start
, ed2
->offset
);
4311 er2
->skip_end
= min(er2
->skip_end
, ed2
->size
- ed2
->offset
- ed2
->num_bytes
);
4313 } else if (er2
->address
> ed2
->address
)
4319 er
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent_range
), ALLOC_TAG
); // FIXME - should be from lookaside?
4321 ERR("out of memory\n");
4325 er
->address
= ed2
->address
;
4326 er
->length
= ed2
->size
;
4327 er
->offset
= ext
->offset
- ed2
->offset
;
4328 er
->changed
= FALSE
;
4330 er
->skip_start
= ed2
->offset
;
4331 er
->skip_end
= ed2
->size
- ed2
->offset
- ed2
->num_bytes
;
4333 if (er
->skip_start
!= 0 || er
->skip_end
!= 0)
4336 InsertHeadList(le2
->Blink
, &er
->list_entry
);
4345 if (num_extents
== 0 || (num_extents
== 1 && !truncating
))
4348 le
= extent_ranges
.Flink
;
4349 while (le
!= &extent_ranges
) {
4350 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4355 er
->chunk
= get_chunk_from_address(fcb
->Vcb
, er
->address
);
4358 ERR("get_chunk_from_address(%llx) failed\n", er
->address
);
4363 while (le2
!= &extent_ranges
) {
4364 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4366 if (!er2
->chunk
&& er2
->address
>= er
->chunk
->offset
&& er2
->address
< er
->chunk
->offset
+ er
->chunk
->chunk_item
->size
)
4367 er2
->chunk
= er
->chunk
;
4377 // truncate beginning or end of extent if unused
4379 le
= extent_ranges
.Flink
;
4380 while (le
!= &extent_ranges
) {
4381 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4383 if (er
->skip_start
> 0) {
4384 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
4385 while (le2
!= &fcb
->extents
) {
4386 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
4388 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4389 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4391 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
4394 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4395 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4396 if (!NT_SUCCESS(Status
)) {
4397 ERR("update_changed_extent_ref returned %08x\n", Status
);
4401 ext
->extent_data
.decoded_size
-= er
->skip_start
;
4402 ed2
->size
-= er
->skip_start
;
4403 ed2
->address
+= er
->skip_start
;
4404 ed2
->offset
-= er
->skip_start
;
4406 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4407 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4414 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
))
4415 add_checksum_entry(fcb
->Vcb
, er
->address
, (ULONG
)(er
->skip_start
/ fcb
->Vcb
->superblock
.sector_size
), NULL
, NULL
);
4417 ExAcquireResourceExclusiveLite(&er
->chunk
->lock
, TRUE
);
4419 if (!er
->chunk
->cache_loaded
) {
4420 NTSTATUS Status
= load_cache_chunk(fcb
->Vcb
, er
->chunk
, NULL
);
4422 if (!NT_SUCCESS(Status
)) {
4423 ERR("load_cache_chunk returned %08x\n", Status
);
4424 ExReleaseResourceLite(&er
->chunk
->lock
);
4429 er
->chunk
->used
-= er
->skip_start
;
4431 space_list_add(er
->chunk
, er
->address
, er
->skip_start
, NULL
);
4433 ExReleaseResourceLite(&er
->chunk
->lock
);
4435 er
->address
+= er
->skip_start
;
4436 er
->length
-= er
->skip_start
;
4439 if (er
->skip_end
> 0) {
4440 LIST_ENTRY
* le2
= fcb
->extents
.Flink
;
4441 while (le2
!= &fcb
->extents
) {
4442 extent
* ext
= CONTAINING_RECORD(le2
, extent
, list_entry
);
4444 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4445 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4447 if (ed2
->size
!= 0 && ed2
->address
== er
->address
) {
4450 Status
= update_changed_extent_ref(fcb
->Vcb
, er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4451 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4452 if (!NT_SUCCESS(Status
)) {
4453 ERR("update_changed_extent_ref returned %08x\n", Status
);
4457 ext
->extent_data
.decoded_size
-= er
->skip_end
;
4458 ed2
->size
-= er
->skip_end
;
4460 add_changed_extent_ref(er
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4461 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4468 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
))
4469 add_checksum_entry(fcb
->Vcb
, er
->address
+ er
->length
- er
->skip_end
, (ULONG
)(er
->skip_end
/ fcb
->Vcb
->superblock
.sector_size
), NULL
, NULL
);
4471 ExAcquireResourceExclusiveLite(&er
->chunk
->lock
, TRUE
);
4473 if (!er
->chunk
->cache_loaded
) {
4474 NTSTATUS Status
= load_cache_chunk(fcb
->Vcb
, er
->chunk
, NULL
);
4476 if (!NT_SUCCESS(Status
)) {
4477 ERR("load_cache_chunk returned %08x\n", Status
);
4478 ExReleaseResourceLite(&er
->chunk
->lock
);
4483 er
->chunk
->used
-= er
->skip_end
;
4485 space_list_add(er
->chunk
, er
->address
+ er
->length
- er
->skip_end
, er
->skip_end
, NULL
);
4487 ExReleaseResourceLite(&er
->chunk
->lock
);
4489 er
->length
-= er
->skip_end
;
4496 if (num_extents
< 2)
4499 // merge together adjacent extents
4500 le
= extent_ranges
.Flink
;
4501 while (le
!= &extent_ranges
) {
4502 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4504 if (le
->Flink
!= &extent_ranges
&& er
->length
< MAX_EXTENT_SIZE
) {
4505 extent_range
* er2
= CONTAINING_RECORD(le
->Flink
, extent_range
, list_entry
);
4507 if (er
->chunk
== er2
->chunk
) {
4508 if (er2
->address
== er
->address
+ er
->length
&& er2
->offset
>= er
->offset
+ er
->length
) {
4509 if (er
->length
+ er2
->length
<= MAX_EXTENT_SIZE
) {
4510 er
->length
+= er2
->length
;
4513 RemoveEntryList(&er2
->list_entry
);
4529 le
= fcb
->extents
.Flink
;
4530 while (le
!= &fcb
->extents
) {
4531 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4533 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->unique
) {
4534 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4536 if (ed2
->size
!= 0) {
4539 le2
= extent_ranges
.Flink
;
4540 while (le2
!= &extent_ranges
) {
4541 extent_range
* er2
= CONTAINING_RECORD(le2
, extent_range
, list_entry
);
4543 if (ed2
->address
>= er2
->address
&& ed2
->address
+ ed2
->size
<= er2
->address
+ er2
->length
&& er2
->changed
) {
4546 Status
= update_changed_extent_ref(fcb
->Vcb
, er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4547 -1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, TRUE
, Irp
);
4548 if (!NT_SUCCESS(Status
)) {
4549 ERR("update_changed_extent_ref returned %08x\n", Status
);
4553 ed2
->offset
+= ed2
->address
- er2
->address
;
4554 ed2
->address
= er2
->address
;
4555 ed2
->size
= er2
->length
;
4556 ext
->extent_data
.decoded_size
= ed2
->size
;
4558 add_changed_extent_ref(er2
->chunk
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
,
4559 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
4573 while (!IsListEmpty(&extent_ranges
)) {
4574 le
= RemoveHeadList(&extent_ranges
);
4575 er
= CONTAINING_RECORD(le
, extent_range
, list_entry
);
4581 NTSTATUS
flush_fcb(fcb
* fcb
, BOOL cache
, LIST_ENTRY
* batchlist
, PIRP Irp
) {
4587 #ifdef DEBUG_PARANOID
4588 UINT64 old_size
= 0;
4589 BOOL extents_changed
;
4594 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
, fcb
->adshash
);
4595 if (!NT_SUCCESS(Status
)) {
4596 ERR("delete_xattr returned %08x\n", Status
);
4600 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
,
4601 fcb
->adshash
, (UINT8
*)fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
4602 if (!NT_SUCCESS(Status
)) {
4603 ERR("set_xattr returned %08x\n", Status
);
4608 Status
= STATUS_SUCCESS
;
4613 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0xffffffffffffffff, NULL
, 0, Batch_DeleteInode
);
4614 if (!NT_SUCCESS(Status
)) {
4615 ERR("insert_tree_item_batch returned %08x\n", Status
);
4619 Status
= STATUS_SUCCESS
;
4623 #ifdef DEBUG_PARANOID
4624 extents_changed
= fcb
->extents_changed
;
4627 if (fcb
->extents_changed
) {
4629 BOOL prealloc
= FALSE
, extents_inline
= FALSE
;
4632 // delete ignored extent items
4633 le
= fcb
->extents
.Flink
;
4634 while (le
!= &fcb
->extents
) {
4635 LIST_ENTRY
* le2
= le
->Flink
;
4636 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4639 RemoveEntryList(&ext
->list_entry
);
4642 ExFreePool(ext
->csum
);
4650 le
= fcb
->extents
.Flink
;
4651 while (le
!= &fcb
->extents
) {
4652 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4654 if (ext
->inserted
&& ext
->csum
&& ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
) {
4655 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4657 if (ed2
->size
> 0) { // not sparse
4658 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
)
4659 add_checksum_entry(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (ULONG
)(ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
), ext
->csum
, Irp
);
4661 add_checksum_entry(fcb
->Vcb
, ed2
->address
, (ULONG
)(ed2
->size
/ fcb
->Vcb
->superblock
.sector_size
), ext
->csum
, Irp
);
4668 if (!IsListEmpty(&fcb
->extents
)) {
4669 rationalize_extents(fcb
, Irp
);
4671 // merge together adjacent EXTENT_DATAs pointing to same extent
4673 le
= fcb
->extents
.Flink
;
4674 while (le
!= &fcb
->extents
) {
4675 LIST_ENTRY
* le2
= le
->Flink
;
4676 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4678 if ((ext
->extent_data
.type
== EXTENT_TYPE_REGULAR
|| ext
->extent_data
.type
== EXTENT_TYPE_PREALLOC
) && le
->Flink
!= &fcb
->extents
) {
4679 extent
* nextext
= CONTAINING_RECORD(le
->Flink
, extent
, list_entry
);
4681 if (ext
->extent_data
.type
== nextext
->extent_data
.type
) {
4682 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->extent_data
.data
;
4683 EXTENT_DATA2
* ned2
= (EXTENT_DATA2
*)nextext
->extent_data
.data
;
4685 if (ed2
->size
!= 0 && ed2
->address
== ned2
->address
&& ed2
->size
== ned2
->size
&&
4686 nextext
->offset
== ext
->offset
+ ed2
->num_bytes
&& ned2
->offset
== ed2
->offset
+ ed2
->num_bytes
) {
4689 if (ext
->extent_data
.compression
== BTRFS_COMPRESSION_NONE
&& ext
->csum
) {
4690 ULONG len
= (ULONG
)((ed2
->num_bytes
+ ned2
->num_bytes
) / fcb
->Vcb
->superblock
.sector_size
);
4693 csum
= ExAllocatePoolWithTag(NonPagedPool
, len
* sizeof(UINT32
), ALLOC_TAG
);
4695 ERR("out of memory\n");
4696 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4700 RtlCopyMemory(csum
, ext
->csum
, (ULONG
)(ed2
->num_bytes
* sizeof(UINT32
) / fcb
->Vcb
->superblock
.sector_size
));
4701 RtlCopyMemory(&csum
[ed2
->num_bytes
/ fcb
->Vcb
->superblock
.sector_size
], nextext
->csum
,
4702 (ULONG
)(ned2
->num_bytes
* sizeof(UINT32
) / fcb
->Vcb
->superblock
.sector_size
));
4704 ExFreePool(ext
->csum
);
4708 ext
->extent_data
.generation
= fcb
->Vcb
->superblock
.generation
;
4709 ed2
->num_bytes
+= ned2
->num_bytes
;
4711 RemoveEntryList(&nextext
->list_entry
);
4714 ExFreePool(nextext
->csum
);
4716 ExFreePool(nextext
);
4718 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
4721 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
4723 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
4724 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, FALSE
, Irp
);
4725 if (!NT_SUCCESS(Status
)) {
4726 ERR("update_changed_extent_ref returned %08x\n", Status
);
4740 if (!fcb
->created
) {
4741 // delete existing EXTENT_DATA items
4743 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, 0, NULL
, 0, Batch_DeleteExtentData
);
4744 if (!NT_SUCCESS(Status
)) {
4745 ERR("insert_tree_item_batch returned %08x\n", Status
);
4750 // add new EXTENT_DATAs
4754 le
= fcb
->extents
.Flink
;
4755 while (le
!= &fcb
->extents
) {
4756 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4759 ext
->inserted
= FALSE
;
4761 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && ext
->offset
> last_end
) {
4762 Status
= insert_sparse_extent(fcb
, batchlist
, last_end
, ext
->offset
- last_end
);
4763 if (!NT_SUCCESS(Status
)) {
4764 ERR("insert_sparse_extent returned %08x\n", Status
);
4769 ed
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
4771 ERR("out of memory\n");
4772 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4776 RtlCopyMemory(ed
, &ext
->extent_data
, ext
->datalen
);
4778 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, ext
->offset
,
4779 ed
, ext
->datalen
, Batch_Insert
);
4780 if (!NT_SUCCESS(Status
)) {
4781 ERR("insert_tree_item_batch returned %08x\n", Status
);
4785 if (ed
->type
== EXTENT_TYPE_PREALLOC
)
4788 if (ed
->type
== EXTENT_TYPE_INLINE
)
4789 extents_inline
= TRUE
;
4791 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
)) {
4792 if (ed
->type
== EXTENT_TYPE_INLINE
)
4793 last_end
= ext
->offset
+ ed
->decoded_size
;
4795 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4797 last_end
= ext
->offset
+ ed2
->num_bytes
;
4804 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && !extents_inline
&&
4805 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
) {
4806 Status
= insert_sparse_extent(fcb
, batchlist
, last_end
, sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
);
4807 if (!NT_SUCCESS(Status
)) {
4808 ERR("insert_sparse_extent returned %08x\n", Status
);
4813 // update prealloc flag in INODE_ITEM
4816 fcb
->inode_item
.flags
&= ~BTRFS_INODE_PREALLOC
;
4818 fcb
->inode_item
.flags
|= BTRFS_INODE_PREALLOC
;
4820 fcb
->inode_item_changed
= TRUE
;
4822 fcb
->extents_changed
= FALSE
;
4825 if ((!fcb
->created
&& fcb
->inode_item_changed
) || cache
) {
4826 searchkey
.obj_id
= fcb
->inode
;
4827 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4828 searchkey
.offset
= 0xffffffffffffffff;
4830 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4831 if (!NT_SUCCESS(Status
)) {
4832 ERR("error - find_item returned %08x\n", Status
);
4836 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4838 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4840 ERR("out of memory\n");
4841 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4845 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4847 Status
= insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
);
4848 if (!NT_SUCCESS(Status
)) {
4849 ERR("insert_tree_item returned %08x\n", Status
);
4855 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4856 Status
= STATUS_INTERNAL_ERROR
;
4860 #ifdef DEBUG_PARANOID
4861 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp
.item
->data
;
4863 old_size
= ii2
->st_size
;
4866 ii_offset
= tp
.item
->key
.offset
;
4870 Status
= delete_tree_item(fcb
->Vcb
, &tp
);
4871 if (!NT_SUCCESS(Status
)) {
4872 ERR("delete_tree_item returned %08x\n", Status
);
4876 searchkey
.obj_id
= fcb
->inode
;
4877 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4878 searchkey
.offset
= ii_offset
;
4880 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4881 if (!NT_SUCCESS(Status
)) {
4882 ERR("error - find_item returned %08x\n", Status
);
4886 if (keycmp(tp
.item
->key
, searchkey
)) {
4887 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4888 Status
= STATUS_INTERNAL_ERROR
;
4891 RtlCopyMemory(tp
.item
->data
, &fcb
->inode_item
, min(tp
.item
->size
, sizeof(INODE_ITEM
)));
4894 #ifdef DEBUG_PARANOID
4895 if (!extents_changed
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& old_size
!= fcb
->inode_item
.st_size
) {
4896 ERR("error - size has changed but extents not marked as changed\n");
4903 fcb
->created
= FALSE
;
4905 if (!cache
&& fcb
->inode_item_changed
) {
4906 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4908 ERR("out of memory\n");
4909 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4913 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4915 Status
= insert_tree_item_batch(batchlist
, fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, ii_offset
, ii
, sizeof(INODE_ITEM
),
4917 if (!NT_SUCCESS(Status
)) {
4918 ERR("insert_tree_item_batch returned %08x\n", Status
);
4922 fcb
->inode_item_changed
= FALSE
;
4925 if (fcb
->sd_dirty
) {
4926 if (!fcb
->sd_deleted
) {
4927 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, (UINT16
)strlen(EA_NTACL
),
4928 EA_NTACL_HASH
, (UINT8
*)fcb
->sd
, (UINT16
)RtlLengthSecurityDescriptor(fcb
->sd
));
4929 if (!NT_SUCCESS(Status
)) {
4930 ERR("set_xattr returned %08x\n", Status
);
4934 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, (UINT16
)strlen(EA_NTACL
), EA_NTACL_HASH
);
4935 if (!NT_SUCCESS(Status
)) {
4936 ERR("delete_xattr returned %08x\n", Status
);
4941 fcb
->sd_deleted
= FALSE
;
4942 fcb
->sd_dirty
= FALSE
;
4945 if (fcb
->atts_changed
) {
4946 if (!fcb
->atts_deleted
) {
4947 UINT8 val
[16], *val2
;
4948 ULONG atts
= fcb
->atts
;
4950 TRACE("inserting new DOSATTRIB xattr\n");
4952 if (fcb
->inode
== SUBVOL_ROOT_INODE
)
4953 atts
&= ~FILE_ATTRIBUTE_READONLY
;
4955 val2
= &val
[sizeof(val
) - 1];
4958 UINT8 c
= atts
% 16;
4959 *val2
= c
<= 9 ? (c
+ '0') : (c
- 0xa + 'a');
4963 } while (atts
!= 0);
4969 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, (UINT16
)strlen(EA_DOSATTRIB
),
4970 EA_DOSATTRIB_HASH
, val2
, (UINT16
)(val
+ sizeof(val
) - val2
));
4971 if (!NT_SUCCESS(Status
)) {
4972 ERR("set_xattr returned %08x\n", Status
);
4976 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, (UINT16
)strlen(EA_DOSATTRIB
), EA_DOSATTRIB_HASH
);
4977 if (!NT_SUCCESS(Status
)) {
4978 ERR("delete_xattr returned %08x\n", Status
);
4983 fcb
->atts_changed
= FALSE
;
4984 fcb
->atts_deleted
= FALSE
;
4987 if (fcb
->reparse_xattr_changed
) {
4988 if (fcb
->reparse_xattr
.Buffer
&& fcb
->reparse_xattr
.Length
> 0) {
4989 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, (UINT16
)strlen(EA_REPARSE
),
4990 EA_REPARSE_HASH
, (UINT8
*)fcb
->reparse_xattr
.Buffer
, (UINT16
)fcb
->reparse_xattr
.Length
);
4991 if (!NT_SUCCESS(Status
)) {
4992 ERR("set_xattr returned %08x\n", Status
);
4996 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, (UINT16
)strlen(EA_REPARSE
), EA_REPARSE_HASH
);
4997 if (!NT_SUCCESS(Status
)) {
4998 ERR("delete_xattr returned %08x\n", Status
);
5003 fcb
->reparse_xattr_changed
= FALSE
;
5006 if (fcb
->ea_changed
) {
5007 if (fcb
->ea_xattr
.Buffer
&& fcb
->ea_xattr
.Length
> 0) {
5008 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_EA
, (UINT16
)strlen(EA_EA
),
5009 EA_EA_HASH
, (UINT8
*)fcb
->ea_xattr
.Buffer
, (UINT16
)fcb
->ea_xattr
.Length
);
5010 if (!NT_SUCCESS(Status
)) {
5011 ERR("set_xattr returned %08x\n", Status
);
5015 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_EA
, (UINT16
)strlen(EA_EA
), EA_EA_HASH
);
5016 if (!NT_SUCCESS(Status
)) {
5017 ERR("delete_xattr returned %08x\n", Status
);
5022 fcb
->ea_changed
= FALSE
;
5025 if (fcb
->prop_compression_changed
) {
5026 if (fcb
->prop_compression
== PropCompression_None
) {
5027 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
), EA_PROP_COMPRESSION_HASH
);
5028 if (!NT_SUCCESS(Status
)) {
5029 ERR("delete_xattr returned %08x\n", Status
);
5032 } else if (fcb
->prop_compression
== PropCompression_Zlib
) {
5033 const char zlib
[] = "zlib";
5035 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
),
5036 EA_PROP_COMPRESSION_HASH
, (UINT8
*)zlib
, (UINT16
)strlen(zlib
));
5037 if (!NT_SUCCESS(Status
)) {
5038 ERR("set_xattr returned %08x\n", Status
);
5041 } else if (fcb
->prop_compression
== PropCompression_LZO
) {
5042 const char lzo
[] = "lzo";
5044 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, EA_PROP_COMPRESSION
, (UINT16
)strlen(EA_PROP_COMPRESSION
),
5045 EA_PROP_COMPRESSION_HASH
, (UINT8
*)lzo
, (UINT16
)strlen(lzo
));
5046 if (!NT_SUCCESS(Status
)) {
5047 ERR("set_xattr returned %08x\n", Status
);
5052 fcb
->prop_compression_changed
= FALSE
;
5055 if (fcb
->xattrs_changed
) {
5058 le
= fcb
->xattrs
.Flink
;
5059 while (le
!= &fcb
->xattrs
) {
5060 xattr
* xa
= CONTAINING_RECORD(le
, xattr
, list_entry
);
5061 LIST_ENTRY
* le2
= le
->Flink
;
5064 UINT32 hash
= calc_crc32c(0xfffffffe, (UINT8
*)xa
->data
, xa
->namelen
);
5066 if (xa
->valuelen
== 0) {
5067 Status
= delete_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, xa
->data
, xa
->namelen
, hash
);
5068 if (!NT_SUCCESS(Status
)) {
5069 ERR("delete_xattr returned %08x\n", Status
);
5073 RemoveEntryList(&xa
->list_entry
);
5076 Status
= set_xattr(fcb
->Vcb
, batchlist
, fcb
->subvol
, fcb
->inode
, xa
->data
, xa
->namelen
,
5077 hash
, (UINT8
*)&xa
->data
[xa
->namelen
], xa
->valuelen
);
5078 if (!NT_SUCCESS(Status
)) {
5079 ERR("set_xattr returned %08x\n", Status
);
5090 fcb
->xattrs_changed
= FALSE
;
5093 Status
= STATUS_SUCCESS
;
5101 if (!ExIsResourceAcquiredExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
)) {
5102 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->dirty_fcbs_lock
, TRUE
);
5106 RemoveEntryList(&fcb
->list_entry_dirty
);
5109 ExReleaseResourceLite(&fcb
->Vcb
->dirty_fcbs_lock
);
5115 void add_trim_entry_avoid_sb(device_extension
* Vcb
, device
* dev
, UINT64 address
, UINT64 size
) {
5117 ULONG sblen
= (ULONG
)sector_align(sizeof(superblock
), Vcb
->superblock
.sector_size
);
5120 while (superblock_addrs
[i
] != 0) {
5121 if (superblock_addrs
[i
] + sblen
>= address
&& superblock_addrs
[i
] < address
+ size
) {
5122 if (superblock_addrs
[i
] > address
)
5123 add_trim_entry(dev
, address
, superblock_addrs
[i
] - address
);
5125 if (size
<= superblock_addrs
[i
] + sblen
- address
)
5128 size
-= superblock_addrs
[i
] + sblen
- address
;
5129 address
= superblock_addrs
[i
] + sblen
;
5130 } else if (superblock_addrs
[i
] > address
+ size
)
5136 add_trim_entry(dev
, address
, size
);
5139 static NTSTATUS
drop_chunk(device_extension
* Vcb
, chunk
* c
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5144 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];;
5146 TRACE("dropping chunk %llx\n", c
->offset
);
5148 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
5149 factor
= c
->chunk_item
->num_stripes
;
5150 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
5151 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
5152 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
)
5153 factor
= c
->chunk_item
->num_stripes
- 1;
5154 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)
5155 factor
= c
->chunk_item
->num_stripes
- 2;
5156 else // SINGLE, DUPLICATE, RAID1
5160 if (Vcb
->trim
&& !Vcb
->options
.no_trim
) {
5161 UINT64 len
= c
->chunk_item
->size
/ factor
;
5163 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5164 if (c
->devices
[i
] && c
->devices
[i
]->devobj
&& !c
->devices
[i
]->readonly
&& c
->devices
[i
]->trim
)
5165 add_trim_entry_avoid_sb(Vcb
, c
->devices
[i
], cis
[i
].offset
, len
);
5170 Status
= load_stored_free_space_cache(Vcb
, c
, TRUE
, Irp
);
5172 if (!NT_SUCCESS(Status
) && Status
!= STATUS_NOT_FOUND
)
5173 WARN("load_stored_free_space_cache returned %08x\n", Status
);
5176 // remove free space cache
5178 c
->cache
->deleted
= TRUE
;
5180 Status
= excise_extents(Vcb
, c
->cache
, 0, c
->cache
->inode_item
.st_size
, Irp
, rollback
);
5181 if (!NT_SUCCESS(Status
)) {
5182 ERR("excise_extents returned %08x\n", Status
);
5186 Status
= flush_fcb(c
->cache
, TRUE
, batchlist
, Irp
);
5188 free_fcb(Vcb
, c
->cache
);
5190 if (!NT_SUCCESS(Status
)) {
5191 ERR("flush_fcb returned %08x\n", Status
);
5195 searchkey
.obj_id
= FREE_SPACE_CACHE_ID
;
5196 searchkey
.obj_type
= 0;
5197 searchkey
.offset
= c
->offset
;
5199 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5200 if (!NT_SUCCESS(Status
)) {
5201 ERR("error - find_item returned %08x\n", Status
);
5205 if (!keycmp(tp
.item
->key
, searchkey
)) {
5206 Status
= delete_tree_item(Vcb
, &tp
);
5207 if (!NT_SUCCESS(Status
)) {
5208 ERR("delete_tree_item returned %08x\n", Status
);
5214 if (Vcb
->space_root
) {
5215 Status
= insert_tree_item_batch(batchlist
, Vcb
, Vcb
->space_root
, c
->offset
, TYPE_FREE_SPACE_INFO
, c
->chunk_item
->size
,
5216 NULL
, 0, Batch_DeleteFreeSpace
);
5217 if (!NT_SUCCESS(Status
)) {
5218 ERR("insert_tree_item_batch returned %08x\n", Status
);
5223 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5225 // remove DEV_EXTENTs from tree 4
5226 searchkey
.obj_id
= cis
[i
].dev_id
;
5227 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
5228 searchkey
.offset
= cis
[i
].offset
;
5230 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
5231 if (!NT_SUCCESS(Status
)) {
5232 ERR("error - find_item returned %08x\n", Status
);
5236 if (!keycmp(tp
.item
->key
, searchkey
)) {
5237 Status
= delete_tree_item(Vcb
, &tp
);
5238 if (!NT_SUCCESS(Status
)) {
5239 ERR("delete_tree_item returned %08x\n", Status
);
5243 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
5244 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
5246 c
->devices
[i
]->devitem
.bytes_used
-= de
->length
;
5248 if (Vcb
->balance
.thread
&& Vcb
->balance
.shrinking
&& Vcb
->balance
.opts
[0].devid
== c
->devices
[i
]->devitem
.dev_id
) {
5249 if (cis
[i
].offset
< Vcb
->balance
.opts
[0].drange_start
&& cis
[i
].offset
+ de
->length
> Vcb
->balance
.opts
[0].drange_start
)
5250 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, Vcb
->balance
.opts
[0].drange_start
- cis
[i
].offset
, NULL
, rollback
);
5252 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, de
->length
, NULL
, rollback
);
5255 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
5257 UINT64 len
= c
->chunk_item
->size
/ factor
;
5259 c
->devices
[i
]->devitem
.bytes_used
-= len
;
5261 if (Vcb
->balance
.thread
&& Vcb
->balance
.shrinking
&& Vcb
->balance
.opts
[0].devid
== c
->devices
[i
]->devitem
.dev_id
) {
5262 if (cis
[i
].offset
< Vcb
->balance
.opts
[0].drange_start
&& cis
[i
].offset
+ len
> Vcb
->balance
.opts
[0].drange_start
)
5263 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, Vcb
->balance
.opts
[0].drange_start
- cis
[i
].offset
, NULL
, rollback
);
5265 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, len
, NULL
, rollback
);
5269 // modify DEV_ITEMs in chunk tree
5270 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5271 if (c
->devices
[i
]) {
5275 searchkey
.obj_id
= 1;
5276 searchkey
.obj_type
= TYPE_DEV_ITEM
;
5277 searchkey
.offset
= c
->devices
[i
]->devitem
.dev_id
;
5279 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
5280 if (!NT_SUCCESS(Status
)) {
5281 ERR("error - find_item returned %08x\n", Status
);
5285 if (keycmp(tp
.item
->key
, searchkey
)) {
5286 ERR("error - could not find DEV_ITEM for device %llx\n", searchkey
.offset
);
5287 return STATUS_INTERNAL_ERROR
;
5290 Status
= delete_tree_item(Vcb
, &tp
);
5291 if (!NT_SUCCESS(Status
)) {
5292 ERR("delete_tree_item returned %08x\n", Status
);
5296 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
5298 ERR("out of memory\n");
5299 return STATUS_INSUFFICIENT_RESOURCES
;
5302 RtlCopyMemory(di
, &c
->devices
[i
]->devitem
, sizeof(DEV_ITEM
));
5304 Status
= insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, c
->devices
[i
]->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
);
5305 if (!NT_SUCCESS(Status
)) {
5306 ERR("insert_tree_item returned %08x\n", Status
);
5310 for (j
= i
+ 1; j
< c
->chunk_item
->num_stripes
; j
++) {
5311 if (c
->devices
[j
] == c
->devices
[i
])
5312 c
->devices
[j
] = NULL
;
5318 // remove CHUNK_ITEM from chunk tree
5319 searchkey
.obj_id
= 0x100;
5320 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
5321 searchkey
.offset
= c
->offset
;
5323 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
5324 if (!NT_SUCCESS(Status
)) {
5325 ERR("error - find_item returned %08x\n", Status
);
5329 if (!keycmp(tp
.item
->key
, searchkey
)) {
5330 Status
= delete_tree_item(Vcb
, &tp
);
5332 if (!NT_SUCCESS(Status
)) {
5333 ERR("delete_tree_item returned %08x\n", Status
);
5337 WARN("could not find CHUNK_ITEM for chunk %llx\n", c
->offset
);
5339 // remove BLOCK_GROUP_ITEM from extent tree
5340 searchkey
.obj_id
= c
->offset
;
5341 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
5342 searchkey
.offset
= 0xffffffffffffffff;
5344 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5345 if (!NT_SUCCESS(Status
)) {
5346 ERR("error - find_item returned %08x\n", Status
);
5350 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
5351 Status
= delete_tree_item(Vcb
, &tp
);
5353 if (!NT_SUCCESS(Status
)) {
5354 ERR("delete_tree_item returned %08x\n", Status
);
5358 WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c
->offset
);
5361 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
)
5362 remove_from_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
);
5364 RemoveEntryList(&c
->list_entry
);
5366 // clear raid56 incompat flag if dropping last RAID5/6 chunk
5368 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
5370 BOOL clear_flag
= TRUE
;
5372 le
= Vcb
->chunks
.Flink
;
5373 while (le
!= &Vcb
->chunks
) {
5374 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5376 if (c2
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c2
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
5385 Vcb
->superblock
.incompat_flags
&= ~BTRFS_INCOMPAT_FLAGS_RAID56
;
5388 Vcb
->superblock
.bytes_used
-= c
->oldused
;
5390 ExFreePool(c
->chunk_item
);
5391 ExFreePool(c
->devices
);
5393 while (!IsListEmpty(&c
->space
)) {
5394 space
* s
= CONTAINING_RECORD(c
->space
.Flink
, space
, list_entry
);
5396 RemoveEntryList(&s
->list_entry
);
5400 while (!IsListEmpty(&c
->deleting
)) {
5401 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
5403 RemoveEntryList(&s
->list_entry
);
5407 ExDeleteResourceLite(&c
->partial_stripes_lock
);
5408 ExDeleteResourceLite(&c
->range_locks_lock
);
5409 ExDeleteResourceLite(&c
->lock
);
5410 ExDeleteResourceLite(&c
->changed_extents_lock
);
5414 return STATUS_SUCCESS
;
5417 static NTSTATUS
partial_stripe_read(device_extension
* Vcb
, chunk
* c
, partial_stripe
* ps
, UINT64 startoff
, UINT16 parity
, ULONG offset
, ULONG len
) {
5419 ULONG sl
= (ULONG
)(c
->chunk_item
->stripe_length
/ Vcb
->superblock
.sector_size
);
5420 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
5423 ULONG readlen
= min(offset
+ len
, offset
+ (sl
- (offset
% sl
))) - offset
;
5426 stripe
= (parity
+ (offset
/ sl
) + 1) % c
->chunk_item
->num_stripes
;
5428 if (c
->devices
[stripe
]->devobj
) {
5429 Status
= sync_read_phys(c
->devices
[stripe
]->devobj
, cis
[stripe
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5430 readlen
* Vcb
->superblock
.sector_size
, ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), FALSE
);
5431 if (!NT_SUCCESS(Status
)) {
5432 ERR("sync_read_phys returned %08x\n", Status
);
5435 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
5439 scratch
= ExAllocatePoolWithTag(NonPagedPool
, readlen
* Vcb
->superblock
.sector_size
, ALLOC_TAG
);
5441 ERR("out of memory\n");
5442 return STATUS_INSUFFICIENT_RESOURCES
;
5445 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
5447 if (!c
->devices
[i
]->devobj
) {
5448 ExFreePool(scratch
);
5449 return STATUS_UNEXPECTED_IO_ERROR
;
5452 if (i
== 0 || (stripe
== 0 && i
== 1)) {
5453 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5454 readlen
* Vcb
->superblock
.sector_size
, ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), FALSE
);
5455 if (!NT_SUCCESS(Status
)) {
5456 ERR("sync_read_phys returned %08x\n", Status
);
5457 ExFreePool(scratch
);
5461 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5462 readlen
* Vcb
->superblock
.sector_size
, scratch
, FALSE
);
5463 if (!NT_SUCCESS(Status
)) {
5464 ERR("sync_read_phys returned %08x\n", Status
);
5465 ExFreePool(scratch
);
5469 do_xor(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
, readlen
* Vcb
->superblock
.sector_size
);
5474 ExFreePool(scratch
);
5477 UINT16 k
, i
, logstripe
, error_stripe
, num_errors
= 0;
5479 scratch
= ExAllocatePoolWithTag(NonPagedPool
, (c
->chunk_item
->num_stripes
+ 2) * readlen
* Vcb
->superblock
.sector_size
, ALLOC_TAG
);
5481 ERR("out of memory\n");
5482 return STATUS_INSUFFICIENT_RESOURCES
;
5485 i
= (parity
+ 1) % c
->chunk_item
->num_stripes
;
5486 for (k
= 0; k
< c
->chunk_item
->num_stripes
; k
++) {
5488 if (c
->devices
[i
]->devobj
) {
5489 Status
= sync_read_phys(c
->devices
[i
]->devobj
, cis
[i
].offset
+ startoff
+ ((offset
% sl
) * Vcb
->superblock
.sector_size
),
5490 readlen
* Vcb
->superblock
.sector_size
, scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
), FALSE
);
5491 if (!NT_SUCCESS(Status
)) {
5492 ERR("sync_read_phys returned %08x\n", Status
);
5501 if (num_errors
> 1) {
5502 ExFreePool(scratch
);
5503 return STATUS_UNEXPECTED_IO_ERROR
;
5508 i
= (i
+ 1) % c
->chunk_item
->num_stripes
;
5511 if (num_errors
== 0 || error_stripe
== c
->chunk_item
->num_stripes
- 1) {
5512 for (k
= 0; k
< c
->chunk_item
->num_stripes
- 1; k
++) {
5513 if (k
!= logstripe
) {
5514 if (k
== 0 || (k
== 1 && logstripe
== 0)) {
5515 RtlCopyMemory(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
),
5516 readlen
* Vcb
->superblock
.sector_size
);
5518 do_xor(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (k
* readlen
* Vcb
->superblock
.sector_size
),
5519 readlen
* Vcb
->superblock
.sector_size
);
5524 raid6_recover2(scratch
, c
->chunk_item
->num_stripes
, readlen
* Vcb
->superblock
.sector_size
, logstripe
,
5525 error_stripe
, scratch
+ (c
->chunk_item
->num_stripes
* readlen
* Vcb
->superblock
.sector_size
));
5527 RtlCopyMemory(ps
->data
+ (offset
* Vcb
->superblock
.sector_size
), scratch
+ (c
->chunk_item
->num_stripes
* readlen
* Vcb
->superblock
.sector_size
),
5528 readlen
* Vcb
->superblock
.sector_size
);
5531 ExFreePool(scratch
);
5538 return STATUS_SUCCESS
;
5541 NTSTATUS
flush_partial_stripe(device_extension
* Vcb
, chunk
* c
, partial_stripe
* ps
) {
5543 UINT16 parity2
, stripe
, startoffstripe
;
5546 ULONG runlength
, index
, last1
;
5547 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
5549 UINT16 k
, num_data_stripes
= c
->chunk_item
->num_stripes
- (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
? 1 : 2);
5550 UINT64 ps_length
= num_data_stripes
* c
->chunk_item
->stripe_length
;
5551 ULONG stripe_length
= (ULONG
)c
->chunk_item
->stripe_length
;
5553 // FIXME - do writes asynchronously?
5555 get_raid0_offset(ps
->address
- c
->offset
, stripe_length
, num_data_stripes
, &startoff
, &startoffstripe
);
5557 parity2
= (((ps
->address
- c
->offset
) / ps_length
) + c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
5559 // read data (or reconstruct if degraded)
5561 runlength
= RtlFindFirstRunClear(&ps
->bmp
, &index
);
5564 while (runlength
!= 0) {
5565 if (index
> last1
) {
5566 Status
= partial_stripe_read(Vcb
, c
, ps
, startoff
, parity2
, last1
, index
- last1
);
5567 if (!NT_SUCCESS(Status
)) {
5568 ERR("partial_stripe_read returned %08x\n", Status
);
5573 last1
= index
+ runlength
;
5575 runlength
= RtlFindNextForwardRunClear(&ps
->bmp
, index
+ runlength
, &index
);
5578 if (last1
< ps_length
/ Vcb
->superblock
.sector_size
) {
5579 Status
= partial_stripe_read(Vcb
, c
, ps
, startoff
, parity2
, last1
, (ULONG
)((ps_length
/ Vcb
->superblock
.sector_size
) - last1
));
5580 if (!NT_SUCCESS(Status
)) {
5581 ERR("partial_stripe_read returned %08x\n", Status
);
5586 // set unallocated data to 0
5587 le
= c
->space
.Flink
;
5588 while (le
!= &c
->space
) {
5589 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
5591 if (s
->address
+ s
->size
> ps
->address
&& s
->address
< ps
->address
+ ps_length
) {
5592 UINT64 start
= max(ps
->address
, s
->address
);
5593 UINT64 end
= min(ps
->address
+ ps_length
, s
->address
+ s
->size
);
5595 RtlZeroMemory(ps
->data
+ start
- ps
->address
, (ULONG
)(end
- start
));
5596 } else if (s
->address
>= ps
->address
+ ps_length
)
5602 le
= c
->deleting
.Flink
;
5603 while (le
!= &c
->deleting
) {
5604 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
5606 if (s
->address
+ s
->size
> ps
->address
&& s
->address
< ps
->address
+ ps_length
) {
5607 UINT64 start
= max(ps
->address
, s
->address
);
5608 UINT64 end
= min(ps
->address
+ ps_length
, s
->address
+ s
->size
);
5610 RtlZeroMemory(ps
->data
+ start
- ps
->address
, (ULONG
)(end
- start
));
5611 } else if (s
->address
>= ps
->address
+ ps_length
)
5617 stripe
= (parity2
+ 1) % c
->chunk_item
->num_stripes
;
5620 for (k
= 0; k
< num_data_stripes
; k
++) {
5621 if (c
->devices
[stripe
]->devobj
) {
5622 Status
= write_data_phys(c
->devices
[stripe
]->devobj
, cis
[stripe
].offset
+ startoff
, data
, stripe_length
);
5623 if (!NT_SUCCESS(Status
)) {
5624 ERR("write_data_phys returned %08x\n", Status
);
5629 data
+= stripe_length
;
5630 stripe
= (stripe
+ 1) % c
->chunk_item
->num_stripes
;
5634 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
5635 if (c
->devices
[parity2
]->devobj
) {
5638 for (i
= 1; i
< c
->chunk_item
->num_stripes
- 1; i
++) {
5639 do_xor(ps
->data
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5642 Status
= write_data_phys(c
->devices
[parity2
]->devobj
, cis
[parity2
].offset
+ startoff
, ps
->data
, stripe_length
);
5643 if (!NT_SUCCESS(Status
)) {
5644 ERR("write_data_phys returned %08x\n", Status
);
5649 UINT16 parity1
= (parity2
+ c
->chunk_item
->num_stripes
- 1) % c
->chunk_item
->num_stripes
;
5651 if (c
->devices
[parity1
]->devobj
|| c
->devices
[parity2
]->devobj
) {
5655 scratch
= ExAllocatePoolWithTag(NonPagedPool
, stripe_length
* 2, ALLOC_TAG
);
5657 ERR("out of memory\n");
5658 return STATUS_INSUFFICIENT_RESOURCES
;
5661 i
= c
->chunk_item
->num_stripes
- 3;
5664 if (i
== c
->chunk_item
->num_stripes
- 3) {
5665 RtlCopyMemory(scratch
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5666 RtlCopyMemory(scratch
+ stripe_length
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5668 do_xor(scratch
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5670 galois_double(scratch
+ stripe_length
, stripe_length
);
5671 do_xor(scratch
+ stripe_length
, ps
->data
+ (i
* stripe_length
), stripe_length
);
5680 if (c
->devices
[parity1
]->devobj
) {
5681 Status
= write_data_phys(c
->devices
[parity1
]->devobj
, cis
[parity1
].offset
+ startoff
, scratch
, stripe_length
);
5682 if (!NT_SUCCESS(Status
)) {
5683 ERR("write_data_phys returned %08x\n", Status
);
5684 ExFreePool(scratch
);
5689 if (c
->devices
[parity2
]->devobj
) {
5690 Status
= write_data_phys(c
->devices
[parity2
]->devobj
, cis
[parity2
].offset
+ startoff
, scratch
+ stripe_length
, stripe_length
);
5691 if (!NT_SUCCESS(Status
)) {
5692 ERR("write_data_phys returned %08x\n", Status
);
5693 ExFreePool(scratch
);
5698 ExFreePool(scratch
);
5702 return STATUS_SUCCESS
;
5705 static NTSTATUS
update_chunks(device_extension
* Vcb
, LIST_ENTRY
* batchlist
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5706 LIST_ENTRY
*le
, *le2
;
5708 UINT64 used_minus_cache
;
5710 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
5712 // FIXME - do tree chunks before data chunks
5714 le
= Vcb
->chunks
.Flink
;
5715 while (le
!= &Vcb
->chunks
) {
5716 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
5721 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
5723 // flush partial stripes
5724 if (!Vcb
->readonly
&& (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID6
)) {
5725 ExAcquireResourceExclusiveLite(&c
->partial_stripes_lock
, TRUE
);
5727 while (!IsListEmpty(&c
->partial_stripes
)) {
5728 partial_stripe
* ps
= CONTAINING_RECORD(RemoveHeadList(&c
->partial_stripes
), partial_stripe
, list_entry
);
5730 Status
= flush_partial_stripe(Vcb
, c
, ps
);
5733 ExFreePool(ps
->bmparr
);
5737 if (!NT_SUCCESS(Status
)) {
5738 ERR("flush_partial_stripe returned %08x\n", Status
);
5739 ExReleaseResourceLite(&c
->partial_stripes_lock
);
5740 ExReleaseResourceLite(&c
->lock
);
5741 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5746 ExReleaseResourceLite(&c
->partial_stripes_lock
);
5749 if (c
->list_entry_balance
.Flink
) {
5750 ExReleaseResourceLite(&c
->lock
);
5755 if (c
->space_changed
|| c
->created
) {
5756 used_minus_cache
= c
->used
;
5758 // subtract self-hosted cache
5759 if (used_minus_cache
> 0 && c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->cache
&& c
->cache
->inode_item
.st_size
== c
->used
) {
5762 le3
= c
->cache
->extents
.Flink
;
5763 while (le3
!= &c
->cache
->extents
) {
5764 extent
* ext
= CONTAINING_RECORD(le3
, extent
, list_entry
);
5765 EXTENT_DATA
* ed
= &ext
->extent_data
;
5768 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
5769 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
5771 if (ed2
->size
!= 0 && ed2
->address
>= c
->offset
&& ed2
->address
+ ed2
->size
<= c
->offset
+ c
->chunk_item
->size
)
5772 used_minus_cache
-= ed2
->size
;
5780 if (used_minus_cache
== 0) {
5781 Status
= drop_chunk(Vcb
, c
, batchlist
, Irp
, rollback
);
5782 if (!NT_SUCCESS(Status
)) {
5783 ERR("drop_chunk returned %08x\n", Status
);
5784 ExReleaseResourceLite(&c
->lock
);
5785 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5788 } else if (c
->created
) {
5789 Status
= create_chunk(Vcb
, c
, Irp
);
5790 if (!NT_SUCCESS(Status
)) {
5791 ERR("create_chunk returned %08x\n", Status
);
5792 ExReleaseResourceLite(&c
->lock
);
5793 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5798 if (used_minus_cache
> 0)
5799 ExReleaseResourceLite(&c
->lock
);
5806 ExReleaseResourceLite(&Vcb
->chunk_lock
);
5808 return STATUS_SUCCESS
;
5811 static NTSTATUS
delete_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, UINT64 parinode
, PANSI_STRING utf8
, PIRP Irp
) {
5816 searchkey
.obj_id
= parsubvolid
;
5817 searchkey
.obj_type
= TYPE_ROOT_REF
;
5818 searchkey
.offset
= subvolid
;
5820 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5821 if (!NT_SUCCESS(Status
)) {
5822 ERR("error - find_item returned %08x\n", Status
);
5826 if (!keycmp(searchkey
, tp
.item
->key
)) {
5827 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
5828 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
));
5829 return STATUS_INTERNAL_ERROR
;
5834 rr
= (ROOT_REF
*)tp
.item
->data
;
5835 len
= tp
.item
->size
;
5840 if (len
< sizeof(ROOT_REF
) || len
< offsetof(ROOT_REF
, name
[0]) + rr
->n
) {
5841 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5845 itemlen
= (UINT16
)offsetof(ROOT_REF
, name
[0]) + rr
->n
;
5847 if (rr
->dir
== parinode
&& rr
->n
== utf8
->Length
&& RtlCompareMemory(rr
->name
, utf8
->Buffer
, rr
->n
) == rr
->n
) {
5848 UINT16 newlen
= tp
.item
->size
- itemlen
;
5850 Status
= delete_tree_item(Vcb
, &tp
);
5851 if (!NT_SUCCESS(Status
)) {
5852 ERR("delete_tree_item returned %08x\n", Status
);
5857 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5859 UINT8
*newrr
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *rroff
;
5862 ERR("out of memory\n");
5863 return STATUS_INSUFFICIENT_RESOURCES
;
5866 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
5868 if ((UINT8
*)rr
> tp
.item
->data
) {
5869 RtlCopyMemory(newrr
, tp
.item
->data
, (UINT8
*)rr
- tp
.item
->data
);
5870 rroff
= newrr
+ ((UINT8
*)rr
- tp
.item
->data
);
5875 if ((UINT8
*)&rr
->name
[rr
->n
] < tp
.item
->data
+ tp
.item
->size
)
5876 RtlCopyMemory(rroff
, &rr
->name
[rr
->n
], tp
.item
->size
- ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
));
5878 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
);
5879 if (!NT_SUCCESS(Status
)) {
5880 ERR("insert_tree_item returned %08x\n", Status
);
5889 if (len
> itemlen
) {
5891 rr
= (ROOT_REF
*)&rr
->name
[rr
->n
];
5897 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey
.offset
, searchkey
.obj_id
);
5898 return STATUS_NOT_FOUND
;
5901 return STATUS_SUCCESS
;
5905 #pragma warning(push)
5906 #pragma warning(suppress: 28194)
5908 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
) {
5913 searchkey
.obj_id
= parsubvolid
;
5914 searchkey
.obj_type
= TYPE_ROOT_REF
;
5915 searchkey
.offset
= subvolid
;
5917 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5918 if (!NT_SUCCESS(Status
)) {
5919 ERR("error - find_item returned %08x\n", Status
);
5923 if (!keycmp(searchkey
, tp
.item
->key
)) {
5924 UINT16 rrsize
= tp
.item
->size
+ (UINT16
)offsetof(ROOT_REF
, name
[0]) + rr
->n
;
5927 rr2
= ExAllocatePoolWithTag(PagedPool
, rrsize
, ALLOC_TAG
);
5929 ERR("out of memory\n");
5930 return STATUS_INSUFFICIENT_RESOURCES
;
5933 if (tp
.item
->size
> 0)
5934 RtlCopyMemory(rr2
, tp
.item
->data
, tp
.item
->size
);
5936 RtlCopyMemory(rr2
+ tp
.item
->size
, rr
, offsetof(ROOT_REF
, name
[0]) + rr
->n
);
5939 Status
= delete_tree_item(Vcb
, &tp
);
5940 if (!NT_SUCCESS(Status
)) {
5941 ERR("delete_tree_item returned %08x\n", Status
);
5946 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr2
, rrsize
, NULL
, Irp
);
5947 if (!NT_SUCCESS(Status
)) {
5948 ERR("insert_tree_item returned %08x\n", Status
);
5953 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
);
5954 if (!NT_SUCCESS(Status
)) {
5955 ERR("insert_tree_item returned %08x\n", Status
);
5961 return STATUS_SUCCESS
;
5964 #pragma warning(pop)
5967 static NTSTATUS
update_root_backref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, PIRP Irp
) {
5974 searchkey
.obj_id
= parsubvolid
;
5975 searchkey
.obj_type
= TYPE_ROOT_REF
;
5976 searchkey
.offset
= subvolid
;
5978 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5979 if (!NT_SUCCESS(Status
)) {
5980 ERR("error - find_item returned %08x\n", Status
);
5984 if (!keycmp(tp
.item
->key
, searchkey
) && tp
.item
->size
> 0) {
5985 datalen
= tp
.item
->size
;
5987 data
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
5989 ERR("out of memory\n");
5990 return STATUS_INSUFFICIENT_RESOURCES
;
5993 RtlCopyMemory(data
, tp
.item
->data
, datalen
);
5999 searchkey
.obj_id
= subvolid
;
6000 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
6001 searchkey
.offset
= parsubvolid
;
6003 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6004 if (!NT_SUCCESS(Status
)) {
6005 ERR("error - find_item returned %08x\n", Status
);
6013 if (!keycmp(tp
.item
->key
, searchkey
)) {
6014 Status
= delete_tree_item(Vcb
, &tp
);
6015 if (!NT_SUCCESS(Status
)) {
6016 ERR("delete_tree_item returned %08x\n", Status
);
6026 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, subvolid
, TYPE_ROOT_BACKREF
, parsubvolid
, data
, datalen
, NULL
, Irp
);
6027 if (!NT_SUCCESS(Status
)) {
6028 ERR("insert_tree_item returned %08x\n", Status
);
6034 return STATUS_SUCCESS
;
6037 static NTSTATUS
add_root_item_to_cache(device_extension
* Vcb
, UINT64 root
, PIRP Irp
) {
6042 searchkey
.obj_id
= root
;
6043 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
6044 searchkey
.offset
= 0xffffffffffffffff;
6046 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6047 if (!NT_SUCCESS(Status
)) {
6048 ERR("error - find_item returned %08x\n", Status
);
6052 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6053 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
6054 return STATUS_INTERNAL_ERROR
;
6057 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
6058 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
6060 ERR("out of memory\n");
6061 return STATUS_INSUFFICIENT_RESOURCES
;
6064 if (tp
.item
->size
> 0)
6065 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
6067 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
6069 Status
= delete_tree_item(Vcb
, &tp
);
6070 if (!NT_SUCCESS(Status
)) {
6071 ERR("delete_tree_item returned %08x\n", Status
);
6076 Status
= insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
);
6077 if (!NT_SUCCESS(Status
)) {
6078 ERR("insert_tree_item returned %08x\n", Status
);
6083 tp
.tree
->write
= TRUE
;
6086 return STATUS_SUCCESS
;
6089 static NTSTATUS
flush_fileref(file_ref
* fileref
, LIST_ENTRY
* batchlist
, PIRP Irp
) {
6092 // if fileref created and then immediately deleted, do nothing
6093 if (fileref
->created
&& fileref
->deleted
) {
6094 fileref
->dirty
= FALSE
;
6095 return STATUS_SUCCESS
;
6098 if (fileref
->fcb
->ads
) {
6099 fileref
->dirty
= FALSE
;
6100 return STATUS_SUCCESS
;
6103 if (fileref
->created
) {
6108 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6110 disize
= (UINT16
)(offsetof(DIR_ITEM
, name
[0]) + fileref
->dc
->utf8
.Length
);
6111 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6113 ERR("out of memory\n");
6114 return STATUS_INSUFFICIENT_RESOURCES
;
6117 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6118 di
->key
.obj_id
= fileref
->fcb
->inode
;
6119 di
->key
.obj_type
= TYPE_INODE_ITEM
;
6121 } else { // subvolume
6122 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
6123 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
6124 di
->key
.offset
= 0xffffffffffffffff;
6127 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
6129 di
->n
= (UINT16
)fileref
->dc
->utf8
.Length
;
6130 di
->type
= fileref
->fcb
->type
;
6131 RtlCopyMemory(di
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6133 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6135 ERR("out of memory\n");
6136 return STATUS_INSUFFICIENT_RESOURCES
;
6139 RtlCopyMemory(di2
, di
, disize
);
6141 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6142 fileref
->dc
->index
, di
, disize
, Batch_Insert
);
6143 if (!NT_SUCCESS(Status
)) {
6144 ERR("insert_tree_item_batch returned %08x\n", Status
);
6148 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
, crc32
,
6149 di2
, disize
, Batch_DirItem
);
6150 if (!NT_SUCCESS(Status
)) {
6151 ERR("insert_tree_item_batch returned %08x\n", Status
);
6155 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6158 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
6160 ERR("out of memory\n");
6161 return STATUS_INSUFFICIENT_RESOURCES
;
6164 ir
->index
= fileref
->dc
->index
;
6165 ir
->n
= fileref
->dc
->utf8
.Length
;
6166 RtlCopyMemory(ir
->name
, fileref
->dc
->utf8
.Buffer
, ir
->n
);
6168 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6169 ir
, sizeof(INODE_REF
) - 1 + ir
->n
, Batch_InodeRef
);
6170 if (!NT_SUCCESS(Status
)) {
6171 ERR("insert_tree_item_batch returned %08x\n", Status
);
6174 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) {
6178 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->dc
->utf8
.Length
;
6180 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
6182 ERR("out of memory\n");
6183 return STATUS_INSUFFICIENT_RESOURCES
;
6186 rr
->dir
= fileref
->parent
->fcb
->inode
;
6187 rr
->index
= fileref
->dc
->index
;
6188 rr
->n
= fileref
->dc
->utf8
.Length
;
6189 RtlCopyMemory(rr
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6191 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
);
6192 if (!NT_SUCCESS(Status
)) {
6193 ERR("add_root_ref returned %08x\n", Status
);
6197 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6198 if (!NT_SUCCESS(Status
)) {
6199 ERR("update_root_backref returned %08x\n", Status
);
6204 fileref
->created
= FALSE
;
6205 } else if (fileref
->deleted
) {
6210 name
= &fileref
->oldutf8
;
6212 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)name
->Buffer
, name
->Length
);
6214 TRACE("deleting %.*S\n", file_desc_fileref(fileref
));
6216 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + name
->Length
, ALLOC_TAG
);
6218 ERR("out of memory\n");
6219 return STATUS_INSUFFICIENT_RESOURCES
;
6223 di
->n
= name
->Length
;
6224 RtlCopyMemory(di
->name
, name
->Buffer
, name
->Length
);
6226 // delete DIR_ITEM (0x54)
6228 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
,
6229 crc32
, di
, sizeof(DIR_ITEM
) - 1 + name
->Length
, Batch_DeleteDirItem
);
6230 if (!NT_SUCCESS(Status
)) {
6231 ERR("insert_tree_item_batch returned %08x\n", Status
);
6235 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6238 // delete INODE_REF (0xc)
6240 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + name
->Length
, ALLOC_TAG
);
6242 ERR("out of memory\n");
6243 return STATUS_INSUFFICIENT_RESOURCES
;
6246 ir
->index
= fileref
->oldindex
;
6247 ir
->n
= name
->Length
;
6248 RtlCopyMemory(ir
->name
, name
->Buffer
, name
->Length
);
6250 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
,
6251 fileref
->parent
->fcb
->inode
, ir
, sizeof(INODE_REF
) - 1 + name
->Length
, Batch_DeleteInodeRef
);
6252 if (!NT_SUCCESS(Status
)) {
6253 ERR("insert_tree_item_batch returned %08x\n", Status
);
6256 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) { // subvolume
6257 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, name
, Irp
);
6258 if (!NT_SUCCESS(Status
)) {
6259 ERR("delete_root_ref returned %08x\n", Status
);
6263 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6264 if (!NT_SUCCESS(Status
)) {
6265 ERR("update_root_backref returned %08x\n", Status
);
6270 // delete DIR_INDEX (0x60)
6272 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6273 fileref
->oldindex
, NULL
, 0, Batch_Delete
);
6274 if (!NT_SUCCESS(Status
)) {
6275 ERR("insert_tree_item_batch returned %08x\n", Status
);
6279 if (fileref
->oldutf8
.Buffer
) {
6280 ExFreePool(fileref
->oldutf8
.Buffer
);
6281 fileref
->oldutf8
.Buffer
= NULL
;
6283 } else { // rename or change type
6284 PANSI_STRING oldutf8
= fileref
->oldutf8
.Buffer
? &fileref
->oldutf8
: &fileref
->dc
->utf8
;
6285 UINT32 crc32
, oldcrc32
;
6287 DIR_ITEM
*olddi
, *di
, *di2
;
6289 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6291 if (!fileref
->oldutf8
.Buffer
)
6294 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->oldutf8
.Buffer
, fileref
->oldutf8
.Length
);
6296 olddi
= ExAllocatePoolWithTag(PagedPool
, sizeof(DIR_ITEM
) - 1 + oldutf8
->Length
, ALLOC_TAG
);
6298 ERR("out of memory\n");
6299 return STATUS_INSUFFICIENT_RESOURCES
;
6303 olddi
->n
= (UINT16
)oldutf8
->Length
;
6304 RtlCopyMemory(olddi
->name
, oldutf8
->Buffer
, oldutf8
->Length
);
6306 // delete DIR_ITEM (0x54)
6308 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
,
6309 oldcrc32
, olddi
, sizeof(DIR_ITEM
) - 1 + oldutf8
->Length
, Batch_DeleteDirItem
);
6310 if (!NT_SUCCESS(Status
)) {
6311 ERR("insert_tree_item_batch returned %08x\n", Status
);
6316 // add DIR_ITEM (0x54)
6318 disize
= (UINT16
)(offsetof(DIR_ITEM
, name
[0]) + fileref
->dc
->utf8
.Length
);
6319 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6321 ERR("out of memory\n");
6322 return STATUS_INSUFFICIENT_RESOURCES
;
6325 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
6327 ERR("out of memory\n");
6329 return STATUS_INSUFFICIENT_RESOURCES
;
6333 di
->key
= fileref
->dc
->key
;
6334 else if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6335 di
->key
.obj_id
= fileref
->fcb
->inode
;
6336 di
->key
.obj_type
= TYPE_INODE_ITEM
;
6338 } else { // subvolume
6339 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
6340 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
6341 di
->key
.offset
= 0xffffffffffffffff;
6344 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
6346 di
->n
= (UINT16
)fileref
->dc
->utf8
.Length
;
6347 di
->type
= fileref
->fcb
->type
;
6348 RtlCopyMemory(di
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6350 RtlCopyMemory(di2
, di
, disize
);
6352 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_ITEM
, crc32
,
6353 di
, disize
, Batch_DirItem
);
6354 if (!NT_SUCCESS(Status
)) {
6355 ERR("insert_tree_item_batch returned %08x\n", Status
);
6361 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
6362 INODE_REF
*ir
, *ir2
;
6364 // delete INODE_REF (0xc)
6366 ir
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + oldutf8
->Length
, ALLOC_TAG
);
6368 ERR("out of memory\n");
6370 return STATUS_INSUFFICIENT_RESOURCES
;
6373 ir
->index
= fileref
->dc
->index
;
6374 ir
->n
= oldutf8
->Length
;
6375 RtlCopyMemory(ir
->name
, oldutf8
->Buffer
, ir
->n
);
6377 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6378 ir
, sizeof(INODE_REF
) - 1 + ir
->n
, Batch_DeleteInodeRef
);
6379 if (!NT_SUCCESS(Status
)) {
6380 ERR("insert_tree_item_batch returned %08x\n", Status
);
6386 // add INODE_REF (0xc)
6388 ir2
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_REF
) - 1 + fileref
->dc
->utf8
.Length
, ALLOC_TAG
);
6390 ERR("out of memory\n");
6392 return STATUS_INSUFFICIENT_RESOURCES
;
6395 ir2
->index
= fileref
->dc
->index
;
6396 ir2
->n
= fileref
->dc
->utf8
.Length
;
6397 RtlCopyMemory(ir2
->name
, fileref
->dc
->utf8
.Buffer
, ir2
->n
);
6399 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
, fileref
->fcb
->inode
, TYPE_INODE_REF
, fileref
->parent
->fcb
->inode
,
6400 ir2
, sizeof(INODE_REF
) - 1 + ir2
->n
, Batch_InodeRef
);
6401 if (!NT_SUCCESS(Status
)) {
6402 ERR("insert_tree_item_batch returned %08x\n", Status
);
6407 } else if (fileref
->fcb
!= fileref
->fcb
->Vcb
->dummy_fcb
) { // subvolume
6411 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
);
6412 if (!NT_SUCCESS(Status
)) {
6413 ERR("delete_root_ref returned %08x\n", Status
);
6418 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->dc
->utf8
.Length
;
6420 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
6422 ERR("out of memory\n");
6424 return STATUS_INSUFFICIENT_RESOURCES
;
6427 rr
->dir
= fileref
->parent
->fcb
->inode
;
6428 rr
->index
= fileref
->dc
->index
;
6429 rr
->n
= fileref
->dc
->utf8
.Length
;
6430 RtlCopyMemory(rr
->name
, fileref
->dc
->utf8
.Buffer
, fileref
->dc
->utf8
.Length
);
6432 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
);
6433 if (!NT_SUCCESS(Status
)) {
6434 ERR("add_root_ref returned %08x\n", Status
);
6439 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
);
6440 if (!NT_SUCCESS(Status
)) {
6441 ERR("update_root_backref returned %08x\n", Status
);
6447 // delete DIR_INDEX (0x60)
6449 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6450 fileref
->dc
->index
, NULL
, 0, Batch_Delete
);
6451 if (!NT_SUCCESS(Status
)) {
6452 ERR("insert_tree_item_batch returned %08x\n", Status
);
6457 // add DIR_INDEX (0x60)
6459 Status
= insert_tree_item_batch(batchlist
, fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
,
6460 fileref
->dc
->index
, di2
, disize
, Batch_Insert
);
6461 if (!NT_SUCCESS(Status
)) {
6462 ERR("insert_tree_item_batch returned %08x\n", Status
);
6467 if (fileref
->oldutf8
.Buffer
) {
6468 ExFreePool(fileref
->oldutf8
.Buffer
);
6469 fileref
->oldutf8
.Buffer
= NULL
;
6473 fileref
->dirty
= FALSE
;
6475 return STATUS_SUCCESS
;
6478 static void flush_disk_caches(device_extension
* Vcb
) {
6480 ioctl_context context
;
6485 le
= Vcb
->devices
.Flink
;
6487 while (le
!= &Vcb
->devices
) {
6488 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6490 if (dev
->devobj
&& !dev
->readonly
&& dev
->can_flush
)
6496 if (context
.left
== 0)
6501 KeInitializeEvent(&context
.Event
, NotificationEvent
, FALSE
);
6503 context
.stripes
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(ioctl_context_stripe
) * context
.left
, ALLOC_TAG
);
6504 if (!context
.stripes
) {
6505 ERR("out of memory\n");
6509 RtlZeroMemory(context
.stripes
, sizeof(ioctl_context_stripe
) * context
.left
);
6511 le
= Vcb
->devices
.Flink
;
6513 while (le
!= &Vcb
->devices
) {
6514 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6516 if (dev
->devobj
&& !dev
->readonly
&& dev
->can_flush
) {
6517 PIO_STACK_LOCATION IrpSp
;
6518 ioctl_context_stripe
* stripe
= &context
.stripes
[num
];
6520 RtlZeroMemory(&stripe
->apte
, sizeof(ATA_PASS_THROUGH_EX
));
6522 stripe
->apte
.Length
= sizeof(ATA_PASS_THROUGH_EX
);
6523 stripe
->apte
.TimeOutValue
= 5;
6524 stripe
->apte
.CurrentTaskFile
[6] = IDE_COMMAND_FLUSH_CACHE
;
6526 stripe
->Irp
= IoAllocateIrp(dev
->devobj
->StackSize
, FALSE
);
6529 ERR("IoAllocateIrp failed\n");
6533 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
6534 IrpSp
->MajorFunction
= IRP_MJ_DEVICE_CONTROL
;
6536 IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
= IOCTL_ATA_PASS_THROUGH
;
6537 IrpSp
->Parameters
.DeviceIoControl
.InputBufferLength
= sizeof(ATA_PASS_THROUGH_EX
);
6538 IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
= sizeof(ATA_PASS_THROUGH_EX
);
6540 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= &stripe
->apte
;
6541 stripe
->Irp
->Flags
|= IRP_BUFFERED_IO
| IRP_INPUT_OPERATION
;
6542 stripe
->Irp
->UserBuffer
= &stripe
->apte
;
6543 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
6545 IoSetCompletionRoutine(stripe
->Irp
, ioctl_completion
, &context
, TRUE
, TRUE
, TRUE
);
6547 IoCallDriver(dev
->devobj
, stripe
->Irp
);
6556 KeWaitForSingleObject(&context
.Event
, Executive
, KernelMode
, FALSE
, NULL
);
6558 ExFreePool(context
.stripes
);
6561 static NTSTATUS
flush_changed_dev_stats(device_extension
* Vcb
, device
* dev
, PIRP Irp
) {
6568 searchkey
.obj_id
= 0;
6569 searchkey
.obj_type
= TYPE_DEV_STATS
;
6570 searchkey
.offset
= dev
->devitem
.dev_id
;
6572 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
6573 if (!NT_SUCCESS(Status
)) {
6574 ERR("find_item returned %08x\n", Status
);
6578 if (!keycmp(tp
.item
->key
, searchkey
)) {
6579 Status
= delete_tree_item(Vcb
, &tp
);
6580 if (!NT_SUCCESS(Status
)) {
6581 ERR("delete_tree_item returned %08x\n", Status
);
6586 statslen
= sizeof(UINT64
) * 5;
6587 stats
= ExAllocatePoolWithTag(PagedPool
, statslen
, ALLOC_TAG
);
6589 ERR("out of memory\n");
6590 return STATUS_INSUFFICIENT_RESOURCES
;
6593 RtlCopyMemory(stats
, dev
->stats
, statslen
);
6595 Status
= insert_tree_item(Vcb
, Vcb
->dev_root
, 0, TYPE_DEV_STATS
, dev
->devitem
.dev_id
, stats
, statslen
, NULL
, Irp
);
6596 if (!NT_SUCCESS(Status
)) {
6597 ERR("insert_tree_item returned %08x\n", Status
);
6602 return STATUS_SUCCESS
;
6605 static NTSTATUS
flush_subvol(device_extension
* Vcb
, root
* r
, PIRP Irp
) {
6608 if (r
!= Vcb
->root_root
&& r
!= Vcb
->chunk_root
) {
6613 searchkey
.obj_id
= r
->id
;
6614 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
6615 searchkey
.offset
= 0xffffffffffffffff;
6617 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
6618 if (!NT_SUCCESS(Status
)) {
6619 ERR("error - find_item returned %08x\n", Status
);
6623 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
6624 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
6625 return STATUS_INTERNAL_ERROR
;
6628 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
6630 ERR("out of memory\n");
6631 return STATUS_INSUFFICIENT_RESOURCES
;
6634 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
6636 Status
= delete_tree_item(Vcb
, &tp
);
6637 if (!NT_SUCCESS(Status
)) {
6638 ERR("delete_tree_item returned %08x\n", Status
);
6642 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
);
6643 if (!NT_SUCCESS(Status
)) {
6644 ERR("insert_tree_item returned %08x\n", Status
);
6653 if (!Vcb
->uuid_root
) {
6656 TRACE("uuid root doesn't exist, creating it\n");
6658 Status
= create_root(Vcb
, BTRFS_ROOT_UUID
, &uuid_root
, FALSE
, 0, Irp
);
6660 if (!NT_SUCCESS(Status
)) {
6661 ERR("create_root returned %08x\n", Status
);
6665 Vcb
->uuid_root
= uuid_root
;
6668 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.received_uuid
, sizeof(UINT64
));
6669 searchkey
.obj_type
= TYPE_SUBVOL_REC_UUID
;
6670 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.received_uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
6672 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
6673 if (!NT_SUCCESS(Status
)) {
6674 ERR("find_item returned %08x\n", Status
);
6678 if (!keycmp(tp
.item
->key
, searchkey
)) {
6679 if (tp
.item
->size
+ sizeof(UINT64
) <= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
)) {
6682 ids
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ sizeof(UINT64
), ALLOC_TAG
);
6684 ERR("out of memory\n");
6685 return STATUS_INSUFFICIENT_RESOURCES
;
6688 RtlCopyMemory(ids
, tp
.item
->data
, tp
.item
->size
);
6689 RtlCopyMemory((UINT8
*)ids
+ tp
.item
->size
, &r
->id
, sizeof(UINT64
));
6691 Status
= delete_tree_item(Vcb
, &tp
);
6692 if (!NT_SUCCESS(Status
)) {
6693 ERR("delete_tree_item returned %08x\n", Status
);
6698 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, ids
, tp
.item
->size
+ sizeof(UINT64
), NULL
, Irp
);
6699 if (!NT_SUCCESS(Status
)) {
6700 ERR("insert_tree_item returned %08x\n", Status
);
6708 root_num
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
), ALLOC_TAG
);
6710 ERR("out of memory\n");
6711 return STATUS_INSUFFICIENT_RESOURCES
;
6716 Status
= insert_tree_item(Vcb
, Vcb
->uuid_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, root_num
, sizeof(UINT64
), NULL
, Irp
);
6717 if (!NT_SUCCESS(Status
)) {
6718 ERR("insert_tree_item returned %08x\n", Status
);
6719 ExFreePool(root_num
);
6724 r
->received
= FALSE
;
6729 return STATUS_SUCCESS
;
6732 static NTSTATUS
test_not_full(device_extension
* Vcb
) {
6733 UINT64 reserve
, could_alloc
, free_space
;
6736 // This function ensures we drop into readonly mode if we're about to leave very little
6737 // space for metadata - this is similar to the "global reserve" of the Linux driver.
6738 // Otherwise we might completely fill our space, at which point due to COW we can't
6739 // delete anything in order to fix this.
6741 reserve
= Vcb
->extent_root
->root_item
.bytes_used
;
6742 reserve
+= Vcb
->root_root
->root_item
.bytes_used
;
6743 if (Vcb
->checksum_root
) reserve
+= Vcb
->checksum_root
->root_item
.bytes_used
;
6745 reserve
= max(reserve
, 0x1000000); // 16 M
6746 reserve
= min(reserve
, 0x20000000); // 512 M
6748 // Find out how much space would be available for new metadata chunks
6752 if (Vcb
->metadata_flags
& BLOCK_FLAG_RAID5
) {
6753 UINT64 s1
= 0, s2
= 0, s3
= 0;
6755 le
= Vcb
->devices
.Flink
;
6756 while (le
!= &Vcb
->devices
) {
6757 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6759 if (!dev
->readonly
) {
6760 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6766 } else if (space
>= s2
) {
6769 } else if (space
>= s3
)
6776 could_alloc
= s3
* 2;
6777 } else if (Vcb
->metadata_flags
& (BLOCK_FLAG_RAID10
| BLOCK_FLAG_RAID6
)) {
6778 UINT64 s1
= 0, s2
= 0, s3
= 0, s4
= 0;
6780 le
= Vcb
->devices
.Flink
;
6781 while (le
!= &Vcb
->devices
) {
6782 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6784 if (!dev
->readonly
) {
6785 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6792 } else if (space
>= s2
) {
6796 } else if (space
>= s3
) {
6799 } else if (space
>= s4
)
6806 could_alloc
= s4
* 2;
6807 } else if (Vcb
->metadata_flags
& (BLOCK_FLAG_RAID0
| BLOCK_FLAG_RAID1
)) {
6808 UINT64 s1
= 0, s2
= 0;
6810 le
= Vcb
->devices
.Flink
;
6811 while (le
!= &Vcb
->devices
) {
6812 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6814 if (!dev
->readonly
) {
6815 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6820 } else if (space
>= s2
)
6827 if (Vcb
->metadata_flags
& BLOCK_FLAG_RAID1
)
6830 could_alloc
= s2
* 2;
6831 } else if (Vcb
->metadata_flags
& BLOCK_FLAG_DUPLICATE
) {
6832 le
= Vcb
->devices
.Flink
;
6833 while (le
!= &Vcb
->devices
) {
6834 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6836 if (!dev
->readonly
) {
6837 UINT64 space
= (dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
) / 2;
6839 could_alloc
= max(could_alloc
, space
);
6845 le
= Vcb
->devices
.Flink
;
6846 while (le
!= &Vcb
->devices
) {
6847 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
6849 if (!dev
->readonly
) {
6850 UINT64 space
= dev
->devitem
.num_bytes
- dev
->devitem
.bytes_used
;
6852 could_alloc
= max(could_alloc
, space
);
6859 if (could_alloc
>= reserve
)
6860 return STATUS_SUCCESS
;
6864 le
= Vcb
->chunks
.Flink
;
6865 while (le
!= &Vcb
->chunks
) {
6866 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
6868 if (!c
->reloc
&& !c
->readonly
&& c
->chunk_item
->type
& BLOCK_FLAG_METADATA
) {
6869 free_space
+= c
->chunk_item
->size
- c
->used
;
6871 if (free_space
+ could_alloc
>= reserve
)
6872 return STATUS_SUCCESS
;
6878 return STATUS_DISK_FULL
;
6881 static NTSTATUS
do_write2(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6883 LIST_ENTRY
*le
, batchlist
;
6884 BOOL cache_changed
= FALSE
;
6885 volume_device_extension
* vde
;
6886 BOOL no_cache
= FALSE
;
6887 #ifdef DEBUG_FLUSH_TIMES
6888 UINT64 filerefs
= 0, fcbs
= 0;
6889 LARGE_INTEGER freq
, time1
, time2
;
6891 #ifdef DEBUG_WRITE_LOOPS
6895 TRACE("(%p)\n", Vcb
);
6897 InitializeListHead(&batchlist
);
6899 #ifdef DEBUG_FLUSH_TIMES
6900 time1
= KeQueryPerformanceCounter(&freq
);
6903 ExAcquireResourceExclusiveLite(&Vcb
->dirty_filerefs_lock
, TRUE
);
6905 while (!IsListEmpty(&Vcb
->dirty_filerefs
)) {
6906 file_ref
* fr
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->dirty_filerefs
), file_ref
, list_entry_dirty
);
6908 flush_fileref(fr
, &batchlist
, Irp
);
6909 free_fileref(Vcb
, fr
);
6911 #ifdef DEBUG_FLUSH_TIMES
6916 ExReleaseResourceLite(&Vcb
->dirty_filerefs_lock
);
6918 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
6919 if (!NT_SUCCESS(Status
)) {
6920 ERR("commit_batch_list returned %08x\n", Status
);
6924 #ifdef DEBUG_FLUSH_TIMES
6925 time2
= KeQueryPerformanceCounter(NULL
);
6927 ERR("flushed %llu filerefs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
6929 time1
= KeQueryPerformanceCounter(&freq
);
6932 // We process deleted streams first, so we don't run over our xattr
6933 // limit unless we absolutely have to.
6934 // We also process deleted normal files, to avoid any problems
6935 // caused by inode collisions.
6937 ExAcquireResourceExclusiveLite(&Vcb
->dirty_fcbs_lock
, TRUE
);
6939 le
= Vcb
->dirty_fcbs
.Flink
;
6940 while (le
!= &Vcb
->dirty_fcbs
) {
6941 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_dirty
);
6942 LIST_ENTRY
* le2
= le
->Flink
;
6945 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
6946 Status
= flush_fcb(fcb
, FALSE
, &batchlist
, Irp
);
6947 ExReleaseResourceLite(fcb
->Header
.Resource
);
6951 if (!NT_SUCCESS(Status
)) {
6952 ERR("flush_fcb returned %08x\n", Status
);
6953 clear_batch_list(Vcb
, &batchlist
);
6954 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6958 #ifdef DEBUG_FLUSH_TIMES
6966 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
6967 if (!NT_SUCCESS(Status
)) {
6968 ERR("commit_batch_list returned %08x\n", Status
);
6969 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6973 le
= Vcb
->dirty_fcbs
.Flink
;
6974 while (le
!= &Vcb
->dirty_fcbs
) {
6975 fcb
* fcb
= CONTAINING_RECORD(le
, struct _fcb
, list_entry_dirty
);
6976 LIST_ENTRY
* le2
= le
->Flink
;
6978 if (fcb
->subvol
!= Vcb
->root_root
) {
6979 ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, TRUE
);
6980 Status
= flush_fcb(fcb
, FALSE
, &batchlist
, Irp
);
6981 ExReleaseResourceLite(fcb
->Header
.Resource
);
6984 if (!NT_SUCCESS(Status
)) {
6985 ERR("flush_fcb returned %08x\n", Status
);
6986 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
6990 #ifdef DEBUG_FLUSH_TIMES
6998 ExReleaseResourceLite(&Vcb
->dirty_fcbs_lock
);
7000 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
7001 if (!NT_SUCCESS(Status
)) {
7002 ERR("commit_batch_list returned %08x\n", Status
);
7006 #ifdef DEBUG_FLUSH_TIMES
7007 time2
= KeQueryPerformanceCounter(NULL
);
7009 ERR("flushed %llu fcbs in %llu (freq = %llu)\n", filerefs
, time2
.QuadPart
- time1
.QuadPart
, freq
.QuadPart
);
7012 // no need to get dirty_subvols_lock here, as we have tree_lock exclusively
7013 while (!IsListEmpty(&Vcb
->dirty_subvols
)) {
7014 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->dirty_subvols
), root
, list_entry_dirty
);
7016 Status
= flush_subvol(Vcb
, r
, Irp
);
7017 if (!NT_SUCCESS(Status
)) {
7018 ERR("flush_subvol returned %08x\n", Status
);
7023 if (!IsListEmpty(&Vcb
->drop_roots
)) {
7024 Status
= drop_roots(Vcb
, Irp
, rollback
);
7026 if (!NT_SUCCESS(Status
)) {
7027 ERR("drop_roots returned %08x\n", Status
);
7032 Status
= update_chunks(Vcb
, &batchlist
, Irp
, rollback
);
7034 if (!NT_SUCCESS(Status
)) {
7035 ERR("update_chunks returned %08x\n", Status
);
7039 Status
= commit_batch_list(Vcb
, &batchlist
, Irp
);
7041 // If only changing superblock, e.g. changing label, we still need to rewrite
7042 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
7043 if (!Vcb
->root_root
->treeholder
.tree
|| !Vcb
->root_root
->treeholder
.tree
->write
) {
7048 searchkey
.obj_id
= 0;
7049 searchkey
.obj_type
= 0;
7050 searchkey
.offset
= 0;
7052 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
7053 if (!NT_SUCCESS(Status
)) {
7054 ERR("error - find_item returned %08x\n", Status
);
7058 Vcb
->root_root
->treeholder
.tree
->write
= TRUE
;
7061 // make sure we always update the extent tree
7062 Status
= add_root_item_to_cache(Vcb
, BTRFS_ROOT_EXTENT
, Irp
);
7063 if (!NT_SUCCESS(Status
)) {
7064 ERR("add_root_item_to_cache returned %08x\n", Status
);
7068 if (Vcb
->stats_changed
) {
7069 le
= Vcb
->devices
.Flink
;
7070 while (le
!= &Vcb
->devices
) {
7071 device
* dev
= CONTAINING_RECORD(le
, device
, list_entry
);
7073 if (dev
->stats_changed
) {
7074 Status
= flush_changed_dev_stats(Vcb
, dev
, Irp
);
7075 if (!NT_SUCCESS(Status
)) {
7076 ERR("flush_changed_dev_stats returned %08x\n", Status
);
7079 dev
->stats_changed
= FALSE
;
7085 Vcb
->stats_changed
= FALSE
;
7089 Status
= add_parents(Vcb
, Irp
);
7090 if (!NT_SUCCESS(Status
)) {
7091 ERR("add_parents returned %08x\n", Status
);
7095 Status
= allocate_tree_extents(Vcb
, Irp
, rollback
);
7096 if (!NT_SUCCESS(Status
)) {
7097 ERR("allocate_tree_extents returned %08x\n", Status
);
7101 Status
= do_splits(Vcb
, Irp
, rollback
);
7102 if (!NT_SUCCESS(Status
)) {
7103 ERR("do_splits returned %08x\n", Status
);
7107 Status
= update_chunk_usage(Vcb
, Irp
, rollback
);
7108 if (!NT_SUCCESS(Status
)) {
7109 ERR("update_chunk_usage returned %08x\n", Status
);
7113 if (!(Vcb
->superblock
.compat_ro_flags
& BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE
)) {
7115 Status
= allocate_cache(Vcb
, &cache_changed
, Irp
, rollback
);
7116 if (!NT_SUCCESS(Status
)) {
7117 WARN("allocate_cache returned %08x\n", Status
);
7119 cache_changed
= FALSE
;
7123 Status
= update_chunk_caches_tree(Vcb
, Irp
);
7124 if (!NT_SUCCESS(Status
)) {
7125 ERR("update_chunk_caches_tree returned %08x\n", Status
);
7130 #ifdef DEBUG_WRITE_LOOPS
7134 ERR("cache has changed, looping again\n");
7136 } while (cache_changed
|| !trees_consistent(Vcb
));
7138 #ifdef DEBUG_WRITE_LOOPS
7139 ERR("%u loops\n", loops
);
7142 TRACE("trees consistent\n");
7144 Status
= update_root_root(Vcb
, no_cache
, Irp
, rollback
);
7145 if (!NT_SUCCESS(Status
)) {
7146 ERR("update_root_root returned %08x\n", Status
);
7150 Status
= write_trees(Vcb
, Irp
);
7151 if (!NT_SUCCESS(Status
)) {
7152 ERR("write_trees returned %08x\n", Status
);
7156 Status
= test_not_full(Vcb
);
7157 if (!NT_SUCCESS(Status
)) {
7158 ERR("test_not_full returned %08x\n", Status
);
7162 #ifdef DEBUG_PARANOID
7163 le
= Vcb
->trees
.Flink
;
7164 while (le
!= &Vcb
->trees
) {
7165 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
7169 searchkey
.obj_id
= t
->header
.address
;
7170 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
7171 searchkey
.offset
= 0xffffffffffffffff;
7173 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
7174 if (!NT_SUCCESS(Status
)) {
7175 ERR("error - find_item returned %08x\n", Status
);
7179 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
7180 searchkey
.obj_id
= t
->header
.address
;
7181 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
7182 searchkey
.offset
= 0xffffffffffffffff;
7184 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
7185 if (!NT_SUCCESS(Status
)) {
7186 ERR("error - find_item returned %08x\n", Status
);
7190 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
7191 ERR("error - could not find entry in extent tree for tree at %llx\n", t
->header
.address
);
7192 Status
= STATUS_INTERNAL_ERROR
;
7201 Vcb
->superblock
.cache_generation
= Vcb
->superblock
.generation
;
7203 if (!Vcb
->options
.no_barrier
)
7204 flush_disk_caches(Vcb
);
7206 Status
= write_superblocks(Vcb
, Irp
);
7207 if (!NT_SUCCESS(Status
)) {
7208 ERR("write_superblocks returned %08x\n", Status
);
7215 pdo_device_extension
* pdode
= vde
->pdode
;
7217 ExAcquireResourceSharedLite(&pdode
->child_lock
, TRUE
);
7219 le
= pdode
->children
.Flink
;
7221 while (le
!= &pdode
->children
) {
7222 volume_child
* vc
= CONTAINING_RECORD(le
, volume_child
, list_entry
);
7224 vc
->generation
= Vcb
->superblock
.generation
;
7228 ExReleaseResourceLite(&pdode
->child_lock
);
7231 clean_space_cache(Vcb
);
7233 le
= Vcb
->chunks
.Flink
;
7234 while (le
!= &Vcb
->chunks
) {
7235 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
7238 c
->space_changed
= FALSE
;
7243 Vcb
->superblock
.generation
++;
7245 Status
= STATUS_SUCCESS
;
7247 le
= Vcb
->trees
.Flink
;
7248 while (le
!= &Vcb
->trees
) {
7249 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
7256 Vcb
->need_write
= FALSE
;
7258 while (!IsListEmpty(&Vcb
->drop_roots
)) {
7259 root
* r
= CONTAINING_RECORD(RemoveHeadList(&Vcb
->drop_roots
), root
, list_entry
);
7261 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
7262 ExFreePool(r
->nonpaged
);
7267 TRACE("do_write returning %08x\n", Status
);
7272 NTSTATUS
do_write(device_extension
* Vcb
, PIRP Irp
) {
7273 LIST_ENTRY rollback
;
7276 InitializeListHead(&rollback
);
7278 Status
= do_write2(Vcb
, Irp
, &rollback
);
7280 if (!NT_SUCCESS(Status
)) {
7281 ERR("do_write2 returned %08x, dropping into readonly mode\n", Status
);
7282 Vcb
->readonly
= TRUE
;
7283 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_FORCED_CLOSED
);
7284 do_rollback(Vcb
, &rollback
);
7286 clear_rollback(&rollback
);
7292 static void print_stats(device_extension
* Vcb
) {
7295 ERR("READ STATS:\n");
7296 ERR("number of reads: %llu\n", Vcb
->stats
.num_reads
);
7297 ERR("data read: %llu bytes\n", Vcb
->stats
.data_read
);
7298 ERR("total time taken: %llu\n", Vcb
->stats
.read_total_time
);
7299 ERR("csum time taken: %llu\n", Vcb
->stats
.read_csum_time
);
7300 ERR("disk time taken: %llu\n", Vcb
->stats
.read_disk_time
);
7301 ERR("other time taken: %llu\n", Vcb
->stats
.read_total_time
- Vcb
->stats
.read_csum_time
- Vcb
->stats
.read_disk_time
);
7303 KeQueryPerformanceCounter(&freq
);
7305 ERR("OPEN STATS (freq = %llu):\n", freq
.QuadPart
);
7306 ERR("number of opens: %llu\n", Vcb
->stats
.num_opens
);
7307 ERR("total time taken: %llu\n", Vcb
->stats
.open_total_time
);
7308 ERR("number of overwrites: %llu\n", Vcb
->stats
.num_overwrites
);
7309 ERR("total time taken: %llu\n", Vcb
->stats
.overwrite_total_time
);
7310 ERR("number of creates: %llu\n", Vcb
->stats
.num_creates
);
7311 ERR("calls to open_fcb: %llu\n", Vcb
->stats
.open_fcb_calls
);
7312 ERR("time spent in open_fcb: %llu\n", Vcb
->stats
.open_fcb_time
);
7313 ERR("calls to open_fileref_child: %llu\n", Vcb
->stats
.open_fileref_child_calls
);
7314 ERR("time spent in open_fileref_child: %llu\n", Vcb
->stats
.open_fileref_child_time
);
7315 ERR("time spent waiting for fcb_lock: %llu\n", Vcb
->stats
.fcb_lock_time
);
7316 ERR("total time taken: %llu\n", Vcb
->stats
.create_total_time
);
7318 RtlZeroMemory(&Vcb
->stats
, sizeof(debug_stats
));
7322 static void do_flush(device_extension
* Vcb
) {
7325 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
7331 if (Vcb
->need_write
&& !Vcb
->readonly
)
7332 Status
= do_write(Vcb
, NULL
);
7334 Status
= STATUS_SUCCESS
;
7338 if (!NT_SUCCESS(Status
))
7339 ERR("do_write returned %08x\n", Status
);
7341 ExReleaseResourceLite(&Vcb
->tree_lock
);
7344 _Function_class_(KSTART_ROUTINE
)
7346 void NTAPI
flush_thread(void* context
) {
7348 void flush_thread(void* context
) {
7350 DEVICE_OBJECT
* devobj
= context
;
7351 device_extension
* Vcb
= devobj
->DeviceExtension
;
7352 LARGE_INTEGER due_time
;
7354 ObReferenceObject(devobj
);
7356 KeInitializeTimer(&Vcb
->flush_thread_timer
);
7358 due_time
.QuadPart
= (UINT64
)Vcb
->options
.flush_interval
* -10000000;
7360 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
7363 KeWaitForSingleObject(&Vcb
->flush_thread_timer
, Executive
, KernelMode
, FALSE
, NULL
);
7365 if (!(devobj
->Vpb
->Flags
& VPB_MOUNTED
) || Vcb
->removing
)
7371 KeSetTimer(&Vcb
->flush_thread_timer
, due_time
, NULL
);
7374 ObDereferenceObject(devobj
);
7375 KeCancelTimer(&Vcb
->flush_thread_timer
);
7377 KeSetEvent(&Vcb
->flush_thread_finished
, 0, FALSE
);
7379 PsTerminateSystemThread(STATUS_SUCCESS
);