1 /* Copyright (c) Mark Harmstone 2016
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
20 #define MAX_CSUM_SIZE (4096 - sizeof(tree_header) - sizeof(leaf_node))
22 // #define DEBUG_WRITE_LOOPS
25 BOOL chunk_test
= FALSE
;
36 } EXTENT_ITEM_DATA_REF
;
48 } EXTENT_ITEM_SKINNY_METADATA
;
50 // static BOOL extent_item_is_shared(EXTENT_ITEM* ei, ULONG len);
51 static NTSTATUS STDCALL
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
);
52 static void update_checksum_tree(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
);
53 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
);
55 static NTSTATUS STDCALL
write_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
56 write_context
* context
= conptr
;
58 context
->iosb
= Irp
->IoStatus
;
59 KeSetEvent(&context
->Event
, 0, FALSE
);
61 // return STATUS_SUCCESS;
62 return STATUS_MORE_PROCESSING_REQUIRED
;
65 static NTSTATUS STDCALL
write_data_phys(PDEVICE_OBJECT device
, UINT64 address
, void* data
, UINT32 length
) {
69 PIO_STACK_LOCATION IrpSp
;
70 write_context
* context
= NULL
;
72 TRACE("(%p, %llx, %p, %x)\n", device
, address
, data
, length
);
74 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_context
), ALLOC_TAG
);
76 ERR("out of memory\n");
77 return STATUS_INSUFFICIENT_RESOURCES
;
80 RtlZeroMemory(context
, sizeof(write_context
));
82 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
84 offset
.QuadPart
= address
;
86 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_WRITE, Vcb->device, data, length, &offset, NULL, &context->iosb);
88 Irp
= IoAllocateIrp(device
->StackSize
, FALSE
);
91 ERR("IoAllocateIrp failed\n");
92 Status
= STATUS_INTERNAL_ERROR
;
96 IrpSp
= IoGetNextIrpStackLocation(Irp
);
97 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
99 if (device
->Flags
& DO_BUFFERED_IO
) {
100 Irp
->AssociatedIrp
.SystemBuffer
= data
;
102 Irp
->Flags
= IRP_BUFFERED_IO
;
103 } else if (device
->Flags
& DO_DIRECT_IO
) {
104 Irp
->MdlAddress
= IoAllocateMdl(data
, length
, FALSE
, FALSE
, NULL
);
105 if (!Irp
->MdlAddress
) {
106 DbgPrint("IoAllocateMdl failed\n");
110 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
112 Irp
->UserBuffer
= data
;
115 IrpSp
->Parameters
.Write
.Length
= length
;
116 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
118 Irp
->UserIosb
= &context
->iosb
;
120 Irp
->UserEvent
= &context
->Event
;
122 IoSetCompletionRoutine(Irp
, write_completion
, context
, TRUE
, TRUE
, TRUE
);
124 // FIXME - support multiple devices
125 Status
= IoCallDriver(device
, Irp
);
127 if (Status
== STATUS_PENDING
) {
128 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
129 Status
= context
->iosb
.Status
;
132 if (!NT_SUCCESS(Status
)) {
133 ERR("IoCallDriver returned %08x\n", Status
);
136 if (device
->Flags
& DO_DIRECT_IO
) {
137 MmUnlockPages(Irp
->MdlAddress
);
138 IoFreeMdl(Irp
->MdlAddress
);
151 static NTSTATUS STDCALL
write_superblock(device_extension
* Vcb
, device
* device
) {
157 Status
= STATUS_INTERNAL_ERROR
;
160 RtlCopyMemory(&Vcb
->superblock
.dev_item
, &device
->devitem
, sizeof(DEV_ITEM
));
162 // FIXME - only write one superblock if on SSD (?)
163 while (superblock_addrs
[i
] > 0 && device
->length
>= superblock_addrs
[i
] + sizeof(superblock
)) {
164 TRACE("writing superblock %u\n", i
);
166 Vcb
->superblock
.sb_phys_addr
= superblock_addrs
[i
];
168 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
170 TRACE("crc32 is %08x\n", crc32
);
171 RtlCopyMemory(&Vcb
->superblock
.checksum
, &crc32
, sizeof(UINT32
));
173 Status
= write_data_phys(device
->devobj
, superblock_addrs
[i
], &Vcb
->superblock
, sizeof(superblock
));
175 if (!NT_SUCCESS(Status
))
182 ERR("no superblocks written!\n");
188 static BOOL
find_address_in_chunk(device_extension
* Vcb
, chunk
* c
, UINT64 length
, UINT64
* address
) {
192 TRACE("(%p, %llx, %llx, %p)\n", Vcb
, c
->offset
, length
, address
);
194 if (IsListEmpty(&c
->space_size
))
197 le
= c
->space_size
.Flink
;
198 while (le
!= &c
->space_size
) {
199 s
= CONTAINING_RECORD(le
, space
, list_entry_size
);
201 if (s
->size
== length
) {
202 *address
= s
->address
;
204 } else if (s
->size
< length
) {
205 if (le
== c
->space_size
.Flink
)
208 s
= CONTAINING_RECORD(le
->Blink
, space
, list_entry_size
);
210 *address
= s
->address
;
217 s
= CONTAINING_RECORD(c
->space_size
.Blink
, space
, list_entry_size
);
219 if (s
->size
> length
) {
220 *address
= s
->address
;
227 chunk
* get_chunk_from_address(device_extension
* Vcb
, UINT64 address
) {
231 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
233 le2
= Vcb
->chunks
.Flink
;
234 while (le2
!= &Vcb
->chunks
) {
235 c
= CONTAINING_RECORD(le2
, chunk
, list_entry
);
237 // TRACE("chunk: %llx, %llx\n", c->offset, c->chunk_item->size);
239 if (address
>= c
->offset
&& address
< c
->offset
+ c
->chunk_item
->size
) {
240 ExReleaseResourceLite(&Vcb
->chunk_lock
);
247 ExReleaseResourceLite(&Vcb
->chunk_lock
);
257 static UINT64
find_new_chunk_address(device_extension
* Vcb
, UINT64 size
) {
263 le
= Vcb
->chunks
.Flink
;
264 while (le
!= &Vcb
->chunks
) {
265 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
267 if (c
->offset
>= lastaddr
+ size
)
270 lastaddr
= c
->offset
+ c
->chunk_item
->size
;
278 static NTSTATUS
update_dev_item(device_extension
* Vcb
, device
* device
, PIRP Irp
, LIST_ENTRY
* rollback
) {
284 searchkey
.obj_id
= 1;
285 searchkey
.obj_type
= TYPE_DEV_ITEM
;
286 searchkey
.offset
= device
->devitem
.dev_id
;
288 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
289 if (!NT_SUCCESS(Status
)) {
290 ERR("error - find_item returned %08x\n", Status
);
294 if (keycmp(&tp
.item
->key
, &searchkey
)) {
295 ERR("error - could not find DEV_ITEM for device %llx\n", device
->devitem
.dev_id
);
296 return STATUS_INTERNAL_ERROR
;
299 delete_tree_item(Vcb
, &tp
, rollback
);
301 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
303 ERR("out of memory\n");
304 return STATUS_INSUFFICIENT_RESOURCES
;
307 RtlCopyMemory(di
, &device
->devitem
, sizeof(DEV_ITEM
));
309 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, device
->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
, rollback
)) {
310 ERR("insert_tree_item failed\n");
311 return STATUS_INTERNAL_ERROR
;
314 return STATUS_SUCCESS
;
317 static void regen_bootstrap(device_extension
* Vcb
) {
323 le
= Vcb
->sys_chunks
.Flink
;
324 while (le
!= &Vcb
->sys_chunks
) {
325 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
327 TRACE("%llx,%x,%llx\n", sc2
->key
.obj_id
, sc2
->key
.obj_type
, sc2
->key
.offset
);
329 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], &sc2
->key
, sizeof(KEY
));
332 RtlCopyMemory(&Vcb
->superblock
.sys_chunk_array
[i
], sc2
->data
, sc2
->size
);
339 static NTSTATUS
add_to_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
, void* data
, ULONG size
) {
343 if (Vcb
->superblock
.n
+ sizeof(KEY
) + size
> SYS_CHUNK_ARRAY_SIZE
) {
344 ERR("error - bootstrap is full\n");
345 return STATUS_INTERNAL_ERROR
;
348 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
350 ERR("out of memory\n");
351 return STATUS_INSUFFICIENT_RESOURCES
;
354 sc
->key
.obj_id
= obj_id
;
355 sc
->key
.obj_type
= obj_type
;
356 sc
->key
.offset
= offset
;
358 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
360 ERR("out of memory\n");
362 return STATUS_INSUFFICIENT_RESOURCES
;
365 RtlCopyMemory(sc
->data
, data
, sc
->size
);
367 le
= Vcb
->sys_chunks
.Flink
;
368 while (le
!= &Vcb
->sys_chunks
) {
369 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
371 if (keycmp(&sc2
->key
, &sc
->key
) == 1)
376 InsertTailList(le
, &sc
->list_entry
);
378 Vcb
->superblock
.n
+= sizeof(KEY
) + size
;
380 regen_bootstrap(Vcb
);
382 return STATUS_SUCCESS
;
385 static BOOL
find_new_dup_stripes(device_extension
* Vcb
, stripe
* stripes
, UINT64 max_stripe_size
) {
386 UINT64 j
, devnum
, devusage
= 0xffffffffffffffff;
387 space
*devdh1
= NULL
, *devdh2
= NULL
;
389 for (j
= 0; j
< Vcb
->superblock
.num_devices
; j
++) {
392 usage
= (Vcb
->devices
[j
].devitem
.bytes_used
* 4096) / Vcb
->devices
[j
].devitem
.num_bytes
;
394 // favour devices which have been used the least
395 if (usage
< devusage
) {
396 if (!IsListEmpty(&Vcb
->devices
[j
].space
)) {
398 space
*dh1
= NULL
, *dh2
= NULL
;
400 le
= Vcb
->devices
[j
].space
.Flink
;
401 while (le
!= &Vcb
->devices
[j
].space
) {
402 space
* dh
= CONTAINING_RECORD(le
, space
, list_entry
);
404 if (dh
->size
>= max_stripe_size
&& (!dh1
|| dh
->size
< dh1
->size
)) {
412 if (dh1
&& (dh2
|| dh1
->size
>= 2 * max_stripe_size
)) {
416 devdh2
= dh2
? dh2
: dh1
;
425 stripes
[0].device
= &Vcb
->devices
[devnum
];
426 stripes
[0].dh
= devdh1
;
427 stripes
[1].device
= stripes
[0].device
;
428 stripes
[1].dh
= devdh2
;
433 static BOOL
find_new_stripe(device_extension
* Vcb
, stripe
* stripes
, UINT16 i
, UINT64 max_stripe_size
, UINT16 type
) {
434 UINT64 j
, k
, devnum
= 0xffffffffffffffff, devusage
= 0xffffffffffffffff;
437 for (j
= 0; j
< Vcb
->superblock
.num_devices
; j
++) {
441 // skip this device if it already has a stripe
443 for (k
= 0; k
< i
; k
++) {
444 if (stripes
[k
].device
== &Vcb
->devices
[j
]) {
452 usage
= (Vcb
->devices
[j
].devitem
.bytes_used
* 4096) / Vcb
->devices
[j
].devitem
.num_bytes
;
454 // favour devices which have been used the least
455 if (usage
< devusage
) {
456 if (!IsListEmpty(&Vcb
->devices
[j
].space
)) {
459 le
= Vcb
->devices
[j
].space
.Flink
;
460 while (le
!= &Vcb
->devices
[j
].space
) {
461 space
* dh
= CONTAINING_RECORD(le
, space
, list_entry
);
463 if ((devnum
!= j
&& dh
->size
>= max_stripe_size
) ||
464 (devnum
== j
&& dh
->size
>= max_stripe_size
&& dh
->size
< devdh
->size
)
481 stripes
[i
].dh
= devdh
;
482 stripes
[i
].device
= &Vcb
->devices
[devnum
];
487 chunk
* alloc_chunk(device_extension
* Vcb
, UINT64 flags
) {
488 UINT64 max_stripe_size
, max_chunk_size
, stripe_size
, stripe_length
, factor
;
489 UINT64 total_size
= 0, i
, logaddr
;
490 UINT16 type
, num_stripes
, sub_stripes
, max_stripes
, min_stripes
;
491 stripe
* stripes
= NULL
;
493 CHUNK_ITEM_STRIPE
* cis
;
496 BOOL success
= FALSE
;
498 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
500 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
501 total_size
+= Vcb
->devices
[i
].devitem
.num_bytes
;
503 TRACE("total_size = %llx\n", total_size
);
505 // We purposely check for DATA first - mixed blocks have the same size
507 if (flags
& BLOCK_FLAG_DATA
) {
508 max_stripe_size
= 0x40000000; // 1 GB
509 max_chunk_size
= 10 * max_stripe_size
;
510 } else if (flags
& BLOCK_FLAG_METADATA
) {
511 if (total_size
> 0xC80000000) // 50 GB
512 max_stripe_size
= 0x40000000; // 1 GB
514 max_stripe_size
= 0x10000000; // 256 MB
516 max_chunk_size
= max_stripe_size
;
517 } else if (flags
& BLOCK_FLAG_SYSTEM
) {
518 max_stripe_size
= 0x2000000; // 32 MB
519 max_chunk_size
= 2 * max_stripe_size
;
522 max_chunk_size
= min(max_chunk_size
, total_size
/ 10); // cap at 10%
524 TRACE("would allocate a new chunk of %llx bytes and stripe %llx\n", max_chunk_size
, max_stripe_size
);
526 if (flags
& BLOCK_FLAG_DUPLICATE
) {
530 type
= BLOCK_FLAG_DUPLICATE
;
531 } else if (flags
& BLOCK_FLAG_RAID0
) {
533 max_stripes
= Vcb
->superblock
.num_devices
;
535 type
= BLOCK_FLAG_RAID0
;
536 } else if (flags
& BLOCK_FLAG_RAID1
) {
540 type
= BLOCK_FLAG_RAID1
;
541 } else if (flags
& BLOCK_FLAG_RAID10
) {
543 max_stripes
= Vcb
->superblock
.num_devices
;
545 type
= BLOCK_FLAG_RAID10
;
546 } else if (flags
& BLOCK_FLAG_RAID5
) {
547 FIXME("RAID5 not yet supported\n");
549 } else if (flags
& BLOCK_FLAG_RAID6
) {
550 FIXME("RAID6 not yet supported\n");
559 stripes
= ExAllocatePoolWithTag(PagedPool
, sizeof(stripe
) * max_stripes
, ALLOC_TAG
);
561 ERR("out of memory\n");
567 if (type
== BLOCK_FLAG_DUPLICATE
) {
568 if (!find_new_dup_stripes(Vcb
, stripes
, max_stripe_size
))
571 num_stripes
= max_stripes
;
573 for (i
= 0; i
< max_stripes
; i
++) {
574 if (!find_new_stripe(Vcb
, stripes
, i
, max_stripe_size
, type
))
581 // for RAID10, round down to an even number of stripes
582 if (type
== BLOCK_FLAG_RAID10
&& (num_stripes
% sub_stripes
) != 0) {
583 num_stripes
-= num_stripes
% sub_stripes
;
586 if (num_stripes
< min_stripes
) {
587 WARN("found %u stripes, needed at least %u\n", num_stripes
, min_stripes
);
591 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
593 ERR("out of memory\n");
597 cisize
= sizeof(CHUNK_ITEM
) + (num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
598 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, cisize
, ALLOC_TAG
);
599 if (!c
->chunk_item
) {
600 ERR("out of memory\n");
604 stripe_length
= 0x10000; // FIXME? BTRFS_STRIPE_LEN in kernel
606 stripe_size
= max_stripe_size
;
607 for (i
= 0; i
< num_stripes
; i
++) {
608 if (stripes
[i
].dh
->size
< stripe_size
)
609 stripe_size
= stripes
[i
].dh
->size
;
612 if (type
== 0 || type
== BLOCK_FLAG_DUPLICATE
|| type
== BLOCK_FLAG_RAID1
)
614 else if (type
== BLOCK_FLAG_RAID0
)
615 factor
= num_stripes
;
616 else if (type
== BLOCK_FLAG_RAID10
)
617 factor
= num_stripes
/ sub_stripes
;
619 if (stripe_size
* factor
> max_chunk_size
)
620 stripe_size
= max_chunk_size
/ factor
;
622 if (stripe_size
% stripe_length
> 0)
623 stripe_size
-= stripe_size
% stripe_length
;
625 if (stripe_size
== 0)
628 c
->chunk_item
->size
= stripe_size
* factor
;
629 c
->chunk_item
->root_id
= Vcb
->extent_root
->id
;
630 c
->chunk_item
->stripe_length
= stripe_length
;
631 c
->chunk_item
->type
= flags
;
632 c
->chunk_item
->opt_io_alignment
= c
->chunk_item
->stripe_length
;
633 c
->chunk_item
->opt_io_width
= c
->chunk_item
->stripe_length
;
634 c
->chunk_item
->sector_size
= stripes
[0].device
->devitem
.minimal_io_size
;
635 c
->chunk_item
->num_stripes
= num_stripes
;
636 c
->chunk_item
->sub_stripes
= sub_stripes
;
638 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * num_stripes
, ALLOC_TAG
);
640 ERR("out of memory\n");
644 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
645 for (i
= 0; i
< num_stripes
; i
++) {
646 cis
[i
].dev_id
= stripes
[i
].device
->devitem
.dev_id
;
648 if (type
== BLOCK_FLAG_DUPLICATE
&& i
== 1 && stripes
[i
].dh
== stripes
[0].dh
)
649 cis
[i
].offset
= stripes
[0].dh
->address
+ stripe_size
;
651 cis
[i
].offset
= stripes
[i
].dh
->address
;
653 cis
[i
].dev_uuid
= stripes
[i
].device
->devitem
.device_uuid
;
655 c
->devices
[i
] = stripes
[i
].device
;
658 logaddr
= find_new_chunk_address(Vcb
, c
->chunk_item
->size
);
660 Vcb
->superblock
.chunk_root_generation
= Vcb
->superblock
.generation
;
664 c
->used
= c
->oldused
= 0;
666 InitializeListHead(&c
->space
);
667 InitializeListHead(&c
->space_size
);
668 InitializeListHead(&c
->deleting
);
669 InitializeListHead(&c
->changed_extents
);
671 ExInitializeResourceLite(&c
->lock
);
672 ExInitializeResourceLite(&c
->changed_extents_lock
);
674 s
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(space
), ALLOC_TAG
);
676 ERR("out of memory\n");
680 s
->address
= c
->offset
;
681 s
->size
= c
->chunk_item
->size
;
682 InsertTailList(&c
->space
, &s
->list_entry
);
683 InsertTailList(&c
->space_size
, &s
->list_entry_size
);
685 protect_superblocks(Vcb
, c
);
687 for (i
= 0; i
< num_stripes
; i
++) {
688 stripes
[i
].device
->devitem
.bytes_used
+= stripe_size
;
690 space_list_subtract2(&stripes
[i
].device
->space
, NULL
, cis
[i
].offset
, stripe_size
, NULL
);
700 if (c
&& c
->chunk_item
) ExFreePool(c
->chunk_item
);
701 if (c
) ExFreePool(c
);
702 if (s
) ExFreePool(s
);
707 le
= Vcb
->chunks
.Flink
;
708 while (le
!= &Vcb
->chunks
) {
709 chunk
* c2
= CONTAINING_RECORD(le
, chunk
, list_entry
);
711 if (c2
->offset
> c
->offset
) {
712 InsertHeadList(le
->Blink
, &c
->list_entry
);
721 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
724 InsertTailList(&Vcb
->chunks_changed
, &c
->list_entry_changed
);
727 ExReleaseResourceLite(&Vcb
->chunk_lock
);
729 return success
? c
: NULL
;
732 NTSTATUS STDCALL
write_data(device_extension
* Vcb
, UINT64 address
, void* data
, BOOL need_free
, UINT32 length
, write_data_context
* wtc
, PIRP Irp
, chunk
* c
) {
735 CHUNK_ITEM_STRIPE
* cis
;
736 write_data_stripe
* stripe
;
737 UINT64
*stripestart
= NULL
, *stripeend
= NULL
;
738 UINT8
** stripedata
= NULL
;
741 TRACE("(%p, %llx, %p, %x)\n", Vcb
, address
, data
, length
);
744 c
= get_chunk_from_address(Vcb
, address
);
746 ERR("could not get chunk for address %llx\n", address
);
747 return STATUS_INTERNAL_ERROR
;
751 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
752 FIXME("RAID5 not yet supported\n");
753 return STATUS_NOT_IMPLEMENTED
;
754 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
755 FIXME("RAID6 not yet supported\n");
756 return STATUS_NOT_IMPLEMENTED
;
759 stripestart
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
761 ERR("out of memory\n");
762 return STATUS_INSUFFICIENT_RESOURCES
;
765 stripeend
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
767 ERR("out of memory\n");
768 ExFreePool(stripestart
);
769 return STATUS_INSUFFICIENT_RESOURCES
;
772 stripedata
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT8
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
774 ERR("out of memory\n");
775 ExFreePool(stripeend
);
776 ExFreePool(stripestart
);
777 return STATUS_INSUFFICIENT_RESOURCES
;
779 RtlZeroMemory(stripedata
, sizeof(UINT8
*) * c
->chunk_item
->num_stripes
);
781 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
783 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
784 UINT64 startoff
, endoff
;
785 UINT16 startoffstripe
, endoffstripe
, stripenum
;
786 UINT64 pos
, *stripeoff
;
788 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
790 ERR("out of memory\n");
791 ExFreePool(stripedata
);
792 ExFreePool(stripeend
);
793 ExFreePool(stripestart
);
794 return STATUS_INSUFFICIENT_RESOURCES
;
797 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &startoff
, &startoffstripe
);
798 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
, &endoff
, &endoffstripe
);
800 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
801 if (startoffstripe
> i
) {
802 stripestart
[i
] = startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
803 } else if (startoffstripe
== i
) {
804 stripestart
[i
] = startoff
;
806 stripestart
[i
] = startoff
- (startoff
% c
->chunk_item
->stripe_length
);
809 if (endoffstripe
> i
) {
810 stripeend
[i
] = endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
811 } else if (endoffstripe
== i
) {
812 stripeend
[i
] = endoff
+ 1;
814 stripeend
[i
] = endoff
- (endoff
% c
->chunk_item
->stripe_length
);
817 if (stripestart
[i
] != stripeend
[i
]) {
818 stripedata
[i
] = ExAllocatePoolWithTag(NonPagedPool
, stripeend
[i
] - stripestart
[i
], ALLOC_TAG
);
820 if (!stripedata
[i
]) {
821 ERR("out of memory\n");
822 ExFreePool(stripeoff
);
823 Status
= STATUS_INSUFFICIENT_RESOURCES
;
830 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
);
832 stripenum
= startoffstripe
;
833 while (pos
< length
) {
835 UINT32 writelen
= min(stripeend
[stripenum
] - stripestart
[stripenum
],
836 c
->chunk_item
->stripe_length
- (stripestart
[stripenum
] % c
->chunk_item
->stripe_length
));
838 RtlCopyMemory(stripedata
[stripenum
], data
, writelen
);
839 stripeoff
[stripenum
] += writelen
;
841 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
842 RtlCopyMemory(stripedata
[stripenum
] + stripeoff
[stripenum
], (UINT8
*)data
+ pos
, length
- pos
);
845 RtlCopyMemory(stripedata
[stripenum
] + stripeoff
[stripenum
], (UINT8
*)data
+ pos
, c
->chunk_item
->stripe_length
);
846 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
847 pos
+= c
->chunk_item
->stripe_length
;
850 stripenum
= (stripenum
+ 1) % c
->chunk_item
->num_stripes
;
853 ExFreePool(stripeoff
);
859 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
860 UINT64 startoff
, endoff
;
861 UINT16 startoffstripe
, endoffstripe
, stripenum
;
862 UINT64 pos
, *stripeoff
;
864 stripeoff
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, ALLOC_TAG
);
866 ERR("out of memory\n");
867 ExFreePool(stripedata
);
868 ExFreePool(stripeend
);
869 ExFreePool(stripestart
);
870 return STATUS_INSUFFICIENT_RESOURCES
;
873 get_raid0_offset(address
- c
->offset
, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, &startoff
, &startoffstripe
);
874 get_raid0_offset(address
+ length
- c
->offset
- 1, c
->chunk_item
->stripe_length
, c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
, &endoff
, &endoffstripe
);
876 startoffstripe
*= c
->chunk_item
->sub_stripes
;
877 endoffstripe
*= c
->chunk_item
->sub_stripes
;
879 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
+= c
->chunk_item
->sub_stripes
) {
882 if (startoffstripe
> i
) {
883 stripestart
[i
] = startoff
- (startoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
884 } else if (startoffstripe
== i
) {
885 stripestart
[i
] = startoff
;
887 stripestart
[i
] = startoff
- (startoff
% c
->chunk_item
->stripe_length
);
890 if (endoffstripe
> i
) {
891 stripeend
[i
] = endoff
- (endoff
% c
->chunk_item
->stripe_length
) + c
->chunk_item
->stripe_length
;
892 } else if (endoffstripe
== i
) {
893 stripeend
[i
] = endoff
+ 1;
895 stripeend
[i
] = endoff
- (endoff
% c
->chunk_item
->stripe_length
);
898 if (stripestart
[i
] != stripeend
[i
]) {
899 stripedata
[i
] = ExAllocatePoolWithTag(NonPagedPool
, stripeend
[i
] - stripestart
[i
], ALLOC_TAG
);
901 if (!stripedata
[i
]) {
902 ERR("out of memory\n");
903 ExFreePool(stripeoff
);
904 Status
= STATUS_INSUFFICIENT_RESOURCES
;
909 for (j
= 1; j
< c
->chunk_item
->sub_stripes
; j
++) {
910 stripestart
[i
+j
] = stripestart
[i
];
911 stripeend
[i
+j
] = stripeend
[i
];
912 stripedata
[i
+j
] = stripedata
[i
];
917 RtlZeroMemory(stripeoff
, sizeof(UINT64
) * c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
919 stripenum
= startoffstripe
/ c
->chunk_item
->sub_stripes
;
920 while (pos
< length
) {
922 UINT32 writelen
= min(stripeend
[stripenum
* c
->chunk_item
->sub_stripes
] - stripestart
[stripenum
* c
->chunk_item
->sub_stripes
],
923 c
->chunk_item
->stripe_length
- (stripestart
[stripenum
* c
->chunk_item
->sub_stripes
] % c
->chunk_item
->stripe_length
));
925 RtlCopyMemory(stripedata
[stripenum
* c
->chunk_item
->sub_stripes
], data
, writelen
);
926 stripeoff
[stripenum
] += writelen
;
928 } else if (length
- pos
< c
->chunk_item
->stripe_length
) {
929 RtlCopyMemory(stripedata
[stripenum
* c
->chunk_item
->sub_stripes
] + stripeoff
[stripenum
], (UINT8
*)data
+ pos
, length
- pos
);
932 RtlCopyMemory(stripedata
[stripenum
* c
->chunk_item
->sub_stripes
] + stripeoff
[stripenum
], (UINT8
*)data
+ pos
, c
->chunk_item
->stripe_length
);
933 stripeoff
[stripenum
] += c
->chunk_item
->stripe_length
;
934 pos
+= c
->chunk_item
->stripe_length
;
937 stripenum
= (stripenum
+ 1) % (c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
);
940 ExFreePool(stripeoff
);
947 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
948 stripestart
[i
] = address
- c
->offset
;
949 stripeend
[i
] = stripestart
[i
] + length
;
950 stripedata
[i
] = data
;
952 need_free2
= need_free
;
955 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
956 PIO_STACK_LOCATION IrpSp
;
958 // FIXME - handle missing devices
960 stripe
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_stripe
), ALLOC_TAG
);
962 ERR("out of memory\n");
963 Status
= STATUS_INSUFFICIENT_RESOURCES
;
967 if (stripestart
[i
] == stripeend
[i
]) {
968 stripe
->status
= WriteDataStatus_Ignore
;
972 stripe
->context
= (struct _write_data_context
*)wtc
;
973 stripe
->buf
= stripedata
[i
];
974 stripe
->need_free
= need_free2
;
975 stripe
->device
= c
->devices
[i
];
976 RtlZeroMemory(&stripe
->iosb
, sizeof(IO_STATUS_BLOCK
));
977 stripe
->status
= WriteDataStatus_Pending
;
980 stripe
->Irp
= IoAllocateIrp(stripe
->device
->devobj
->StackSize
, FALSE
);
983 ERR("IoAllocateIrp failed\n");
984 Status
= STATUS_INTERNAL_ERROR
;
988 stripe
->Irp
= IoMakeAssociatedIrp(Irp
, stripe
->device
->devobj
->StackSize
);
991 ERR("IoMakeAssociatedIrp failed\n");
992 Status
= STATUS_INTERNAL_ERROR
;
997 IrpSp
= IoGetNextIrpStackLocation(stripe
->Irp
);
998 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
1000 if (stripe
->device
->devobj
->Flags
& DO_BUFFERED_IO
) {
1001 stripe
->Irp
->AssociatedIrp
.SystemBuffer
= stripedata
[i
];
1003 stripe
->Irp
->Flags
= IRP_BUFFERED_IO
;
1004 } else if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1005 stripe
->Irp
->MdlAddress
= IoAllocateMdl(stripedata
[i
], stripeend
[i
] - stripestart
[i
], FALSE
, FALSE
, NULL
);
1006 if (!stripe
->Irp
->MdlAddress
) {
1007 ERR("IoAllocateMdl failed\n");
1008 Status
= STATUS_INTERNAL_ERROR
;
1012 MmProbeAndLockPages(stripe
->Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
1014 stripe
->Irp
->UserBuffer
= stripedata
[i
];
1017 IrpSp
->Parameters
.Write
.Length
= stripeend
[i
] - stripestart
[i
];
1018 IrpSp
->Parameters
.Write
.ByteOffset
.QuadPart
= stripestart
[i
] + cis
[i
].offset
;
1020 stripe
->Irp
->UserIosb
= &stripe
->iosb
;
1021 wtc
->stripes_left
++;
1023 IoSetCompletionRoutine(stripe
->Irp
, write_data_completion
, stripe
, TRUE
, TRUE
, TRUE
);
1026 InsertTailList(&wtc
->stripes
, &stripe
->list_entry
);
1029 Status
= STATUS_SUCCESS
;
1033 if (stripestart
) ExFreePool(stripestart
);
1034 if (stripeend
) ExFreePool(stripeend
);
1035 if (stripedata
) ExFreePool(stripedata
);
1037 if (!NT_SUCCESS(Status
)) {
1038 free_write_data_stripes(wtc
);
1045 NTSTATUS STDCALL
write_data_complete(device_extension
* Vcb
, UINT64 address
, void* data
, UINT32 length
, PIRP Irp
, chunk
* c
) {
1046 write_data_context
* wtc
;
1049 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_context
), ALLOC_TAG
);
1051 ERR("out of memory\n");
1052 return STATUS_INSUFFICIENT_RESOURCES
;
1055 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
1056 InitializeListHead(&wtc
->stripes
);
1058 wtc
->stripes_left
= 0;
1060 Status
= write_data(Vcb
, address
, data
, FALSE
, length
, wtc
, Irp
, c
);
1061 if (!NT_SUCCESS(Status
)) {
1062 ERR("write_data returned %08x\n", Status
);
1063 free_write_data_stripes(wtc
);
1068 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
1069 // launch writes and wait
1070 LIST_ENTRY
* le
= wtc
->stripes
.Flink
;
1071 while (le
!= &wtc
->stripes
) {
1072 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1074 if (stripe
->status
!= WriteDataStatus_Ignore
)
1075 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
1080 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
1082 le
= wtc
->stripes
.Flink
;
1083 while (le
!= &wtc
->stripes
) {
1084 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1086 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
1087 Status
= stripe
->iosb
.Status
;
1094 free_write_data_stripes(wtc
);
1099 return STATUS_SUCCESS
;
1102 static void clean_space_cache_chunk(device_extension
* Vcb
, chunk
* c
) {
1103 // FIXME - loop through c->deleting and do TRIM if device supports it
1104 // FIXME - also find way of doing TRIM of dropped chunks
1106 while (!IsListEmpty(&c
->deleting
)) {
1107 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
1109 RemoveEntryList(&s
->list_entry
);
1114 static void clean_space_cache(device_extension
* Vcb
) {
1117 TRACE("(%p)\n", Vcb
);
1119 while (!IsListEmpty(&Vcb
->chunks_changed
)) {
1120 c
= CONTAINING_RECORD(Vcb
->chunks_changed
.Flink
, chunk
, list_entry_changed
);
1122 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1124 clean_space_cache_chunk(Vcb
, c
);
1125 RemoveEntryList(&c
->list_entry_changed
);
1126 c
->list_entry_changed
.Flink
= NULL
;
1128 ExReleaseResourceLite(&c
->lock
);
1132 static BOOL
trees_consistent(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1133 ULONG maxsize
= Vcb
->superblock
.node_size
- sizeof(tree_header
);
1136 le
= Vcb
->trees
.Flink
;
1137 while (le
!= &Vcb
->trees
) {
1138 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1141 if (t
->header
.num_items
== 0 && t
->parent
) {
1142 #ifdef DEBUG_WRITE_LOOPS
1143 ERR("empty tree found, looping again\n");
1148 if (t
->size
> maxsize
) {
1149 #ifdef DEBUG_WRITE_LOOPS
1150 ERR("overlarge tree found (%u > %u), looping again\n", t
->size
, maxsize
);
1155 if (!t
->has_new_address
) {
1156 #ifdef DEBUG_WRITE_LOOPS
1157 ERR("tree found without new address, looping again\n");
1169 static NTSTATUS
add_parents(device_extension
* Vcb
, LIST_ENTRY
* rollback
) {
1173 for (level
= 0; level
<= 255; level
++) {
1174 BOOL nothing_found
= TRUE
;
1176 TRACE("level = %u\n", level
);
1178 le
= Vcb
->trees
.Flink
;
1179 while (le
!= &Vcb
->trees
) {
1180 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1182 if (t
->write
&& t
->header
.level
== level
) {
1183 TRACE("tree %p: root = %llx, level = %x, parent = %p\n", t
, t
->header
.tree_id
, t
->header
.level
, t
->parent
);
1185 nothing_found
= FALSE
;
1188 if (!t
->parent
->write
)
1189 TRACE("adding tree %p (level %x)\n", t
->parent
, t
->header
.level
);
1191 t
->parent
->write
= TRUE
;
1202 return STATUS_SUCCESS
;
1205 static void add_parents_to_cache(device_extension
* Vcb
, tree
* t
) {
1212 static BOOL
insert_tree_extent_skinny(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64 address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1213 EXTENT_ITEM_SKINNY_METADATA
* eism
;
1214 traverse_ptr insert_tp
;
1216 eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1218 ERR("out of memory\n");
1222 eism
->ei
.refcount
= 1;
1223 eism
->ei
.generation
= Vcb
->superblock
.generation
;
1224 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1225 eism
->type
= TYPE_TREE_BLOCK_REF
;
1226 eism
->tbr
.offset
= root_id
;
1228 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, Irp
, rollback
)) {
1229 ERR("insert_tree_item failed\n");
1234 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1236 space_list_subtract(Vcb
, c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
1238 ExReleaseResourceLite(&c
->lock
);
1240 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1245 static BOOL
insert_tree_extent(device_extension
* Vcb
, UINT8 level
, UINT64 root_id
, chunk
* c
, UINT64
* new_address
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1247 EXTENT_ITEM_TREE2
* eit2
;
1248 traverse_ptr insert_tp
;
1250 TRACE("(%p, %x, %llx, %p, %p, %p, %p)\n", Vcb
, level
, root_id
, c
, new_address
, rollback
);
1252 if (!find_address_in_chunk(Vcb
, c
, Vcb
->superblock
.node_size
, &address
))
1255 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1256 BOOL b
= insert_tree_extent_skinny(Vcb
, level
, root_id
, c
, address
, Irp
, rollback
);
1259 *new_address
= address
;
1264 eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1266 ERR("out of memory\n");
1270 eit2
->eit
.extent_item
.refcount
= 1;
1271 eit2
->eit
.extent_item
.generation
= Vcb
->superblock
.generation
;
1272 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1273 // eit2->eit.firstitem = wt->firstitem;
1274 eit2
->eit
.level
= level
;
1275 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1276 eit2
->tbr
.offset
= root_id
;
1278 // #ifdef DEBUG_PARANOID
1279 // if (wt->firstitem.obj_type == 0xcc) { // TESTING
1280 // ERR("error - firstitem not set (wt = %p, tree = %p, address = %x)\n", wt, wt->tree, (UINT32)address);
1281 // ERR("num_items = %u, level = %u, root = %x, delete = %u\n", wt->tree->header.num_items, wt->tree->header.level, (UINT32)wt->tree->root->id, wt->delete);
1286 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, Irp
, rollback
)) {
1287 ERR("insert_tree_item failed\n");
1292 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1294 space_list_subtract(Vcb
, c
, FALSE
, address
, Vcb
->superblock
.node_size
, rollback
);
1296 ExReleaseResourceLite(&c
->lock
);
1298 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1300 *new_address
= address
;
1305 NTSTATUS
get_tree_new_address(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1306 chunk
*origchunk
= NULL
, *c
;
1308 UINT64 flags
= t
->flags
, addr
;
1311 if (t
->root
->id
== BTRFS_ROOT_CHUNK
)
1312 flags
= BLOCK_FLAG_SYSTEM
| BLOCK_FLAG_DUPLICATE
;
1313 else if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS
)
1314 flags
= BLOCK_FLAG_DATA
| BLOCK_FLAG_METADATA
;
1316 flags
= BLOCK_FLAG_METADATA
| BLOCK_FLAG_DUPLICATE
;
1319 // TRACE("flags = %x\n", (UINT32)wt->flags);
1321 // if (!chunk_test) { // TESTING
1322 // if ((c = alloc_chunk(Vcb, flags))) {
1323 // if ((c->chunk_item->size - c->used) >= Vcb->superblock.node_size) {
1324 // if (insert_tree_extent(Vcb, t, c)) {
1325 // chunk_test = TRUE;
1326 // return STATUS_SUCCESS;
1332 if (t
->has_address
) {
1333 origchunk
= get_chunk_from_address(Vcb
, t
->header
.address
);
1335 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, origchunk
, &addr
, Irp
, rollback
)) {
1336 t
->new_address
= addr
;
1337 t
->has_new_address
= TRUE
;
1338 return STATUS_SUCCESS
;
1342 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
1344 le
= Vcb
->chunks
.Flink
;
1345 while (le
!= &Vcb
->chunks
) {
1346 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1348 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1350 if (c
!= origchunk
&& c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1351 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, c
, &addr
, Irp
, rollback
)) {
1352 ExReleaseResourceLite(&c
->lock
);
1353 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1354 t
->new_address
= addr
;
1355 t
->has_new_address
= TRUE
;
1356 return STATUS_SUCCESS
;
1360 ExReleaseResourceLite(&c
->lock
);
1365 // allocate new chunk if necessary
1366 if ((c
= alloc_chunk(Vcb
, flags
))) {
1367 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1369 if ((c
->chunk_item
->size
- c
->used
) >= Vcb
->superblock
.node_size
) {
1370 if (insert_tree_extent(Vcb
, t
->header
.level
, t
->header
.tree_id
, c
, &addr
, Irp
, rollback
)) {
1371 ExReleaseResourceLite(&c
->lock
);
1372 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1373 t
->new_address
= addr
;
1374 t
->has_new_address
= TRUE
;
1375 return STATUS_SUCCESS
;
1379 ExReleaseResourceLite(&c
->lock
);
1382 ExReleaseResourceLite(&Vcb
->chunk_lock
);
1384 ERR("couldn't find any metadata chunks with %x bytes free\n", Vcb
->superblock
.node_size
);
1386 return STATUS_DISK_FULL
;
1389 static BOOL
reduce_tree_extent_skinny(device_extension
* Vcb
, UINT64 address
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1395 searchkey
.obj_id
= address
;
1396 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
1397 searchkey
.offset
= 0xffffffffffffffff;
1399 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1400 if (!NT_SUCCESS(Status
)) {
1401 ERR("error - find_item returned %08x\n", Status
);
1405 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1406 TRACE("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1410 if (tp
.item
->size
< sizeof(EXTENT_ITEM_SKINNY_METADATA
)) {
1411 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_SKINNY_METADATA
));
1415 delete_tree_item(Vcb
, &tp
, rollback
);
1417 c
= get_chunk_from_address(Vcb
, address
);
1420 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1422 decrease_chunk_usage(c
, Vcb
->superblock
.node_size
);
1424 space_list_add(Vcb
, c
, TRUE
, address
, Vcb
->superblock
.node_size
, rollback
);
1426 ExReleaseResourceLite(&c
->lock
);
1428 ERR("could not find chunk for address %llx\n", address
);
1434 // static void check_tree_num_items(tree* t) {
1438 // le2 = t->itemlist.Flink;
1440 // while (le2 != &t->itemlist) {
1441 // tree_data* td = CONTAINING_RECORD(le2, tree_data, list_entry);
1444 // le2 = le2->Flink;
1447 // if (t->header.num_items != ni) {
1448 // ERR("tree %p not okay: num_items was %x, expecting %x\n", t, ni, t->header.num_items);
1451 // ERR("tree %p okay\n", t);
1455 // static void check_trees_num_items(LIST_ENTRY* tc) {
1456 // LIST_ENTRY* le = tc->Flink;
1457 // while (le != tc) {
1458 // tree_cache* tc2 = CONTAINING_RECORD(le, tree_cache, list_entry);
1460 // check_tree_num_items(tc2->tree);
1466 static void convert_old_tree_extent(device_extension
* Vcb
, tree_data
* td
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1468 traverse_ptr tp
, tp2
, insert_tp
;
1469 EXTENT_REF_V0
* erv0
;
1472 TRACE("(%p, %p, %p)\n", Vcb
, td
, t
);
1474 searchkey
.obj_id
= td
->treeholder
.address
;
1475 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1476 searchkey
.offset
= 0xffffffffffffffff;
1478 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1479 if (!NT_SUCCESS(Status
)) {
1480 ERR("error - find_item returned %08x\n", Status
);
1484 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1485 TRACE("could not find EXTENT_REF_V0 for %llx\n", searchkey
.obj_id
);
1489 searchkey
.obj_id
= td
->treeholder
.address
;
1490 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1491 searchkey
.offset
= Vcb
->superblock
.node_size
;
1493 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
, Irp
);
1494 if (!NT_SUCCESS(Status
)) {
1495 ERR("error - find_item returned %08x\n", Status
);
1499 if (keycmp(&searchkey
, &tp2
.item
->key
)) {
1500 ERR("could not find %llx,%x,%llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1504 if (tp
.item
->size
< sizeof(EXTENT_REF_V0
)) {
1505 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_REF_V0
));
1509 erv0
= (EXTENT_REF_V0
*)tp
.item
->data
;
1511 delete_tree_item(Vcb
, &tp
, rollback
);
1512 delete_tree_item(Vcb
, &tp2
, rollback
);
1514 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1515 EXTENT_ITEM_SKINNY_METADATA
* eism
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), ALLOC_TAG
);
1518 ERR("out of memory\n");
1522 eism
->ei
.refcount
= 1;
1523 eism
->ei
.generation
= erv0
->gen
;
1524 eism
->ei
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1525 eism
->type
= TYPE_TREE_BLOCK_REF
;
1526 eism
->tbr
.offset
= t
->header
.tree_id
;
1528 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, td
->treeholder
.address
, TYPE_METADATA_ITEM
, t
->header
.level
-1, eism
, sizeof(EXTENT_ITEM_SKINNY_METADATA
), &insert_tp
, Irp
, rollback
)) {
1529 ERR("insert_tree_item failed\n");
1533 EXTENT_ITEM_TREE2
* eit2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_ITEM_TREE2
), ALLOC_TAG
);
1536 ERR("out of memory\n");
1540 eit2
->eit
.extent_item
.refcount
= 1;
1541 eit2
->eit
.extent_item
.generation
= erv0
->gen
;
1542 eit2
->eit
.extent_item
.flags
= EXTENT_ITEM_TREE_BLOCK
;
1543 eit2
->eit
.firstitem
= td
->key
;
1544 eit2
->eit
.level
= t
->header
.level
- 1;
1545 eit2
->type
= TYPE_TREE_BLOCK_REF
;
1546 eit2
->tbr
.offset
= t
->header
.tree_id
;
1548 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, td
->treeholder
.address
, TYPE_EXTENT_ITEM
, Vcb
->superblock
.node_size
, eit2
, sizeof(EXTENT_ITEM_TREE2
), &insert_tp
, Irp
, rollback
)) {
1549 ERR("insert_tree_item failed\n");
1554 add_parents_to_cache(Vcb
, insert_tp
.tree
);
1555 add_parents_to_cache(Vcb
, tp
.tree
);
1556 add_parents_to_cache(Vcb
, tp2
.tree
);
1559 static NTSTATUS
reduce_tree_extent(device_extension
* Vcb
, UINT64 address
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1563 EXTENT_ITEM_V0
* eiv0
;
1567 // FIXME - deal with refcounts > 1
1569 TRACE("(%p, %llx, %p)\n", Vcb
, address
, t
);
1571 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
1572 if (reduce_tree_extent_skinny(Vcb
, address
, t
, Irp
, rollback
)) {
1573 return STATUS_SUCCESS
;
1577 searchkey
.obj_id
= address
;
1578 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1579 searchkey
.offset
= Vcb
->superblock
.node_size
;
1581 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1582 if (!NT_SUCCESS(Status
)) {
1583 ERR("error - find_item returned %08x\n", Status
);
1587 if (keycmp(&tp
.item
->key
, &searchkey
)) {
1588 ERR("could not find %llx,%x,%llx in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
1590 return STATUS_INTERNAL_ERROR
;
1593 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1594 eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
1596 if (eiv0
->refcount
> 1) {
1597 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (eiv0->refcount == %llx)\n", eiv0
->refcount
);
1598 return STATUS_INTERNAL_ERROR
;
1601 if (tp
.item
->size
< sizeof(EXTENT_ITEM
)) {
1602 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
));
1603 return STATUS_INTERNAL_ERROR
;
1606 ei
= (EXTENT_ITEM
*)tp
.item
->data
;
1608 if (ei
->refcount
> 1) {
1609 FIXME("FIXME - cannot deal with refcounts larger than 1 at present (ei->refcount == %llx)\n", ei
->refcount
);
1610 return STATUS_INTERNAL_ERROR
;
1614 delete_tree_item(Vcb
, &tp
, rollback
);
1616 // if EXTENT_ITEM_V0, delete corresponding B4 item
1617 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
1620 searchkey
.obj_id
= address
;
1621 searchkey
.obj_type
= TYPE_EXTENT_REF_V0
;
1622 searchkey
.offset
= 0xffffffffffffffff;
1624 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp2
, &searchkey
, FALSE
, Irp
);
1625 if (!NT_SUCCESS(Status
)) {
1626 ERR("error - find_item returned %08x\n", Status
);
1630 if (tp2
.item
->key
.obj_id
== searchkey
.obj_id
&& tp2
.item
->key
.obj_type
== searchkey
.obj_type
) {
1631 delete_tree_item(Vcb
, &tp2
, rollback
);
1635 if (t
&& !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
)) {
1638 // when writing old internal trees, convert related extents
1640 le
= t
->itemlist
.Flink
;
1641 while (le
!= &t
->itemlist
) {
1642 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
1644 // ERR("%llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
1646 if (!td
->ignore
&& !td
->inserted
) {
1647 if (t
->header
.level
> 0) {
1648 convert_old_tree_extent(Vcb
, td
, t
, Irp
, rollback
);
1649 } else if (td
->key
.obj_type
== TYPE_EXTENT_DATA
&& td
->size
>= sizeof(EXTENT_DATA
)) {
1650 EXTENT_DATA
* ed
= (EXTENT_DATA
*)td
->data
;
1652 if ((ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) && td
->size
>= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
1653 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
1655 if (ed2
->address
!= 0) {
1656 TRACE("trying to convert old data extent %llx,%llx\n", ed2
->address
, ed2
->size
);
1657 convert_old_data_extent(Vcb
, ed2
->address
, ed2
->size
, Irp
, rollback
);
1667 c
= get_chunk_from_address(Vcb
, address
);
1670 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
1672 decrease_chunk_usage(c
, tp
.item
->key
.offset
);
1674 space_list_add(Vcb
, c
, TRUE
, address
, tp
.item
->key
.offset
, rollback
);
1676 ExReleaseResourceLite(&c
->lock
);
1678 ERR("could not find chunk for address %llx\n", address
);
1680 return STATUS_SUCCESS
;
1683 static NTSTATUS
allocate_tree_extents(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1687 TRACE("(%p)\n", Vcb
);
1689 le
= Vcb
->trees
.Flink
;
1690 while (le
!= &Vcb
->trees
) {
1691 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1693 if (t
->write
&& !t
->has_new_address
) {
1696 Status
= get_tree_new_address(Vcb
, t
, Irp
, rollback
);
1697 if (!NT_SUCCESS(Status
)) {
1698 ERR("get_tree_new_address returned %08x\n", Status
);
1702 TRACE("allocated extent %llx\n", t
->new_address
);
1704 if (t
->has_address
) {
1705 Status
= reduce_tree_extent(Vcb
, t
->header
.address
, t
, Irp
, rollback
);
1707 if (!NT_SUCCESS(Status
)) {
1708 ERR("reduce_tree_extent returned %08x\n", Status
);
1713 c
= get_chunk_from_address(Vcb
, t
->new_address
);
1716 increase_chunk_usage(c
, Vcb
->superblock
.node_size
);
1718 ERR("could not find chunk for address %llx\n", t
->new_address
);
1719 return STATUS_INTERNAL_ERROR
;
1726 return STATUS_SUCCESS
;
1729 static NTSTATUS
update_root_root(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1733 TRACE("(%p)\n", Vcb
);
1735 le
= Vcb
->trees
.Flink
;
1736 while (le
!= &Vcb
->trees
) {
1737 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1739 if (t
->write
&& !t
->parent
) {
1740 if (t
->root
!= Vcb
->root_root
&& t
->root
!= Vcb
->chunk_root
) {
1744 searchkey
.obj_id
= t
->root
->id
;
1745 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
1746 searchkey
.offset
= 0xffffffffffffffff;
1748 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
1749 if (!NT_SUCCESS(Status
)) {
1750 ERR("error - find_item returned %08x\n", Status
);
1754 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1755 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
1757 return STATUS_INTERNAL_ERROR
;
1760 TRACE("updating the address for root %llx to %llx\n", searchkey
.obj_id
, t
->new_address
);
1762 t
->root
->root_item
.block_number
= t
->new_address
;
1763 t
->root
->root_item
.root_level
= t
->header
.level
;
1764 t
->root
->root_item
.generation
= Vcb
->superblock
.generation
;
1765 t
->root
->root_item
.generation2
= Vcb
->superblock
.generation
;
1767 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, delete and create new entry
1768 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
1771 ERR("out of memory\n");
1772 return STATUS_INSUFFICIENT_RESOURCES
;
1775 RtlCopyMemory(ri
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1777 delete_tree_item(Vcb
, &tp
, rollback
);
1779 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
, rollback
)) {
1780 ERR("insert_tree_item failed\n");
1781 return STATUS_INTERNAL_ERROR
;
1784 RtlCopyMemory(tp
.item
->data
, &t
->root
->root_item
, sizeof(ROOT_ITEM
));
1787 t
->root
->treeholder
.address
= t
->new_address
;
1793 Status
= update_chunk_caches(Vcb
, Irp
, rollback
);
1794 if (!NT_SUCCESS(Status
)) {
1795 ERR("update_chunk_caches returned %08x\n", Status
);
1799 return STATUS_SUCCESS
;
1802 static NTSTATUS STDCALL
write_data_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1803 write_data_stripe
* stripe
= conptr
;
1804 write_data_context
* context
= (write_data_context
*)stripe
->context
;
1807 // FIXME - we need a lock here
1809 if (stripe
->status
== WriteDataStatus_Cancelling
) {
1810 stripe
->status
= WriteDataStatus_Cancelled
;
1814 stripe
->iosb
= Irp
->IoStatus
;
1816 if (NT_SUCCESS(Irp
->IoStatus
.Status
)) {
1817 stripe
->status
= WriteDataStatus_Success
;
1819 le
= context
->stripes
.Flink
;
1821 stripe
->status
= WriteDataStatus_Error
;
1823 while (le
!= &context
->stripes
) {
1824 write_data_stripe
* s2
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1826 if (s2
->status
== WriteDataStatus_Pending
) {
1827 s2
->status
= WriteDataStatus_Cancelling
;
1828 IoCancelIrp(s2
->Irp
);
1836 if (InterlockedDecrement(&context
->stripes_left
) == 0)
1837 KeSetEvent(&context
->Event
, 0, FALSE
);
1839 return STATUS_MORE_PROCESSING_REQUIRED
;
1842 void free_write_data_stripes(write_data_context
* wtc
) {
1843 LIST_ENTRY
*le
, *le2
, *nextle
;
1845 le
= wtc
->stripes
.Flink
;
1846 while (le
!= &wtc
->stripes
) {
1847 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1850 if (stripe
->device
->devobj
->Flags
& DO_DIRECT_IO
) {
1851 MmUnlockPages(stripe
->Irp
->MdlAddress
);
1852 IoFreeMdl(stripe
->Irp
->MdlAddress
);
1859 le
= wtc
->stripes
.Flink
;
1860 while (le
!= &wtc
->stripes
) {
1861 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
1865 if (stripe
->buf
&& stripe
->need_free
) {
1866 ExFreePool(stripe
->buf
);
1869 while (le2
!= &wtc
->stripes
) {
1870 write_data_stripe
* s2
= CONTAINING_RECORD(le2
, write_data_stripe
, list_entry
);
1872 if (s2
->buf
== stripe
->buf
)
1886 static NTSTATUS
write_trees(device_extension
* Vcb
, PIRP Irp
) {
1892 write_data_context
* wtc
;
1894 TRACE("(%p)\n", Vcb
);
1896 for (level
= 0; level
<= 255; level
++) {
1897 BOOL nothing_found
= TRUE
;
1899 TRACE("level = %u\n", level
);
1901 le
= Vcb
->trees
.Flink
;
1902 while (le
!= &Vcb
->trees
) {
1903 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
1905 if (t
->write
&& t
->header
.level
== level
) {
1906 KEY firstitem
, searchkey
;
1909 EXTENT_ITEM_TREE
* eit
;
1911 if (!t
->has_new_address
) {
1912 ERR("error - tried to write tree with no new address\n");
1916 le2
= t
->itemlist
.Flink
;
1917 while (le2
!= &t
->itemlist
) {
1918 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
1920 firstitem
= td
->key
;
1927 t
->paritem
->key
= firstitem
;
1928 t
->paritem
->treeholder
.address
= t
->new_address
;
1929 t
->paritem
->treeholder
.generation
= Vcb
->superblock
.generation
;
1932 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
)) {
1933 searchkey
.obj_id
= t
->new_address
;
1934 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
1935 searchkey
.offset
= Vcb
->superblock
.node_size
;
1937 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
1938 if (!NT_SUCCESS(Status
)) {
1939 ERR("error - find_item returned %08x\n", Status
);
1943 if (keycmp(&searchkey
, &tp
.item
->key
)) {
1944 // traverse_ptr next_tp;
1946 // tree_data* paritem;
1948 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
);
1950 // searchkey.obj_id = 0;
1951 // searchkey.obj_type = 0;
1952 // searchkey.offset = 0;
1954 // find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
1958 // if (tp.tree->paritem != paritem) {
1959 // paritem = tp.tree->paritem;
1960 // ERR("paritem: %llx,%x,%llx\n", paritem->key.obj_id, paritem->key.obj_type, paritem->key.offset);
1963 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
1965 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
1967 // free_traverse_ptr(&tp);
1972 // free_traverse_ptr(&tp);
1974 return STATUS_INTERNAL_ERROR
;
1977 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
1978 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
));
1979 return STATUS_INTERNAL_ERROR
;
1982 eit
= (EXTENT_ITEM_TREE
*)tp
.item
->data
;
1983 eit
->firstitem
= firstitem
;
1986 nothing_found
= FALSE
;
1996 TRACE("allocated tree extents\n");
1998 wtc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(write_data_context
), ALLOC_TAG
);
2000 ERR("out of memory\n");
2001 return STATUS_INSUFFICIENT_RESOURCES
;
2004 KeInitializeEvent(&wtc
->Event
, NotificationEvent
, FALSE
);
2005 InitializeListHead(&wtc
->stripes
);
2007 wtc
->stripes_left
= 0;
2009 le
= Vcb
->trees
.Flink
;
2010 while (le
!= &Vcb
->trees
) {
2011 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2012 #ifdef DEBUG_PARANOID
2013 UINT32 num_items
= 0, size
= 0;
2019 #ifdef DEBUG_PARANOID
2020 le2
= t
->itemlist
.Flink
;
2021 while (le2
!= &t
->itemlist
) {
2022 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2026 if (t
->header
.level
== 0)
2032 if (t
->header
.level
== 0)
2033 size
+= num_items
* sizeof(leaf_node
);
2035 size
+= num_items
* sizeof(internal_node
);
2037 if (num_items
!= t
->header
.num_items
) {
2038 ERR("tree %llx, level %x: num_items was %x, expected %x\n", t
->root
->id
, t
->header
.level
, num_items
, t
->header
.num_items
);
2042 if (size
!= t
->size
) {
2043 ERR("tree %llx, level %x: size was %x, expected %x\n", t
->root
->id
, t
->header
.level
, size
, t
->size
);
2047 if (t
->header
.num_items
== 0 && t
->parent
) {
2048 ERR("tree %llx, level %x: tried to write empty tree with parent\n", t
->root
->id
, t
->header
.level
);
2052 if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2053 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
));
2058 ERR("tree %p\n", t
);
2059 le2
= t
->itemlist
.Flink
;
2060 while (le2
!= &t
->itemlist
) {
2061 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2063 ERR("%llx,%x,%llx inserted=%u\n", td
->key
.obj_id
, td
->key
.obj_type
, td
->key
.offset
, td
->inserted
);
2070 t
->header
.address
= t
->new_address
;
2071 t
->header
.generation
= Vcb
->superblock
.generation
;
2072 t
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
2073 t
->has_address
= TRUE
;
2075 data
= ExAllocatePoolWithTag(NonPagedPool
, Vcb
->superblock
.node_size
, ALLOC_TAG
);
2077 ERR("out of memory\n");
2078 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2082 body
= data
+ sizeof(tree_header
);
2084 RtlCopyMemory(data
, &t
->header
, sizeof(tree_header
));
2085 RtlZeroMemory(body
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
2087 if (t
->header
.level
== 0) {
2088 leaf_node
* itemptr
= (leaf_node
*)body
;
2091 UINT8
* dataptr
= data
+ Vcb
->superblock
.node_size
;
2093 le2
= t
->itemlist
.Flink
;
2094 while (le2
!= &t
->itemlist
) {
2095 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2097 dataptr
= dataptr
- td
->size
;
2099 itemptr
[i
].key
= td
->key
;
2100 itemptr
[i
].offset
= (UINT8
*)dataptr
- (UINT8
*)body
;
2101 itemptr
[i
].size
= td
->size
;
2105 RtlCopyMemory(dataptr
, td
->data
, td
->size
);
2111 internal_node
* itemptr
= (internal_node
*)body
;
2115 le2
= t
->itemlist
.Flink
;
2116 while (le2
!= &t
->itemlist
) {
2117 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
2119 itemptr
[i
].key
= td
->key
;
2120 itemptr
[i
].address
= td
->treeholder
.address
;
2121 itemptr
[i
].generation
= td
->treeholder
.generation
;
2129 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&((tree_header
*)data
)->fs_uuid
, Vcb
->superblock
.node_size
- sizeof(((tree_header
*)data
)->csum
));
2131 *((UINT32
*)data
) = crc32
;
2132 TRACE("setting crc32 to %08x\n", crc32
);
2134 Status
= write_data(Vcb
, t
->new_address
, data
, TRUE
, Vcb
->superblock
.node_size
, wtc
, NULL
, NULL
);
2135 if (!NT_SUCCESS(Status
)) {
2136 ERR("write_data returned %08x\n", Status
);
2144 Status
= STATUS_SUCCESS
;
2146 if (wtc
->stripes
.Flink
!= &wtc
->stripes
) {
2147 // launch writes and wait
2148 le
= wtc
->stripes
.Flink
;
2149 while (le
!= &wtc
->stripes
) {
2150 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2152 if (stripe
->status
!= WriteDataStatus_Ignore
)
2153 IoCallDriver(stripe
->device
->devobj
, stripe
->Irp
);
2158 KeWaitForSingleObject(&wtc
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2160 le
= wtc
->stripes
.Flink
;
2161 while (le
!= &wtc
->stripes
) {
2162 write_data_stripe
* stripe
= CONTAINING_RECORD(le
, write_data_stripe
, list_entry
);
2164 if (stripe
->status
!= WriteDataStatus_Ignore
&& !NT_SUCCESS(stripe
->iosb
.Status
)) {
2165 Status
= stripe
->iosb
.Status
;
2172 free_write_data_stripes(wtc
);
2181 static void update_backup_superblock(device_extension
* Vcb
, superblock_backup
* sb
, PIRP Irp
) {
2185 RtlZeroMemory(sb
, sizeof(superblock_backup
));
2187 sb
->root_tree_addr
= Vcb
->superblock
.root_tree_addr
;
2188 sb
->root_tree_generation
= Vcb
->superblock
.generation
;
2189 sb
->root_level
= Vcb
->superblock
.root_level
;
2191 sb
->chunk_tree_addr
= Vcb
->superblock
.chunk_tree_addr
;
2192 sb
->chunk_tree_generation
= Vcb
->superblock
.chunk_root_generation
;
2193 sb
->chunk_root_level
= Vcb
->superblock
.chunk_root_level
;
2195 searchkey
.obj_id
= BTRFS_ROOT_EXTENT
;
2196 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
2197 searchkey
.offset
= 0xffffffffffffffff;
2199 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2200 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2201 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2203 sb
->extent_tree_addr
= ri
->block_number
;
2204 sb
->extent_tree_generation
= ri
->generation
;
2205 sb
->extent_root_level
= ri
->root_level
;
2209 searchkey
.obj_id
= BTRFS_ROOT_FSTREE
;
2211 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2212 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2213 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2215 sb
->fs_tree_addr
= ri
->block_number
;
2216 sb
->fs_tree_generation
= ri
->generation
;
2217 sb
->fs_root_level
= ri
->root_level
;
2221 searchkey
.obj_id
= BTRFS_ROOT_DEVTREE
;
2223 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2224 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2225 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2227 sb
->dev_root_addr
= ri
->block_number
;
2228 sb
->dev_root_generation
= ri
->generation
;
2229 sb
->dev_root_level
= ri
->root_level
;
2233 searchkey
.obj_id
= BTRFS_ROOT_CHECKSUM
;
2235 if (NT_SUCCESS(find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
))) {
2236 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
&& tp
.item
->size
>= sizeof(ROOT_ITEM
)) {
2237 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2239 sb
->csum_root_addr
= ri
->block_number
;
2240 sb
->csum_root_generation
= ri
->generation
;
2241 sb
->csum_root_level
= ri
->root_level
;
2245 sb
->total_bytes
= Vcb
->superblock
.total_bytes
;
2246 sb
->bytes_used
= Vcb
->superblock
.bytes_used
;
2247 sb
->num_devices
= Vcb
->superblock
.num_devices
;
2250 static NTSTATUS
write_superblocks(device_extension
* Vcb
, PIRP Irp
) {
2255 TRACE("(%p)\n", Vcb
);
2257 le
= Vcb
->trees
.Flink
;
2258 while (le
!= &Vcb
->trees
) {
2259 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
2261 if (t
->write
&& !t
->parent
) {
2262 if (t
->root
== Vcb
->root_root
) {
2263 Vcb
->superblock
.root_tree_addr
= t
->new_address
;
2264 Vcb
->superblock
.root_level
= t
->header
.level
;
2265 } else if (t
->root
== Vcb
->chunk_root
) {
2266 Vcb
->superblock
.chunk_tree_addr
= t
->new_address
;
2267 Vcb
->superblock
.chunk_root_generation
= t
->header
.generation
;
2268 Vcb
->superblock
.chunk_root_level
= t
->header
.level
;
2275 for (i
= 0; i
< BTRFS_NUM_BACKUP_ROOTS
- 1; i
++) {
2276 RtlCopyMemory(&Vcb
->superblock
.backup
[i
], &Vcb
->superblock
.backup
[i
+1], sizeof(superblock_backup
));
2279 update_backup_superblock(Vcb
, &Vcb
->superblock
.backup
[BTRFS_NUM_BACKUP_ROOTS
- 1], Irp
);
2281 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2282 if (Vcb
->devices
[i
].devobj
) {
2283 Status
= write_superblock(Vcb
, &Vcb
->devices
[i
]);
2284 if (!NT_SUCCESS(Status
)) {
2285 ERR("write_superblock returned %08x\n", Status
);
2291 return STATUS_SUCCESS
;
2294 static NTSTATUS
flush_changed_extent(device_extension
* Vcb
, chunk
* c
, changed_extent
* ce
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2295 LIST_ENTRY
*le
, *le2
;
2299 le
= ce
->refs
.Flink
;
2300 while (le
!= &ce
->refs
) {
2301 changed_extent_ref
* cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
2302 LIST_ENTRY
* le3
= le
->Flink
;
2303 UINT64 old_count
= 0;
2305 le2
= ce
->old_refs
.Flink
;
2306 while (le2
!= &ce
->old_refs
) {
2307 changed_extent_ref
* cer2
= CONTAINING_RECORD(le2
, changed_extent_ref
, list_entry
);
2309 if (cer2
->edr
.root
== cer
->edr
.root
&& cer2
->edr
.objid
== cer
->edr
.objid
&& cer2
->edr
.offset
== cer
->edr
.offset
) {
2310 old_count
= cer2
->edr
.count
;
2312 RemoveEntryList(&cer2
->list_entry
);
2320 old_size
= ce
->old_count
> 0 ? ce
->old_size
: ce
->size
;
2322 if (cer
->edr
.count
> old_count
) {
2323 Status
= increase_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
, cer
->edr
.count
- old_count
, Irp
, rollback
);
2325 if (!NT_SUCCESS(Status
)) {
2326 ERR("increase_extent_refcount_data returned %08x\n", Status
);
2329 } else if (cer
->edr
.count
< old_count
) {
2330 Status
= decrease_extent_refcount_data(Vcb
, ce
->address
, old_size
, cer
->edr
.root
, cer
->edr
.objid
, cer
->edr
.offset
,
2331 old_count
- cer
->edr
.count
, Irp
, rollback
);
2333 if (!NT_SUCCESS(Status
)) {
2334 ERR("decrease_extent_refcount_data returned %08x\n", Status
);
2339 if (ce
->size
!= ce
->old_size
&& ce
->old_count
> 0) {
2344 searchkey
.obj_id
= ce
->address
;
2345 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2346 searchkey
.offset
= ce
->old_size
;
2348 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2349 if (!NT_SUCCESS(Status
)) {
2350 ERR("error - find_item returned %08x\n", Status
);
2354 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2355 ERR("could not find (%llx,%x,%llx) in extent tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2356 return STATUS_INTERNAL_ERROR
;
2359 if (tp
.item
->size
> 0) {
2360 data
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2363 ERR("out of memory\n");
2364 return STATUS_INSUFFICIENT_RESOURCES
;
2367 RtlCopyMemory(data
, tp
.item
->data
, tp
.item
->size
);
2371 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, ce
->address
, TYPE_EXTENT_ITEM
, ce
->size
, data
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
2372 ERR("insert_tree_item failed\n");
2373 return STATUS_INTERNAL_ERROR
;
2376 delete_tree_item(Vcb
, &tp
, rollback
);
2379 RemoveEntryList(&cer
->list_entry
);
2385 #ifdef DEBUG_PARANOID
2386 if (!IsListEmpty(&ce
->old_refs
))
2387 WARN("old_refs not empty\n");
2390 if (ce
->count
== 0) {
2392 LIST_ENTRY changed_sector_list
;
2394 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
2396 ERR("out of memory\n");
2397 return STATUS_INSUFFICIENT_RESOURCES
;
2400 sc
->ol
.key
= ce
->address
;
2401 sc
->checksums
= NULL
;
2402 sc
->length
= ce
->size
/ Vcb
->superblock
.sector_size
;
2406 InitializeListHead(&changed_sector_list
);
2407 insert_into_ordered_list(&changed_sector_list
, &sc
->ol
);
2409 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
2410 commit_checksum_changes(Vcb
, &changed_sector_list
);
2411 ExReleaseResourceLite(&Vcb
->checksum_lock
);
2414 decrease_chunk_usage(c
, ce
->size
);
2416 space_list_add(Vcb
, c
, TRUE
, ce
->address
, ce
->size
, rollback
);
2419 RemoveEntryList(&ce
->list_entry
);
2422 return STATUS_SUCCESS
;
2425 static NTSTATUS
update_chunk_usage(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2426 LIST_ENTRY
*le
= Vcb
->chunks
.Flink
, *le2
;
2430 BLOCK_GROUP_ITEM
* bgi
;
2432 BOOL flushed_extents
= FALSE
;
2434 TRACE("(%p)\n", Vcb
);
2436 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
2438 while (le
!= &Vcb
->chunks
) {
2439 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2441 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
2443 le2
= c
->changed_extents
.Flink
;
2444 while (le2
!= &c
->changed_extents
) {
2445 LIST_ENTRY
* le3
= le2
->Flink
;
2446 changed_extent
* ce
= CONTAINING_RECORD(le2
, changed_extent
, list_entry
);
2448 Status
= flush_changed_extent(Vcb
, c
, ce
, Irp
, rollback
);
2449 if (!NT_SUCCESS(Status
)) {
2450 ERR("flush_changed_extent returned %08x\n", Status
);
2451 ExReleaseResourceLite(&c
->lock
);
2455 flushed_extents
= TRUE
;
2460 if (c
->used
!= c
->oldused
) {
2461 searchkey
.obj_id
= c
->offset
;
2462 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
2463 searchkey
.offset
= c
->chunk_item
->size
;
2465 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
2466 if (!NT_SUCCESS(Status
)) {
2467 ERR("error - find_item returned %08x\n", Status
);
2468 ExReleaseResourceLite(&c
->lock
);
2472 if (keycmp(&searchkey
, &tp
.item
->key
)) {
2473 ERR("could not find (%llx,%x,%llx) in extent_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2475 Status
= STATUS_INTERNAL_ERROR
;
2476 ExReleaseResourceLite(&c
->lock
);
2480 if (tp
.item
->size
< sizeof(BLOCK_GROUP_ITEM
)) {
2481 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
));
2482 Status
= STATUS_INTERNAL_ERROR
;
2483 ExReleaseResourceLite(&c
->lock
);
2487 bgi
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2489 ERR("out of memory\n");
2490 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2491 ExReleaseResourceLite(&c
->lock
);
2495 RtlCopyMemory(bgi
, tp
.item
->data
, tp
.item
->size
);
2496 bgi
->used
= c
->used
;
2498 TRACE("adjusting usage of chunk %llx to %llx\n", c
->offset
, c
->used
);
2500 delete_tree_item(Vcb
, &tp
, rollback
);
2502 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, bgi
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
2503 ERR("insert_tree_item failed\n");
2505 Status
= STATUS_INTERNAL_ERROR
;
2506 ExReleaseResourceLite(&c
->lock
);
2510 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2511 TRACE("chunk_item type = %llx\n", c
->chunk_item
->type
);
2513 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
) {
2514 Vcb
->superblock
.bytes_used
+= c
->used
- c
->oldused
;
2515 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID1
|| c
->chunk_item
->type
& BLOCK_FLAG_DUPLICATE
|| c
->chunk_item
->type
& BLOCK_FLAG_RAID10
) {
2516 Vcb
->superblock
.bytes_used
+= 2 * (c
->used
- c
->oldused
);
2517 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID5
) {
2518 FIXME("RAID5 not yet supported\n");
2520 Status
= STATUS_INTERNAL_ERROR
;
2521 ExReleaseResourceLite(&c
->lock
);
2523 } else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID6
) {
2524 FIXME("RAID6 not yet supported\n");
2526 Status
= STATUS_INTERNAL_ERROR
;
2527 ExReleaseResourceLite(&c
->lock
);
2530 Vcb
->superblock
.bytes_used
+= c
->used
- c
->oldused
;
2533 TRACE("bytes_used = %llx\n", Vcb
->superblock
.bytes_used
);
2535 c
->oldused
= c
->used
;
2538 ExReleaseResourceLite(&c
->lock
);
2543 if (flushed_extents
) {
2544 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
2545 if (!IsListEmpty(&Vcb
->sector_checksums
)) {
2546 update_checksum_tree(Vcb
, Irp
, rollback
);
2548 ExReleaseResourceLite(&Vcb
->checksum_lock
);
2551 Status
= STATUS_SUCCESS
;
2554 ExReleaseResourceLite(&Vcb
->chunk_lock
);
2559 static void get_first_item(tree
* t
, KEY
* key
) {
2562 le
= t
->itemlist
.Flink
;
2563 while (le
!= &t
->itemlist
) {
2564 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2571 static NTSTATUS STDCALL
split_tree_at(device_extension
* Vcb
, tree
* t
, tree_data
* newfirstitem
, UINT32 numitems
, UINT32 size
) {
2574 tree_data
* oldlastitem
;
2576 // // tree_data *firsttd, *lasttd;
2577 // // LIST_ENTRY* le;
2578 // #ifdef DEBUG_PARANOID
2579 // KEY lastkey1, lastkey2;
2580 // traverse_ptr tp, next_tp;
2581 // ULONG numitems1, numitems2;
2584 TRACE("splitting tree in %llx at (%llx,%x,%llx)\n", t
->root
->id
, newfirstitem
->key
.obj_id
, newfirstitem
->key
.obj_type
, newfirstitem
->key
.offset
);
2586 // #ifdef DEBUG_PARANOID
2587 // lastkey1.obj_id = 0xffffffffffffffff;
2588 // lastkey1.obj_type = 0xff;
2589 // lastkey1.offset = 0xffffffffffffffff;
2591 // if (!find_item(Vcb, t->root, &tp, &lastkey1, NULL, FALSE))
2592 // ERR("error - find_item failed\n");
2594 // lastkey1 = tp.item->key;
2596 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2597 // free_traverse_ptr(&tp);
2601 // free_traverse_ptr(&tp);
2605 nt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2607 ERR("out of memory\n");
2608 return STATUS_INSUFFICIENT_RESOURCES
;
2611 RtlCopyMemory(&nt
->header
, &t
->header
, sizeof(tree_header
));
2612 nt
->header
.address
= 0;
2613 nt
->header
.generation
= Vcb
->superblock
.generation
;
2614 nt
->header
.num_items
= t
->header
.num_items
- numitems
;
2615 nt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2617 nt
->has_address
= FALSE
;
2619 nt
->parent
= t
->parent
;
2621 // nt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2622 nt
->new_address
= 0;
2623 nt
->has_new_address
= FALSE
;
2624 nt
->flags
= t
->flags
;
2625 InitializeListHead(&nt
->itemlist
);
2627 // ExInitializeResourceLite(&nt->nonpaged->load_tree_lock);
2629 oldlastitem
= CONTAINING_RECORD(newfirstitem
->list_entry
.Blink
, tree_data
, list_entry
);
2631 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2632 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2634 // // TRACE("old tree in %x was from (%x,%x,%x) to (%x,%x,%x)\n",
2635 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2636 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2638 // // le = wt->tree->itemlist.Flink;
2639 // // while (le != &wt->tree->itemlist) {
2640 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2641 // // TRACE("old tree item was (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2642 // // le = le->Flink;
2645 nt
->itemlist
.Flink
= &newfirstitem
->list_entry
;
2646 nt
->itemlist
.Blink
= t
->itemlist
.Blink
;
2647 nt
->itemlist
.Flink
->Blink
= &nt
->itemlist
;
2648 nt
->itemlist
.Blink
->Flink
= &nt
->itemlist
;
2650 t
->itemlist
.Blink
= &oldlastitem
->list_entry
;
2651 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2653 // // le = wt->tree->itemlist.Flink;
2654 // // while (le != &wt->tree->itemlist) {
2655 // // td = CONTAINING_RECORD(le, tree_data, list_entry);
2656 // // TRACE("old tree item now (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2657 // // le = le->Flink;
2660 // // firsttd = CONTAINING_RECORD(wt->tree->itemlist.Flink, tree_data, list_entry);
2661 // // lasttd = CONTAINING_RECORD(wt->tree->itemlist.Blink, tree_data, list_entry);
2663 // // TRACE("old tree in %x is now from (%x,%x,%x) to (%x,%x,%x)\n",
2664 // // (UINT32)wt->tree->root->id, (UINT32)firsttd->key.obj_id, firsttd->key.obj_type, (UINT32)firsttd->key.offset,
2665 // // (UINT32)lasttd->key.obj_id, lasttd->key.obj_type, (UINT32)lasttd->key.offset);
2667 nt
->size
= t
->size
- size
;
2669 t
->header
.num_items
= numitems
;
2672 InterlockedIncrement(&Vcb
->open_trees
);
2673 InsertTailList(&Vcb
->trees
, &nt
->list_entry
);
2676 // // td = wt->tree->items;
2678 // // if (!td->ignore) {
2679 // // TRACE("old tree item: (%x,%x,%x)\n", (UINT32)td->key.obj_id, td->key.obj_type, (UINT32)td->key.offset);
2681 // // td = td->next;
2684 // // oldlastitem->next = NULL;
2685 // // wt->tree->lastitem = oldlastitem;
2687 // // TRACE("last item is now (%x,%x,%x)\n", (UINT32)oldlastitem->key.obj_id, oldlastitem->key.obj_type, (UINT32)oldlastitem->key.offset);
2689 if (nt
->header
.level
> 0) {
2690 LIST_ENTRY
* le
= nt
->itemlist
.Flink
;
2692 while (le
!= &nt
->itemlist
) {
2693 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2695 if (td2
->treeholder
.tree
)
2696 td2
->treeholder
.tree
->parent
= nt
;
2703 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2705 ERR("out of memory\n");
2706 return STATUS_INSUFFICIENT_RESOURCES
;
2709 td
->key
= newfirstitem
->key
;
2711 InsertHeadList(&t
->paritem
->list_entry
, &td
->list_entry
);
2714 td
->inserted
= TRUE
;
2715 td
->treeholder
.tree
= nt
;
2716 // td->treeholder.nonpaged->status = tree_holder_loaded;
2719 nt
->parent
->header
.num_items
++;
2720 nt
->parent
->size
+= sizeof(internal_node
);
2725 TRACE("adding new tree parent\n");
2727 if (nt
->header
.level
== 255) {
2728 ERR("cannot add parent to tree at level 255\n");
2729 return STATUS_INTERNAL_ERROR
;
2732 pt
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
2734 ERR("out of memory\n");
2735 return STATUS_INSUFFICIENT_RESOURCES
;
2738 RtlCopyMemory(&pt
->header
, &nt
->header
, sizeof(tree_header
));
2739 pt
->header
.address
= 0;
2740 pt
->header
.num_items
= 2;
2741 pt
->header
.level
= nt
->header
.level
+ 1;
2742 pt
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
;
2744 pt
->has_address
= FALSE
;
2749 pt
->new_address
= 0;
2750 pt
->has_new_address
= FALSE
;
2751 // pt->nonpaged = ExAllocatePoolWithTag(NonPagedPool, sizeof(tree_nonpaged), ALLOC_TAG);
2752 pt
->size
= pt
->header
.num_items
* sizeof(internal_node
);
2753 pt
->flags
= t
->flags
;
2754 InitializeListHead(&pt
->itemlist
);
2756 // ExInitializeResourceLite(&pt->nonpaged->load_tree_lock);
2758 InterlockedIncrement(&Vcb
->open_trees
);
2759 InsertTailList(&Vcb
->trees
, &pt
->list_entry
);
2761 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2763 ERR("out of memory\n");
2764 return STATUS_INSUFFICIENT_RESOURCES
;
2767 get_first_item(t
, &td
->key
);
2769 td
->inserted
= FALSE
;
2770 td
->treeholder
.address
= 0;
2771 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2772 td
->treeholder
.tree
= t
;
2773 // td->treeholder.nonpaged->status = tree_holder_loaded;
2774 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2777 td
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree_data
), ALLOC_TAG
);
2779 ERR("out of memory\n");
2780 return STATUS_INSUFFICIENT_RESOURCES
;
2783 td
->key
= newfirstitem
->key
;
2785 td
->inserted
= FALSE
;
2786 td
->treeholder
.address
= 0;
2787 td
->treeholder
.generation
= Vcb
->superblock
.generation
;
2788 td
->treeholder
.tree
= nt
;
2789 // td->treeholder.nonpaged->status = tree_holder_loaded;
2790 InsertTailList(&pt
->itemlist
, &td
->list_entry
);
2795 t
->root
->treeholder
.tree
= pt
;
2801 t
->root
->root_item
.bytes_used
+= Vcb
->superblock
.node_size
;
2803 // #ifdef DEBUG_PARANOID
2804 // lastkey2.obj_id = 0xffffffffffffffff;
2805 // lastkey2.obj_type = 0xff;
2806 // lastkey2.offset = 0xffffffffffffffff;
2808 // if (!find_item(Vcb, wt->tree->root, &tp, &lastkey2, NULL, FALSE))
2809 // ERR("error - find_item failed\n");
2811 // lastkey2 = tp.item->key;
2814 // while (find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
2815 // free_traverse_ptr(&tp);
2819 // free_traverse_ptr(&tp);
2822 // ERR("lastkey1 = %llx,%x,%llx\n", lastkey1.obj_id, lastkey1.obj_type, lastkey1.offset);
2823 // ERR("lastkey2 = %llx,%x,%llx\n", lastkey2.obj_id, lastkey2.obj_type, lastkey2.offset);
2824 // ERR("numitems1 = %u\n", numitems1);
2825 // ERR("numitems2 = %u\n", numitems2);
2828 return STATUS_SUCCESS
;
2831 static NTSTATUS STDCALL
split_tree(device_extension
* Vcb
, tree
* t
) {
2833 UINT32 size
, ds
, numitems
;
2838 // FIXME - naïve implementation: maximizes number of filled trees
2840 le
= t
->itemlist
.Flink
;
2841 while (le
!= &t
->itemlist
) {
2842 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2845 if (t
->header
.level
== 0)
2846 ds
= sizeof(leaf_node
) + td
->size
;
2848 ds
= sizeof(internal_node
);
2850 // FIXME - move back if previous item was deleted item with same key
2851 if (size
+ ds
> Vcb
->superblock
.node_size
- sizeof(tree_header
))
2852 return split_tree_at(Vcb
, t
, td
, numitems
, size
);
2861 return STATUS_SUCCESS
;
2864 static NTSTATUS
try_tree_amalgamate(device_extension
* Vcb
, tree
* t
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2866 tree_data
* nextparitem
= NULL
;
2868 tree
*next_tree
, *par
;
2871 TRACE("trying to amalgamate tree in root %llx, level %x (size %u)\n", t
->root
->id
, t
->header
.level
, t
->size
);
2873 // FIXME - doesn't capture everything, as it doesn't ascend
2874 // FIXME - write proper function and put it in treefuncs.c
2875 le
= t
->paritem
->list_entry
.Flink
;
2876 while (le
!= &t
->parent
->itemlist
) {
2877 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2888 return STATUS_SUCCESS
;
2890 // FIXME - loop, and capture more than one tree if we can
2892 TRACE("nextparitem: key = %llx,%x,%llx\n", nextparitem
->key
.obj_id
, nextparitem
->key
.obj_type
, nextparitem
->key
.offset
);
2893 // nextparitem = t->paritem;
2895 // ExAcquireResourceExclusiveLite(&t->parent->nonpaged->load_tree_lock, TRUE);
2897 Status
= do_load_tree(Vcb
, &nextparitem
->treeholder
, t
->root
, t
->parent
, nextparitem
, &loaded
, NULL
);
2898 if (!NT_SUCCESS(Status
)) {
2899 ERR("do_load_tree returned %08x\n", Status
);
2903 // ExReleaseResourceLite(&t->parent->nonpaged->load_tree_lock);
2905 next_tree
= nextparitem
->treeholder
.tree
;
2907 if (t
->size
+ next_tree
->size
<= Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
2908 // merge two trees into one
2910 t
->header
.num_items
+= next_tree
->header
.num_items
;
2911 t
->size
+= next_tree
->size
;
2913 if (next_tree
->header
.level
> 0) {
2914 le
= next_tree
->itemlist
.Flink
;
2916 while (le
!= &next_tree
->itemlist
) {
2917 tree_data
* td2
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2919 if (td2
->treeholder
.tree
)
2920 td2
->treeholder
.tree
->parent
= t
;
2926 t
->itemlist
.Blink
->Flink
= next_tree
->itemlist
.Flink
;
2927 t
->itemlist
.Blink
->Flink
->Blink
= t
->itemlist
.Blink
;
2928 t
->itemlist
.Blink
= next_tree
->itemlist
.Blink
;
2929 t
->itemlist
.Blink
->Flink
= &t
->itemlist
;
2932 // le = t->itemlist.Flink;
2933 // while (le != &t->itemlist) {
2934 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
2935 // if (!td->ignore) {
2936 // ERR("key: %llx,%x,%llx\n", td->key.obj_id, td->key.obj_type, td->key.offset);
2941 next_tree
->itemlist
.Flink
= next_tree
->itemlist
.Blink
= &next_tree
->itemlist
;
2943 next_tree
->header
.num_items
= 0;
2944 next_tree
->size
= 0;
2946 if (next_tree
->has_new_address
) { // delete associated EXTENT_ITEM
2947 Status
= reduce_tree_extent(Vcb
, next_tree
->new_address
, next_tree
, Irp
, rollback
);
2949 if (!NT_SUCCESS(Status
)) {
2950 ERR("reduce_tree_extent returned %08x\n", Status
);
2953 } else if (next_tree
->has_address
) {
2954 Status
= reduce_tree_extent(Vcb
, next_tree
->header
.address
, next_tree
, Irp
, rollback
);
2956 if (!NT_SUCCESS(Status
)) {
2957 ERR("reduce_tree_extent returned %08x\n", Status
);
2962 if (!nextparitem
->ignore
) {
2963 nextparitem
->ignore
= TRUE
;
2964 next_tree
->parent
->header
.num_items
--;
2965 next_tree
->parent
->size
-= sizeof(internal_node
);
2968 par
= next_tree
->parent
;
2974 RemoveEntryList(&nextparitem
->list_entry
);
2975 ExFreePool(next_tree
->paritem
);
2976 next_tree
->paritem
= NULL
;
2978 next_tree
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
2980 free_tree(next_tree
);
2982 // rebalance by moving items from second tree into first
2983 ULONG avg_size
= (t
->size
+ next_tree
->size
) / 2;
2984 KEY firstitem
= {0, 0, 0};
2986 TRACE("attempting rebalance\n");
2988 le
= next_tree
->itemlist
.Flink
;
2989 while (le
!= &next_tree
->itemlist
&& t
->size
< avg_size
&& next_tree
->header
.num_items
> 1) {
2990 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
2994 if (next_tree
->header
.level
== 0)
2995 size
= sizeof(leaf_node
) + td
->size
;
2997 size
= sizeof(internal_node
);
3001 if (t
->size
+ size
< Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3002 RemoveEntryList(&td
->list_entry
);
3003 InsertTailList(&t
->itemlist
, &td
->list_entry
);
3005 if (next_tree
->header
.level
> 0 && td
->treeholder
.tree
)
3006 td
->treeholder
.tree
->parent
= t
;
3009 next_tree
->size
-= size
;
3011 next_tree
->header
.num_items
--;
3012 t
->header
.num_items
++;
3017 le
= next_tree
->itemlist
.Flink
;
3020 le
= next_tree
->itemlist
.Flink
;
3021 while (le
!= &next_tree
->itemlist
) {
3022 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3025 firstitem
= td
->key
;
3032 // ERR("firstitem = %llx,%x,%llx\n", firstitem.obj_id, firstitem.obj_type, firstitem.offset);
3034 // FIXME - once ascension is working, make this work with parent's parent, etc.
3035 if (next_tree
->paritem
)
3036 next_tree
->paritem
->key
= firstitem
;
3045 return STATUS_SUCCESS
;
3048 static NTSTATUS
update_extent_level(device_extension
* Vcb
, UINT64 address
, tree
* t
, UINT8 level
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3053 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA
) {
3054 searchkey
.obj_id
= address
;
3055 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
3056 searchkey
.offset
= t
->header
.level
;
3058 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3059 if (!NT_SUCCESS(Status
)) {
3060 ERR("error - find_item returned %08x\n", Status
);
3064 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
3065 EXTENT_ITEM_SKINNY_METADATA
* eism
;
3067 if (tp
.item
->size
> 0) {
3068 eism
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3071 ERR("out of memory\n");
3072 return STATUS_INSUFFICIENT_RESOURCES
;
3075 RtlCopyMemory(eism
, tp
.item
->data
, tp
.item
->size
);
3079 delete_tree_item(Vcb
, &tp
, rollback
);
3081 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, address
, TYPE_METADATA_ITEM
, level
, eism
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
3082 ERR("insert_tree_item failed\n");
3084 return STATUS_INTERNAL_ERROR
;
3087 return STATUS_SUCCESS
;
3091 searchkey
.obj_id
= address
;
3092 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
3093 searchkey
.offset
= 0xffffffffffffffff;
3095 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3096 if (!NT_SUCCESS(Status
)) {
3097 ERR("error - find_item returned %08x\n", Status
);
3101 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
) {
3102 EXTENT_ITEM_TREE
* eit
;
3104 if (tp
.item
->size
< sizeof(EXTENT_ITEM_TREE
)) {
3105 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
));
3106 return STATUS_INTERNAL_ERROR
;
3109 eit
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
3112 ERR("out of memory\n");
3113 return STATUS_INSUFFICIENT_RESOURCES
;
3116 RtlCopyMemory(eit
, tp
.item
->data
, tp
.item
->size
);
3118 delete_tree_item(Vcb
, &tp
, rollback
);
3122 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, eit
, tp
.item
->size
, NULL
, Irp
, rollback
)) {
3123 ERR("insert_tree_item failed\n");
3125 return STATUS_INTERNAL_ERROR
;
3128 return STATUS_SUCCESS
;
3131 ERR("could not find EXTENT_ITEM for address %llx\n", address
);
3133 return STATUS_INTERNAL_ERROR
;
3136 static NTSTATUS STDCALL
do_splits(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3137 // LIST_ENTRY *le, *le2;
3140 UINT8 level
, max_level
;
3142 BOOL empty
, done_deletions
= FALSE
;
3146 TRACE("(%p)\n", Vcb
);
3150 for (level
= 0; level
<= 255; level
++) {
3151 LIST_ENTRY
*le
, *nextle
;
3155 TRACE("doing level %u\n", level
);
3157 le
= Vcb
->trees
.Flink
;
3159 while (le
!= &Vcb
->trees
) {
3160 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3164 if (t
->write
&& t
->header
.level
== level
) {
3167 if (t
->header
.num_items
== 0) {
3170 KEY firstitem
= {0xcccccccccccccccc,0xcc,0xcccccccccccccccc};
3175 done_deletions
= TRUE
;
3177 le2
= t
->itemlist
.Flink
;
3178 while (le2
!= &t
->itemlist
) {
3179 tree_data
* td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3180 firstitem
= td
->key
;
3184 TRACE("deleting tree in root %llx (first item was %llx,%x,%llx)\n",
3185 t
->root
->id
, firstitem
.obj_id
, firstitem
.obj_type
, firstitem
.offset
);
3187 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3189 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3190 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, Irp
, rollback
);
3192 if (!NT_SUCCESS(Status
)) {
3193 ERR("reduce_tree_extent returned %08x\n", Status
);
3197 t
->has_new_address
= FALSE
;
3198 } else if (t
->has_address
) {
3199 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, Irp
, rollback
);
3201 if (!NT_SUCCESS(Status
)) {
3202 ERR("reduce_tree_extent returned %08x\n", Status
);
3206 t
->has_address
= FALSE
;
3209 if (!t
->paritem
->ignore
) {
3210 t
->paritem
->ignore
= TRUE
;
3211 t
->parent
->header
.num_items
--;
3212 t
->parent
->size
-= sizeof(internal_node
);
3215 RemoveEntryList(&t
->paritem
->list_entry
);
3216 ExFreePool(t
->paritem
);
3220 } else if (t
->header
.level
!= 0) {
3221 if (t
->has_new_address
) {
3222 Status
= update_extent_level(Vcb
, t
->new_address
, t
, 0, Irp
, rollback
);
3224 if (!NT_SUCCESS(Status
)) {
3225 ERR("update_extent_level returned %08x\n", Status
);
3230 t
->header
.level
= 0;
3232 } else if (t
->size
> Vcb
->superblock
.node_size
- sizeof(tree_header
)) {
3233 TRACE("splitting overlarge tree (%x > %x)\n", t
->size
, Vcb
->superblock
.node_size
- sizeof(tree_header
));
3234 Status
= split_tree(Vcb
, t
);
3236 if (!NT_SUCCESS(Status
)) {
3237 ERR("split_tree returned %08x\n", Status
);
3249 TRACE("nothing found for level %u\n", level
);
3254 min_size
= (Vcb
->superblock
.node_size
- sizeof(tree_header
)) / 2;
3256 for (level
= 0; level
<= max_level
; level
++) {
3259 le
= Vcb
->trees
.Flink
;
3261 while (le
!= &Vcb
->trees
) {
3262 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3264 if (t
->write
&& t
->header
.level
== level
&& t
->header
.num_items
> 0 && t
->parent
&& t
->size
< min_size
) {
3265 Status
= try_tree_amalgamate(Vcb
, t
, Irp
, rollback
);
3266 if (!NT_SUCCESS(Status
)) {
3267 ERR("try_tree_amalgamate returned %08x\n", Status
);
3276 // simplify trees if top tree only has one entry
3278 if (done_deletions
) {
3279 for (level
= max_level
; level
> 0; level
--) {
3280 LIST_ENTRY
*le
, *nextle
;
3282 le
= Vcb
->trees
.Flink
;
3283 while (le
!= &Vcb
->trees
) {
3285 t
= CONTAINING_RECORD(le
, tree
, list_entry
);
3287 if (t
->write
&& t
->header
.level
== level
) {
3288 if (!t
->parent
&& t
->header
.num_items
== 1) {
3289 LIST_ENTRY
* le2
= t
->itemlist
.Flink
;
3291 tree
* child_tree
= NULL
;
3293 while (le2
!= &t
->itemlist
) {
3294 td
= CONTAINING_RECORD(le2
, tree_data
, list_entry
);
3300 TRACE("deleting top-level tree in root %llx with one item\n", t
->root
->id
);
3302 if (t
->has_new_address
) { // delete associated EXTENT_ITEM
3303 Status
= reduce_tree_extent(Vcb
, t
->new_address
, t
, Irp
, rollback
);
3305 if (!NT_SUCCESS(Status
)) {
3306 ERR("reduce_tree_extent returned %08x\n", Status
);
3310 t
->has_new_address
= FALSE
;
3311 } else if (t
->has_address
) {
3312 Status
= reduce_tree_extent(Vcb
,t
->header
.address
, t
, Irp
, rollback
);
3314 if (!NT_SUCCESS(Status
)) {
3315 ERR("reduce_tree_extent returned %08x\n", Status
);
3319 t
->has_address
= FALSE
;
3322 if (!td
->treeholder
.tree
) { // load first item if not already loaded
3323 KEY searchkey
= {0,0,0};
3326 Status
= find_item(Vcb
, t
->root
, &tp
, &searchkey
, FALSE
, Irp
);
3327 if (!NT_SUCCESS(Status
)) {
3328 ERR("error - find_item returned %08x\n", Status
);
3333 child_tree
= td
->treeholder
.tree
;
3336 child_tree
->parent
= NULL
;
3337 child_tree
->paritem
= NULL
;
3340 t
->root
->root_item
.bytes_used
-= Vcb
->superblock
.node_size
;
3345 child_tree
->root
->treeholder
.tree
= child_tree
;
3354 return STATUS_SUCCESS
;
3357 static NTSTATUS
remove_root_extents(device_extension
* Vcb
, root
* r
, tree_holder
* th
, UINT8 level
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3362 Status
= load_tree(Vcb
, th
->address
, r
, &th
->tree
, NULL
, NULL
);
3364 if (!NT_SUCCESS(Status
)) {
3365 ERR("load_tree(%llx) returned %08x\n", th
->address
, Status
);
3370 if (th
->tree
->header
.level
> 0) {
3371 LIST_ENTRY
* le
= th
->tree
->itemlist
.Flink
;
3373 while (le
!= &th
->tree
->itemlist
) {
3374 tree_data
* td
= CONTAINING_RECORD(le
, tree_data
, list_entry
);
3377 Status
= remove_root_extents(Vcb
, r
, &td
->treeholder
, th
->tree
->header
.level
- 1, Irp
, rollback
);
3379 if (!NT_SUCCESS(Status
)) {
3380 ERR("remove_root_extents returned %08x\n", Status
);
3390 if (!th
->tree
|| th
->tree
->has_address
) {
3391 Status
= reduce_tree_extent(Vcb
, th
->address
, NULL
, Irp
, rollback
);
3393 if (!NT_SUCCESS(Status
)) {
3394 ERR("reduce_tree_extent(%llx) returned %08x\n", th
->address
, Status
);
3399 return STATUS_SUCCESS
;
3402 static NTSTATUS
drop_root(device_extension
* Vcb
, root
* r
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3407 Status
= remove_root_extents(Vcb
, r
, &r
->treeholder
, r
->root_item
.root_level
, Irp
, rollback
);
3408 if (!NT_SUCCESS(Status
)) {
3409 ERR("remove_root_extents returned %08x\n", Status
);
3413 // remove entry in uuid root (tree 9)
3414 if (Vcb
->uuid_root
) {
3415 RtlCopyMemory(&searchkey
.obj_id
, &r
->root_item
.uuid
.uuid
[0], sizeof(UINT64
));
3416 searchkey
.obj_type
= TYPE_SUBVOL_UUID
;
3417 RtlCopyMemory(&searchkey
.offset
, &r
->root_item
.uuid
.uuid
[sizeof(UINT64
)], sizeof(UINT64
));
3419 if (searchkey
.obj_id
!= 0 || searchkey
.offset
!= 0) {
3420 Status
= find_item(Vcb
, Vcb
->uuid_root
, &tp
, &searchkey
, FALSE
, Irp
);
3421 if (!NT_SUCCESS(Status
)) {
3422 WARN("find_item returned %08x\n", Status
);
3424 if (!keycmp(&tp
.item
->key
, &searchkey
))
3425 delete_tree_item(Vcb
, &tp
, rollback
);
3427 WARN("could not find (%llx,%x,%llx) in uuid tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3434 searchkey
.obj_id
= r
->id
;
3435 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
3436 searchkey
.offset
= 0xffffffffffffffff;
3438 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3439 if (!NT_SUCCESS(Status
)) {
3440 ERR("find_item returned %08x\n", Status
);
3444 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
3445 delete_tree_item(Vcb
, &tp
, rollback
);
3447 WARN("could not find (%llx,%x,%llx) in root_root\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3449 // delete items in tree cache
3451 free_trees_root(Vcb
, r
);
3453 return STATUS_SUCCESS
;
3456 static NTSTATUS
drop_roots(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3457 LIST_ENTRY
*le
= Vcb
->drop_roots
.Flink
, *le2
;
3460 while (le
!= &Vcb
->drop_roots
) {
3461 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3465 Status
= drop_root(Vcb
, r
, Irp
, rollback
);
3466 if (!NT_SUCCESS(Status
)) {
3467 ERR("drop_root(%llx) returned %08x\n", r
->id
, Status
);
3474 return STATUS_SUCCESS
;
3477 static NTSTATUS
create_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3479 CHUNK_ITEM_STRIPE
* cis
;
3480 BLOCK_GROUP_ITEM
* bgi
;
3484 ci
= ExAllocatePoolWithTag(PagedPool
, c
->size
, ALLOC_TAG
);
3486 ERR("out of memory\n");
3487 return STATUS_INSUFFICIENT_RESOURCES
;
3490 RtlCopyMemory(ci
, c
->chunk_item
, c
->size
);
3492 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
, NULL
, Irp
, rollback
)) {
3493 ERR("insert_tree_item failed\n");
3495 return STATUS_INTERNAL_ERROR
;
3498 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
) {
3499 Status
= add_to_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
, ci
, c
->size
);
3500 if (!NT_SUCCESS(Status
)) {
3501 ERR("add_to_bootstrap returned %08x\n", Status
);
3506 // add BLOCK_GROUP_ITEM to tree 2
3508 bgi
= ExAllocatePoolWithTag(PagedPool
, sizeof(BLOCK_GROUP_ITEM
), ALLOC_TAG
);
3510 ERR("out of memory\n");
3511 return STATUS_INSUFFICIENT_RESOURCES
;
3514 bgi
->used
= c
->used
;
3515 bgi
->chunk_tree
= 0x100;
3516 bgi
->flags
= c
->chunk_item
->type
;
3518 if (!insert_tree_item(Vcb
, Vcb
->extent_root
, c
->offset
, TYPE_BLOCK_GROUP_ITEM
, c
->chunk_item
->size
, bgi
, sizeof(BLOCK_GROUP_ITEM
), NULL
, Irp
, rollback
)) {
3519 ERR("insert_tree_item failed\n");
3521 return STATUS_INSUFFICIENT_RESOURCES
;
3524 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
3525 factor
= c
->chunk_item
->num_stripes
;
3526 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
3527 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
3528 else // SINGLE, DUPLICATE, RAID1
3531 // add DEV_EXTENTs to tree 4
3533 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3535 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3538 de
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_EXTENT
), ALLOC_TAG
);
3540 ERR("out of memory\n");
3541 return STATUS_INSUFFICIENT_RESOURCES
;
3544 de
->chunktree
= Vcb
->chunk_root
->id
;
3546 de
->address
= c
->offset
;
3547 de
->length
= c
->chunk_item
->size
/ factor
;
3548 de
->chunktree_uuid
= Vcb
->chunk_root
->treeholder
.tree
->header
.chunk_tree_uuid
;
3550 if (!insert_tree_item(Vcb
, Vcb
->dev_root
, c
->devices
[i
]->devitem
.dev_id
, TYPE_DEV_EXTENT
, cis
[i
].offset
, de
, sizeof(DEV_EXTENT
), NULL
, Irp
, rollback
)) {
3551 ERR("insert_tree_item failed\n");
3553 return STATUS_INTERNAL_ERROR
;
3556 // FIXME - no point in calling this twice for the same device
3557 Status
= update_dev_item(Vcb
, c
->devices
[i
], Irp
, rollback
);
3558 if (!NT_SUCCESS(Status
)) {
3559 ERR("update_dev_item returned %08x\n", Status
);
3566 return STATUS_SUCCESS
;
3569 static void remove_from_bootstrap(device_extension
* Vcb
, UINT64 obj_id
, UINT8 obj_type
, UINT64 offset
) {
3573 le
= Vcb
->sys_chunks
.Flink
;
3574 while (le
!= &Vcb
->sys_chunks
) {
3575 sc2
= CONTAINING_RECORD(le
, sys_chunk
, list_entry
);
3577 if (sc2
->key
.obj_id
== obj_id
&& sc2
->key
.obj_type
== obj_type
&& sc2
->key
.offset
== offset
) {
3578 RemoveEntryList(&sc2
->list_entry
);
3580 Vcb
->superblock
.n
-= sizeof(KEY
) + sc2
->size
;
3582 ExFreePool(sc2
->data
);
3584 regen_bootstrap(Vcb
);
3592 static NTSTATUS
drop_chunk(device_extension
* Vcb
, chunk
* c
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3597 CHUNK_ITEM_STRIPE
* cis
;
3599 TRACE("dropping chunk %llx\n", c
->offset
);
3601 // remove free space cache
3603 c
->cache
->deleted
= TRUE
;
3605 flush_fcb(c
->cache
, TRUE
, Irp
, rollback
);
3609 searchkey
.obj_id
= FREE_SPACE_CACHE_ID
;
3610 searchkey
.obj_type
= 0;
3611 searchkey
.offset
= c
->offset
;
3613 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3614 if (!NT_SUCCESS(Status
)) {
3615 ERR("error - find_item returned %08x\n", Status
);
3619 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
3620 delete_tree_item(Vcb
, &tp
, rollback
);
3624 if (c
->chunk_item
->type
& BLOCK_FLAG_RAID0
)
3625 factor
= c
->chunk_item
->num_stripes
;
3626 else if (c
->chunk_item
->type
& BLOCK_FLAG_RAID10
)
3627 factor
= c
->chunk_item
->num_stripes
/ c
->chunk_item
->sub_stripes
;
3628 else // SINGLE, DUPLICATE, RAID1
3631 cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3632 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3634 // remove DEV_EXTENTs from tree 4
3635 searchkey
.obj_id
= cis
[i
].dev_id
;
3636 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
3637 searchkey
.offset
= cis
[i
].offset
;
3639 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
3640 if (!NT_SUCCESS(Status
)) {
3641 ERR("error - find_item returned %08x\n", Status
);
3645 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
3646 delete_tree_item(Vcb
, &tp
, rollback
);
3648 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
3649 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
3651 c
->devices
[i
]->devitem
.bytes_used
-= de
->length
;
3653 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, de
->length
, rollback
);
3656 WARN("could not find (%llx,%x,%llx) in dev tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3658 UINT64 len
= c
->chunk_item
->size
/ factor
;
3660 c
->devices
[i
]->devitem
.bytes_used
-= len
;
3661 space_list_add2(&c
->devices
[i
]->space
, NULL
, cis
[i
].offset
, len
, rollback
);
3665 // modify DEV_ITEMs in chunk tree
3666 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3667 if (c
->devices
[i
]) {
3671 searchkey
.obj_id
= 1;
3672 searchkey
.obj_type
= TYPE_DEV_ITEM
;
3673 searchkey
.offset
= c
->devices
[i
]->devitem
.dev_id
;
3675 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3676 if (!NT_SUCCESS(Status
)) {
3677 ERR("error - find_item returned %08x\n", Status
);
3681 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3682 ERR("error - could not find DEV_ITEM for device %llx\n", searchkey
.offset
);
3683 return STATUS_INTERNAL_ERROR
;
3686 delete_tree_item(Vcb
, &tp
, rollback
);
3688 di
= ExAllocatePoolWithTag(PagedPool
, sizeof(DEV_ITEM
), ALLOC_TAG
);
3690 ERR("out of memory\n");
3691 return STATUS_INSUFFICIENT_RESOURCES
;
3694 RtlCopyMemory(di
, &c
->devices
[i
]->devitem
, sizeof(DEV_ITEM
));
3696 if (!insert_tree_item(Vcb
, Vcb
->chunk_root
, 1, TYPE_DEV_ITEM
, c
->devices
[i
]->devitem
.dev_id
, di
, sizeof(DEV_ITEM
), NULL
, Irp
, rollback
)) {
3697 ERR("insert_tree_item failed\n");
3698 return STATUS_INTERNAL_ERROR
;
3701 for (j
= i
+ 1; j
< c
->chunk_item
->num_stripes
; j
++) {
3702 if (c
->devices
[j
] == c
->devices
[i
])
3703 c
->devices
[j
] = NULL
;
3709 // remove CHUNK_ITEM from chunk tree
3710 searchkey
.obj_id
= 0x100;
3711 searchkey
.obj_type
= TYPE_CHUNK_ITEM
;
3712 searchkey
.offset
= c
->offset
;
3714 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
3715 if (!NT_SUCCESS(Status
)) {
3716 ERR("error - find_item returned %08x\n", Status
);
3720 if (!keycmp(&tp
.item
->key
, &searchkey
))
3721 delete_tree_item(Vcb
, &tp
, rollback
);
3723 WARN("could not find CHUNK_ITEM for chunk %llx\n", c
->offset
);
3725 // remove BLOCK_GROUP_ITEM from extent tree
3726 searchkey
.obj_id
= c
->offset
;
3727 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3728 searchkey
.offset
= 0xffffffffffffffff;
3730 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3731 if (!NT_SUCCESS(Status
)) {
3732 ERR("error - find_item returned %08x\n", Status
);
3736 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
3737 delete_tree_item(Vcb
, &tp
, rollback
);
3739 WARN("could not find BLOCK_GROUP_ITEM for chunk %llx\n", c
->offset
);
3742 if (c
->chunk_item
->type
& BLOCK_FLAG_SYSTEM
)
3743 remove_from_bootstrap(Vcb
, 0x100, TYPE_CHUNK_ITEM
, c
->offset
);
3745 RemoveEntryList(&c
->list_entry
);
3747 if (c
->list_entry_changed
.Flink
)
3748 RemoveEntryList(&c
->list_entry_changed
);
3750 ExFreePool(c
->chunk_item
);
3751 ExFreePool(c
->devices
);
3753 while (!IsListEmpty(&c
->space
)) {
3754 space
* s
= CONTAINING_RECORD(c
->space
.Flink
, space
, list_entry
);
3756 RemoveEntryList(&s
->list_entry
);
3760 while (!IsListEmpty(&c
->deleting
)) {
3761 space
* s
= CONTAINING_RECORD(c
->deleting
.Flink
, space
, list_entry
);
3763 RemoveEntryList(&s
->list_entry
);
3767 ExDeleteResourceLite(&c
->lock
);
3768 ExDeleteResourceLite(&c
->changed_extents_lock
);
3772 return STATUS_SUCCESS
;
3775 static NTSTATUS
update_chunks(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3776 LIST_ENTRY
*le
= Vcb
->chunks_changed
.Flink
, *le2
;
3778 UINT64 used_minus_cache
;
3780 ExAcquireResourceExclusiveLite(&Vcb
->chunk_lock
, TRUE
);
3782 // FIXME - do tree chunks before data chunks
3784 while (le
!= &Vcb
->chunks_changed
) {
3785 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry_changed
);
3789 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
3791 used_minus_cache
= c
->used
;
3793 // subtract self-hosted cache
3794 if (used_minus_cache
> 0 && c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->cache
&& c
->cache
->inode_item
.st_size
== c
->used
) {
3797 le3
= c
->cache
->extents
.Flink
;
3798 while (le3
!= &c
->cache
->extents
) {
3799 extent
* ext
= CONTAINING_RECORD(le3
, extent
, list_entry
);
3800 EXTENT_DATA
* ed
= ext
->data
;
3803 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
3804 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
3808 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
3809 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
3811 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
3812 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
,
3813 sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
3817 if (ed2
->size
!= 0 && ed2
->address
>= c
->offset
&& ed2
->address
+ ed2
->size
<= c
->offset
+ c
->chunk_item
->size
)
3818 used_minus_cache
-= ed2
->size
;
3826 if (used_minus_cache
== 0) {
3827 Status
= drop_chunk(Vcb
, c
, Irp
, rollback
);
3828 if (!NT_SUCCESS(Status
)) {
3829 ERR("drop_chunk returned %08x\n", Status
);
3830 ExReleaseResourceLite(&c
->lock
);
3831 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3834 } else if (c
->created
) {
3835 Status
= create_chunk(Vcb
, c
, Irp
, rollback
);
3836 if (!NT_SUCCESS(Status
)) {
3837 ERR("create_chunk returned %08x\n", Status
);
3838 ExReleaseResourceLite(&c
->lock
);
3839 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3844 if (used_minus_cache
> 0)
3845 ExReleaseResourceLite(&c
->lock
);
3850 ExReleaseResourceLite(&Vcb
->chunk_lock
);
3852 return STATUS_SUCCESS
;
3855 static NTSTATUS STDCALL
set_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
* data
, UINT16 datalen
, PIRP Irp
, LIST_ENTRY
* rollback
) {
3858 ULONG xasize
, maxlen
;
3862 TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
3864 searchkey
.obj_id
= inode
;
3865 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
3866 searchkey
.offset
= crc32
;
3868 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3869 if (!NT_SUCCESS(Status
)) {
3870 ERR("error - find_item returned %08x\n", Status
);
3874 xasize
= sizeof(DIR_ITEM
) - 1 + (ULONG
)strlen(name
) + datalen
;
3875 maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
3877 if (!keycmp(&tp
.item
->key
, &searchkey
)) { // key exists
3879 ULONG size
= tp
.item
->size
;
3881 xa
= (DIR_ITEM
*)tp
.item
->data
;
3883 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3884 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
3889 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
3890 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
3894 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
3896 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
3901 if (tp
.item
->size
+ xasize
- oldxasize
> maxlen
) {
3902 ERR("DIR_ITEM would be over maximum size (%u + %u - %u > %u)\n", tp
.item
->size
, xasize
, oldxasize
, maxlen
);
3903 return STATUS_INTERNAL_ERROR
;
3906 newdata
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ xasize
- oldxasize
, ALLOC_TAG
);
3908 ERR("out of memory\n");
3909 return STATUS_INSUFFICIENT_RESOURCES
;
3912 pos
= (UINT8
*)xa
- tp
.item
->data
;
3913 if (pos
+ oldxasize
< tp
.item
->size
) { // copy after changed xattr
3914 RtlCopyMemory(newdata
+ pos
+ xasize
, tp
.item
->data
+ pos
+ oldxasize
, tp
.item
->size
- pos
- oldxasize
);
3917 if (pos
> 0) { // copy before changed xattr
3918 RtlCopyMemory(newdata
, tp
.item
->data
, pos
);
3919 xa
= (DIR_ITEM
*)(newdata
+ pos
);
3921 xa
= (DIR_ITEM
*)newdata
;
3924 xa
->key
.obj_type
= 0;
3926 xa
->transid
= Vcb
->superblock
.generation
;
3928 xa
->n
= (UINT16
)strlen(name
);
3929 xa
->type
= BTRFS_TYPE_EA
;
3930 RtlCopyMemory(xa
->name
, name
, strlen(name
));
3931 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
3933 delete_tree_item(Vcb
, &tp
, rollback
);
3934 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, tp
.item
->size
+ xasize
- oldxasize
, NULL
, Irp
, rollback
);
3939 if ((UINT8
*)xa
- (UINT8
*)tp
.item
->data
+ oldxasize
>= size
) {
3940 // not found, add to end of data
3942 if (tp
.item
->size
+ xasize
> maxlen
) {
3943 ERR("DIR_ITEM would be over maximum size (%u + %u > %u)\n", tp
.item
->size
, xasize
, maxlen
);
3944 return STATUS_INTERNAL_ERROR
;
3947 newdata
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ xasize
, ALLOC_TAG
);
3949 ERR("out of memory\n");
3950 return STATUS_INSUFFICIENT_RESOURCES
;
3953 RtlCopyMemory(newdata
, tp
.item
->data
, tp
.item
->size
);
3955 xa
= (DIR_ITEM
*)((UINT8
*)newdata
+ tp
.item
->size
);
3957 xa
->key
.obj_type
= 0;
3959 xa
->transid
= Vcb
->superblock
.generation
;
3961 xa
->n
= (UINT16
)strlen(name
);
3962 xa
->type
= BTRFS_TYPE_EA
;
3963 RtlCopyMemory(xa
->name
, name
, strlen(name
));
3964 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
3966 delete_tree_item(Vcb
, &tp
, rollback
);
3967 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, tp
.item
->size
+ xasize
, NULL
, Irp
, rollback
);
3971 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
3977 if (xasize
> maxlen
) {
3978 ERR("DIR_ITEM would be over maximum size (%u > %u)\n", xasize
, maxlen
);
3979 return STATUS_INTERNAL_ERROR
;
3982 xa
= ExAllocatePoolWithTag(PagedPool
, xasize
, ALLOC_TAG
);
3984 ERR("out of memory\n");
3985 return STATUS_INSUFFICIENT_RESOURCES
;
3989 xa
->key
.obj_type
= 0;
3991 xa
->transid
= Vcb
->superblock
.generation
;
3993 xa
->n
= (UINT16
)strlen(name
);
3994 xa
->type
= BTRFS_TYPE_EA
;
3995 RtlCopyMemory(xa
->name
, name
, strlen(name
));
3996 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
3998 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, xa
, xasize
, NULL
, Irp
, rollback
);
4001 return STATUS_SUCCESS
;
4004 static BOOL STDCALL
delete_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4010 TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb
, subvol
->id
, inode
, name
, crc32
);
4012 searchkey
.obj_id
= inode
;
4013 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
4014 searchkey
.offset
= crc32
;
4016 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4017 if (!NT_SUCCESS(Status
)) {
4018 ERR("error - find_item returned %08x\n", Status
);
4022 if (!keycmp(&tp
.item
->key
, &searchkey
)) { // key exists
4023 ULONG size
= tp
.item
->size
;
4025 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
4026 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
4030 xa
= (DIR_ITEM
*)tp
.item
->data
;
4035 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
4036 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4041 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
4043 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
4045 UINT8
*newdata
, *dioff
;
4047 newsize
= tp
.item
->size
- (sizeof(DIR_ITEM
) - 1 + xa
->n
+ xa
->m
);
4049 delete_tree_item(Vcb
, &tp
, rollback
);
4052 TRACE("xattr %s deleted\n", name
);
4057 // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
4058 newdata
= ExAllocatePoolWithTag(PagedPool
, newsize
, ALLOC_TAG
);
4060 ERR("out of memory\n");
4064 if ((UINT8
*)xa
> tp
.item
->data
) {
4065 RtlCopyMemory(newdata
, tp
.item
->data
, (UINT8
*)xa
- tp
.item
->data
);
4066 dioff
= newdata
+ ((UINT8
*)xa
- tp
.item
->data
);
4071 if ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
< tp
.item
->size
)
4072 RtlCopyMemory(dioff
, &xa
->name
[xa
->n
+xa
->m
], tp
.item
->size
- ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
));
4074 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, newsize
, NULL
, Irp
, rollback
);
4080 if (xa
->m
+ xa
->n
>= size
) { // FIXME - test this works
4081 WARN("xattr %s not found\n", name
);
4085 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
4091 WARN("xattr %s not found\n", name
);
4097 static NTSTATUS
insert_sparse_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4101 TRACE("((%llx, %llx), %llx, %llx)\n", fcb
->subvol
->id
, fcb
->inode
, start
, length
);
4103 ed
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
4105 ERR("out of memory\n");
4106 return STATUS_INSUFFICIENT_RESOURCES
;
4109 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
4110 ed
->decoded_size
= length
;
4111 ed
->compression
= BTRFS_COMPRESSION_NONE
;
4112 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
4113 ed
->encoding
= BTRFS_ENCODING_NONE
;
4114 ed
->type
= EXTENT_TYPE_REGULAR
;
4116 ed2
= (EXTENT_DATA2
*)ed
->data
;
4120 ed2
->num_bytes
= length
;
4122 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, start
, ed
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), NULL
, Irp
, rollback
)) {
4123 ERR("insert_tree_item failed\n");
4124 return STATUS_INTERNAL_ERROR
;
4127 return STATUS_SUCCESS
;
4130 void flush_fcb(fcb
* fcb
, BOOL cache
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4136 #ifdef DEBUG_PARANOID
4137 UINT64 old_size
= 0;
4138 BOOL extents_changed
;
4141 // ExAcquireResourceExclusiveLite(fcb->Header.Resource, TRUE);
4143 while (!IsListEmpty(&fcb
->index_list
)) {
4144 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->index_list
);
4145 index_entry
* ie
= CONTAINING_RECORD(le
, index_entry
, list_entry
);
4147 if (ie
->utf8
.Buffer
) ExFreePool(ie
->utf8
.Buffer
);
4148 if (ie
->filepart_uc
.Buffer
) ExFreePool(ie
->filepart_uc
.Buffer
);
4152 fcb
->index_loaded
= FALSE
;
4156 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, Irp
, rollback
);
4158 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, fcb
->adsxattr
.Buffer
, fcb
->adshash
, (UINT8
*)fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
, Irp
, rollback
);
4159 if (!NT_SUCCESS(Status
)) {
4160 ERR("set_xattr returned %08x\n", Status
);
4167 #ifdef DEBUG_PARANOID
4168 extents_changed
= fcb
->extents_changed
;
4171 if (fcb
->extents_changed
) {
4173 traverse_ptr next_tp
;
4175 BOOL prealloc
= FALSE
, extents_inline
= FALSE
;
4178 // delete ignored extent items
4179 le
= fcb
->extents
.Flink
;
4180 while (le
!= &fcb
->extents
) {
4181 LIST_ENTRY
* le2
= le
->Flink
;
4182 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4185 RemoveEntryList(&ext
->list_entry
);
4186 ExFreePool(ext
->data
);
4193 le
= fcb
->extents
.Flink
;
4194 while (le
!= &fcb
->extents
) {
4195 LIST_ENTRY
* le2
= le
->Flink
;
4196 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4198 if ((ext
->data
->type
== EXTENT_TYPE_REGULAR
|| ext
->data
->type
== EXTENT_TYPE_PREALLOC
) && le
->Flink
!= &fcb
->extents
) {
4199 extent
* nextext
= CONTAINING_RECORD(le
->Flink
, extent
, list_entry
);
4201 if (ext
->data
->type
== nextext
->data
->type
) {
4202 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ext
->data
->data
;
4203 EXTENT_DATA2
* ned2
= (EXTENT_DATA2
*)nextext
->data
->data
;
4205 if (ed2
->size
!= 0 && ed2
->address
== ned2
->address
&& ed2
->size
== ned2
->size
&&
4206 nextext
->offset
== ext
->offset
+ ed2
->num_bytes
&& ned2
->offset
== ed2
->offset
+ ed2
->num_bytes
) {
4209 ext
->data
->generation
= fcb
->Vcb
->superblock
.generation
;
4210 ed2
->num_bytes
+= ned2
->num_bytes
;
4212 RemoveEntryList(&nextext
->list_entry
);
4213 ExFreePool(nextext
->data
);
4214 ExFreePool(nextext
);
4216 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
4219 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
4221 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
4222 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
4223 if (!NT_SUCCESS(Status
)) {
4224 ERR("update_changed_extent_ref returned %08x\n", Status
);
4237 // delete existing EXTENT_DATA items
4239 searchkey
.obj_id
= fcb
->inode
;
4240 searchkey
.obj_type
= TYPE_EXTENT_DATA
;
4241 searchkey
.offset
= 0;
4243 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4244 if (!NT_SUCCESS(Status
)) {
4245 ERR("error - find_item returned %08x\n", Status
);
4250 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== searchkey
.obj_type
)
4251 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4253 b
= find_next_item(fcb
->Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
4258 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
> searchkey
.obj_type
))
4263 if (!fcb
->deleted
) {
4264 // add new EXTENT_DATAs
4268 le
= fcb
->extents
.Flink
;
4269 while (le
!= &fcb
->extents
) {
4270 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
4273 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && ext
->offset
> last_end
) {
4274 Status
= insert_sparse_extent(fcb
, last_end
, ext
->offset
- last_end
, Irp
, rollback
);
4275 if (!NT_SUCCESS(Status
)) {
4276 ERR("insert_sparse_extent returned %08x\n", Status
);
4281 ed
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
4283 ERR("out of memory\n");
4284 Status
= STATUS_INSUFFICIENT_RESOURCES
;
4288 RtlCopyMemory(ed
, ext
->data
, ext
->datalen
);
4290 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_EXTENT_DATA
, ext
->offset
, ed
, ext
->datalen
, NULL
, Irp
, rollback
)) {
4291 ERR("insert_tree_item failed\n");
4295 if (ext
->datalen
>= sizeof(EXTENT_DATA
) && ed
->type
== EXTENT_TYPE_PREALLOC
)
4298 if (ext
->datalen
>= sizeof(EXTENT_DATA
) && ed
->type
== EXTENT_TYPE_INLINE
)
4299 extents_inline
= TRUE
;
4301 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
)) {
4302 if (ed
->type
== EXTENT_TYPE_INLINE
)
4303 last_end
= ext
->offset
+ ed
->decoded_size
;
4305 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
4307 last_end
= ext
->offset
+ ed2
->num_bytes
;
4314 if (!(fcb
->Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_NO_HOLES
) && !extents_inline
&&
4315 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) > last_end
) {
4316 Status
= insert_sparse_extent(fcb
, last_end
, sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
) - last_end
, Irp
, rollback
);
4317 if (!NT_SUCCESS(Status
)) {
4318 ERR("insert_sparse_extent returned %08x\n", Status
);
4323 // update prealloc flag in INODE_ITEM
4326 fcb
->inode_item
.flags
&= ~BTRFS_INODE_PREALLOC
;
4328 fcb
->inode_item
.flags
|= BTRFS_INODE_PREALLOC
;
4331 fcb
->extents_changed
= FALSE
;
4334 if (!fcb
->created
|| cache
) {
4335 searchkey
.obj_id
= fcb
->inode
;
4336 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4337 searchkey
.offset
= 0xffffffffffffffff;
4339 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4340 if (!NT_SUCCESS(Status
)) {
4341 ERR("error - find_item returned %08x\n", Status
);
4345 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
4347 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4349 ERR("out of memory\n");
4353 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4355 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, 0, ii
, sizeof(INODE_ITEM
), NULL
, Irp
, rollback
)) {
4356 ERR("insert_tree_item failed\n");
4362 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4366 #ifdef DEBUG_PARANOID
4367 INODE_ITEM
* ii2
= (INODE_ITEM
*)tp
.item
->data
;
4369 old_size
= ii2
->st_size
;
4372 ii_offset
= tp
.item
->key
.offset
;
4376 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4378 searchkey
.obj_id
= fcb
->inode
;
4379 searchkey
.obj_type
= TYPE_INODE_ITEM
;
4380 searchkey
.offset
= ii_offset
;
4382 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4383 if (!NT_SUCCESS(Status
)) {
4384 ERR("error - find_item returned %08x\n", Status
);
4388 if (keycmp(&tp
.item
->key
, &searchkey
)) {
4389 ERR("could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->inode
, fcb
->subvol
->id
);
4392 RtlCopyMemory(tp
.item
->data
, &fcb
->inode_item
, min(tp
.item
->size
, sizeof(INODE_ITEM
)));
4397 #ifdef DEBUG_PARANOID
4398 if (!extents_changed
&& fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& old_size
!= fcb
->inode_item
.st_size
) {
4399 ERR("error - size has changed but extents not marked as changed\n");
4404 fcb
->created
= FALSE
;
4409 // delete XATTR_ITEMs
4411 searchkey
.obj_id
= fcb
->inode
;
4412 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
4413 searchkey
.offset
= 0;
4415 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4416 if (!NT_SUCCESS(Status
)) {
4417 ERR("error - find_item returned %08x\n", Status
);
4421 while (find_next_item(fcb
->Vcb
, &tp
, &tp2
, FALSE
, Irp
)) {
4424 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
4425 // FIXME - do metadata thing here too?
4426 if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
4427 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
4428 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4438 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
4440 ERR("out of memory\n");
4444 RtlCopyMemory(ii
, &fcb
->inode_item
, sizeof(INODE_ITEM
));
4446 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, TYPE_INODE_ITEM
, ii_offset
, ii
, sizeof(INODE_ITEM
), NULL
, Irp
, rollback
)) {
4447 ERR("insert_tree_item failed\n");
4452 if (fcb
->sd_dirty
) {
4453 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_NTACL
, EA_NTACL_HASH
, (UINT8
*)fcb
->sd
, RtlLengthSecurityDescriptor(fcb
->sd
), Irp
, rollback
);
4454 if (!NT_SUCCESS(Status
)) {
4455 ERR("set_xattr returned %08x\n", Status
);
4458 fcb
->sd_dirty
= FALSE
;
4461 if (fcb
->atts_changed
) {
4462 if (!fcb
->atts_deleted
) {
4465 TRACE("inserting new DOSATTRIB xattr\n");
4466 sprintf(val
, "0x%lx", fcb
->atts
);
4468 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
*)val
, strlen(val
), Irp
, rollback
);
4469 if (!NT_SUCCESS(Status
)) {
4470 ERR("set_xattr returned %08x\n", Status
);
4474 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, Irp
, rollback
);
4476 fcb
->atts_changed
= FALSE
;
4477 fcb
->atts_deleted
= FALSE
;
4480 if (fcb
->reparse_xattr_changed
) {
4481 if (fcb
->reparse_xattr
.Buffer
&& fcb
->reparse_xattr
.Length
> 0) {
4482 Status
= set_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, (UINT8
*)fcb
->reparse_xattr
.Buffer
, fcb
->reparse_xattr
.Length
, Irp
, rollback
);
4483 if (!NT_SUCCESS(Status
)) {
4484 ERR("set_xattr returned %08x\n", Status
);
4488 delete_xattr(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, EA_REPARSE
, EA_REPARSE_HASH
, Irp
, rollback
);
4490 fcb
->reparse_xattr_changed
= FALSE
;
4496 // ExReleaseResourceLite(fcb->Header.Resource);
4500 static NTSTATUS
delete_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, UINT64 parinode
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4505 searchkey
.obj_id
= parsubvolid
;
4506 searchkey
.obj_type
= TYPE_ROOT_REF
;
4507 searchkey
.offset
= subvolid
;
4509 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4510 if (!NT_SUCCESS(Status
)) {
4511 ERR("error - find_item returned %08x\n", Status
);
4515 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
4516 if (tp
.item
->size
< sizeof(ROOT_REF
)) {
4517 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
));
4518 return STATUS_INTERNAL_ERROR
;
4523 rr
= (ROOT_REF
*)tp
.item
->data
;
4524 len
= tp
.item
->size
;
4529 if (len
< sizeof(ROOT_REF
) || len
< sizeof(ROOT_REF
) - 1 + rr
->n
) {
4530 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4534 itemlen
= sizeof(ROOT_REF
) - sizeof(char) + rr
->n
;
4536 if (rr
->dir
== parinode
&& rr
->n
== utf8
->Length
&& RtlCompareMemory(rr
->name
, utf8
->Buffer
, rr
->n
) == rr
->n
) {
4537 ULONG newlen
= tp
.item
->size
- itemlen
;
4539 delete_tree_item(Vcb
, &tp
, rollback
);
4542 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4544 UINT8
*newrr
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *rroff
;
4547 ERR("out of memory\n");
4548 return STATUS_INSUFFICIENT_RESOURCES
;
4551 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4553 if ((UINT8
*)rr
> tp
.item
->data
) {
4554 RtlCopyMemory(newrr
, tp
.item
->data
, (UINT8
*)rr
- tp
.item
->data
);
4555 rroff
= newrr
+ ((UINT8
*)rr
- tp
.item
->data
);
4560 if ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
< tp
.item
->size
)
4561 RtlCopyMemory(rroff
, &rr
->name
[rr
->n
], tp
.item
->size
- ((UINT8
*)&rr
->name
[rr
->n
] - tp
.item
->data
));
4563 insert_tree_item(Vcb
, Vcb
->root_root
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newrr
, newlen
, NULL
, Irp
, rollback
);
4569 if (len
> itemlen
) {
4571 rr
= (ROOT_REF
*)&rr
->name
[rr
->n
];
4577 WARN("could not find ROOT_REF entry for subvol %llx in %llx\n", searchkey
.offset
, searchkey
.obj_id
);
4578 return STATUS_NOT_FOUND
;
4581 return STATUS_SUCCESS
;
4584 static NTSTATUS
add_root_ref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, ROOT_REF
* rr
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4589 searchkey
.obj_id
= parsubvolid
;
4590 searchkey
.obj_type
= TYPE_ROOT_REF
;
4591 searchkey
.offset
= subvolid
;
4593 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4594 if (!NT_SUCCESS(Status
)) {
4595 ERR("error - find_item returned %08x\n", Status
);
4599 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
4600 ULONG rrsize
= tp
.item
->size
+ sizeof(ROOT_REF
) - 1 + rr
->n
;
4603 rr2
= ExAllocatePoolWithTag(PagedPool
, rrsize
, ALLOC_TAG
);
4605 ERR("out of memory\n");
4606 return STATUS_INSUFFICIENT_RESOURCES
;
4609 if (tp
.item
->size
> 0)
4610 RtlCopyMemory(rr2
, tp
.item
->data
, tp
.item
->size
);
4612 RtlCopyMemory(rr2
+ tp
.item
->size
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
);
4615 delete_tree_item(Vcb
, &tp
, rollback
);
4617 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr2
, rrsize
, NULL
, Irp
, rollback
)) {
4618 ERR("error - failed to insert item\n");
4620 return STATUS_INTERNAL_ERROR
;
4623 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, rr
, sizeof(ROOT_REF
) - 1 + rr
->n
, NULL
, Irp
, rollback
)) {
4624 ERR("error - failed to insert item\n");
4626 return STATUS_INTERNAL_ERROR
;
4630 return STATUS_SUCCESS
;
4633 static NTSTATUS STDCALL
update_root_backref(device_extension
* Vcb
, UINT64 subvolid
, UINT64 parsubvolid
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4640 searchkey
.obj_id
= parsubvolid
;
4641 searchkey
.obj_type
= TYPE_ROOT_REF
;
4642 searchkey
.offset
= subvolid
;
4644 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4645 if (!NT_SUCCESS(Status
)) {
4646 ERR("error - find_item returned %08x\n", Status
);
4650 if (!keycmp(&tp
.item
->key
, &searchkey
) && tp
.item
->size
> 0) {
4651 datalen
= tp
.item
->size
;
4653 data
= ExAllocatePoolWithTag(PagedPool
, datalen
, ALLOC_TAG
);
4655 ERR("out of memory\n");
4656 return STATUS_INSUFFICIENT_RESOURCES
;
4659 RtlCopyMemory(data
, tp
.item
->data
, datalen
);
4664 searchkey
.obj_id
= subvolid
;
4665 searchkey
.obj_type
= TYPE_ROOT_BACKREF
;
4666 searchkey
.offset
= parsubvolid
;
4668 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
4669 if (!NT_SUCCESS(Status
)) {
4670 ERR("error - find_item returned %08x\n", Status
);
4678 if (!keycmp(&tp
.item
->key
, &searchkey
))
4679 delete_tree_item(Vcb
, &tp
, rollback
);
4682 if (!insert_tree_item(Vcb
, Vcb
->root_root
, subvolid
, TYPE_ROOT_BACKREF
, parsubvolid
, data
, datalen
, NULL
, Irp
, rollback
)) {
4683 ERR("error - failed to insert item\n");
4685 return STATUS_INTERNAL_ERROR
;
4689 return STATUS_SUCCESS
;
4692 static NTSTATUS
flush_fileref(file_ref
* fileref
, PIRP Irp
, LIST_ENTRY
* rollback
) {
4695 // if fileref created and then immediately deleted, do nothing
4696 if (fileref
->created
&& fileref
->deleted
) {
4697 fileref
->dirty
= FALSE
;
4698 return STATUS_SUCCESS
;
4701 if (fileref
->fcb
->ads
) {
4702 fileref
->dirty
= FALSE
;
4703 return STATUS_SUCCESS
;
4706 if (fileref
->created
) {
4711 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4713 disize
= sizeof(DIR_ITEM
) - 1 + fileref
->utf8
.Length
;
4714 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
4716 ERR("out of memory\n");
4717 return STATUS_INSUFFICIENT_RESOURCES
;
4720 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
4721 di
->key
.obj_id
= fileref
->fcb
->inode
;
4722 di
->key
.obj_type
= TYPE_INODE_ITEM
;
4724 } else { // subvolume
4725 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
4726 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
4727 di
->key
.offset
= 0xffffffffffffffff;
4730 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
4732 di
->n
= (UINT16
)fileref
->utf8
.Length
;
4733 di
->type
= fileref
->fcb
->type
;
4734 RtlCopyMemory(di
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4736 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
4738 ERR("out of memory\n");
4739 return STATUS_INSUFFICIENT_RESOURCES
;
4742 RtlCopyMemory(di2
, di
, disize
);
4744 if (!insert_tree_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
, fileref
->index
, di
, disize
, NULL
, Irp
, rollback
)) {
4745 ERR("insert_tree_item failed\n");
4746 Status
= STATUS_INTERNAL_ERROR
;
4750 Status
= add_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, crc32
, di2
, disize
, Irp
, rollback
);
4751 if (!NT_SUCCESS(Status
)) {
4752 ERR("add_dir_item returned %08x\n", Status
);
4756 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
4757 Status
= add_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, fileref
->index
, &fileref
->utf8
, Irp
, rollback
);
4758 if (!NT_SUCCESS(Status
)) {
4759 ERR("add_inode_ref returned %08x\n", Status
);
4766 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->utf8
.Length
;
4768 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
4770 ERR("out of memory\n");
4771 return STATUS_INSUFFICIENT_RESOURCES
;
4774 rr
->dir
= fileref
->parent
->fcb
->inode
;
4775 rr
->index
= fileref
->index
;
4776 rr
->n
= fileref
->utf8
.Length
;
4777 RtlCopyMemory(rr
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4779 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
, rollback
);
4780 if (!NT_SUCCESS(Status
)) {
4781 ERR("add_root_ref returned %08x\n", Status
);
4785 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
4786 if (!NT_SUCCESS(Status
)) {
4787 ERR("update_root_backref returned %08x\n", Status
);
4792 fileref
->created
= FALSE
;
4793 } else if (fileref
->deleted
) {
4799 if (fileref
->oldutf8
.Buffer
)
4800 name
= &fileref
->oldutf8
;
4802 name
= &fileref
->utf8
;
4804 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)name
->Buffer
, name
->Length
);
4806 TRACE("deleting %.*S\n", file_desc_fileref(fileref
));
4808 // delete DIR_ITEM (0x54)
4810 Status
= delete_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, crc32
, name
, Irp
, rollback
);
4811 if (!NT_SUCCESS(Status
)) {
4812 ERR("delete_dir_item returned %08x\n", Status
);
4816 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
4817 // delete INODE_REF (0xc)
4819 Status
= delete_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, name
, Irp
, rollback
);
4820 if (!NT_SUCCESS(Status
)) {
4821 ERR("delete_inode_ref returned %08x\n", Status
);
4824 } else { // subvolume
4825 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, name
, Irp
, rollback
);
4826 if (!NT_SUCCESS(Status
)) {
4827 ERR("delete_root_ref returned %08x\n", Status
);
4830 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
4831 if (!NT_SUCCESS(Status
)) {
4832 ERR("update_root_backref returned %08x\n", Status
);
4837 // delete DIR_INDEX (0x60)
4839 searchkey
.obj_id
= fileref
->parent
->fcb
->inode
;
4840 searchkey
.obj_type
= TYPE_DIR_INDEX
;
4841 searchkey
.offset
= fileref
->index
;
4843 Status
= find_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4844 if (!NT_SUCCESS(Status
)) {
4845 ERR("error - find_item returned %08x\n", Status
);
4846 Status
= STATUS_INTERNAL_ERROR
;
4850 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
4851 delete_tree_item(fileref
->fcb
->Vcb
, &tp
, rollback
);
4852 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4855 if (fileref
->oldutf8
.Buffer
) {
4856 ExFreePool(fileref
->oldutf8
.Buffer
);
4857 fileref
->oldutf8
.Buffer
= NULL
;
4859 } else { // rename or change type
4860 PANSI_STRING oldutf8
= fileref
->oldutf8
.Buffer
? &fileref
->oldutf8
: &fileref
->utf8
;
4861 UINT32 crc32
, oldcrc32
;
4867 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4869 if (!fileref
->oldutf8
.Buffer
)
4872 oldcrc32
= calc_crc32c(0xfffffffe, (UINT8
*)fileref
->oldutf8
.Buffer
, fileref
->oldutf8
.Length
);
4874 // delete DIR_ITEM (0x54)
4876 Status
= delete_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, oldcrc32
, oldutf8
, Irp
, rollback
);
4877 if (!NT_SUCCESS(Status
)) {
4878 ERR("delete_dir_item returned %08x\n", Status
);
4882 // add DIR_ITEM (0x54)
4884 disize
= sizeof(DIR_ITEM
) - 1 + fileref
->utf8
.Length
;
4885 di
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
4887 ERR("out of memory\n");
4888 return STATUS_INSUFFICIENT_RESOURCES
;
4891 di2
= ExAllocatePoolWithTag(PagedPool
, disize
, ALLOC_TAG
);
4893 ERR("out of memory\n");
4895 return STATUS_INSUFFICIENT_RESOURCES
;
4898 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
4899 di
->key
.obj_id
= fileref
->fcb
->inode
;
4900 di
->key
.obj_type
= TYPE_INODE_ITEM
;
4902 } else { // subvolume
4903 di
->key
.obj_id
= fileref
->fcb
->subvol
->id
;
4904 di
->key
.obj_type
= TYPE_ROOT_ITEM
;
4905 di
->key
.offset
= 0xffffffffffffffff;
4908 di
->transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
4910 di
->n
= (UINT16
)fileref
->utf8
.Length
;
4911 di
->type
= fileref
->fcb
->type
;
4912 RtlCopyMemory(di
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4914 RtlCopyMemory(di2
, di
, disize
);
4916 Status
= add_dir_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, crc32
, di
, disize
, Irp
, rollback
);
4917 if (!NT_SUCCESS(Status
)) {
4918 ERR("add_dir_item returned %08x\n", Status
);
4922 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
4923 // delete INODE_REF (0xc)
4925 Status
= delete_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
, rollback
);
4926 if (!NT_SUCCESS(Status
)) {
4927 ERR("delete_inode_ref returned %08x\n", Status
);
4931 // add INODE_REF (0xc)
4933 Status
= add_inode_ref(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->fcb
->inode
, fileref
->parent
->fcb
->inode
, fileref
->index
, &fileref
->utf8
, Irp
, rollback
);
4934 if (!NT_SUCCESS(Status
)) {
4935 ERR("add_inode_ref returned %08x\n", Status
);
4938 } else { // subvolume
4942 // FIXME - make sure this works with duff subvols within snapshots
4944 Status
= delete_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, fileref
->parent
->fcb
->inode
, oldutf8
, Irp
, rollback
);
4945 if (!NT_SUCCESS(Status
)) {
4946 ERR("delete_root_ref returned %08x\n", Status
);
4949 rrlen
= sizeof(ROOT_REF
) - 1 + fileref
->utf8
.Length
;
4951 rr
= ExAllocatePoolWithTag(PagedPool
, rrlen
, ALLOC_TAG
);
4953 ERR("out of memory\n");
4954 return STATUS_INSUFFICIENT_RESOURCES
;
4957 rr
->dir
= fileref
->parent
->fcb
->inode
;
4958 rr
->index
= fileref
->index
;
4959 rr
->n
= fileref
->utf8
.Length
;
4960 RtlCopyMemory(rr
->name
, fileref
->utf8
.Buffer
, fileref
->utf8
.Length
);
4962 Status
= add_root_ref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, rr
, Irp
, rollback
);
4963 if (!NT_SUCCESS(Status
)) {
4964 ERR("add_root_ref returned %08x\n", Status
);
4968 Status
= update_root_backref(fileref
->fcb
->Vcb
, fileref
->fcb
->subvol
->id
, fileref
->parent
->fcb
->subvol
->id
, Irp
, rollback
);
4969 if (!NT_SUCCESS(Status
)) {
4970 ERR("update_root_backref returned %08x\n", Status
);
4975 // delete DIR_INDEX (0x60)
4977 searchkey
.obj_id
= fileref
->parent
->fcb
->inode
;
4978 searchkey
.obj_type
= TYPE_DIR_INDEX
;
4979 searchkey
.offset
= fileref
->index
;
4981 Status
= find_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
4982 if (!NT_SUCCESS(Status
)) {
4983 ERR("error - find_item returned %08x\n", Status
);
4984 Status
= STATUS_INTERNAL_ERROR
;
4988 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
4989 delete_tree_item(fileref
->fcb
->Vcb
, &tp
, rollback
);
4990 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
4992 WARN("could not find (%llx,%x,%llx) in subvol %llx\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, fileref
->fcb
->subvol
->id
);
4994 // add DIR_INDEX (0x60)
4996 if (!insert_tree_item(fileref
->fcb
->Vcb
, fileref
->parent
->fcb
->subvol
, fileref
->parent
->fcb
->inode
, TYPE_DIR_INDEX
, fileref
->index
, di2
, disize
, NULL
, Irp
, rollback
)) {
4997 ERR("insert_tree_item failed\n");
4998 Status
= STATUS_INTERNAL_ERROR
;
5002 if (fileref
->oldutf8
.Buffer
) {
5003 ExFreePool(fileref
->oldutf8
.Buffer
);
5004 fileref
->oldutf8
.Buffer
= NULL
;
5008 fileref
->dirty
= FALSE
;
5010 return STATUS_SUCCESS
;
5013 static void convert_shared_data_refs(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5017 le
= Vcb
->trees
.Flink
;
5018 while (le
!= &Vcb
->trees
) {
5019 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
5021 if (t
->write
&& t
->header
.level
== 0 &&
5022 (t
->header
.flags
& HEADER_FLAG_SHARED_BACKREF
|| !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
))) {
5024 BOOL old
= !(t
->header
.flags
& HEADER_FLAG_MIXED_BACKREF
);
5026 le2
= Vcb
->shared_extents
.Flink
;
5027 while (le2
!= &Vcb
->shared_extents
) {
5028 shared_data
* sd
= CONTAINING_RECORD(le2
, shared_data
, list_entry
);
5030 if (sd
->address
== t
->header
.address
) {
5031 LIST_ENTRY
* le3
= sd
->entries
.Flink
;
5032 while (le3
!= &sd
->entries
) {
5033 shared_data_entry
* sde
= CONTAINING_RECORD(le3
, shared_data_entry
, list_entry
);
5035 TRACE("tree %llx; root %llx, objid %llx, offset %llx, count %x\n",
5036 t
->header
.address
, sde
->edr
.root
, sde
->edr
.objid
, sde
->edr
.offset
, sde
->edr
.count
);
5038 Status
= increase_extent_refcount_data(Vcb
, sde
->address
, sde
->size
, sde
->edr
.root
, sde
->edr
.objid
, sde
->edr
.offset
, sde
->edr
.count
, Irp
, rollback
);
5040 if (!NT_SUCCESS(Status
))
5041 WARN("increase_extent_refcount_data returned %08x\n", Status
);
5044 Status
= decrease_extent_refcount_old(Vcb
, sde
->address
, sde
->size
, sd
->address
, Irp
, rollback
);
5046 if (!NT_SUCCESS(Status
))
5047 WARN("decrease_extent_refcount_old returned %08x\n", Status
);
5049 Status
= decrease_extent_refcount_shared_data(Vcb
, sde
->address
, sde
->size
, sd
->address
, sd
->parent
, Irp
, rollback
);
5051 if (!NT_SUCCESS(Status
))
5052 WARN("decrease_extent_refcount_shared_data returned %08x\n", Status
);
5063 t
->header
.flags
&= ~HEADER_FLAG_SHARED_BACKREF
;
5064 t
->header
.flags
|= HEADER_FLAG_MIXED_BACKREF
;
5071 static NTSTATUS
add_root_item_to_cache(device_extension
* Vcb
, UINT64 root
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5076 searchkey
.obj_id
= root
;
5077 searchkey
.obj_type
= TYPE_ROOT_ITEM
;
5078 searchkey
.offset
= 0xffffffffffffffff;
5080 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5081 if (!NT_SUCCESS(Status
)) {
5082 ERR("error - find_item returned %08x\n", Status
);
5086 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5087 ERR("could not find ROOT_ITEM for tree %llx\n", searchkey
.obj_id
);
5089 return STATUS_INTERNAL_ERROR
;
5092 if (tp
.item
->size
< sizeof(ROOT_ITEM
)) { // if not full length, create new entry with new bits zeroed
5093 ROOT_ITEM
* ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
5095 ERR("out of memory\n");
5096 return STATUS_INSUFFICIENT_RESOURCES
;
5099 if (tp
.item
->size
> 0)
5100 RtlCopyMemory(ri
, tp
.item
->data
, tp
.item
->size
);
5102 RtlZeroMemory(((UINT8
*)ri
) + tp
.item
->size
, sizeof(ROOT_ITEM
) - tp
.item
->size
);
5104 delete_tree_item(Vcb
, &tp
, rollback
);
5106 if (!insert_tree_item(Vcb
, Vcb
->root_root
, searchkey
.obj_id
, searchkey
.obj_type
, tp
.item
->key
.offset
, ri
, sizeof(ROOT_ITEM
), NULL
, Irp
, rollback
)) {
5107 ERR("insert_tree_item failed\n");
5108 return STATUS_INTERNAL_ERROR
;
5111 tp
.tree
->write
= TRUE
;
5114 return STATUS_SUCCESS
;
5117 static NTSTATUS
add_root_items_to_cache(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5121 le
= Vcb
->trees
.Flink
;
5122 while (le
!= &Vcb
->trees
) {
5123 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
5125 if (t
->write
&& t
->root
!= Vcb
->chunk_root
&& t
->root
!= Vcb
->root_root
) {
5126 Status
= add_root_item_to_cache(Vcb
, t
->root
->id
, Irp
, rollback
);
5127 if (!NT_SUCCESS(Status
)) {
5128 ERR("add_root_item_to_cache returned %08x\n", Status
);
5136 // make sure we always update the extent tree
5137 Status
= add_root_item_to_cache(Vcb
, BTRFS_ROOT_EXTENT
, Irp
, rollback
);
5138 if (!NT_SUCCESS(Status
)) {
5139 ERR("add_root_item_to_cache returned %08x\n", Status
);
5143 return STATUS_SUCCESS
;
5146 NTSTATUS STDCALL
do_write(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5149 BOOL cache_changed
= FALSE
;
5151 #ifdef DEBUG_WRITE_LOOPS
5155 TRACE("(%p)\n", Vcb
);
5157 while (!IsListEmpty(&Vcb
->dirty_filerefs
)) {
5158 dirty_fileref
* dirt
;
5160 le
= RemoveHeadList(&Vcb
->dirty_filerefs
);
5162 dirt
= CONTAINING_RECORD(le
, dirty_fileref
, list_entry
);
5164 flush_fileref(dirt
->fileref
, Irp
, rollback
);
5165 free_fileref(dirt
->fileref
);
5169 // We process deleted streams first, so we don't run over our xattr
5170 // limit unless we absolutely have to.
5172 le
= Vcb
->dirty_fcbs
.Flink
;
5173 while (le
!= &Vcb
->dirty_fcbs
) {
5175 LIST_ENTRY
* le2
= le
->Flink
;
5177 dirt
= CONTAINING_RECORD(le
, dirty_fcb
, list_entry
);
5179 if (dirt
->fcb
->deleted
&& dirt
->fcb
->ads
) {
5180 RemoveEntryList(le
);
5182 flush_fcb(dirt
->fcb
, FALSE
, Irp
, rollback
);
5183 free_fcb(dirt
->fcb
);
5190 le
= Vcb
->dirty_fcbs
.Flink
;
5191 while (le
!= &Vcb
->dirty_fcbs
) {
5193 LIST_ENTRY
* le2
= le
->Flink
;
5195 dirt
= CONTAINING_RECORD(le
, dirty_fcb
, list_entry
);
5197 if (dirt
->fcb
->subvol
!= Vcb
->root_root
|| dirt
->fcb
->deleted
) {
5198 RemoveEntryList(le
);
5200 flush_fcb(dirt
->fcb
, FALSE
, Irp
, rollback
);
5201 free_fcb(dirt
->fcb
);
5208 convert_shared_data_refs(Vcb
, Irp
, rollback
);
5210 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
5211 if (!IsListEmpty(&Vcb
->sector_checksums
)) {
5212 update_checksum_tree(Vcb
, Irp
, rollback
);
5214 ExReleaseResourceLite(&Vcb
->checksum_lock
);
5216 if (!IsListEmpty(&Vcb
->drop_roots
)) {
5217 Status
= drop_roots(Vcb
, Irp
, rollback
);
5219 if (!NT_SUCCESS(Status
)) {
5220 ERR("drop_roots returned %08x\n", Status
);
5225 if (!IsListEmpty(&Vcb
->chunks_changed
)) {
5226 Status
= update_chunks(Vcb
, Irp
, rollback
);
5228 if (!NT_SUCCESS(Status
)) {
5229 ERR("update_chunks returned %08x\n", Status
);
5234 // If only changing superblock, e.g. changing label, we still need to rewrite
5235 // the root tree so the generations match, otherwise you won't be able to mount on Linux.
5236 if (!Vcb
->root_root
->treeholder
.tree
|| !Vcb
->root_root
->treeholder
.tree
->write
) {
5241 searchkey
.obj_id
= 0;
5242 searchkey
.obj_type
= 0;
5243 searchkey
.offset
= 0;
5245 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
5246 if (!NT_SUCCESS(Status
)) {
5247 ERR("error - find_item returned %08x\n", Status
);
5251 Vcb
->root_root
->treeholder
.tree
->write
= TRUE
;
5254 Status
= add_root_items_to_cache(Vcb
, Irp
, rollback
);
5255 if (!NT_SUCCESS(Status
)) {
5256 ERR("add_root_items_to_cache returned %08x\n", Status
);
5261 Status
= add_parents(Vcb
, rollback
);
5262 if (!NT_SUCCESS(Status
)) {
5263 ERR("add_parents returned %08x\n", Status
);
5267 Status
= do_splits(Vcb
, Irp
, rollback
);
5268 if (!NT_SUCCESS(Status
)) {
5269 ERR("do_splits returned %08x\n", Status
);
5273 Status
= allocate_tree_extents(Vcb
, Irp
, rollback
);
5274 if (!NT_SUCCESS(Status
)) {
5275 ERR("add_parents returned %08x\n", Status
);
5279 Status
= update_chunk_usage(Vcb
, Irp
, rollback
);
5280 if (!NT_SUCCESS(Status
)) {
5281 ERR("update_chunk_usage returned %08x\n", Status
);
5285 Status
= allocate_cache(Vcb
, &cache_changed
, Irp
, rollback
);
5286 if (!NT_SUCCESS(Status
)) {
5287 ERR("allocate_cache returned %08x\n", Status
);
5291 #ifdef DEBUG_WRITE_LOOPS
5295 ERR("cache has changed, looping again\n");
5297 } while (cache_changed
|| !trees_consistent(Vcb
, rollback
));
5299 #ifdef DEBUG_WRITE_LOOPS
5300 ERR("%u loops\n", loops
);
5303 TRACE("trees consistent\n");
5305 Status
= update_root_root(Vcb
, Irp
, rollback
);
5306 if (!NT_SUCCESS(Status
)) {
5307 ERR("update_root_root returned %08x\n", Status
);
5311 Status
= write_trees(Vcb
, Irp
);
5312 if (!NT_SUCCESS(Status
)) {
5313 ERR("write_trees returned %08x\n", Status
);
5317 Vcb
->superblock
.cache_generation
= Vcb
->superblock
.generation
;
5319 Status
= write_superblocks(Vcb
, Irp
);
5320 if (!NT_SUCCESS(Status
)) {
5321 ERR("write_superblocks returned %08x\n", Status
);
5325 clean_space_cache(Vcb
);
5327 Vcb
->superblock
.generation
++;
5329 Status
= STATUS_SUCCESS
;
5331 le
= Vcb
->trees
.Flink
;
5332 while (le
!= &Vcb
->trees
) {
5333 tree
* t
= CONTAINING_RECORD(le
, tree
, list_entry
);
5335 #ifdef DEBUG_PARANOID
5339 searchkey
.obj_id
= t
->header
.address
;
5340 searchkey
.obj_type
= TYPE_METADATA_ITEM
;
5341 searchkey
.offset
= 0xffffffffffffffff;
5343 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5344 if (!NT_SUCCESS(Status
)) {
5345 ERR("error - find_item returned %08x\n", Status
);
5349 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5350 searchkey
.obj_id
= t
->header
.address
;
5351 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5352 searchkey
.offset
= 0xffffffffffffffff;
5354 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5355 if (!NT_SUCCESS(Status
)) {
5356 ERR("error - find_item returned %08x\n", Status
);
5360 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5361 ERR("error - could not find entry in extent tree for tree at %llx\n", t
->header
.address
);
5372 Vcb
->need_write
= FALSE
;
5374 while (!IsListEmpty(&Vcb
->drop_roots
)) {
5375 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->drop_roots
);
5376 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
5378 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
5379 ExFreePool(r
->nonpaged
);
5384 TRACE("do_write returning %08x\n", Status
);
5389 static __inline BOOL
entry_in_ordered_list(LIST_ENTRY
* list
, UINT64 value
) {
5390 LIST_ENTRY
* le
= list
->Flink
;
5393 while (le
!= list
) {
5394 ol
= (ordered_list
*)le
;
5396 if (ol
->key
> value
)
5398 else if (ol
->key
== value
)
5407 static changed_extent
* get_changed_extent_item(chunk
* c
, UINT64 address
, UINT64 size
, BOOL no_csum
) {
5411 le
= c
->changed_extents
.Flink
;
5412 while (le
!= &c
->changed_extents
) {
5413 ce
= CONTAINING_RECORD(le
, changed_extent
, list_entry
);
5415 if (ce
->address
== address
&& ce
->size
== size
)
5421 ce
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent
), ALLOC_TAG
);
5423 ERR("out of memory\n");
5427 ce
->address
= address
;
5429 ce
->old_size
= size
;
5432 ce
->no_csum
= no_csum
;
5433 InitializeListHead(&ce
->refs
);
5434 InitializeListHead(&ce
->old_refs
);
5436 InsertTailList(&c
->changed_extents
, &ce
->list_entry
);
5441 NTSTATUS
update_changed_extent_ref(device_extension
* Vcb
, chunk
* c
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 objid
, UINT64 offset
, signed long long count
,
5442 BOOL no_csum
, UINT64 new_size
, PIRP Irp
) {
5445 changed_extent_ref
* cer
;
5451 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
5453 ce
= get_changed_extent_item(c
, address
, size
, no_csum
);
5456 ERR("get_changed_extent_item failed\n");
5457 Status
= STATUS_INTERNAL_ERROR
;
5461 if (IsListEmpty(&ce
->refs
) && IsListEmpty(&ce
->old_refs
)) { // new entry
5462 searchkey
.obj_id
= address
;
5463 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
5464 searchkey
.offset
= 0xffffffffffffffff;
5466 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
5467 if (!NT_SUCCESS(Status
)) {
5468 ERR("error - find_item returned %08x\n", Status
);
5472 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
5473 ERR("could not find address %llx in extent tree\n", address
);
5474 Status
= STATUS_INTERNAL_ERROR
;
5478 if (tp
.item
->key
.offset
!= size
) {
5479 ERR("extent %llx had size %llx, not %llx as expected\n", address
, tp
.item
->key
.offset
, size
);
5480 Status
= STATUS_INTERNAL_ERROR
;
5484 if (tp
.item
->size
== sizeof(EXTENT_ITEM_V0
)) {
5485 EXTENT_ITEM_V0
* eiv0
= (EXTENT_ITEM_V0
*)tp
.item
->data
;
5487 ce
->count
= ce
->old_count
= eiv0
->refcount
;
5488 } else if (tp
.item
->size
>= sizeof(EXTENT_ITEM
)) {
5489 EXTENT_ITEM
* ei
= (EXTENT_ITEM
*)tp
.item
->data
;
5491 ce
->count
= ce
->old_count
= ei
->refcount
;
5493 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
));
5494 Status
= STATUS_INTERNAL_ERROR
;
5499 ce
->size
= new_size
;
5501 le
= ce
->refs
.Flink
;
5502 while (le
!= &ce
->refs
) {
5503 cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
5505 if (cer
->edr
.root
== root
&& cer
->edr
.objid
== objid
&& cer
->edr
.offset
== offset
) {
5507 cer
->edr
.count
+= count
;
5508 Status
= STATUS_SUCCESS
;
5515 old_count
= find_extent_data_refcount(Vcb
, address
, size
, root
, objid
, offset
, Irp
);
5517 if (old_count
> 0) {
5518 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
5521 ERR("out of memory\n");
5522 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5526 cer
->edr
.root
= root
;
5527 cer
->edr
.objid
= objid
;
5528 cer
->edr
.offset
= offset
;
5529 cer
->edr
.count
= old_count
;
5531 InsertTailList(&ce
->old_refs
, &cer
->list_entry
);
5534 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
5537 ERR("out of memory\n");
5538 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5542 cer
->edr
.root
= root
;
5543 cer
->edr
.objid
= objid
;
5544 cer
->edr
.offset
= offset
;
5545 cer
->edr
.count
= old_count
+ count
;
5547 InsertTailList(&ce
->refs
, &cer
->list_entry
);
5551 Status
= STATUS_SUCCESS
;
5554 ExReleaseResourceLite(&c
->changed_extents_lock
);
5559 NTSTATUS
excise_extents(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, PIRP Irp
, LIST_ENTRY
* rollback
) {
5563 le
= fcb
->extents
.Flink
;
5565 while (le
!= &fcb
->extents
) {
5566 LIST_ENTRY
* le2
= le
->Flink
;
5567 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
5568 EXTENT_DATA
* ed
= ext
->data
;
5573 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
5574 ERR("extent at %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
5575 Status
= STATUS_INTERNAL_ERROR
;
5579 if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
5580 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
5581 ERR("extent at %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
5582 Status
= STATUS_INTERNAL_ERROR
;
5586 ed2
= (EXTENT_DATA2
*)ed
->data
;
5589 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
5591 if (ext
->offset
< end_data
&& ext
->offset
+ len
> start_data
) {
5592 if (ed
->type
== EXTENT_TYPE_INLINE
) {
5593 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
5594 remove_fcb_extent(fcb
, ext
, rollback
);
5596 fcb
->inode_item
.st_blocks
-= len
;
5597 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
5602 size
= len
- (end_data
- ext
->offset
);
5604 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
5606 ERR("out of memory\n");
5607 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5611 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5613 ERR("out of memory\n");
5614 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5619 ned
->generation
= Vcb
->superblock
.generation
;
5620 ned
->decoded_size
= size
;
5621 ned
->compression
= ed
->compression
;
5622 ned
->encryption
= ed
->encryption
;
5623 ned
->encoding
= ed
->encoding
;
5624 ned
->type
= ed
->type
;
5626 RtlCopyMemory(&ned
->data
[0], &ed
->data
[end_data
- ext
->offset
], size
);
5628 newext
->offset
= end_data
;
5630 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
5631 newext
->unique
= ext
->unique
;
5632 newext
->ignore
= FALSE
;
5633 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
5635 remove_fcb_extent(fcb
, ext
, rollback
);
5637 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
5638 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
5643 size
= start_data
- ext
->offset
;
5645 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
5647 ERR("out of memory\n");
5648 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5652 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5654 ERR("out of memory\n");
5655 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5660 ned
->generation
= Vcb
->superblock
.generation
;
5661 ned
->decoded_size
= size
;
5662 ned
->compression
= ed
->compression
;
5663 ned
->encryption
= ed
->encryption
;
5664 ned
->encoding
= ed
->encoding
;
5665 ned
->type
= ed
->type
;
5667 RtlCopyMemory(&ned
->data
[0], &ed
->data
[0], size
);
5669 newext
->offset
= ext
->offset
;
5671 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
5672 newext
->unique
= ext
->unique
;
5673 newext
->ignore
= FALSE
;
5674 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
5676 remove_fcb_extent(fcb
, ext
, rollback
);
5678 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
5679 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
5680 EXTENT_DATA
*ned1
, *ned2
;
5682 extent
*newext1
, *newext2
;
5684 size
= start_data
- ext
->offset
;
5686 ned1
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
5688 ERR("out of memory\n");
5689 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5693 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5695 ERR("out of memory\n");
5696 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5701 ned1
->generation
= Vcb
->superblock
.generation
;
5702 ned1
->decoded_size
= size
;
5703 ned1
->compression
= ed
->compression
;
5704 ned1
->encryption
= ed
->encryption
;
5705 ned1
->encoding
= ed
->encoding
;
5706 ned1
->type
= ed
->type
;
5708 RtlCopyMemory(&ned1
->data
[0], &ed
->data
[0], size
);
5710 newext1
->offset
= ext
->offset
;
5711 newext1
->data
= ned1
;
5712 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
5713 newext1
->unique
= FALSE
;
5714 newext1
->ignore
= FALSE
;
5716 size
= ext
->offset
+ len
- end_data
;
5718 ned2
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + size
, ALLOC_TAG
);
5720 ERR("out of memory\n");
5721 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5723 ExFreePool(newext1
);
5727 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5729 ERR("out of memory\n");
5730 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5732 ExFreePool(newext1
);
5737 ned2
->generation
= Vcb
->superblock
.generation
;
5738 ned2
->decoded_size
= size
;
5739 ned2
->compression
= ed
->compression
;
5740 ned2
->encryption
= ed
->encryption
;
5741 ned2
->encoding
= ed
->encoding
;
5742 ned2
->type
= ed
->type
;
5744 RtlCopyMemory(&ned2
->data
[0], &ed
->data
[end_data
- ext
->offset
], size
);
5746 newext2
->offset
= end_data
;
5747 newext2
->data
= ned2
;
5748 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + size
;
5749 newext2
->unique
= FALSE
;
5750 newext2
->ignore
= FALSE
;
5752 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
5753 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
5755 remove_fcb_extent(fcb
, ext
, rollback
);
5757 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
5759 } else if (ed
->type
== EXTENT_TYPE_REGULAR
|| ed
->type
== EXTENT_TYPE_PREALLOC
) {
5760 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove all
5761 if (ed2
->address
!= 0) {
5764 fcb
->inode_item
.st_blocks
-= len
;
5766 c
= get_chunk_from_address(Vcb
, ed2
->address
);
5769 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
5771 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, -1,
5772 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
5773 if (!NT_SUCCESS(Status
)) {
5774 ERR("update_changed_extent_ref returned %08x\n", Status
);
5780 remove_fcb_extent(fcb
, ext
, rollback
);
5781 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove beginning
5786 if (ed2
->address
!= 0)
5787 fcb
->inode_item
.st_blocks
-= end_data
- ext
->offset
;
5789 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5791 ERR("out of memory\n");
5792 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5796 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5798 ERR("out of memory\n");
5799 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5804 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
5806 ned
->generation
= Vcb
->superblock
.generation
;
5807 ned
->decoded_size
= ed
->decoded_size
;
5808 ned
->compression
= ed
->compression
;
5809 ned
->encryption
= ed
->encryption
;
5810 ned
->encoding
= ed
->encoding
;
5811 ned
->type
= ed
->type
;
5812 ned2
->address
= ed2
->address
;
5813 ned2
->size
= ed2
->size
;
5814 ned2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- ext
->offset
));
5815 ned2
->num_bytes
= ed2
->num_bytes
- (end_data
- ext
->offset
);
5817 newext
->offset
= end_data
;
5819 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
5820 newext
->unique
= ext
->unique
;
5821 newext
->ignore
= FALSE
;
5822 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
5824 remove_fcb_extent(fcb
, ext
, rollback
);
5825 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ len
) { // remove end
5830 if (ed2
->address
!= 0)
5831 fcb
->inode_item
.st_blocks
-= ext
->offset
+ len
- start_data
;
5833 ned
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5835 ERR("out of memory\n");
5836 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5840 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5842 ERR("out of memory\n");
5843 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5848 ned2
= (EXTENT_DATA2
*)&ned
->data
[0];
5850 ned
->generation
= Vcb
->superblock
.generation
;
5851 ned
->decoded_size
= ed
->decoded_size
;
5852 ned
->compression
= ed
->compression
;
5853 ned
->encryption
= ed
->encryption
;
5854 ned
->encoding
= ed
->encoding
;
5855 ned
->type
= ed
->type
;
5856 ned2
->address
= ed2
->address
;
5857 ned2
->size
= ed2
->size
;
5858 ned2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
5859 ned2
->num_bytes
= start_data
- ext
->offset
;
5861 newext
->offset
= ext
->offset
;
5863 newext
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
5864 newext
->unique
= ext
->unique
;
5865 newext
->ignore
= FALSE
;
5866 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
5868 remove_fcb_extent(fcb
, ext
, rollback
);
5869 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ len
) { // remove middle
5870 EXTENT_DATA
*neda
, *nedb
;
5871 EXTENT_DATA2
*neda2
, *nedb2
;
5872 extent
*newext1
, *newext2
;
5874 if (ed2
->address
!= 0) {
5877 fcb
->inode_item
.st_blocks
-= end_data
- start_data
;
5879 c
= get_chunk_from_address(Vcb
, ed2
->address
);
5882 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
5884 Status
= update_changed_extent_ref(Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
5885 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
5886 if (!NT_SUCCESS(Status
)) {
5887 ERR("update_changed_extent_ref returned %08x\n", Status
);
5893 neda
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5895 ERR("out of memory\n");
5896 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5900 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5902 ERR("out of memory\n");
5903 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5908 nedb
= ExAllocatePoolWithTag(PagedPool
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
), ALLOC_TAG
);
5910 ERR("out of memory\n");
5911 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5913 ExFreePool(newext1
);
5917 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
5919 ERR("out of memory\n");
5920 Status
= STATUS_INSUFFICIENT_RESOURCES
;
5922 ExFreePool(newext1
);
5927 neda2
= (EXTENT_DATA2
*)&neda
->data
[0];
5929 neda
->generation
= Vcb
->superblock
.generation
;
5930 neda
->decoded_size
= ed
->decoded_size
;
5931 neda
->compression
= ed
->compression
;
5932 neda
->encryption
= ed
->encryption
;
5933 neda
->encoding
= ed
->encoding
;
5934 neda
->type
= ed
->type
;
5935 neda2
->address
= ed2
->address
;
5936 neda2
->size
= ed2
->size
;
5937 neda2
->offset
= ed2
->address
== 0 ? 0 : ed2
->offset
;
5938 neda2
->num_bytes
= start_data
- ext
->offset
;
5940 nedb2
= (EXTENT_DATA2
*)&nedb
->data
[0];
5942 nedb
->generation
= Vcb
->superblock
.generation
;
5943 nedb
->decoded_size
= ed
->decoded_size
;
5944 nedb
->compression
= ed
->compression
;
5945 nedb
->encryption
= ed
->encryption
;
5946 nedb
->encoding
= ed
->encoding
;
5947 nedb
->type
= ed
->type
;
5948 nedb2
->address
= ed2
->address
;
5949 nedb2
->size
= ed2
->size
;
5950 nedb2
->offset
= ed2
->address
== 0 ? 0 : (ed2
->offset
+ (end_data
- ext
->offset
));
5951 nedb2
->num_bytes
= ext
->offset
+ len
- end_data
;
5953 newext1
->offset
= ext
->offset
;
5954 newext1
->data
= neda
;
5955 newext1
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
5956 newext1
->unique
= FALSE
;
5957 newext1
->ignore
= FALSE
;
5959 newext2
->offset
= end_data
;
5960 newext2
->data
= nedb
;
5961 newext2
->datalen
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
5962 newext2
->unique
= FALSE
;
5963 newext2
->ignore
= FALSE
;
5965 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
5966 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
5968 remove_fcb_extent(fcb
, ext
, rollback
);
5977 // FIXME - do bitmap analysis of changed extents, and free what we can
5979 Status
= STATUS_SUCCESS
;
5982 fcb
->extents_changed
= TRUE
;
5983 mark_fcb_dirty(fcb
);
5988 static NTSTATUS
do_write_data(device_extension
* Vcb
, UINT64 address
, void* data
, UINT64 length
, LIST_ENTRY
* changed_sector_list
, PIRP Irp
) {
5993 Status
= write_data_complete(Vcb
, address
, data
, length
, Irp
, NULL
);
5994 if (!NT_SUCCESS(Status
)) {
5995 ERR("write_data returned %08x\n", Status
);
5999 if (changed_sector_list
) {
6000 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
6002 ERR("out of memory\n");
6003 return STATUS_INSUFFICIENT_RESOURCES
;
6006 sc
->ol
.key
= address
;
6007 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
6008 sc
->deleted
= FALSE
;
6010 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
6011 if (!sc
->checksums
) {
6012 ERR("out of memory\n");
6014 return STATUS_INSUFFICIENT_RESOURCES
;
6017 for (i
= 0; i
< sc
->length
; i
++) {
6018 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
6021 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
6024 return STATUS_SUCCESS
;
6027 static void add_insert_extent_rollback(LIST_ENTRY
* rollback
, fcb
* fcb
, extent
* ext
) {
6028 rollback_extent
* re
;
6030 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
6032 ERR("out of memory\n");
6039 add_rollback(rollback
, ROLLBACK_INSERT_EXTENT
, re
);
6042 static BOOL
add_extent_to_fcb(fcb
* fcb
, UINT64 offset
, EXTENT_DATA
* ed
, ULONG edsize
, BOOL unique
, LIST_ENTRY
* rollback
) {
6046 ext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
6048 ERR("out of memory\n");
6052 ext
->offset
= offset
;
6054 ext
->datalen
= edsize
;
6055 ext
->unique
= unique
;
6056 ext
->ignore
= FALSE
;
6058 le
= fcb
->extents
.Flink
;
6059 while (le
!= &fcb
->extents
) {
6060 extent
* oldext
= CONTAINING_RECORD(le
, extent
, list_entry
);
6062 if (!oldext
->ignore
) {
6063 if (oldext
->offset
> offset
) {
6064 InsertHeadList(le
->Blink
, &ext
->list_entry
);
6072 InsertTailList(&fcb
->extents
, &ext
->list_entry
);
6075 add_insert_extent_rollback(rollback
, fcb
, ext
);
6080 static void remove_fcb_extent(fcb
* fcb
, extent
* ext
, LIST_ENTRY
* rollback
) {
6082 rollback_extent
* re
;
6086 re
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(rollback_extent
), ALLOC_TAG
);
6088 ERR("out of memory\n");
6095 add_rollback(rollback
, ROLLBACK_DELETE_EXTENT
, re
);
6099 static void add_changed_extent_ref(chunk
* c
, UINT64 address
, UINT64 size
, UINT64 root
, UINT64 objid
, UINT64 offset
, UINT32 count
, BOOL no_csum
) {
6101 changed_extent_ref
* cer
;
6104 ce
= get_changed_extent_item(c
, address
, size
, no_csum
);
6107 ERR("get_changed_extent_item failed\n");
6111 le
= ce
->refs
.Flink
;
6112 while (le
!= &ce
->refs
) {
6113 cer
= CONTAINING_RECORD(le
, changed_extent_ref
, list_entry
);
6115 if (cer
->edr
.root
== root
&& cer
->edr
.objid
== objid
&& cer
->edr
.offset
== offset
) {
6117 cer
->edr
.count
+= count
;
6124 cer
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_extent_ref
), ALLOC_TAG
);
6127 ERR("out of memory\n");
6131 cer
->edr
.root
= root
;
6132 cer
->edr
.objid
= objid
;
6133 cer
->edr
.offset
= offset
;
6134 cer
->edr
.count
= count
;
6136 InsertTailList(&ce
->refs
, &cer
->list_entry
);
6141 BOOL
insert_extent_chunk(device_extension
* Vcb
, fcb
* fcb
, chunk
* c
, UINT64 start_data
, UINT64 length
, BOOL prealloc
, void* data
,
6142 LIST_ENTRY
* changed_sector_list
, PIRP Irp
, LIST_ENTRY
* rollback
, UINT8 compression
, UINT64 decoded_size
) {
6147 ULONG edsize
= sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
);
6148 // #ifdef DEBUG_PARANOID
6153 TRACE("(%p, (%llx, %llx), %llx, %llx, %llx, %u, %p, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, c
->offset
, start_data
, length
, prealloc
, data
, changed_sector_list
, rollback
);
6155 if (!find_address_in_chunk(Vcb
, c
, length
, &address
))
6158 // #ifdef DEBUG_PARANOID
6159 // searchkey.obj_id = address;
6160 // searchkey.obj_type = TYPE_EXTENT_ITEM;
6161 // searchkey.offset = 0xffffffffffffffff;
6163 // Status = find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE);
6164 // if (!NT_SUCCESS(Status)) {
6165 // ERR("error - find_item returned %08x\n", Status);
6166 // } else if (tp.item->key.obj_id == searchkey.obj_id && tp.item->key.obj_type == searchkey.obj_type) {
6167 // ERR("address %llx already allocated\n", address);
6173 Status
= do_write_data(Vcb
, address
, data
, length
, changed_sector_list
, Irp
);
6174 if (!NT_SUCCESS(Status
)) {
6175 ERR("do_write_data returned %08x\n", Status
);
6180 // add extent data to inode
6181 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
6183 ERR("out of memory\n");
6187 ed
->generation
= Vcb
->superblock
.generation
;
6188 ed
->decoded_size
= decoded_size
;
6189 ed
->compression
= compression
;
6190 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
6191 ed
->encoding
= BTRFS_ENCODING_NONE
;
6192 ed
->type
= prealloc
? EXTENT_TYPE_PREALLOC
: EXTENT_TYPE_REGULAR
;
6194 ed2
= (EXTENT_DATA2
*)ed
->data
;
6195 ed2
->address
= address
;
6198 ed2
->num_bytes
= decoded_size
;
6200 if (!add_extent_to_fcb(fcb
, start_data
, ed
, edsize
, TRUE
, rollback
)) {
6201 ERR("add_extent_to_fcb failed\n");
6206 increase_chunk_usage(c
, length
);
6207 space_list_subtract(Vcb
, c
, FALSE
, address
, length
, rollback
);
6209 fcb
->inode_item
.st_blocks
+= decoded_size
;
6211 fcb
->extents_changed
= TRUE
;
6212 mark_fcb_dirty(fcb
);
6214 ExAcquireResourceExclusiveLite(&c
->changed_extents_lock
, TRUE
);
6216 add_changed_extent_ref(c
, address
, length
, fcb
->subvol
->id
, fcb
->inode
, start_data
, 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
);
6218 ExReleaseResourceLite(&c
->changed_extents_lock
);
6223 static BOOL
extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
6224 LIST_ENTRY
* changed_sector_list
, extent
* ext
, chunk
* c
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6226 EXTENT_DATA2
*ed2
, *ed2orig
;
6228 UINT64 addr
, origsize
;
6232 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
,
6233 length
, data
, changed_sector_list
, ext
, c
, rollback
);
6235 ed2orig
= (EXTENT_DATA2
*)ext
->data
->data
;
6237 origsize
= ed2orig
->size
;
6238 addr
= ed2orig
->address
+ ed2orig
->size
;
6240 Status
= write_data_complete(Vcb
, addr
, data
, length
, Irp
, c
);
6241 if (!NT_SUCCESS(Status
)) {
6242 ERR("write_data returned %08x\n", Status
);
6246 le
= fcb
->extents
.Flink
;
6247 while (le
!= &fcb
->extents
) {
6248 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
6250 if (!ext2
->ignore
&& (ext2
->data
->type
== EXTENT_TYPE_REGULAR
|| ext2
->data
->type
== EXTENT_TYPE_PREALLOC
)) {
6251 EXTENT_DATA2
* ed2b
= (EXTENT_DATA2
*)ext2
->data
->data
;
6253 if (ed2b
->address
== ed2orig
->address
) {
6254 ed2b
->size
= origsize
+ length
;
6255 ext2
->data
->decoded_size
= origsize
+ length
;
6262 ed
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
6264 ERR("out of memory\n");
6268 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
6270 ERR("out of memory\n");
6275 RtlCopyMemory(ed
, ext
->data
, ext
->datalen
);
6277 ed2
= (EXTENT_DATA2
*)ed
->data
;
6278 ed2
->offset
= ed2orig
->offset
+ ed2orig
->num_bytes
;
6279 ed2
->num_bytes
= length
;
6281 RtlCopyMemory(newext
, ext
, sizeof(extent
));
6282 newext
->offset
= ext
->offset
+ ed2orig
->num_bytes
;
6285 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
6287 add_insert_extent_rollback(rollback
, fcb
, newext
);
6289 Status
= update_changed_extent_ref(Vcb
, c
, ed2orig
->address
, origsize
, fcb
->subvol
->id
, fcb
->inode
, newext
->offset
- ed2
->offset
,
6290 1, fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
6292 if (!NT_SUCCESS(Status
)) {
6293 ERR("update_changed_extent_ref returned %08x\n", Status
);
6297 if (changed_sector_list
) {
6299 changed_sector
* sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
6301 ERR("out of memory\n");
6306 sc
->length
= length
/ Vcb
->superblock
.sector_size
;
6307 sc
->deleted
= FALSE
;
6309 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
6310 if (!sc
->checksums
) {
6311 ERR("out of memory\n");
6316 for (i
= 0; i
< sc
->length
; i
++) {
6317 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ (i
* Vcb
->superblock
.sector_size
), Vcb
->superblock
.sector_size
);
6319 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
6322 increase_chunk_usage(c
, length
);
6324 space_list_subtract(Vcb
, c
, FALSE
, addr
, length
, NULL
); // no rollback as we don't reverse extending the extent
6326 fcb
->inode_item
.st_blocks
+= length
;
6331 static BOOL
try_extend_data(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
,
6332 LIST_ENTRY
* changed_sector_list
, PIRP Irp
, UINT64
* written
, LIST_ENTRY
* rollback
) {
6333 BOOL success
= FALSE
;
6341 le
= fcb
->extents
.Flink
;
6343 while (le
!= &fcb
->extents
) {
6344 extent
* nextext
= CONTAINING_RECORD(le
, extent
, list_entry
);
6346 if (!nextext
->ignore
) {
6347 if (nextext
->offset
== start_data
) {
6350 } else if (nextext
->offset
> start_data
)
6363 TRACE("extent was not unique\n");
6369 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
6370 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
6374 if (ed
->type
!= EXTENT_TYPE_REGULAR
) {
6375 TRACE("not extending extent which is not EXTENT_TYPE_REGULAR\n");
6379 ed2
= (EXTENT_DATA2
*)ed
->data
;
6381 if (ext
->datalen
< sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
)) {
6382 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
) - 1 + sizeof(EXTENT_DATA2
));
6386 if (ext
->offset
+ ed2
->num_bytes
!= start_data
) {
6387 TRACE("last EXTENT_DATA does not run up to start_data (%llx + %llx != %llx)\n", ext
->offset
, ed2
->num_bytes
, start_data
);
6391 if (ed
->compression
!= BTRFS_COMPRESSION_NONE
) {
6392 TRACE("not extending a compressed extent\n");
6396 if (ed
->encryption
!= BTRFS_ENCRYPTION_NONE
) {
6397 WARN("encryption not supported\n");
6401 if (ed
->encoding
!= BTRFS_ENCODING_NONE
) {
6402 WARN("other encodings not supported\n");
6406 if (ed2
->size
- ed2
->offset
!= ed2
->num_bytes
) {
6407 TRACE("last EXTENT_DATA does not run all the way to the end of the extent\n");
6411 if (ed2
->size
>= MAX_EXTENT_SIZE
) {
6412 TRACE("extent size was too large to extend (%llx >= %llx)\n", ed2
->size
, (UINT64
)MAX_EXTENT_SIZE
);
6416 c
= get_chunk_from_address(Vcb
, ed2
->address
);
6418 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
6420 le
= c
->space
.Flink
;
6421 while (le
!= &c
->space
) {
6422 s
= CONTAINING_RECORD(le
, space
, list_entry
);
6424 if (s
->address
== ed2
->address
+ ed2
->size
) {
6425 UINT64 newlen
= min(min(s
->size
, length
), MAX_EXTENT_SIZE
- ed2
->size
);
6427 success
= extend_data(Vcb
, fcb
, start_data
, newlen
, data
, changed_sector_list
, ext
, c
, Irp
, rollback
);
6433 } else if (s
->address
> ed2
->address
+ ed2
->size
)
6439 ExReleaseResourceLite(&c
->lock
);
6444 static NTSTATUS
insert_prealloc_extent(fcb
* fcb
, UINT64 start
, UINT64 length
, LIST_ENTRY
* rollback
) {
6450 UINT64 flags
, origlength
= length
;
6453 BOOL page_file
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
;
6455 flags
= fcb
->Vcb
->data_flags
;
6457 // FIXME - try and maximize contiguous ranges first. If we can't do that,
6458 // allocate all the free space we find until it's enough.
6461 UINT64 extlen
= min(MAX_EXTENT_SIZE
, length
);
6463 ExAcquireResourceSharedLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
6465 le
= fcb
->Vcb
->chunks
.Flink
;
6466 while (le
!= &fcb
->Vcb
->chunks
) {
6467 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
6469 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
6471 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
6472 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
)) {
6473 ExReleaseResourceLite(&c
->lock
);
6474 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
6479 ExReleaseResourceLite(&c
->lock
);
6484 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
6486 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
6488 if ((c
= alloc_chunk(fcb
->Vcb
, flags
))) {
6489 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
6491 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
6493 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= extlen
) {
6494 if (insert_extent_chunk(fcb
->Vcb
, fcb
, c
, start
, extlen
, !page_file
, NULL
, NULL
, NULL
, rollback
, BTRFS_COMPRESSION_NONE
, extlen
)) {
6495 ExReleaseResourceLite(&c
->lock
);
6500 ExReleaseResourceLite(&c
->lock
);
6502 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
6504 WARN("couldn't find any data chunks with %llx bytes free\n", origlength
);
6505 Status
= STATUS_DISK_FULL
;
6511 } while (length
> 0);
6513 Status
= STATUS_SUCCESS
;
6519 // static void print_tree(tree* t) {
6520 // LIST_ENTRY* le = t->itemlist.Flink;
6521 // while (le != &t->itemlist) {
6522 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
6523 // ERR("%llx,%x,%llx (ignore = %s)\n", td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
6528 NTSTATUS
insert_extent(device_extension
* Vcb
, fcb
* fcb
, UINT64 start_data
, UINT64 length
, void* data
, LIST_ENTRY
* changed_sector_list
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6531 UINT64 flags
, orig_length
= length
, written
= 0;
6533 TRACE("(%p, (%llx, %llx), %llx, %llx, %p, %p)\n", Vcb
, fcb
->subvol
->id
, fcb
->inode
, start_data
, length
, data
, changed_sector_list
);
6535 // FIXME - split data up if not enough space for just one extent
6537 if (start_data
> 0) {
6538 try_extend_data(Vcb
, fcb
, start_data
, length
, data
, changed_sector_list
, Irp
, &written
, rollback
);
6540 if (written
== length
)
6541 return STATUS_SUCCESS
;
6542 else if (written
> 0) {
6543 start_data
+= written
;
6545 data
= &((UINT8
*)data
)[written
];
6549 flags
= Vcb
->data_flags
;
6551 while (written
< orig_length
) {
6552 UINT64 newlen
= min(length
, MAX_EXTENT_SIZE
);
6555 // Rather than necessarily writing the whole extent at once, we deal with it in blocks of 128 MB.
6556 // First, see if we can write the extent part to an existing chunk.
6558 ExAcquireResourceSharedLite(&Vcb
->chunk_lock
, TRUE
);
6560 le
= Vcb
->chunks
.Flink
;
6561 while (le
!= &Vcb
->chunks
) {
6562 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
6564 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
6566 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
) {
6567 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, changed_sector_list
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
)) {
6570 if (written
== orig_length
) {
6571 ExReleaseResourceLite(&c
->lock
);
6572 ExReleaseResourceLite(&Vcb
->chunk_lock
);
6573 return STATUS_SUCCESS
;
6576 start_data
+= newlen
;
6578 data
= &((UINT8
*)data
)[newlen
];
6584 ExReleaseResourceLite(&c
->lock
);
6589 ExReleaseResourceLite(&fcb
->Vcb
->chunk_lock
);
6593 // Otherwise, see if we can put it in a new chunk.
6595 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->chunk_lock
, TRUE
);
6597 if ((c
= alloc_chunk(Vcb
, flags
))) {
6598 ExReleaseResourceLite(&Vcb
->chunk_lock
);
6600 ExAcquireResourceExclusiveLite(&c
->lock
, TRUE
);
6602 if (c
->chunk_item
->type
== flags
&& (c
->chunk_item
->size
- c
->used
) >= newlen
) {
6603 if (insert_extent_chunk(Vcb
, fcb
, c
, start_data
, newlen
, FALSE
, data
, changed_sector_list
, Irp
, rollback
, BTRFS_COMPRESSION_NONE
, newlen
)) {
6606 if (written
== orig_length
) {
6607 ExReleaseResourceLite(&c
->lock
);
6608 return STATUS_SUCCESS
;
6611 start_data
+= newlen
;
6613 data
= &((UINT8
*)data
)[newlen
];
6618 ExReleaseResourceLite(&c
->lock
);
6620 ExReleaseResourceLite(&Vcb
->chunk_lock
);
6623 FIXME("FIXME - not enough room to write whole extent part, try to write bits and pieces\n"); // FIXME
6628 WARN("couldn't find any data chunks with %llx bytes free\n", length
);
6630 return STATUS_DISK_FULL
;
6633 static void update_checksum_tree(device_extension
* Vcb
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6634 LIST_ENTRY
* le
= Vcb
->sector_checksums
.Flink
;
6636 traverse_ptr tp
, next_tp
;
6641 if (!Vcb
->checksum_root
) {
6642 ERR("no checksum root\n");
6646 while (le
!= &Vcb
->sector_checksums
) {
6647 UINT64 startaddr
, endaddr
;
6652 ULONG runlength
, index
;
6654 cs
= (changed_sector
*)le
;
6656 searchkey
.obj_id
= EXTENT_CSUM_ID
;
6657 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
6658 searchkey
.offset
= cs
->ol
.key
;
6660 // FIXME - create checksum_root if it doesn't exist at all
6662 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
6663 if (!NT_SUCCESS(Status
)) { // tree is completely empty
6664 // FIXME - do proper check here that tree is empty
6666 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * cs
->length
, ALLOC_TAG
);
6668 ERR("out of memory\n");
6672 RtlCopyMemory(checksums
, cs
->checksums
, sizeof(UINT32
) * cs
->length
);
6674 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, cs
->ol
.key
, checksums
, sizeof(UINT32
) * cs
->length
, NULL
, Irp
, rollback
)) {
6675 ERR("insert_tree_item failed\n");
6676 ExFreePool(checksums
);
6683 // FIXME - check entry is TYPE_EXTENT_CSUM?
6685 if (tp
.item
->key
.offset
< cs
->ol
.key
&& tp
.item
->key
.offset
+ (tp
.item
->size
* Vcb
->superblock
.sector_size
/ sizeof(UINT32
)) >= cs
->ol
.key
)
6686 startaddr
= tp
.item
->key
.offset
;
6688 startaddr
= cs
->ol
.key
;
6690 searchkey
.obj_id
= EXTENT_CSUM_ID
;
6691 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
6692 searchkey
.offset
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
6694 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
6695 if (!NT_SUCCESS(Status
)) {
6696 ERR("error - find_item returned %08x\n", Status
);
6700 tplen
= tp
.item
->size
/ sizeof(UINT32
);
6702 if (tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
) >= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
))
6703 endaddr
= tp
.item
->key
.offset
+ (tplen
* Vcb
->superblock
.sector_size
);
6705 endaddr
= cs
->ol
.key
+ (cs
->length
* Vcb
->superblock
.sector_size
);
6707 TRACE("cs starts at %llx (%x sectors)\n", cs
->ol
.key
, cs
->length
);
6708 TRACE("startaddr = %llx\n", startaddr
);
6709 TRACE("endaddr = %llx\n", endaddr
);
6711 len
= (endaddr
- startaddr
) / Vcb
->superblock
.sector_size
;
6713 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * len
, ALLOC_TAG
);
6715 ERR("out of memory\n");
6719 bmparr
= ExAllocatePoolWithTag(PagedPool
, sizeof(ULONG
) * ((len
/8)+1), ALLOC_TAG
);
6721 ERR("out of memory\n");
6722 ExFreePool(checksums
);
6726 RtlInitializeBitMap(&bmp
, bmparr
, len
);
6727 RtlSetAllBits(&bmp
);
6729 searchkey
.obj_id
= EXTENT_CSUM_ID
;
6730 searchkey
.obj_type
= TYPE_EXTENT_CSUM
;
6731 searchkey
.offset
= cs
->ol
.key
;
6733 Status
= find_item(Vcb
, Vcb
->checksum_root
, &tp
, &searchkey
, FALSE
, Irp
);
6734 if (!NT_SUCCESS(Status
)) {
6735 ERR("error - find_item returned %08x\n", Status
);
6739 // set bit = free space, cleared bit = allocated sector
6741 // ERR("start loop\n");
6742 while (tp
.item
->key
.offset
< endaddr
) {
6743 // ERR("%llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset);
6744 if (tp
.item
->key
.offset
>= startaddr
) {
6745 if (tp
.item
->size
> 0) {
6746 RtlCopyMemory(&checksums
[(tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
], tp
.item
->data
, tp
.item
->size
);
6747 RtlClearBits(&bmp
, (tp
.item
->key
.offset
- startaddr
) / Vcb
->superblock
.sector_size
, tp
.item
->size
/ sizeof(UINT32
));
6750 delete_tree_item(Vcb
, &tp
, rollback
);
6753 if (find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
)) {
6758 // ERR("end loop\n");
6761 RtlSetBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
6763 RtlCopyMemory(&checksums
[(cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
], cs
->checksums
, cs
->length
* sizeof(UINT32
));
6764 RtlClearBits(&bmp
, (cs
->ol
.key
- startaddr
) / Vcb
->superblock
.sector_size
, cs
->length
);
6767 runlength
= RtlFindFirstRunClear(&bmp
, &index
);
6769 while (runlength
!= 0) {
6773 if (runlength
* sizeof(UINT32
) > MAX_CSUM_SIZE
)
6774 rl
= MAX_CSUM_SIZE
/ sizeof(UINT32
);
6778 data
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * rl
, ALLOC_TAG
);
6780 ERR("out of memory\n");
6782 ExFreePool(checksums
);
6786 RtlCopyMemory(data
, &checksums
[index
], sizeof(UINT32
) * rl
);
6788 if (!insert_tree_item(Vcb
, Vcb
->checksum_root
, EXTENT_CSUM_ID
, TYPE_EXTENT_CSUM
, startaddr
+ (index
* Vcb
->superblock
.sector_size
), data
, sizeof(UINT32
) * rl
, NULL
, Irp
, rollback
)) {
6789 ERR("insert_tree_item failed\n");
6792 ExFreePool(checksums
);
6798 } while (runlength
> 0);
6800 runlength
= RtlFindNextForwardRunClear(&bmp
, index
, &index
);
6804 ExFreePool(checksums
);
6811 while (!IsListEmpty(&Vcb
->sector_checksums
)) {
6812 le
= RemoveHeadList(&Vcb
->sector_checksums
);
6813 cs
= (changed_sector
*)le
;
6816 ExFreePool(cs
->checksums
);
6822 void commit_checksum_changes(device_extension
* Vcb
, LIST_ENTRY
* changed_sector_list
) {
6823 while (!IsListEmpty(changed_sector_list
)) {
6824 LIST_ENTRY
* le
= RemoveHeadList(changed_sector_list
);
6825 InsertTailList(&Vcb
->sector_checksums
, le
);
6829 NTSTATUS
truncate_file(fcb
* fcb
, UINT64 end
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6832 // FIXME - convert into inline extent if short enough
6834 Status
= excise_extents(fcb
->Vcb
, fcb
, sector_align(end
, fcb
->Vcb
->superblock
.sector_size
),
6835 sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
6836 if (!NT_SUCCESS(Status
)) {
6837 ERR("error - excise_extents failed\n");
6841 fcb
->inode_item
.st_size
= end
;
6842 TRACE("setting st_size to %llx\n", end
);
6844 fcb
->Header
.AllocationSize
.QuadPart
= sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
);
6845 fcb
->Header
.FileSize
.QuadPart
= fcb
->inode_item
.st_size
;
6846 fcb
->Header
.ValidDataLength
.QuadPart
= fcb
->inode_item
.st_size
;
6847 // FIXME - inform cache manager of this
6849 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
6851 return STATUS_SUCCESS
;
6854 NTSTATUS
extend_file(fcb
* fcb
, file_ref
* fileref
, UINT64 end
, BOOL prealloc
, PIRP Irp
, LIST_ENTRY
* rollback
) {
6855 UINT64 oldalloc
, newalloc
;
6859 TRACE("(%p, %p, %x, %u)\n", fcb
, fileref
, end
, prealloc
);
6862 return stream_set_end_of_file_information(fcb
->Vcb
, end
, fcb
, fileref
, NULL
, FALSE
, rollback
);
6867 le
= fcb
->extents
.Blink
;
6868 while (le
!= &fcb
->extents
) {
6869 extent
* ext2
= CONTAINING_RECORD(le
, extent
, list_entry
);
6871 if (!ext2
->ignore
) {
6881 EXTENT_DATA
* ed
= ext
->data
;
6882 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
6884 if (ext
->datalen
< sizeof(EXTENT_DATA
)) {
6885 ERR("extent %llx was %u bytes, expected at least %u\n", ext
->offset
, ext
->datalen
, sizeof(EXTENT_DATA
));
6886 return STATUS_INTERNAL_ERROR
;
6889 oldalloc
= ext
->offset
+ (ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
);
6890 cur_inline
= ed
->type
== EXTENT_TYPE_INLINE
;
6892 if (cur_inline
&& end
> fcb
->Vcb
->options
.max_inline
) {
6893 LIST_ENTRY changed_sector_list
;
6894 BOOL nocsum
= fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
6895 UINT64 origlength
, length
;
6897 UINT64 offset
= ext
->offset
;
6899 TRACE("giving inline file proper extents\n");
6901 origlength
= ed
->decoded_size
;
6906 InitializeListHead(&changed_sector_list
);
6908 length
= sector_align(origlength
, fcb
->Vcb
->superblock
.sector_size
);
6910 data
= ExAllocatePoolWithTag(PagedPool
, length
, ALLOC_TAG
);
6912 ERR("could not allocate %llx bytes for data\n", length
);
6913 return STATUS_INSUFFICIENT_RESOURCES
;
6916 if (length
> origlength
)
6917 RtlZeroMemory(data
+ origlength
, length
- origlength
);
6919 RtlCopyMemory(data
, ed
->data
, origlength
);
6921 fcb
->inode_item
.st_blocks
-= origlength
;
6923 remove_fcb_extent(fcb
, ext
, rollback
);
6925 if (write_fcb_compressed(fcb
)) {
6926 Status
= write_compressed(fcb
, offset
, offset
+ length
, data
, nocsum
? NULL
: &changed_sector_list
, Irp
, rollback
);
6927 if (!NT_SUCCESS(Status
)) {
6928 ERR("write_compressed returned %08x\n", Status
);
6933 Status
= insert_extent(fcb
->Vcb
, fcb
, offset
, length
, data
, nocsum
? NULL
: &changed_sector_list
, Irp
, rollback
);
6934 if (!NT_SUCCESS(Status
)) {
6935 ERR("insert_extent returned %08x\n", Status
);
6941 oldalloc
= ext
->offset
+ length
;
6946 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->checksum_lock
, TRUE
);
6947 commit_checksum_changes(fcb
->Vcb
, &changed_sector_list
);
6948 ExReleaseResourceLite(&fcb
->Vcb
->checksum_lock
);
6955 if (end
> oldalloc
) {
6956 edsize
= sizeof(EXTENT_DATA
) - 1 + end
- ext
->offset
;
6957 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
6960 ERR("out of memory\n");
6961 return STATUS_INSUFFICIENT_RESOURCES
;
6964 RtlZeroMemory(ed
, edsize
);
6965 RtlCopyMemory(ed
, ext
->data
, ext
->datalen
);
6967 ed
->decoded_size
= end
- ext
->offset
;
6969 remove_fcb_extent(fcb
, ext
, rollback
);
6971 if (!add_extent_to_fcb(fcb
, ext
->offset
, ed
, edsize
, ext
->unique
, rollback
)) {
6972 ERR("add_extent_to_fcb failed\n");
6974 return STATUS_INTERNAL_ERROR
;
6977 fcb
->extents_changed
= TRUE
;
6978 mark_fcb_dirty(fcb
);
6981 TRACE("extending inline file (oldalloc = %llx, end = %llx)\n", oldalloc
, end
);
6983 fcb
->inode_item
.st_size
= end
;
6984 TRACE("setting st_size to %llx\n", end
);
6986 fcb
->inode_item
.st_blocks
= end
;
6988 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
6990 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
6992 if (newalloc
> oldalloc
) {
6994 // FIXME - try and extend previous extent first
6996 Status
= insert_prealloc_extent(fcb
, oldalloc
, newalloc
- oldalloc
, rollback
);
6998 if (!NT_SUCCESS(Status
)) {
6999 ERR("insert_prealloc_extent returned %08x\n", Status
);
7004 fcb
->extents_changed
= TRUE
;
7005 mark_fcb_dirty(fcb
);
7008 fcb
->inode_item
.st_size
= end
;
7009 TRACE("setting st_size to %llx\n", end
);
7011 TRACE("newalloc = %llx\n", newalloc
);
7013 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
7014 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
7017 if (end
> fcb
->Vcb
->options
.max_inline
) {
7018 newalloc
= sector_align(end
, fcb
->Vcb
->superblock
.sector_size
);
7021 Status
= insert_prealloc_extent(fcb
, 0, newalloc
, rollback
);
7023 if (!NT_SUCCESS(Status
)) {
7024 ERR("insert_prealloc_extent returned %08x\n", Status
);
7029 fcb
->extents_changed
= TRUE
;
7030 mark_fcb_dirty(fcb
);
7032 fcb
->inode_item
.st_size
= end
;
7033 TRACE("setting st_size to %llx\n", end
);
7035 TRACE("newalloc = %llx\n", newalloc
);
7037 fcb
->Header
.AllocationSize
.QuadPart
= newalloc
;
7038 fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
7043 edsize
= sizeof(EXTENT_DATA
) - 1 + end
;
7044 ed
= ExAllocatePoolWithTag(PagedPool
, edsize
, ALLOC_TAG
);
7047 ERR("out of memory\n");
7048 return STATUS_INSUFFICIENT_RESOURCES
;
7051 ed
->generation
= fcb
->Vcb
->superblock
.generation
;
7052 ed
->decoded_size
= end
;
7053 ed
->compression
= BTRFS_COMPRESSION_NONE
;
7054 ed
->encryption
= BTRFS_ENCRYPTION_NONE
;
7055 ed
->encoding
= BTRFS_ENCODING_NONE
;
7056 ed
->type
= EXTENT_TYPE_INLINE
;
7058 RtlZeroMemory(ed
->data
, end
);
7060 if (!add_extent_to_fcb(fcb
, 0, ed
, edsize
, FALSE
, rollback
)) {
7061 ERR("add_extent_to_fcb failed\n");
7063 return STATUS_INTERNAL_ERROR
;
7066 fcb
->extents_changed
= TRUE
;
7067 mark_fcb_dirty(fcb
);
7069 fcb
->inode_item
.st_size
= end
;
7070 TRACE("setting st_size to %llx\n", end
);
7072 fcb
->inode_item
.st_blocks
= end
;
7074 fcb
->Header
.AllocationSize
.QuadPart
= fcb
->Header
.FileSize
.QuadPart
= fcb
->Header
.ValidDataLength
.QuadPart
= end
;
7079 return STATUS_SUCCESS
;
7082 // #ifdef DEBUG_PARANOID
7083 // static void print_loaded_trees(tree* t, int spaces) {
7088 // for (i = 0; i < spaces; i++) {
7091 // pref[spaces] = 0;
7094 // ERR("%s(not loaded)\n", pref);
7098 // le = t->itemlist.Flink;
7099 // while (le != &t->itemlist) {
7100 // tree_data* td = CONTAINING_RECORD(le, tree_data, list_entry);
7102 // ERR("%s%llx,%x,%llx ignore=%s\n", pref, td->key.obj_id, td->key.obj_type, td->key.offset, td->ignore ? "TRUE" : "FALSE");
7104 // if (t->header.level > 0) {
7105 // print_loaded_trees(td->treeholder.tree, spaces+1);
7112 // static void check_extents_consistent(device_extension* Vcb, fcb* fcb) {
7114 // traverse_ptr tp, next_tp;
7115 // UINT64 length, oldlength, lastoff, alloc;
7118 // EXTENT_DATA2* ed2;
7120 // if (fcb->ads || fcb->inode_item.st_size == 0 || fcb->deleted)
7123 // TRACE("inode = %llx, subvol = %llx\n", fcb->inode, fcb->subvol->id);
7125 // searchkey.obj_id = fcb->inode;
7126 // searchkey.obj_type = TYPE_EXTENT_DATA;
7127 // searchkey.offset = 0;
7129 // Status = find_item(Vcb, fcb->subvol, &tp, &searchkey, FALSE);
7130 // if (!NT_SUCCESS(Status)) {
7131 // ERR("error - find_item returned %08x\n", Status);
7135 // if (keycmp(&searchkey, &tp.item->key)) {
7136 // ERR("could not find EXTENT_DATA at offset 0\n");
7140 // if (tp.item->size < sizeof(EXTENT_DATA)) {
7141 // 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_DATA));
7145 // ed = (EXTENT_DATA*)tp.item->data;
7146 // ed2 = (EXTENT_DATA2*)&ed->data[0];
7148 // length = oldlength = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
7149 // lastoff = tp.item->key.offset;
7151 // TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
7154 // if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
7158 // while (find_next_item(Vcb, &tp, &next_tp, FALSE)) {
7159 // if (next_tp.item->key.obj_id != searchkey.obj_id || next_tp.item->key.obj_type != searchkey.obj_type)
7164 // if (tp.item->size < sizeof(EXTENT_DATA)) {
7165 // 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_DATA));
7169 // ed = (EXTENT_DATA*)tp.item->data;
7170 // ed2 = (EXTENT_DATA2*)&ed->data[0];
7172 // length = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
7174 // TRACE("(%llx,%x,%llx) length = %llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, length);
7176 // if (tp.item->key.offset != lastoff + oldlength) {
7177 // ERR("EXTENT_DATA in %llx,%llx was at %llx, expected %llx\n", fcb->subvol->id, fcb->inode, tp.item->key.offset, lastoff + oldlength);
7181 // if (ed->type != EXTENT_TYPE_REGULAR || ed2->address != 0) {
7185 // oldlength = length;
7186 // lastoff = tp.item->key.offset;
7189 // if (alloc != fcb->inode_item.st_blocks) {
7190 // ERR("allocation size was %llx, expected %llx\n", alloc, fcb->inode_item.st_blocks);
7194 // // if (fcb->inode_item.st_blocks != lastoff + oldlength) {
7195 // // ERR("extents finished at %x, expected %x\n", (UINT32)(lastoff + oldlength), (UINT32)fcb->inode_item.st_blocks);
7202 // if (fcb->subvol->treeholder.tree)
7203 // print_loaded_trees(fcb->subvol->treeholder.tree, 0);
7208 // static void check_extent_tree_consistent(device_extension* Vcb) {
7210 // traverse_ptr tp, next_tp;
7212 // BOOL b, inconsistency;
7214 // searchkey.obj_id = 0;
7215 // searchkey.obj_type = 0;
7216 // searchkey.offset = 0;
7218 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
7219 // ERR("error - could not find any entries in extent_root\n");
7224 // inconsistency = FALSE;
7227 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
7228 // // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
7230 // if (tp.item->key.obj_id < lastaddr) {
7231 // // ERR("inconsistency!\n");
7233 // inconsistency = TRUE;
7236 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
7239 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
7241 // free_traverse_ptr(&tp);
7246 // free_traverse_ptr(&tp);
7248 // if (!inconsistency)
7251 // ERR("Inconsistency detected:\n");
7253 // if (!find_item(Vcb, Vcb->extent_root, &tp, &searchkey, FALSE)) {
7254 // ERR("error - could not find any entries in extent_root\n");
7259 // if (tp.item->key.obj_type == TYPE_EXTENT_ITEM) {
7260 // ERR("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
7262 // if (tp.item->key.obj_id < lastaddr) {
7263 // ERR("inconsistency!\n");
7266 // lastaddr = tp.item->key.obj_id + tp.item->key.offset;
7269 // b = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE);
7271 // free_traverse_ptr(&tp);
7276 // free_traverse_ptr(&tp);
7282 static NTSTATUS
do_write_file_prealloc(fcb
* fcb
, extent
* ext
, UINT64 start_data
, UINT64 end_data
, void* data
, UINT64
* written
,
7283 LIST_ENTRY
* changed_sector_list
, PIRP Irp
, LIST_ENTRY
* rollback
) {
7284 EXTENT_DATA
* ed
= ext
->data
;
7285 EXTENT_DATA2
* ed2
= (EXTENT_DATA2
*)ed
->data
;
7289 if (start_data
<= ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace all
7293 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7295 ERR("out of memory\n");
7296 return STATUS_INSUFFICIENT_RESOURCES
;
7299 newext
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7301 ERR("out of memory\n");
7303 return STATUS_INSUFFICIENT_RESOURCES
;
7306 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
7308 ned
->type
= EXTENT_TYPE_REGULAR
;
7310 Status
= do_write_data(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, ed2
->num_bytes
, changed_sector_list
, Irp
);
7311 if (!NT_SUCCESS(Status
)) {
7312 ERR("do_write_data returned %08x\n", Status
);
7316 *written
= ed2
->num_bytes
;
7318 newext
->offset
= ext
->offset
;
7320 newext
->datalen
= ext
->datalen
;
7321 newext
->unique
= ext
->unique
;
7322 newext
->ignore
= FALSE
;
7323 InsertHeadList(&ext
->list_entry
, &newext
->list_entry
);
7325 add_insert_extent_rollback(rollback
, fcb
, newext
);
7327 remove_fcb_extent(fcb
, ext
, rollback
);
7328 } else if (start_data
<= ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace beginning
7329 EXTENT_DATA
*ned
, *nedb
;
7331 extent
*newext1
, *newext2
;
7333 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7335 ERR("out of memory\n");
7336 return STATUS_INSUFFICIENT_RESOURCES
;
7339 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7341 ERR("out of memory\n");
7343 return STATUS_INSUFFICIENT_RESOURCES
;
7346 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7348 ERR("out of memory\n");
7351 return STATUS_INSUFFICIENT_RESOURCES
;
7354 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7356 ERR("out of memory\n");
7359 ExFreePool(newext1
);
7360 return STATUS_INSUFFICIENT_RESOURCES
;
7363 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
7364 ned
->type
= EXTENT_TYPE_REGULAR
;
7365 ned2
= (EXTENT_DATA2
*)ned
->data
;
7366 ned2
->num_bytes
= end_data
- ext
->offset
;
7368 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
7369 ned2
= (EXTENT_DATA2
*)nedb
->data
;
7370 ned2
->offset
+= end_data
- ext
->offset
;
7371 ned2
->num_bytes
-= end_data
- ext
->offset
;
7373 Status
= do_write_data(fcb
->Vcb
, ed2
->address
+ ed2
->offset
, (UINT8
*)data
+ ext
->offset
- start_data
, end_data
- ext
->offset
, changed_sector_list
, Irp
);
7374 if (!NT_SUCCESS(Status
)) {
7375 ERR("do_write_data returned %08x\n", Status
);
7379 *written
= end_data
- ext
->offset
;
7381 newext1
->offset
= ext
->offset
;
7382 newext1
->data
= ned
;
7383 newext1
->datalen
= ext
->datalen
;
7384 newext1
->unique
= FALSE
;
7385 newext1
->ignore
= FALSE
;
7386 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
7388 add_insert_extent_rollback(rollback
, fcb
, newext1
);
7390 newext2
->offset
= end_data
;
7391 newext2
->data
= nedb
;
7392 newext2
->datalen
= ext
->datalen
;
7393 newext2
->unique
= FALSE
;
7394 newext2
->ignore
= FALSE
;
7395 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
7397 add_insert_extent_rollback(rollback
, fcb
, newext2
);
7399 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
7402 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
7404 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
7405 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
7407 if (!NT_SUCCESS(Status
)) {
7408 ERR("update_changed_extent_ref returned %08x\n", Status
);
7413 remove_fcb_extent(fcb
, ext
, rollback
);
7414 } else if (start_data
> ext
->offset
&& end_data
>= ext
->offset
+ ed2
->num_bytes
) { // replace end
7415 EXTENT_DATA
*ned
, *nedb
;
7417 extent
*newext1
, *newext2
;
7419 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7421 ERR("out of memory\n");
7422 return STATUS_INSUFFICIENT_RESOURCES
;
7425 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7427 ERR("out of memory\n");
7429 return STATUS_INSUFFICIENT_RESOURCES
;
7432 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7434 ERR("out of memory\n");
7437 return STATUS_INSUFFICIENT_RESOURCES
;
7440 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7442 ERR("out of memory\n");
7445 ExFreePool(newext1
);
7446 return STATUS_INSUFFICIENT_RESOURCES
;
7449 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
7451 ned2
= (EXTENT_DATA2
*)ned
->data
;
7452 ned2
->num_bytes
= start_data
- ext
->offset
;
7454 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
7456 nedb
->type
= EXTENT_TYPE_REGULAR
;
7457 ned2
= (EXTENT_DATA2
*)nedb
->data
;
7458 ned2
->offset
+= start_data
- ext
->offset
;
7459 ned2
->num_bytes
= ext
->offset
+ ed2
->num_bytes
- start_data
;
7461 Status
= do_write_data(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, ned2
->num_bytes
, changed_sector_list
, Irp
);
7462 if (!NT_SUCCESS(Status
)) {
7463 ERR("do_write_data returned %08x\n", Status
);
7467 *written
= ned2
->num_bytes
;
7469 newext1
->offset
= ext
->offset
;
7470 newext1
->data
= ned
;
7471 newext1
->datalen
= ext
->datalen
;
7472 newext1
->unique
= FALSE
;
7473 newext1
->ignore
= FALSE
;
7474 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
7476 add_insert_extent_rollback(rollback
, fcb
, newext1
);
7478 newext2
->offset
= start_data
;
7479 newext2
->data
= nedb
;
7480 newext2
->datalen
= ext
->datalen
;
7481 newext2
->unique
= FALSE
;
7482 newext2
->ignore
= FALSE
;
7483 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
7485 add_insert_extent_rollback(rollback
, fcb
, newext2
);
7487 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
7490 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
7492 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 1,
7493 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
7495 if (!NT_SUCCESS(Status
)) {
7496 ERR("update_changed_extent_ref returned %08x\n", Status
);
7501 remove_fcb_extent(fcb
, ext
, rollback
);
7502 } else if (start_data
> ext
->offset
&& end_data
< ext
->offset
+ ed2
->num_bytes
) { // replace middle
7503 EXTENT_DATA
*ned
, *nedb
, *nedc
;
7505 extent
*newext1
, *newext2
, *newext3
;
7507 ned
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7509 ERR("out of memory\n");
7510 return STATUS_INSUFFICIENT_RESOURCES
;
7513 nedb
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7515 ERR("out of memory\n");
7517 return STATUS_INSUFFICIENT_RESOURCES
;
7520 nedc
= ExAllocatePoolWithTag(PagedPool
, ext
->datalen
, ALLOC_TAG
);
7522 ERR("out of memory\n");
7525 return STATUS_INSUFFICIENT_RESOURCES
;
7528 newext1
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7530 ERR("out of memory\n");
7534 return STATUS_INSUFFICIENT_RESOURCES
;
7537 newext2
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7539 ERR("out of memory\n");
7543 ExFreePool(newext1
);
7544 return STATUS_INSUFFICIENT_RESOURCES
;
7547 newext3
= ExAllocatePoolWithTag(PagedPool
, sizeof(extent
), ALLOC_TAG
);
7549 ERR("out of memory\n");
7553 ExFreePool(newext1
);
7554 ExFreePool(newext2
);
7555 return STATUS_INSUFFICIENT_RESOURCES
;
7558 RtlCopyMemory(ned
, ext
->data
, ext
->datalen
);
7559 RtlCopyMemory(nedb
, ext
->data
, ext
->datalen
);
7560 RtlCopyMemory(nedc
, ext
->data
, ext
->datalen
);
7562 ned2
= (EXTENT_DATA2
*)ned
->data
;
7563 ned2
->num_bytes
= start_data
- ext
->offset
;
7565 nedb
->type
= EXTENT_TYPE_REGULAR
;
7566 ned2
= (EXTENT_DATA2
*)nedb
->data
;
7567 ned2
->offset
+= start_data
- ext
->offset
;
7568 ned2
->num_bytes
= end_data
- start_data
;
7570 ned2
= (EXTENT_DATA2
*)nedc
->data
;
7571 ned2
->offset
+= end_data
- ext
->offset
;
7572 ned2
->num_bytes
-= end_data
- ext
->offset
;
7574 ned2
= (EXTENT_DATA2
*)nedb
->data
;
7575 Status
= do_write_data(fcb
->Vcb
, ed2
->address
+ ned2
->offset
, data
, end_data
- start_data
, changed_sector_list
, Irp
);
7576 if (!NT_SUCCESS(Status
)) {
7577 ERR("do_write_data returned %08x\n", Status
);
7581 *written
= end_data
- start_data
;
7583 newext1
->offset
= ext
->offset
;
7584 newext1
->data
= ned
;
7585 newext1
->datalen
= ext
->datalen
;
7586 newext1
->unique
= FALSE
;
7587 newext1
->ignore
= FALSE
;
7588 InsertHeadList(&ext
->list_entry
, &newext1
->list_entry
);
7590 add_insert_extent_rollback(rollback
, fcb
, newext1
);
7592 newext2
->offset
= start_data
;
7593 newext2
->data
= nedb
;
7594 newext2
->datalen
= ext
->datalen
;
7595 newext2
->unique
= FALSE
;
7596 newext2
->ignore
= FALSE
;
7597 InsertHeadList(&newext1
->list_entry
, &newext2
->list_entry
);
7599 add_insert_extent_rollback(rollback
, fcb
, newext2
);
7601 newext3
->offset
= end_data
;
7602 newext3
->data
= nedc
;
7603 newext3
->datalen
= ext
->datalen
;
7604 newext3
->unique
= FALSE
;
7605 newext3
->ignore
= FALSE
;
7606 InsertHeadList(&newext2
->list_entry
, &newext3
->list_entry
);
7608 add_insert_extent_rollback(rollback
, fcb
, newext3
);
7610 c
= get_chunk_from_address(fcb
->Vcb
, ed2
->address
);
7613 ERR("get_chunk_from_address(%llx) failed\n", ed2
->address
);
7615 Status
= update_changed_extent_ref(fcb
->Vcb
, c
, ed2
->address
, ed2
->size
, fcb
->subvol
->id
, fcb
->inode
, ext
->offset
- ed2
->offset
, 2,
7616 fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
, ed2
->size
, Irp
);
7618 if (!NT_SUCCESS(Status
)) {
7619 ERR("update_changed_extent_ref returned %08x\n", Status
);
7624 remove_fcb_extent(fcb
, ext
, rollback
);
7627 return STATUS_SUCCESS
;
7630 NTSTATUS
do_write_file(fcb
* fcb
, UINT64 start
, UINT64 end_data
, void* data
, LIST_ENTRY
* changed_sector_list
, PIRP Irp
, LIST_ENTRY
* rollback
) {
7632 LIST_ENTRY
*le
, *le2
;
7633 UINT64 written
= 0, length
= end_data
- start
;
7634 UINT64 last_cow_start
;
7635 #ifdef DEBUG_PARANOID
7641 le
= fcb
->extents
.Flink
;
7642 while (le
!= &fcb
->extents
) {
7643 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
7648 EXTENT_DATA
* ed
= ext
->data
;
7649 EXTENT_DATA2
* ed2
= ed
->type
== EXTENT_TYPE_INLINE
? NULL
: (EXTENT_DATA2
*)ed
->data
;
7653 len
= ed
->type
== EXTENT_TYPE_INLINE
? ed
->decoded_size
: ed2
->num_bytes
;
7655 if (ext
->offset
+ len
<= start
)
7658 if (ext
->offset
> start
+ written
+ length
)
7661 nocow
= (ext
->unique
&& fcb
->inode_item
.flags
& BTRFS_INODE_NODATACOW
) || ed
->type
== EXTENT_TYPE_PREALLOC
;
7664 if (max(last_cow_start
, start
+ written
) < ext
->offset
) {
7665 UINT64 start_write
= max(last_cow_start
, start
+ written
);
7667 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, ext
->offset
, Irp
, rollback
);
7668 if (!NT_SUCCESS(Status
)) {
7669 ERR("excise_extents returned %08x\n", Status
);
7673 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, ext
->offset
- start_write
, data
, changed_sector_list
, Irp
, rollback
);
7674 if (!NT_SUCCESS(Status
)) {
7675 ERR("insert_extent returned %08x\n", Status
);
7679 written
+= ext
->offset
- start_write
;
7680 length
-= ext
->offset
- start_write
;
7686 if (ed
->type
== EXTENT_TYPE_REGULAR
) {
7687 UINT64 writeaddr
= ed2
->address
+ ed2
->offset
+ start
+ written
- ext
->offset
;
7688 UINT64 write_len
= min(len
, length
);
7690 TRACE("doing non-COW write to %llx\n", writeaddr
);
7692 Status
= write_data_complete(fcb
->Vcb
, writeaddr
, (UINT8
*)data
+ written
, write_len
, Irp
, NULL
);
7693 if (!NT_SUCCESS(Status
)) {
7694 ERR("write_data_complete returned %08x\n", Status
);
7698 if (changed_sector_list
) {
7702 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(changed_sector
), ALLOC_TAG
);
7704 ERR("out of memory\n");
7705 return STATUS_INSUFFICIENT_RESOURCES
;
7708 sc
->ol
.key
= writeaddr
;
7709 sc
->length
= write_len
/ fcb
->Vcb
->superblock
.sector_size
;
7710 sc
->deleted
= FALSE
;
7712 sc
->checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * sc
->length
, ALLOC_TAG
);
7713 if (!sc
->checksums
) {
7714 ERR("out of memory\n");
7716 return STATUS_INSUFFICIENT_RESOURCES
;
7719 for (i
= 0; i
< sc
->length
; i
++) {
7720 sc
->checksums
[i
] = ~calc_crc32c(0xffffffff, (UINT8
*)data
+ written
+ (i
* fcb
->Vcb
->superblock
.sector_size
), fcb
->Vcb
->superblock
.sector_size
);
7723 insert_into_ordered_list(changed_sector_list
, &sc
->ol
);
7726 written
+= write_len
;
7727 length
-= write_len
;
7731 } else if (ed
->type
== EXTENT_TYPE_PREALLOC
) {
7734 Status
= do_write_file_prealloc(fcb
, ext
, start
+ written
, end_data
, (UINT8
*)data
+ written
, &write_len
,
7735 changed_sector_list
, Irp
, rollback
);
7736 if (!NT_SUCCESS(Status
)) {
7737 ERR("do_write_file_prealloc returned %08x\n", Status
);
7741 written
+= write_len
;
7742 length
-= write_len
;
7748 last_cow_start
= ext
->offset
+ len
;
7757 UINT64 start_write
= max(last_cow_start
, start
+ written
);
7759 Status
= excise_extents(fcb
->Vcb
, fcb
, start_write
, end_data
, Irp
, rollback
);
7760 if (!NT_SUCCESS(Status
)) {
7761 ERR("excise_extents returned %08x\n", Status
);
7765 Status
= insert_extent(fcb
->Vcb
, fcb
, start_write
, end_data
- start_write
, data
, changed_sector_list
, Irp
, rollback
);
7766 if (!NT_SUCCESS(Status
)) {
7767 ERR("insert_extent returned %08x\n", Status
);
7772 // FIXME - make extending work again (here?)
7773 // FIXME - make maximum extent size 128 MB again (here?)
7775 #ifdef DEBUG_PARANOID
7776 last_off
= 0xffffffffffffffff;
7778 le
= fcb
->extents
.Flink
;
7779 while (le
!= &fcb
->extents
) {
7780 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
7783 if (ext
->offset
== last_off
) {
7784 ERR("offset %llx duplicated\n", ext
->offset
);
7786 } else if (ext
->offset
< last_off
&& last_off
!= 0xffffffffffffffff) {
7787 ERR("offsets out of order\n");
7791 last_off
= ext
->offset
;
7798 fcb
->extents_changed
= TRUE
;
7799 mark_fcb_dirty(fcb
);
7801 return STATUS_SUCCESS
;
7804 NTSTATUS
write_compressed(fcb
* fcb
, UINT64 start_data
, UINT64 end_data
, void* data
, LIST_ENTRY
* changed_sector_list
, PIRP Irp
, LIST_ENTRY
* rollback
) {
7808 for (i
= 0; i
< sector_align(end_data
- start_data
, COMPRESSED_EXTENT_SIZE
) / COMPRESSED_EXTENT_SIZE
; i
++) {
7812 s2
= start_data
+ (i
* COMPRESSED_EXTENT_SIZE
);
7813 e2
= min(s2
+ COMPRESSED_EXTENT_SIZE
, end_data
);
7815 Status
= write_compressed_bit(fcb
, s2
, e2
, (UINT8
*)data
+ (i
* COMPRESSED_EXTENT_SIZE
), &compressed
, changed_sector_list
, Irp
, rollback
);
7817 if (!NT_SUCCESS(Status
)) {
7818 ERR("write_compressed_bit returned %08x\n", Status
);
7822 // If the first 128 KB of a file is incompressible, we set the nocompress flag so we don't
7823 // bother with the rest of it.
7824 if (s2
== 0 && e2
== COMPRESSED_EXTENT_SIZE
&& !compressed
&& !fcb
->Vcb
->options
.compress_force
) {
7825 fcb
->inode_item
.flags
|= BTRFS_INODE_NOCOMPRESS
;
7826 mark_fcb_dirty(fcb
);
7828 // write subsequent data non-compressed
7829 if (e2
< end_data
) {
7830 Status
= do_write_file(fcb
, e2
, end_data
, (UINT8
*)data
+ e2
, changed_sector_list
, Irp
, rollback
);
7832 if (!NT_SUCCESS(Status
)) {
7833 ERR("do_write_file returned %08x\n", Status
);
7838 return STATUS_SUCCESS
;
7842 return STATUS_SUCCESS
;
7845 NTSTATUS
write_file2(device_extension
* Vcb
, PIRP Irp
, LARGE_INTEGER offset
, void* buf
, ULONG
* length
, BOOL paging_io
, BOOL no_cache
,
7846 BOOL wait
, BOOL deferred_write
, LIST_ENTRY
* rollback
) {
7847 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
7848 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
7850 UINT64 newlength
, start_data
, end_data
;
7854 LIST_ENTRY changed_sector_list
;
7856 BOOL changed_length
= FALSE
, nocsum
/*, lazy_writer = FALSE, write_eof = FALSE*/;
7863 BOOL paging_lock
= FALSE
, fcb_lock
= FALSE
, tree_lock
= FALSE
, pagefile
;
7866 TRACE("(%p, %p, %llx, %p, %x, %u, %u)\n", Vcb
, FileObject
, offset
.QuadPart
, buf
, *length
, paging_io
, no_cache
);
7869 WARN("returning success for zero-length write\n");
7870 return STATUS_SUCCESS
;
7874 ERR("error - FileObject was NULL\n");
7875 return STATUS_ACCESS_DENIED
;
7878 fcb
= FileObject
->FsContext
;
7879 ccb
= FileObject
->FsContext2
;
7880 fileref
= ccb
? ccb
->fileref
: NULL
;
7882 if (fcb
->type
!= BTRFS_TYPE_FILE
&& fcb
->type
!= BTRFS_TYPE_SYMLINK
) {
7883 WARN("tried to write to something other than a file or symlink (inode %llx, type %u, %p, %p)\n", fcb
->inode
, fcb
->type
, &fcb
->type
, fcb
);
7884 return STATUS_INVALID_DEVICE_REQUEST
;
7887 if (offset
.LowPart
== FILE_WRITE_TO_END_OF_FILE
&& offset
.HighPart
== -1) {
7888 offset
= fcb
->Header
.FileSize
;
7889 // write_eof = TRUE;
7892 TRACE("fcb->Header.Flags = %x\n", fcb
->Header
.Flags
);
7894 if (!no_cache
&& !CcCanIWrite(FileObject
, *length
, wait
, deferred_write
))
7895 return STATUS_PENDING
;
7897 if (!wait
&& no_cache
)
7898 return STATUS_PENDING
;
7900 if (no_cache
&& !paging_io
&& FileObject
->SectionObjectPointer
->DataSectionObject
) {
7901 IO_STATUS_BLOCK iosb
;
7903 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
7905 CcFlushCache(FileObject
->SectionObjectPointer
, &offset
, *length
, &iosb
);
7907 if (!NT_SUCCESS(iosb
.Status
)) {
7908 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
7909 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
7915 CcPurgeCacheSection(FileObject
->SectionObjectPointer
, &offset
, *length
, FALSE
);
7919 if (!ExAcquireResourceSharedLite(fcb
->Header
.PagingIoResource
, wait
)) {
7920 Status
= STATUS_PENDING
;
7926 pagefile
= fcb
->Header
.Flags2
& FSRTL_FLAG2_IS_PAGING_FILE
&& paging_io
;
7928 if (!pagefile
&& !ExIsResourceAcquiredExclusiveLite(&Vcb
->tree_lock
)) {
7929 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
7930 Status
= STATUS_PENDING
;
7936 if (no_cache
&& !ExIsResourceAcquiredExclusiveLite(fcb
->Header
.Resource
)) {
7937 if (!ExAcquireResourceExclusiveLite(fcb
->Header
.Resource
, wait
)) {
7938 Status
= STATUS_PENDING
;
7944 nocsum
= fcb
->ads
? TRUE
: fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
;
7946 newlength
= fcb
->ads
? fcb
->adsdata
.Length
: fcb
->inode_item
.st_size
;
7951 TRACE("newlength = %llx\n", newlength
);
7953 // if (KeGetCurrentThread() == fcb->lazy_writer_thread) {
7954 // ERR("lazy writer on the TV\n");
7955 // lazy_writer = TRUE;
7958 if (offset
.QuadPart
+ *length
> newlength
) {
7960 if (offset
.QuadPart
>= newlength
) {
7961 TRACE("paging IO tried to write beyond end of file (file size = %llx, offset = %llx, length = %x)\n", newlength
, offset
.QuadPart
, *length
);
7962 TRACE("filename %S\n", file_desc(FileObject
));
7963 TRACE("FileObject: AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx\n",
7964 fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
7965 Status
= STATUS_SUCCESS
;
7969 *length
= newlength
- offset
.QuadPart
;
7971 newlength
= offset
.QuadPart
+ *length
;
7972 changed_length
= TRUE
;
7974 TRACE("extending length to %llx\n", newlength
);
7978 make_inline
= fcb
->ads
? FALSE
: newlength
<= fcb
->Vcb
->options
.max_inline
;
7980 if (changed_length
) {
7981 if (newlength
> fcb
->Header
.AllocationSize
.QuadPart
) {
7983 // We need to acquire the tree lock if we don't have it already -
7984 // we can't give an inline file proper extents at the same as we're
7986 if (!ExAcquireResourceSharedLite(&Vcb
->tree_lock
, wait
)) {
7987 Status
= STATUS_PENDING
;
7993 Status
= extend_file(fcb
, fileref
, newlength
, FALSE
, Irp
, rollback
);
7994 if (!NT_SUCCESS(Status
)) {
7995 ERR("extend_file returned %08x\n", Status
);
7998 } else if (!fcb
->ads
)
7999 fcb
->inode_item
.st_size
= newlength
;
8001 fcb
->Header
.FileSize
.QuadPart
= newlength
;
8002 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
8004 TRACE("AllocationSize = %llx\n", fcb
->Header
.AllocationSize
.QuadPart
);
8005 TRACE("FileSize = %llx\n", fcb
->Header
.FileSize
.QuadPart
);
8006 TRACE("ValidDataLength = %llx\n", fcb
->Header
.ValidDataLength
.QuadPart
);
8010 if (!FileObject
->PrivateCacheMap
|| changed_length
) {
8013 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
8014 ccfs
.FileSize
= fcb
->Header
.FileSize
;
8015 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
8017 if (!FileObject
->PrivateCacheMap
) {
8018 TRACE("calling CcInitializeCacheMap...\n");
8019 CcInitializeCacheMap(FileObject
, &ccfs
, FALSE
, cache_callbacks
, FileObject
);
8021 CcSetReadAheadGranularity(FileObject
, READ_AHEAD_GRANULARITY
);
8024 CcSetFileSizes(FileObject
, &ccfs
);
8027 if (IrpSp
->MinorFunction
& IRP_MN_MDL
) {
8028 CcPrepareMdlWrite(FileObject
, &offset
, *length
, &Irp
->MdlAddress
, &Irp
->IoStatus
);
8030 Status
= Irp
->IoStatus
.Status
;
8033 TRACE("CcCopyWrite(%p, %llx, %x, %u, %p)\n", FileObject
, offset
.QuadPart
, *length
, wait
, buf
);
8034 if (!CcCopyWrite(FileObject
, &offset
, *length
, wait
, buf
)) {
8035 Status
= STATUS_PENDING
;
8038 TRACE("CcCopyWrite finished\n");
8041 Status
= STATUS_SUCCESS
;
8046 if (changed_length
) {
8049 if (newlength
> fcb
->adsmaxlen
) {
8050 ERR("error - xattr too long (%llu > %u)\n", newlength
, fcb
->adsmaxlen
);
8051 Status
= STATUS_DISK_FULL
;
8055 data2
= ExAllocatePoolWithTag(PagedPool
, newlength
, ALLOC_TAG
);
8057 ERR("out of memory\n");
8058 Status
= STATUS_INSUFFICIENT_RESOURCES
;
8062 if (fcb
->adsdata
.Buffer
) {
8063 RtlCopyMemory(data2
, fcb
->adsdata
.Buffer
, fcb
->adsdata
.Length
);
8064 ExFreePool(fcb
->adsdata
.Buffer
);
8067 if (newlength
> fcb
->adsdata
.Length
)
8068 RtlZeroMemory(&data2
[fcb
->adsdata
.Length
], newlength
- fcb
->adsdata
.Length
);
8071 fcb
->adsdata
.Buffer
= data2
;
8072 fcb
->adsdata
.Length
= fcb
->adsdata
.MaximumLength
= newlength
;
8074 fcb
->Header
.AllocationSize
.QuadPart
= newlength
;
8075 fcb
->Header
.FileSize
.QuadPart
= newlength
;
8076 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
8080 RtlCopyMemory(&fcb
->adsdata
.Buffer
[offset
.QuadPart
], buf
, *length
);
8082 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
8084 mark_fcb_dirty(fcb
);
8087 mark_fileref_dirty(fileref
);
8089 BOOL compress
= write_fcb_compressed(fcb
);
8093 end_data
= sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
);
8094 bufhead
= sizeof(EXTENT_DATA
) - 1;
8095 } else if (compress
) {
8096 start_data
= offset
.QuadPart
& ~(UINT64
)(COMPRESSED_EXTENT_SIZE
- 1);
8097 end_data
= min(sector_align(offset
.QuadPart
+ *length
, COMPRESSED_EXTENT_SIZE
),
8098 sector_align(newlength
, fcb
->Vcb
->superblock
.sector_size
));
8101 start_data
= offset
.QuadPart
& ~(UINT64
)(fcb
->Vcb
->superblock
.sector_size
- 1);
8102 end_data
= sector_align(offset
.QuadPart
+ *length
, fcb
->Vcb
->superblock
.sector_size
);
8106 fcb
->Header
.ValidDataLength
.QuadPart
= newlength
;
8107 TRACE("fcb %p FileSize = %llx\n", fcb
, fcb
->Header
.FileSize
.QuadPart
);
8109 data
= ExAllocatePoolWithTag(PagedPool
, end_data
- start_data
+ bufhead
, ALLOC_TAG
);
8111 ERR("out of memory\n");
8112 Status
= STATUS_INSUFFICIENT_RESOURCES
;
8116 RtlZeroMemory(data
+ bufhead
, end_data
- start_data
);
8118 TRACE("start_data = %llx\n", start_data
);
8119 TRACE("end_data = %llx\n", end_data
);
8121 if (offset
.QuadPart
> start_data
|| offset
.QuadPart
+ *length
< end_data
) {
8122 if (changed_length
) {
8123 if (fcb
->inode_item
.st_size
> start_data
)
8124 Status
= read_file(fcb
, data
+ bufhead
, start_data
, fcb
->inode_item
.st_size
- start_data
, NULL
, Irp
);
8126 Status
= STATUS_SUCCESS
;
8128 Status
= read_file(fcb
, data
+ bufhead
, start_data
, end_data
- start_data
, NULL
, Irp
);
8130 if (!NT_SUCCESS(Status
)) {
8131 ERR("read_file returned %08x\n", Status
);
8137 RtlCopyMemory(data
+ bufhead
+ offset
.QuadPart
- start_data
, buf
, *length
);
8140 InitializeListHead(&changed_sector_list
);
8143 Status
= excise_extents(fcb
->Vcb
, fcb
, start_data
, end_data
, Irp
, rollback
);
8144 if (!NT_SUCCESS(Status
)) {
8145 ERR("error - excise_extents returned %08x\n", Status
);
8150 ed2
= (EXTENT_DATA
*)data
;
8151 ed2
->generation
= fcb
->Vcb
->superblock
.generation
;
8152 ed2
->decoded_size
= newlength
;
8153 ed2
->compression
= BTRFS_COMPRESSION_NONE
;
8154 ed2
->encryption
= BTRFS_ENCRYPTION_NONE
;
8155 ed2
->encoding
= BTRFS_ENCODING_NONE
;
8156 ed2
->type
= EXTENT_TYPE_INLINE
;
8158 if (!add_extent_to_fcb(fcb
, 0, ed2
, sizeof(EXTENT_DATA
) - 1 + newlength
, FALSE
, rollback
)) {
8159 ERR("add_extent_to_fcb failed\n");
8161 Status
= STATUS_INTERNAL_ERROR
;
8165 fcb
->inode_item
.st_blocks
+= newlength
;
8166 } else if (compress
) {
8167 Status
= write_compressed(fcb
, start_data
, end_data
, data
, nocsum
? NULL
: &changed_sector_list
, Irp
, rollback
);
8169 if (!NT_SUCCESS(Status
)) {
8170 ERR("write_compressed returned %08x\n", Status
);
8177 Status
= do_write_file(fcb
, start_data
, end_data
, data
, nocsum
? NULL
: &changed_sector_list
, Irp
, rollback
);
8179 if (!NT_SUCCESS(Status
)) {
8180 ERR("do_write_file returned %08x\n", Status
);
8190 KeQuerySystemTime(&time
);
8191 win_time_to_unix(time
, &now
);
8193 // ERR("no_cache = %s, FileObject->PrivateCacheMap = %p\n", no_cache ? "TRUE" : "FALSE", FileObject->PrivateCacheMap);
8196 // if (!FileObject->PrivateCacheMap) {
8197 // CC_FILE_SIZES ccfs;
8199 // ccfs.AllocationSize = fcb->Header.AllocationSize;
8200 // ccfs.FileSize = fcb->Header.FileSize;
8201 // ccfs.ValidDataLength = fcb->Header.ValidDataLength;
8203 // TRACE("calling CcInitializeCacheMap...\n");
8204 // CcInitializeCacheMap(FileObject, &ccfs, FALSE, cache_callbacks, fcb);
8206 // changed_length = FALSE;
8211 if (fileref
&& fileref
->parent
)
8212 origii
= &fileref
->parent
->fcb
->inode_item
;
8214 ERR("no parent fcb found for stream\n");
8215 Status
= STATUS_INTERNAL_ERROR
;
8219 origii
= &fcb
->inode_item
;
8221 origii
->transid
= Vcb
->superblock
.generation
;
8223 origii
->st_ctime
= now
;
8226 if (changed_length
) {
8227 TRACE("setting st_size to %llx\n", newlength
);
8228 origii
->st_size
= newlength
;
8229 filter
|= FILE_NOTIFY_CHANGE_SIZE
;
8232 origii
->st_mtime
= now
;
8233 filter
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
8236 mark_fcb_dirty(fcb
->ads
? fileref
->parent
->fcb
: fcb
);
8240 ExAcquireResourceExclusiveLite(&Vcb
->checksum_lock
, TRUE
);
8241 commit_checksum_changes(Vcb
, &changed_sector_list
);
8242 ExReleaseResourceLite(&Vcb
->checksum_lock
);
8245 if (changed_length
) {
8248 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
8249 ccfs
.FileSize
= fcb
->Header
.FileSize
;
8250 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
8252 CcSetFileSizes(FileObject
, &ccfs
);
8255 // FIXME - make sure this still called if STATUS_PENDING and async
8257 // if (!CcCopyWrite(FileObject, &offset, *length, TRUE, buf)) {
8258 // ERR("CcCopyWrite failed.\n");
8262 fcb
->subvol
->root_item
.ctransid
= Vcb
->superblock
.generation
;
8263 fcb
->subvol
->root_item
.ctime
= now
;
8265 Status
= STATUS_SUCCESS
;
8268 send_notification_fcb(fcb
->ads
? fileref
->parent
: fileref
, filter
, FILE_ACTION_MODIFIED
);
8271 if (NT_SUCCESS(Status
) && FileObject
->Flags
& FO_SYNCHRONOUS_IO
&& !paging_io
) {
8272 TRACE("CurrentByteOffset was: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
8273 FileObject
->CurrentByteOffset
.QuadPart
= offset
.QuadPart
+ (NT_SUCCESS(Status
) ? *length
: 0);
8274 TRACE("CurrentByteOffset now: %llx\n", FileObject
->CurrentByteOffset
.QuadPart
);
8278 ExReleaseResourceLite(fcb
->Header
.Resource
);
8281 ExReleaseResourceLite(&Vcb
->tree_lock
);
8284 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
8289 NTSTATUS
write_file(device_extension
* Vcb
, PIRP Irp
, BOOL wait
, BOOL deferred_write
) {
8290 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
8293 LARGE_INTEGER offset
= IrpSp
->Parameters
.Write
.ByteOffset
;
8294 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
8295 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
8296 // BOOL locked = FALSE;
8297 // LARGE_INTEGER freq, time1, time2;
8298 LIST_ENTRY rollback
;
8300 InitializeListHead(&rollback
);
8302 // time1 = KeQueryPerformanceCounter(&freq);
8306 Irp
->IoStatus
.Information
= 0;
8308 TRACE("offset = %llx\n", offset
.QuadPart
);
8309 TRACE("length = %x\n", IrpSp
->Parameters
.Write
.Length
);
8311 if (!Irp
->AssociatedIrp
.SystemBuffer
) {
8312 buf
= map_user_buffer(Irp
);
8314 if (Irp
->MdlAddress
&& !buf
) {
8315 ERR("MmGetSystemAddressForMdlSafe returned NULL\n");
8316 Status
= STATUS_INSUFFICIENT_RESOURCES
;
8320 buf
= Irp
->AssociatedIrp
.SystemBuffer
;
8322 TRACE("buf = %p\n", buf
);
8324 // if (Irp->Flags & IRP_NOCACHE) {
8325 // if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, wait)) {
8326 // Status = STATUS_PENDING;
8332 if (fcb
&& !(Irp
->Flags
& IRP_PAGING_IO
) && !FsRtlCheckLockForWriteAccess(&fcb
->lock
, Irp
)) {
8333 WARN("tried to write to locked region\n");
8334 Status
= STATUS_FILE_LOCK_CONFLICT
;
8338 // ERR("Irp->Flags = %x\n", Irp->Flags);
8339 Status
= write_file2(Vcb
, Irp
, offset
, buf
, &IrpSp
->Parameters
.Write
.Length
, Irp
->Flags
& IRP_PAGING_IO
, Irp
->Flags
& IRP_NOCACHE
,
8340 wait
, deferred_write
, &rollback
);
8342 if (Status
== STATUS_PENDING
)
8344 else if (!NT_SUCCESS(Status
)) {
8345 ERR("write_file2 returned %08x\n", Status
);
8350 // Status = consider_write(Vcb);
8352 if (NT_SUCCESS(Status
)) {
8353 Irp
->IoStatus
.Information
= IrpSp
->Parameters
.Write
.Length
;
8355 #ifdef DEBUG_PARANOID
8357 // check_extents_consistent(Vcb, FileObject->FsContext); // TESTING
8359 // check_extent_tree_consistent(Vcb);
8365 if (NT_SUCCESS(Status
))
8366 clear_rollback(&rollback
);
8368 do_rollback(Vcb
, &rollback
);
8370 // ExReleaseResourceLite(&Vcb->tree_lock);
8373 // time2 = KeQueryPerformanceCounter(NULL);
8375 // ERR("time = %u (freq = %u)\n", (UINT32)(time2.QuadPart - time1.QuadPart), (UINT32)freq.QuadPart);
8380 NTSTATUS STDCALL
drv_write(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
8383 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
8384 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
8385 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
8386 fcb
* fcb
= FileObject
? FileObject
->FsContext
: NULL
;
8387 ccb
* ccb
= FileObject
? FileObject
->FsContext2
: NULL
;
8389 FsRtlEnterFileSystem();
8391 top_level
= is_top_level(Irp
);
8393 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
8394 Status
= part0_passthrough(DeviceObject
, Irp
);
8399 ERR("fcb was NULL\n");
8400 Status
= STATUS_INVALID_PARAMETER
;
8405 ERR("ccb was NULL\n");
8406 Status
= STATUS_INVALID_PARAMETER
;
8410 if (fcb
->subvol
->root_item
.flags
& BTRFS_SUBVOL_READONLY
) {
8411 Status
= STATUS_ACCESS_DENIED
;
8415 if (Vcb
->readonly
) {
8416 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
8420 if (Irp
->RequestorMode
== UserMode
&& !(ccb
->access
& (FILE_WRITE_DATA
| FILE_APPEND_DATA
))) {
8421 WARN("insufficient permissions\n");
8422 Status
= STATUS_ACCESS_DENIED
;
8426 // ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
8429 if (IrpSp
->MinorFunction
& IRP_MN_COMPLETE
) {
8430 CcMdlWriteComplete(IrpSp
->FileObject
, &IrpSp
->Parameters
.Write
.ByteOffset
, Irp
->MdlAddress
);
8432 Irp
->MdlAddress
= NULL
;
8433 Status
= STATUS_SUCCESS
;
8435 Status
= write_file(Vcb
, Irp
, IoIsOperationSynchronous(Irp
), FALSE
);
8437 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
8438 Status
= _SEH2_GetExceptionCode();
8442 Irp
->IoStatus
.Status
= Status
;
8444 TRACE("wrote %u bytes\n", Irp
->IoStatus
.Information
);
8446 if (Status
!= STATUS_PENDING
)
8447 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
8449 IoMarkIrpPending(Irp
);
8451 if (!add_thread_job(Vcb
, Irp
))
8452 do_write_job(Vcb
, Irp
);
8457 IoSetTopLevelIrp(NULL
);
8459 FsRtlExitFileSystem();
8461 TRACE("returning %08x\n", Status
);