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/>. */
22 #include "btrfs_drv.h"
37 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
38 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
39 #define COMPAT_RO_SUPPORTED 0
41 static WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
42 static WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
44 PDRIVER_OBJECT drvobj
;
45 PDEVICE_OBJECT devobj
;
47 BOOL have_sse42
= FALSE
;
50 LIST_ENTRY uid_map_list
;
53 ERESOURCE global_loading_lock
;
54 UINT32 debug_log_level
= 0;
55 BOOL log_started
= FALSE
;
56 UNICODE_STRING log_device
, log_file
;
59 PFILE_OBJECT comfo
= NULL
;
60 PDEVICE_OBJECT comdo
= NULL
;
61 HANDLE log_handle
= NULL
;
64 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
);
72 static NTSTATUS STDCALL
dbg_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
73 read_context
* context
= conptr
;
75 // DbgPrint("dbg_completion\n");
77 context
->iosb
= Irp
->IoStatus
;
78 KeSetEvent(&context
->Event
, 0, FALSE
);
80 // return STATUS_SUCCESS;
81 return STATUS_MORE_PROCESSING_REQUIRED
;
84 #ifdef DEBUG_LONG_MESSAGES
85 void STDCALL
_debug_message(const char* func
, UINT8 priority
, const char* file
, unsigned int line
, char* s
, ...) {
87 void STDCALL
_debug_message(const char* func
, UINT8 priority
, char* s
, ...) {
90 PIO_STACK_LOCATION IrpSp
;
94 char *buf2
= NULL
, *buf
;
95 read_context
* context
= NULL
;
98 if (log_started
&& priority
> debug_log_level
)
101 buf2
= ExAllocatePoolWithTag(NonPagedPool
, 1024, ALLOC_TAG
);
104 DbgPrint("Couldn't allocate buffer in debug_message\n");
108 #ifdef DEBUG_LONG_MESSAGES
109 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func
, file
, line
);
111 sprintf(buf2
, "%p:%s:", PsGetCurrentThreadId(), func
);
113 buf
= &buf2
[strlen(buf2
)];
116 vsprintf(buf
, s
, ap
);
118 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
120 } else if (log_device
.Length
> 0) {
122 DbgPrint("comdo is NULL :-(\n");
127 length
= (UINT32
)strlen(buf2
);
129 offset
.u
.LowPart
= 0;
130 offset
.u
.HighPart
= 0;
132 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
134 DbgPrint("Couldn't allocate context in debug_message\n");
138 RtlZeroMemory(context
, sizeof(read_context
));
140 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
142 // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
144 Irp
= IoAllocateIrp(comdo
->StackSize
, FALSE
);
147 DbgPrint("IoAllocateIrp failed\n");
151 IrpSp
= IoGetNextIrpStackLocation(Irp
);
152 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
154 if (comdo
->Flags
& DO_BUFFERED_IO
) {
155 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
157 Irp
->Flags
= IRP_BUFFERED_IO
;
158 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
159 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, FALSE
, FALSE
, NULL
);
160 if (!Irp
->MdlAddress
) {
161 DbgPrint("IoAllocateMdl failed\n");
165 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
167 Irp
->UserBuffer
= buf2
;
170 IrpSp
->Parameters
.Write
.Length
= length
;
171 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
173 Irp
->UserIosb
= &context
->iosb
;
175 Irp
->UserEvent
= &context
->Event
;
177 IoSetCompletionRoutine(Irp
, dbg_completion
, context
, TRUE
, TRUE
, TRUE
);
179 Status
= IoCallDriver(comdo
, Irp
);
181 if (Status
== STATUS_PENDING
) {
182 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
183 Status
= context
->iosb
.Status
;
186 if (comdo
->Flags
& DO_DIRECT_IO
) {
187 MmUnlockPages(Irp
->MdlAddress
);
188 IoFreeMdl(Irp
->MdlAddress
);
191 if (!NT_SUCCESS(Status
)) {
192 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
198 } else if (log_handle
!= NULL
) {
199 IO_STATUS_BLOCK iosb
;
201 length
= (UINT32
)strlen(buf2
);
203 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
205 if (!NT_SUCCESS(Status
)) {
206 DbgPrint("failed to write to file - error %08x\n", Status
);
221 ULONG
sector_align( ULONG NumberToBeAligned
, ULONG Alignment
)
223 if( Alignment
& ( Alignment
- 1 ) )
226 // Alignment not a power of 2
229 return NumberToBeAligned
;
231 if( ( NumberToBeAligned
& ( Alignment
- 1 ) ) != 0 )
233 NumberToBeAligned
= NumberToBeAligned
+ Alignment
;
234 NumberToBeAligned
= NumberToBeAligned
& ( ~ (Alignment
-1) );
236 return NumberToBeAligned
;
239 int keycmp(const KEY
* key1
, const KEY
* key2
) {
240 if (key1
->obj_id
< key2
->obj_id
) {
242 } else if (key1
->obj_id
> key2
->obj_id
) {
246 if (key1
->obj_type
< key2
->obj_type
) {
248 } else if (key1
->obj_type
> key2
->obj_type
) {
252 if (key1
->offset
< key2
->offset
) {
254 } else if (key1
->offset
> key2
->offset
) {
261 BOOL
is_top_level(PIRP Irp
) {
262 if (!IoGetTopLevelIrp()) {
263 IoSetTopLevelIrp(Irp
);
270 static void STDCALL
DriverUnload(PDRIVER_OBJECT DriverObject
) {
271 UNICODE_STRING dosdevice_nameW
;
273 ERR("DriverUnload\n");
277 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
279 dosdevice_nameW
.Buffer
= dosdevice_name
;
280 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
282 IoDeleteSymbolicLink(&dosdevice_nameW
);
283 IoDeleteDevice(DriverObject
->DeviceObject
);
285 while (!IsListEmpty(&uid_map_list
)) {
286 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
287 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
294 // FIXME - free volumes and their devpaths
298 ObDereferenceObject(comfo
);
304 ExDeleteResourceLite(&global_loading_lock
);
306 if (log_device
.Buffer
)
307 ExFreePool(log_device
.Buffer
);
310 ExFreePool(log_file
.Buffer
);
313 BOOL STDCALL
get_last_inode(device_extension
* Vcb
, root
* r
) {
315 traverse_ptr tp
, prev_tp
;
319 searchkey
.obj_id
= 0xffffffffffffffff;
320 searchkey
.obj_type
= 0xff;
321 searchkey
.offset
= 0xffffffffffffffff;
323 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
);
324 if (!NT_SUCCESS(Status
)) {
325 ERR("error - find_item returned %08x\n", Status
);
329 while (find_prev_item(Vcb
, &tp
, &prev_tp
, FALSE
)) {
330 free_traverse_ptr(&tp
);
333 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
335 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
) {
336 r
->lastinode
= tp
.item
->key
.obj_id
;
337 free_traverse_ptr(&tp
);
338 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
343 free_traverse_ptr(&tp
);
345 r
->lastinode
= SUBVOL_ROOT_INODE
;
347 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
352 BOOL STDCALL
get_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
** data
, UINT16
* datalen
) {
359 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
361 searchkey
.obj_id
= inode
;
362 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
363 searchkey
.offset
= crc32
;
365 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
366 if (!NT_SUCCESS(Status
)) {
367 ERR("error - find_item returned %08x\n", Status
);
371 if (keycmp(&tp
.item
->key
, &searchkey
)) {
372 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
373 free_traverse_ptr(&tp
);
377 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
378 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
));
379 free_traverse_ptr(&tp
);
383 xa
= (DIR_ITEM
*)tp
.item
->data
;
384 size
= tp
.item
->size
;
387 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
388 WARN("(%llx,%x,%llx) is truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
389 free_traverse_ptr(&tp
);
393 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
394 TRACE("found xattr %s in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
399 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
401 ERR("out of memory\n");
402 free_traverse_ptr(&tp
);
406 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
410 free_traverse_ptr(&tp
);
414 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
418 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
423 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
425 free_traverse_ptr(&tp
);
430 NTSTATUS STDCALL
set_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
* data
, UINT16 datalen
, LIST_ENTRY
* rollback
) {
437 TRACE("(%p, %llx, %llx, %s, %08x, %p, %u)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
439 searchkey
.obj_id
= inode
;
440 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
441 searchkey
.offset
= crc32
;
443 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
444 if (!NT_SUCCESS(Status
)) {
445 ERR("error - find_item returned %08x\n", Status
);
449 xasize
= sizeof(DIR_ITEM
) - 1 + (ULONG
)strlen(name
) + datalen
;
451 if (!keycmp(&tp
.item
->key
, &searchkey
)) { // key exists
453 ULONG size
= tp
.item
->size
;
455 xa
= (DIR_ITEM
*)tp
.item
->data
;
457 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
458 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
));
463 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
464 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
468 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
470 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
474 newdata
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ xasize
- oldxasize
, ALLOC_TAG
);
476 ERR("out of memory\n");
477 free_traverse_ptr(&tp
);
478 return STATUS_INSUFFICIENT_RESOURCES
;
481 pos
= (UINT8
*)xa
- tp
.item
->data
;
482 if (pos
+ oldxasize
< tp
.item
->size
) { // copy after changed xattr
483 RtlCopyMemory(newdata
+ pos
+ xasize
, tp
.item
->data
+ pos
+ oldxasize
, tp
.item
->size
- pos
- oldxasize
);
486 if (pos
> 0) { // copy before changed xattr
487 RtlCopyMemory(newdata
, tp
.item
->data
, pos
);
488 xa
= (DIR_ITEM
*)(newdata
+ pos
);
490 xa
= (DIR_ITEM
*)newdata
;
493 xa
->key
.obj_type
= 0;
495 xa
->transid
= Vcb
->superblock
.generation
;
497 xa
->n
= (UINT16
)strlen(name
);
498 xa
->type
= BTRFS_TYPE_EA
;
499 RtlCopyMemory(xa
->name
, name
, strlen(name
));
500 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
502 delete_tree_item(Vcb
, &tp
, rollback
);
503 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, tp
.item
->size
+ xasize
- oldxasize
, NULL
, rollback
);
508 if (xa
->m
+ xa
->n
>= size
) { // FIXME - test this works
509 // not found, add to end of data
510 newdata
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ xasize
, ALLOC_TAG
);
512 ERR("out of memory\n");
513 free_traverse_ptr(&tp
);
514 return STATUS_INSUFFICIENT_RESOURCES
;
517 RtlCopyMemory(newdata
, tp
.item
->data
, tp
.item
->size
);
519 xa
= (DIR_ITEM
*)((UINT8
*)newdata
+ tp
.item
->size
);
521 xa
->key
.obj_type
= 0;
523 xa
->transid
= Vcb
->superblock
.generation
;
525 xa
->n
= (UINT16
)strlen(name
);
526 xa
->type
= BTRFS_TYPE_EA
;
527 RtlCopyMemory(xa
->name
, name
, strlen(name
));
528 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
530 delete_tree_item(Vcb
, &tp
, rollback
);
531 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, tp
.item
->size
+ xasize
, NULL
, rollback
);
535 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
541 // add new DIR_ITEM struct
543 xa
= ExAllocatePoolWithTag(PagedPool
, xasize
, ALLOC_TAG
);
545 ERR("out of memory\n");
546 free_traverse_ptr(&tp
);
547 return STATUS_INSUFFICIENT_RESOURCES
;
551 xa
->key
.obj_type
= 0;
553 xa
->transid
= Vcb
->superblock
.generation
;
555 xa
->n
= (UINT16
)strlen(name
);
556 xa
->type
= BTRFS_TYPE_EA
;
557 RtlCopyMemory(xa
->name
, name
, strlen(name
));
558 RtlCopyMemory(xa
->name
+ strlen(name
), data
, datalen
);
560 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, xa
, xasize
, NULL
, rollback
);
563 free_traverse_ptr(&tp
);
565 return STATUS_SUCCESS
;
568 BOOL STDCALL
delete_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, LIST_ENTRY
* rollback
) {
574 TRACE("(%p, %llx, %llx, %s, %08x)\n", Vcb
, subvol
->id
, inode
, name
, crc32
);
576 searchkey
.obj_id
= inode
;
577 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
578 searchkey
.offset
= crc32
;
580 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
581 if (!NT_SUCCESS(Status
)) {
582 ERR("error - find_item returned %08x\n", Status
);
586 if (!keycmp(&tp
.item
->key
, &searchkey
)) { // key exists
587 ULONG size
= tp
.item
->size
;
589 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
590 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
));
592 free_traverse_ptr(&tp
);
595 xa
= (DIR_ITEM
*)tp
.item
->data
;
600 if (size
< sizeof(DIR_ITEM
) || size
< sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
) {
601 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
602 free_traverse_ptr(&tp
);
607 oldxasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
609 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
611 UINT8
*newdata
, *dioff
;
613 newsize
= tp
.item
->size
- (sizeof(DIR_ITEM
) - 1 + xa
->n
+ xa
->m
);
615 delete_tree_item(Vcb
, &tp
, rollback
);
618 TRACE("xattr %s deleted\n", name
);
619 free_traverse_ptr(&tp
);
624 // FIXME - deleting collisions almost certainly works, but we should test it properly anyway
625 newdata
= ExAllocatePoolWithTag(PagedPool
, newsize
, ALLOC_TAG
);
627 ERR("out of memory\n");
628 free_traverse_ptr(&tp
);
632 if ((UINT8
*)xa
> tp
.item
->data
) {
633 RtlCopyMemory(newdata
, tp
.item
->data
, (UINT8
*)xa
- tp
.item
->data
);
634 dioff
= newdata
+ ((UINT8
*)xa
- tp
.item
->data
);
639 if ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
< tp
.item
->size
)
640 RtlCopyMemory(dioff
, &xa
->name
[xa
->n
+xa
->m
], tp
.item
->size
- ((UINT8
*)&xa
->name
[xa
->n
+xa
->m
] - tp
.item
->data
));
642 insert_tree_item(Vcb
, subvol
, inode
, TYPE_XATTR_ITEM
, crc32
, newdata
, newsize
, NULL
, rollback
);
644 free_traverse_ptr(&tp
);
649 if (xa
->m
+ xa
->n
>= size
) { // FIXME - test this works
650 WARN("xattr %s not found\n", name
);
651 free_traverse_ptr(&tp
);
655 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
661 WARN("xattr %s not found\n", name
);
662 free_traverse_ptr(&tp
);
668 NTSTATUS
add_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT32 crc32
, DIR_ITEM
* di
, ULONG disize
, LIST_ENTRY
* rollback
) {
674 searchkey
.obj_id
= inode
;
675 searchkey
.obj_type
= TYPE_DIR_ITEM
;
676 searchkey
.offset
= crc32
;
678 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
679 if (!NT_SUCCESS(Status
)) {
680 ERR("error - find_item returned %08x\n", Status
);
684 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
685 ULONG maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
687 if (tp
.item
->size
+ disize
> maxlen
) {
688 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp
.item
->size
, disize
, maxlen
);
689 free_traverse_ptr(&tp
);
690 return STATUS_INTERNAL_ERROR
;
693 di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ disize
, ALLOC_TAG
);
695 ERR("out of memory\n");
696 free_traverse_ptr(&tp
);
697 return STATUS_INSUFFICIENT_RESOURCES
;
700 if (tp
.item
->size
> 0)
701 RtlCopyMemory(di2
, tp
.item
->data
, tp
.item
->size
);
703 RtlCopyMemory(di2
+ tp
.item
->size
, di
, disize
);
705 delete_tree_item(Vcb
, &tp
, rollback
);
707 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di2
, tp
.item
->size
+ disize
, NULL
, rollback
);
711 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di
, disize
, NULL
, rollback
);
714 free_traverse_ptr(&tp
);
716 return STATUS_SUCCESS
;
719 UINT64
find_next_dir_index(device_extension
* Vcb
, root
* subvol
, UINT64 inode
) {
721 traverse_ptr tp
, prev_tp
;
725 searchkey
.obj_id
= inode
;
726 searchkey
.obj_type
= TYPE_DIR_INDEX
+ 1;
727 searchkey
.offset
= 0;
729 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
730 if (!NT_SUCCESS(Status
)) {
731 ERR("error - find_item returned %08x\n", Status
);
735 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
736 if (!find_prev_item(Vcb
, &tp
, &prev_tp
, FALSE
)) {
737 free_traverse_ptr(&tp
);
740 TRACE("moving back to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
744 if (tp
.item
->key
.obj_id
== searchkey
.obj_id
&& tp
.item
->key
.obj_type
== TYPE_DIR_INDEX
) {
745 dirpos
= tp
.item
->key
.offset
+ 1;
749 free_traverse_ptr(&tp
);
754 static NTSTATUS STDCALL
drv_close(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
756 PIO_STACK_LOCATION IrpSp
;
761 FsRtlEnterFileSystem();
763 top_level
= is_top_level(Irp
);
765 if (DeviceObject
== devobj
) {
766 TRACE("Closing file system\n");
767 Status
= STATUS_SUCCESS
;
771 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
773 // FIXME - unmount if called for volume
774 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
776 Status
= close_file(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
779 Irp
->IoStatus
.Status
= Status
;
780 Irp
->IoStatus
.Information
= 0;
782 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
785 IoSetTopLevelIrp(NULL
);
787 FsRtlExitFileSystem();
789 TRACE("returning %08x\n", Status
);
794 static NTSTATUS STDCALL
drv_write(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
798 FsRtlEnterFileSystem();
800 top_level
= is_top_level(Irp
);
802 // ERR("recursive = %s\n", Irp != IoGetTopLevelIrp() ? "TRUE" : "FALSE");
804 Status
= write_file(DeviceObject
, Irp
);
806 Irp
->IoStatus
.Status
= Status
;
808 TRACE("wrote %u bytes\n", Irp
->IoStatus
.Information
);
810 if (Status
!= STATUS_PENDING
)
811 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
814 IoSetTopLevelIrp(NULL
);
816 FsRtlExitFileSystem();
818 TRACE("returning %08x\n", Status
);
823 static NTSTATUS STDCALL
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
827 FsRtlEnterFileSystem();
829 top_level
= is_top_level(Irp
);
831 FIXME("STUB: query ea\n");
832 Status
= STATUS_NOT_IMPLEMENTED
;
834 Irp
->IoStatus
.Status
= Status
;
835 Irp
->IoStatus
.Information
= 0;
837 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
840 IoSetTopLevelIrp(NULL
);
842 FsRtlExitFileSystem();
847 static NTSTATUS STDCALL
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
849 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
852 FsRtlEnterFileSystem();
854 top_level
= is_top_level(Irp
);
856 FIXME("STUB: set ea\n");
857 Status
= STATUS_NOT_IMPLEMENTED
;
860 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
862 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
864 Irp
->IoStatus
.Status
= Status
;
865 Irp
->IoStatus
.Information
= 0;
867 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
870 IoSetTopLevelIrp(NULL
);
872 FsRtlExitFileSystem();
877 static NTSTATUS STDCALL
drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
879 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
880 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
881 fcb
* fcb
= FileObject
->FsContext
;
884 TRACE("flush buffers\n");
886 FsRtlEnterFileSystem();
888 top_level
= is_top_level(Irp
);
890 Status
= STATUS_SUCCESS
;
891 Irp
->IoStatus
.Status
= Status
;
892 Irp
->IoStatus
.Information
= 0;
894 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
895 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
897 if (fcb
->Header
.PagingIoResource
) {
898 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
899 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
902 Status
= Irp
->IoStatus
.Status
;
905 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
908 IoSetTopLevelIrp(NULL
);
910 FsRtlExitFileSystem();
915 static NTSTATUS STDCALL
drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
916 PIO_STACK_LOCATION IrpSp
;
918 ULONG BytesCopied
= 0;
919 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
923 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
924 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
925 // which prevents UAC from working.
926 // FIXME - only lie if we detect that we're being called by mpr.dll
928 WCHAR
* fs_name
= L
"NTFS";
929 ULONG fs_name_len
= 4 * sizeof(WCHAR
);
931 WCHAR
* fs_name
= L
"Btrfs";
932 ULONG fs_name_len
= 5 * sizeof(WCHAR
);
935 TRACE("query volume information\n");
937 FsRtlEnterFileSystem();
938 top_level
= is_top_level(Irp
);
940 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
942 Status
= STATUS_NOT_IMPLEMENTED
;
944 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
945 case FileFsAttributeInformation
:
947 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
948 BOOL overflow
= FALSE
;
949 ULONG orig_fs_name_len
= fs_name_len
;
951 TRACE("FileFsAttributeInformation\n");
953 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
954 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
955 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
962 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
963 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
964 FILE_SUPPORTS_REPARSE_POINTS
;
966 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
968 // should also be FILE_FILE_COMPRESSION when supported
969 data
->MaximumComponentNameLength
= 255; // FIXME - check
970 data
->FileSystemNameLength
= orig_fs_name_len
;
971 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
973 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
974 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
978 case FileFsControlInformation
:
979 FIXME("STUB: FileFsControlInformation\n");
982 case FileFsDeviceInformation
:
983 FIXME("STUB: FileFsDeviceInformation\n");
986 case FileFsDriverPathInformation
:
987 FIXME("STUB: FileFsDriverPathInformation\n");
990 case FileFsFullSizeInformation
:
992 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
993 UINT64 totalsize
, freespace
;
995 TRACE("FileFsFullSizeInformation\n");
997 // FIXME - calculate correctly for RAID
998 totalsize
= Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
;
999 freespace
= (Vcb
->superblock
.total_bytes
- Vcb
->superblock
.bytes_used
) / Vcb
->superblock
.sector_size
;
1001 ffsi
->TotalAllocationUnits
.QuadPart
= totalsize
;
1002 ffsi
->ActualAvailableAllocationUnits
.QuadPart
= freespace
;
1003 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
1004 ffsi
->SectorsPerAllocationUnit
= 1;
1005 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
1007 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
1008 Status
= STATUS_SUCCESS
;
1013 case FileFsObjectIdInformation
:
1014 FIXME("STUB: FileFsObjectIdInformation\n");
1017 case FileFsSizeInformation
:
1019 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
1020 UINT64 totalsize
, freespace
;
1022 TRACE("FileFsSizeInformation\n");
1024 // FIXME - calculate correctly for RAID
1025 // FIXME - is this returning the right free space?
1026 totalsize
= Vcb
->superblock
.dev_item
.num_bytes
/ Vcb
->superblock
.sector_size
;
1027 freespace
= (Vcb
->superblock
.dev_item
.num_bytes
- Vcb
->superblock
.dev_item
.bytes_used
) / Vcb
->superblock
.sector_size
;
1029 ffsi
->TotalAllocationUnits
.QuadPart
= totalsize
;
1030 ffsi
->AvailableAllocationUnits
.QuadPart
= freespace
;
1031 ffsi
->SectorsPerAllocationUnit
= 1;
1032 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
1034 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
1035 Status
= STATUS_SUCCESS
;
1040 case FileFsVolumeInformation
:
1042 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
1043 FILE_FS_VOLUME_INFORMATION ffvi
;
1044 BOOL overflow
= FALSE
;
1045 ULONG label_len
, orig_label_len
;
1047 TRACE("FileFsVolumeInformation\n");
1048 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
1050 acquire_tree_lock(Vcb
, FALSE
);
1052 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
1053 RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1054 orig_label_len
= label_len
;
1056 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
1057 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
1058 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
1065 TRACE("label_len = %u\n", label_len
);
1067 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
1068 ffvi
.VolumeSerialNumber
= Vcb
->superblock
.uuid
.uuid
[12] << 24 | Vcb
->superblock
.uuid
.uuid
[13] << 16 | Vcb
->superblock
.uuid
.uuid
[14] << 8 | Vcb
->superblock
.uuid
.uuid
[15];
1069 ffvi
.VolumeLabelLength
= orig_label_len
;
1070 ffvi
.SupportsObjects
= FALSE
;
1072 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
1074 if (label_len
> 0) {
1077 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
1078 RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
1079 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
1082 release_tree_lock(Vcb
, FALSE
);
1084 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
1085 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
1090 Status
= STATUS_INVALID_PARAMETER
;
1091 WARN("unknown FsInformatClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
1095 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
1096 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
1097 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
1098 // Status = STATUS_BUFFER_OVERFLOW;
1101 Irp
->IoStatus
.Status
= Status
;
1103 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
1104 Irp
->IoStatus
.Information
= 0;
1106 Irp
->IoStatus
.Information
= BytesCopied
;
1108 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
1111 IoSetTopLevelIrp(NULL
);
1113 FsRtlExitFileSystem();
1115 TRACE("query volume information returning %08x\n", Status
);
1120 static NTSTATUS STDCALL
read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
1121 read_context
* context
= conptr
;
1123 // DbgPrint("read_completion\n");
1125 context
->iosb
= Irp
->IoStatus
;
1126 KeSetEvent(&context
->Event
, 0, FALSE
);
1128 // return STATUS_SUCCESS;
1129 return STATUS_MORE_PROCESSING_REQUIRED
;
1132 // static void test_tree_deletion(device_extension* Vcb) {
1133 // KEY searchkey/*, endkey*/;
1134 // traverse_ptr tp, next_tp;
1137 // searchkey.obj_id = 0x100;
1138 // searchkey.obj_type = 0x54;
1139 // searchkey.offset = 0xca4ab2f5;
1141 // // endkey.obj_id = 0x100;
1142 // // endkey.obj_type = 0x60;
1143 // // endkey.offset = 0x15a;
1146 // while (r && r->id != 0x102)
1150 // ERR("error - could not find root\n");
1154 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
1155 // ERR("error - could not find key\n");
1159 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
1160 // tp.item->ignore = TRUE;
1161 // add_to_tree_cache(tc, tp.tree);
1163 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
1164 // free_traverse_ptr(&tp);
1170 // free_traverse_ptr(&tp);
1173 // static void test_tree_splitting(device_extension* Vcb) {
1176 // for (i = 0; i < 1000; i++) {
1177 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
1179 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
1183 static NTSTATUS STDCALL
set_label(device_extension
* Vcb
, FILE_FS_LABEL_INFORMATION
* ffli
) {
1187 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1189 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1190 if (!NT_SUCCESS(Status
))
1193 if (utf8len
> MAX_LABEL_SIZE
) {
1194 Status
= STATUS_INVALID_VOLUME_LABEL
;
1198 // FIXME - check for '/' and '\\' and reject
1200 acquire_tree_lock(Vcb
, TRUE
);
1202 // utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1204 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
* sizeof(WCHAR
), &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1205 if (!NT_SUCCESS(Status
))
1208 if (utf8len
< MAX_LABEL_SIZE
* sizeof(WCHAR
))
1209 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, (MAX_LABEL_SIZE
* sizeof(WCHAR
)) - utf8len
);
1211 // test_tree_deletion(Vcb); // TESTING
1212 // test_tree_splitting(Vcb);
1214 Status
= consider_write(Vcb
);
1217 release_tree_lock(Vcb
, TRUE
);
1220 TRACE("returning %08x\n", Status
);
1225 static NTSTATUS STDCALL
drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1226 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1227 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1231 TRACE("set volume information\n");
1233 FsRtlEnterFileSystem();
1235 top_level
= is_top_level(Irp
);
1237 Status
= STATUS_NOT_IMPLEMENTED
;
1239 if (Vcb
->readonly
) {
1240 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1244 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1245 case FileFsControlInformation
:
1246 FIXME("STUB: FileFsControlInformation\n");
1249 case FileFsLabelInformation
:
1250 TRACE("FileFsLabelInformation\n");
1252 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1255 case FileFsObjectIdInformation
:
1256 FIXME("STUB: FileFsObjectIdInformation\n");
1260 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1265 Irp
->IoStatus
.Status
= Status
;
1266 Irp
->IoStatus
.Information
= 0;
1268 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1271 IoSetTopLevelIrp(NULL
);
1273 FsRtlExitFileSystem();
1278 NTSTATUS
delete_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 parinode
, UINT32 crc32
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
1283 searchkey
.obj_id
= parinode
;
1284 searchkey
.obj_type
= TYPE_DIR_ITEM
;
1285 searchkey
.offset
= crc32
;
1287 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1288 if (!NT_SUCCESS(Status
)) {
1289 ERR("error - find_item returned %08x\n", Status
);
1293 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1294 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
1295 WARN("(%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(DIR_ITEM
));
1300 di
= (DIR_ITEM
*)tp
.item
->data
;
1301 len
= tp
.item
->size
;
1304 if (di
->n
== utf8
->Length
&& RtlCompareMemory(di
->name
, utf8
->Buffer
, di
->n
) == di
->n
) {
1305 ULONG newlen
= tp
.item
->size
- (sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
);
1307 delete_tree_item(Vcb
, &tp
, rollback
);
1310 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1312 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1315 ERR("out of memory\n");
1316 free_traverse_ptr(&tp
);
1317 return STATUS_INSUFFICIENT_RESOURCES
;
1320 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1322 if ((UINT8
*)di
> tp
.item
->data
) {
1323 RtlCopyMemory(newdi
, tp
.item
->data
, (UINT8
*)di
- tp
.item
->data
);
1324 dioff
= newdi
+ ((UINT8
*)di
- tp
.item
->data
);
1329 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
< tp
.item
->size
)
1330 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], tp
.item
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
));
1332 insert_tree_item(Vcb
, subvol
, parinode
, TYPE_DIR_ITEM
, crc32
, newdi
, newlen
, NULL
, rollback
);
1338 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1339 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1343 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32
);
1346 free_traverse_ptr(&tp
);
1348 return STATUS_SUCCESS
;
1351 NTSTATUS
delete_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, PANSI_STRING utf8
, UINT64
* index
, LIST_ENTRY
* rollback
) {
1354 BOOL changed
= FALSE
;
1357 searchkey
.obj_id
= inode
;
1358 searchkey
.obj_type
= TYPE_INODE_REF
;
1359 searchkey
.offset
= parinode
;
1361 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1362 if (!NT_SUCCESS(Status
)) {
1363 ERR("error - find_item returned %08x\n", Status
);
1367 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1368 if (tp
.item
->size
< sizeof(INODE_REF
)) {
1369 WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(INODE_REF
));
1374 ir
= (INODE_REF
*)tp
.item
->data
;
1375 len
= tp
.item
->size
;
1380 if (len
< sizeof(INODE_REF
) || len
< sizeof(INODE_REF
) - 1 + ir
->n
) {
1381 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1385 itemlen
= sizeof(INODE_REF
) - sizeof(char) + ir
->n
;
1387 if (ir
->n
== utf8
->Length
&& RtlCompareMemory(ir
->name
, utf8
->Buffer
, ir
->n
) == ir
->n
) {
1388 ULONG newlen
= tp
.item
->size
- itemlen
;
1390 delete_tree_item(Vcb
, &tp
, rollback
);
1394 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1396 UINT8
*newir
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *iroff
;
1399 ERR("out of memory\n");
1400 free_traverse_ptr(&tp
);
1401 return STATUS_INSUFFICIENT_RESOURCES
;
1404 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1406 if ((UINT8
*)ir
> tp
.item
->data
) {
1407 RtlCopyMemory(newir
, tp
.item
->data
, (UINT8
*)ir
- tp
.item
->data
);
1408 iroff
= newir
+ ((UINT8
*)ir
- tp
.item
->data
);
1413 if ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
< tp
.item
->size
)
1414 RtlCopyMemory(iroff
, &ir
->name
[ir
->n
], tp
.item
->size
- ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
));
1416 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newir
, newlen
, NULL
, rollback
);
1425 if (len
> itemlen
) {
1427 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
1433 WARN("found INODE_REF entry, but couldn't find filename\n");
1437 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey
.obj_id
, searchkey
.offset
);
1440 free_traverse_ptr(&tp
);
1443 return STATUS_SUCCESS
;
1445 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
))
1446 return STATUS_INTERNAL_ERROR
;
1448 searchkey
.obj_id
= inode
;
1449 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1450 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
1452 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1453 if (!NT_SUCCESS(Status
)) {
1454 ERR("error - find_item returned %08x\n", Status
);
1458 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1459 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
1460 WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(INODE_EXTREF
));
1465 ier
= (INODE_EXTREF
*)tp
.item
->data
;
1466 len
= tp
.item
->size
;
1471 if (len
< sizeof(INODE_EXTREF
) || len
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
1472 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1476 itemlen
= sizeof(INODE_EXTREF
) - sizeof(char) + ier
->n
;
1478 if (ier
->dir
== parinode
&& ier
->n
== utf8
->Length
&& RtlCompareMemory(ier
->name
, utf8
->Buffer
, ier
->n
) == ier
->n
) {
1479 ULONG newlen
= tp
.item
->size
- itemlen
;
1481 delete_tree_item(Vcb
, &tp
, rollback
);
1485 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1487 UINT8
*newier
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *ieroff
;
1490 ERR("out of memory\n");
1491 free_traverse_ptr(&tp
);
1492 return STATUS_INSUFFICIENT_RESOURCES
;
1495 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1497 if ((UINT8
*)ier
> tp
.item
->data
) {
1498 RtlCopyMemory(newier
, tp
.item
->data
, (UINT8
*)ier
- tp
.item
->data
);
1499 ieroff
= newier
+ ((UINT8
*)ier
- tp
.item
->data
);
1504 if ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
< tp
.item
->size
)
1505 RtlCopyMemory(ieroff
, &ier
->name
[ier
->n
], tp
.item
->size
- ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
));
1507 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newier
, newlen
, NULL
, rollback
);
1511 *index
= ier
->index
;
1516 if (len
> itemlen
) {
1518 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
1524 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32
)searchkey
.offset
);
1527 free_traverse_ptr(&tp
);
1529 return changed
? STATUS_SUCCESS
: STATUS_INTERNAL_ERROR
;
1532 NTSTATUS
delete_fcb(fcb
* fcb
, PFILE_OBJECT FileObject
, LIST_ENTRY
* rollback
) {
1538 traverse_ptr tp
, tp2
;
1539 UINT64 parinode
, index
;
1540 INODE_ITEM
*ii
, *dirii
;
1543 LIST_ENTRY changed_sector_list
;
1545 LARGE_INTEGER freq
, time1
, time2
;
1548 // FIXME - throw error if try to delete subvol root(?)
1550 // FIXME - delete all children if deleting directory
1553 WARN("trying to delete already-deleted file\n");
1554 return STATUS_SUCCESS
;
1558 ERR("error - trying to delete root FCB\n");
1559 return STATUS_INTERNAL_ERROR
;
1563 time1
= KeQueryPerformanceCounter(&freq
);
1566 KeQuerySystemTime(&time
);
1567 win_time_to_unix(time
, &now
);
1571 TRACE("deleting ADS\n");
1573 s
= ExAllocatePoolWithTag(PagedPool
, fcb
->adsxattr
.Length
+ 1, ALLOC_TAG
);
1575 ERR("out of memory\n");
1576 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1580 RtlCopyMemory(s
, fcb
->adsxattr
.Buffer
, fcb
->adsxattr
.Length
);
1581 s
[fcb
->adsxattr
.Length
] = 0;
1583 if (!delete_xattr(fcb
->Vcb
, fcb
->par
->subvol
, fcb
->par
->inode
, s
, fcb
->adshash
, rollback
)) {
1584 ERR("failed to delete xattr %s\n", s
);
1589 fcb
->par
->inode_item
.transid
= fcb
->Vcb
->superblock
.generation
;
1590 fcb
->par
->inode_item
.sequence
++;
1591 fcb
->par
->inode_item
.st_ctime
= now
;
1593 searchkey
.obj_id
= fcb
->par
->inode
;
1594 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1595 searchkey
.offset
= 0xffffffffffffffff;
1597 Status
= find_item(fcb
->Vcb
, fcb
->par
->subvol
, &tp
, &searchkey
, FALSE
);
1598 if (!NT_SUCCESS(Status
)) {
1599 ERR("error - find_item returned %08x\n", Status
);
1603 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
1604 ERR("error - could not find INODE_ITEM for inode %llx in subvol %llx\n", fcb
->par
->inode
, fcb
->par
->subvol
->id
);
1605 free_traverse_ptr(&tp
);
1606 Status
= STATUS_INTERNAL_ERROR
;
1610 ii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1612 ERR("out of memory\n");
1613 free_traverse_ptr(&tp
);
1614 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1618 RtlCopyMemory(ii
, &fcb
->par
->inode_item
, sizeof(INODE_ITEM
));
1619 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1621 insert_tree_item(fcb
->Vcb
, fcb
->par
->subvol
, searchkey
.obj_id
, searchkey
.obj_type
, 0, ii
, sizeof(INODE_ITEM
), NULL
, rollback
);
1623 free_traverse_ptr(&tp
);
1625 fcb
->par
->subvol
->root_item
.ctransid
= fcb
->Vcb
->superblock
.generation
;
1626 fcb
->par
->subvol
->root_item
.ctime
= now
;
1631 Status
= RtlUnicodeToUTF8N(NULL
, 0, &bytecount
, fcb
->filepart
.Buffer
, fcb
->filepart
.Length
);
1632 if (!NT_SUCCESS(Status
)) {
1633 ERR("RtlUnicodeToUTF8N failed with error %08x\n", Status
);
1637 utf8
= ExAllocatePoolWithTag(PagedPool
, bytecount
+ 1, ALLOC_TAG
);
1639 ERR("out of memory\n");
1640 return STATUS_INSUFFICIENT_RESOURCES
;
1643 RtlUnicodeToUTF8N(utf8
, bytecount
, &bytecount
, fcb
->filepart
.Buffer
, fcb
->filepart
.Length
);
1644 utf8
[bytecount
] = 0;
1646 crc32
= calc_crc32c(0xfffffffe, (UINT8
*)utf8
, bytecount
);
1648 TRACE("deleting %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
1650 if (fcb
->par
->subvol
== fcb
->subvol
)
1651 parinode
= fcb
->par
->inode
;
1653 parinode
= SUBVOL_ROOT_INODE
;
1655 // delete DIR_ITEM (0x54)
1657 Status
= delete_dir_item(fcb
->Vcb
, fcb
->subvol
, parinode
, crc32
, &fcb
->utf8
, rollback
);
1658 if (!NT_SUCCESS(Status
)) {
1659 ERR("delete_dir_item returned %08x\n", Status
);
1663 // delete INODE_REF (0xc)
1667 Status
= delete_inode_ref(fcb
->Vcb
, fcb
->subvol
, fcb
->inode
, parinode
, &fcb
->utf8
, &index
, rollback
);
1669 // delete DIR_INDEX (0x60)
1671 searchkey
.obj_id
= parinode
;
1672 searchkey
.obj_type
= TYPE_DIR_INDEX
;
1673 searchkey
.offset
= index
;
1675 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
1676 if (!NT_SUCCESS(Status
)) {
1677 ERR("error - find_item returned %08x\n", Status
);
1678 free_traverse_ptr(&tp
);
1679 Status
= STATUS_INTERNAL_ERROR
;
1683 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1684 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1685 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1688 // delete INODE_ITEM (0x1)
1690 searchkey
.obj_id
= fcb
->inode
;
1691 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1692 searchkey
.offset
= 0;
1694 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp2
, &searchkey
, FALSE
);
1695 if (!NT_SUCCESS(Status
)) {
1696 ERR("error - find_item returned %08x\n", Status
);
1697 free_traverse_ptr(&tp
);
1701 free_traverse_ptr(&tp
);
1704 if (keycmp(&searchkey
, &tp
.item
->key
)) {
1705 ERR("error - INODE_ITEM not found\n");
1706 free_traverse_ptr(&tp
);
1707 Status
= STATUS_INTERNAL_ERROR
;
1711 if (tp
.item
->size
< sizeof(INODE_ITEM
)) {
1712 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(INODE_ITEM
));
1713 free_traverse_ptr(&tp
);
1714 Status
= STATUS_INTERNAL_ERROR
;
1718 ii
= (INODE_ITEM
*)tp
.item
->data
;
1719 TRACE("nlink = %u\n", ii
->st_nlink
);
1721 if (ii
->st_nlink
> 1) {
1724 newii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1726 ERR("out of memory\n");
1727 free_traverse_ptr(&tp
);
1728 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1732 RtlCopyMemory(newii
, ii
, sizeof(INODE_ITEM
));
1734 newii
->transid
= fcb
->Vcb
->superblock
.generation
;
1736 newii
->st_ctime
= now
;
1738 TRACE("replacing (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1740 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1742 if (!insert_tree_item(fcb
->Vcb
, fcb
->subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newii
, sizeof(INODE_ITEM
), NULL
, rollback
))
1743 ERR("error - failed to insert item\n");
1745 free_traverse_ptr(&tp
);
1750 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1751 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1753 // delete XATTR_ITEM (0x18)
1755 while (find_next_item(fcb
->Vcb
, &tp
, &tp2
, FALSE
)) {
1756 free_traverse_ptr(&tp
);
1759 if (tp
.item
->key
.obj_id
== fcb
->inode
) {
1760 // FIXME - do metadata thing here too?
1761 if (tp
.item
->key
.obj_type
== TYPE_XATTR_ITEM
) {
1762 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1763 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1769 free_traverse_ptr(&tp
);
1773 InitializeListHead(&changed_sector_list
);
1775 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0) {
1776 Status
= excise_extents(fcb
->Vcb
, fcb
, 0, sector_align(fcb
->inode_item
.st_size
, fcb
->Vcb
->superblock
.sector_size
), &changed_sector_list
, rollback
);
1777 if (!NT_SUCCESS(Status
)) {
1778 ERR("excise_extents returned %08x\n", Status
);
1782 if (!(fcb
->inode_item
.flags
& BTRFS_INODE_NODATASUM
))
1783 update_checksum_tree(fcb
->Vcb
, &changed_sector_list
, rollback
);
1787 // update INODE_ITEM of parent
1789 searchkey
.obj_id
= parinode
;
1790 searchkey
.obj_type
= TYPE_INODE_ITEM
;
1791 searchkey
.offset
= 0;
1793 Status
= find_item(fcb
->Vcb
, fcb
->subvol
, &tp
, &searchkey
, FALSE
);
1794 if (!NT_SUCCESS(Status
)) {
1795 ERR("error - find_tree returned %08x\n", Status
);
1799 if (keycmp(&searchkey
, &tp
.item
->key
)) {
1800 ERR("error - could not find INODE_ITEM for parent directory %llx in subvol %llx\n", parinode
, fcb
->subvol
->id
);
1801 free_traverse_ptr(&tp
);
1802 Status
= STATUS_INTERNAL_ERROR
;
1806 TRACE("fcb->par->inode_item.st_size was %llx\n", fcb
->par
->inode_item
.st_size
);
1807 fcb
->par
->inode_item
.st_size
-= bytecount
* 2;
1808 TRACE("fcb->par->inode_item.st_size now %llx\n", fcb
->par
->inode_item
.st_size
);
1809 fcb
->par
->inode_item
.transid
= fcb
->Vcb
->superblock
.generation
;
1810 fcb
->par
->inode_item
.sequence
++;
1811 fcb
->par
->inode_item
.st_ctime
= now
;
1812 fcb
->par
->inode_item
.st_mtime
= now
;
1814 dirii
= ExAllocatePoolWithTag(PagedPool
, sizeof(INODE_ITEM
), ALLOC_TAG
);
1816 ERR("out of memory\n");
1817 free_traverse_ptr(&tp
);
1818 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1822 RtlCopyMemory(dirii
, &fcb
->par
->inode_item
, sizeof(INODE_ITEM
));
1823 delete_tree_item(fcb
->Vcb
, &tp
, rollback
);
1825 insert_tree_item(fcb
->Vcb
, fcb
->subvol
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
, dirii
, sizeof(INODE_ITEM
), NULL
, rollback
);
1827 free_traverse_ptr(&tp
);
1829 fcb
->subvol
->root_item
.ctransid
= fcb
->Vcb
->superblock
.generation
;
1830 fcb
->subvol
->root_item
.ctime
= now
;
1833 consider_write(fcb
->Vcb
);
1835 fcb
->deleted
= TRUE
;
1837 fcb
->Header
.AllocationSize
.QuadPart
= 0;
1838 fcb
->Header
.FileSize
.QuadPart
= 0;
1839 fcb
->Header
.ValidDataLength
.QuadPart
= 0;
1841 if (FileObject
&& FileObject
->PrivateCacheMap
) {
1844 ccfs
.AllocationSize
= fcb
->Header
.AllocationSize
;
1845 ccfs
.FileSize
= fcb
->Header
.FileSize
;
1846 ccfs
.ValidDataLength
= fcb
->Header
.ValidDataLength
;
1848 CcSetFileSizes(FileObject
, &ccfs
);
1851 // FIXME - set deleted flag of any open FCBs for ADS
1853 TRACE("sending notification for deletion of %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
1855 FsRtlNotifyFullReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fcb
->full_filename
, fcb
->name_offset
* sizeof(WCHAR
), NULL
, NULL
,
1856 fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
,
1857 FILE_ACTION_REMOVED
, NULL
);
1860 time2
= KeQueryPerformanceCounter(NULL
);
1863 TRACE("time = %u (freq = %u)\n", (UINT32
)(time2
.QuadPart
- time1
.QuadPart
), (UINT32
)freq
.QuadPart
);
1865 Status
= STATUS_SUCCESS
;
1874 void _free_fcb(fcb
* fcb
, const char* func
, const char* file
, unsigned int line
) {
1877 rc
= InterlockedDecrement(&fcb
->refcount
);
1879 #ifdef DEBUG_FCB_REFCOUNTS
1880 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1881 #ifdef DEBUG_LONG_MESSAGES
1882 _debug_message(func
, 1, file
, line
, "fcb %p: refcount now %i (%.*S)\n", fcb
, rc
, fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
1884 _debug_message(func
, 1, "fcb %p: refcount now %i (%.*S)\n", fcb
, rc
, fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
1891 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1893 if (fcb
->filepart
.Buffer
)
1894 RtlFreeUnicodeString(&fcb
->filepart
);
1896 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1897 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1898 ExFreePool(fcb
->nonpaged
);
1900 if (fcb
->par
/* && fcb->par != fcb->par->Vcb->root_fcb*/) {
1901 RemoveEntryList(&fcb
->list_entry
);
1902 _free_fcb(fcb
->par
, func
, file
, line
);
1906 fcb
->prev
->next
= fcb
->next
;
1909 fcb
->next
->prev
= fcb
->prev
;
1911 if (fcb
->Vcb
->fcbs
== fcb
)
1912 fcb
->Vcb
->fcbs
= fcb
->next
;
1914 if (fcb
->full_filename
.Buffer
)
1915 ExFreePool(fcb
->full_filename
.Buffer
);
1918 ExFreePool(fcb
->sd
);
1920 if (fcb
->adsxattr
.Buffer
)
1921 ExFreePool(fcb
->adsxattr
.Buffer
);
1923 if (fcb
->utf8
.Buffer
)
1924 ExFreePool(fcb
->utf8
.Buffer
);
1926 FsRtlUninitializeFileLock(&fcb
->lock
);
1928 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1931 #ifdef DEBUG_FCB_REFCOUNTS
1932 #ifdef DEBUG_LONG_MESSAGES
1933 _debug_message(func
, 1, file
, line
, "freeing fcb %p\n", fcb
);
1935 _debug_message(func
, 1, "freeing fcb %p\n", fcb
);
1940 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
1944 TRACE("FileObject = %p\n", FileObject
);
1946 fcb
= FileObject
->FsContext
;
1948 TRACE("FCB was NULL, returning success\n");
1949 return STATUS_SUCCESS
;
1952 ccb
= FileObject
->FsContext2
;
1954 TRACE("close called for %.*S (fcb == %p)\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, fcb
);
1956 FsRtlNotifyCleanup(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, ccb
);
1958 // FIXME - make sure notification gets sent if file is being deleted
1961 if (ccb
->query_string
.Buffer
)
1962 RtlFreeUnicodeString(&ccb
->query_string
);
1967 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1971 return STATUS_SUCCESS
;
1974 static void STDCALL
uninit(device_extension
* Vcb
) {
1978 LIST_ENTRY rollback
;
1980 InitializeListHead(&rollback
);
1982 acquire_tree_lock(Vcb
, TRUE
);
1984 if (Vcb
->write_trees
> 0)
1985 do_write(Vcb
, &rollback
);
1987 free_tree_cache(&Vcb
->tree_cache
);
1989 clear_rollback(&rollback
);
1991 release_tree_lock(Vcb
, TRUE
);
1993 while (Vcb
->roots
) {
1994 root
* r
= Vcb
->roots
->next
;
1996 ExDeleteResourceLite(&Vcb
->roots
->nonpaged
->load_tree_lock
);
1997 ExFreePool(Vcb
->roots
->nonpaged
);
1998 ExFreePool(Vcb
->roots
);
2003 while (!IsListEmpty(&Vcb
->chunks
)) {
2004 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->chunks
);
2005 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2007 while (!IsListEmpty(&c
->space
)) {
2008 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2009 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2015 ExFreePool(c
->devices
);
2017 ExFreePool(c
->chunk_item
);
2021 free_fcb(Vcb
->volume_fcb
);
2022 free_fcb(Vcb
->root_fcb
);
2024 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2025 while (!IsListEmpty(&Vcb
->devices
[i
].disk_holes
)) {
2026 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->devices
[i
].disk_holes
);
2027 disk_hole
* dh
= CONTAINING_RECORD(le
, disk_hole
, listentry
);
2033 ExFreePool(Vcb
->devices
);
2035 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2036 ExDeleteResourceLite(&Vcb
->load_lock
);
2037 ExDeleteResourceLite(&Vcb
->tree_lock
);
2039 ZwClose(Vcb
->flush_thread_handle
);
2042 static NTSTATUS STDCALL
drv_cleanup(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2044 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2045 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2051 FsRtlEnterFileSystem();
2053 top_level
= is_top_level(Irp
);
2055 if (DeviceObject
== devobj
) {
2056 TRACE("closing file system\n");
2057 Status
= STATUS_SUCCESS
;
2064 fcb
= FileObject
->FsContext
;
2066 TRACE("cleanup called for FileObject %p\n", FileObject
);
2067 TRACE("fcb %p (%.*S), refcount = %u, open_count = %u\n", fcb
, fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
, fcb
->refcount
, fcb
->open_count
);
2069 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2071 oc
= InterlockedDecrement(&fcb
->open_count
);
2072 #ifdef DEBUG_FCB_REFCOUNTS
2073 ERR("fcb %p: open_count now %i\n", fcb
, oc
);
2077 if (fcb
->delete_on_close
&& fcb
!= fcb
->Vcb
->root_fcb
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2078 LIST_ENTRY rollback
;
2079 InitializeListHead(&rollback
);
2081 acquire_tree_lock(fcb
->Vcb
, TRUE
);
2083 Status
= delete_fcb(fcb
, FileObject
, &rollback
);
2085 if (NT_SUCCESS(Status
)) {
2086 LARGE_INTEGER newlength
;
2088 if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
)
2089 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2091 newlength
.QuadPart
= 0;
2093 if (!CcUninitializeCacheMap(FileObject
, &newlength
, NULL
)) {
2094 TRACE("CcUninitializeCacheMap failed\n");
2097 clear_rollback(&rollback
);
2099 do_rollback(fcb
->Vcb
, &rollback
);
2101 release_tree_lock(fcb
->Vcb
, TRUE
);
2102 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2103 IO_STATUS_BLOCK iosb
;
2104 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2106 if (!NT_SUCCESS(iosb
.Status
)) {
2107 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2110 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2111 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2113 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2115 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2116 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2119 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2120 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2123 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2126 Status
= STATUS_SUCCESS
;
2129 Irp
->IoStatus
.Status
= Status
;
2130 Irp
->IoStatus
.Information
= 0;
2132 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2135 IoSetTopLevelIrp(NULL
);
2137 FsRtlExitFileSystem();
2142 ULONG STDCALL
get_file_attributes(device_extension
* Vcb
, INODE_ITEM
* ii
, root
* r
, UINT64 inode
, UINT8 type
, BOOL dotfile
, BOOL ignore_xa
) {
2147 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
)) {
2149 if (eaval
[0] == '0' && eaval
[1] == 'x') {
2153 for (i
= 2; i
< ealen
; i
++) {
2156 if (eaval
[i
] >= '0' && eaval
[i
] <= '9')
2157 dosnum
|= eaval
[i
] - '0';
2158 else if (eaval
[i
] >= 'a' && eaval
[i
] <= 'f')
2159 dosnum
|= eaval
[i
] + 10 - 'a';
2160 else if (eaval
[i
] >= 'A' && eaval
[i
] <= 'F')
2161 dosnum
|= eaval
[i
] + 10 - 'a';
2164 TRACE("DOSATTRIB: %08x\n", dosnum
);
2176 case BTRFS_TYPE_DIRECTORY
:
2177 att
= FILE_ATTRIBUTE_DIRECTORY
;
2180 case BTRFS_TYPE_SYMLINK
:
2181 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2190 att
|= FILE_ATTRIBUTE_HIDDEN
;
2193 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2195 // FIXME - get READONLY from ii->st_mode
2196 // FIXME - return SYSTEM for block/char devices?
2199 att
= FILE_ATTRIBUTE_NORMAL
;
2204 // static int STDCALL utf8icmp(char* a, char* b) {
2205 // return strcmp(a, b); // FIXME - don't treat as ASCII
2208 NTSTATUS
sync_read_phys(PDEVICE_OBJECT DeviceObject
, LONGLONG StartingOffset
, ULONG Length
, PUCHAR Buffer
) {
2209 IO_STATUS_BLOCK
* IoStatus
;
2210 LARGE_INTEGER Offset
;
2212 PIO_STACK_LOCATION IrpSp
;
2214 read_context
* context
;
2218 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
2220 ERR("out of memory\n");
2221 return STATUS_INSUFFICIENT_RESOURCES
;
2224 RtlZeroMemory(context
, sizeof(read_context
));
2225 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
2227 IoStatus
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), ALLOC_TAG
);
2229 ERR("out of memory\n");
2230 ExFreePool(context
);
2231 return STATUS_INSUFFICIENT_RESOURCES
;
2234 Offset
.QuadPart
= StartingOffset
;
2236 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2237 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2240 ERR("IoAllocateIrp failed\n");
2241 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2245 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2246 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2248 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2249 FIXME("FIXME - buffered IO\n");
2250 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2251 // TRACE("direct IO\n");
2253 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2254 if (!Irp
->MdlAddress
) {
2255 ERR("IoAllocateMdl failed\n");
2256 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2260 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2263 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2265 // TRACE("neither buffered nor direct IO\n");
2266 Irp
->UserBuffer
= Buffer
;
2269 IrpSp
->Parameters
.Read
.Length
= Length
;
2270 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2272 Irp
->UserIosb
= IoStatus
;
2273 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2275 Irp
->UserEvent
= &context
->Event
;
2277 // IoQueueThreadIrp(Irp);
2279 IoSetCompletionRoutine(Irp
, read_completion
, context
, TRUE
, TRUE
, TRUE
);
2283 // Stack = IoGetNextIrpStackLocation(Irp);
2284 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2287 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2288 Status
= IoCallDriver(DeviceObject
, Irp
);
2290 // TRACE("Waiting for IO Operation for %p\n", Irp);
2291 if (Status
== STATUS_PENDING
) {
2292 // TRACE("Operation pending\n");
2293 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2294 // TRACE("Getting IO Status... for %p\n", Irp);
2295 Status
= context
->iosb
.Status
;
2298 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2299 MmUnlockPages(Irp
->MdlAddress
);
2300 IoFreeMdl(Irp
->MdlAddress
);
2306 ExFreePool(IoStatus
);
2307 ExFreePool(context
);
2312 static NTSTATUS STDCALL
read_superblock(device_extension
* Vcb
, PDEVICE_OBJECT device
) {
2315 unsigned int i
, to_read
;
2318 to_read
= sector_align(sizeof(superblock
), device
->SectorSize
);
2320 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2322 ERR("out of memory\n");
2323 return STATUS_INSUFFICIENT_RESOURCES
;
2328 while (superblock_addrs
[i
] > 0) {
2329 if (i
> 0 && superblock_addrs
[i
] + sizeof(superblock
) > Vcb
->length
)
2332 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
);
2333 if (!NT_SUCCESS(Status
)) {
2334 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2339 TRACE("got superblock %u!\n", i
);
2341 if (i
== 0 || sb
->generation
> Vcb
->superblock
.generation
)
2342 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2349 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
2351 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)Vcb
->superblock
.checksum
));
2353 if (crc32
!= *((UINT32
*)Vcb
->superblock
.checksum
))
2354 return STATUS_INTERNAL_ERROR
; // FIXME - correct error?
2356 TRACE("label is %s\n", Vcb
->superblock
.label
);
2357 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2359 return STATUS_SUCCESS
;
2362 static NTSTATUS STDCALL
dev_ioctl(PDEVICE_OBJECT DeviceObject
, ULONG ControlCode
, PVOID InputBuffer
,
2363 ULONG InputBufferSize
, PVOID OutputBuffer
, ULONG OutputBufferSize
, BOOLEAN Override
)
2368 PIO_STACK_LOCATION Stack
;
2369 IO_STATUS_BLOCK IoStatus
;
2371 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2373 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2383 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2386 Stack
= IoGetNextIrpStackLocation(Irp
);
2387 Stack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2390 Status
= IoCallDriver(DeviceObject
, Irp
);
2392 if (Status
== STATUS_PENDING
) {
2393 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2394 Status
= IoStatus
.Status
;
2400 // static void STDCALL find_chunk_root(device_extension* Vcb) {
2405 // while (i < Vcb->superblock.n) {
2406 // key = &Vcb->superblock.sys_chunk_array[i];
2407 // i += sizeof(KEY);
2413 // static void STDCALL insert_ltp(device_extension* Vcb, log_to_phys* ltp) {
2414 // if (!Vcb->log_to_phys) {
2415 // Vcb->log_to_phys = ltp;
2416 // ltp->next = NULL;
2420 // // FIXME - these should be ordered
2421 // ltp->next = Vcb->log_to_phys;
2422 // Vcb->log_to_phys = ltp;
2425 static NTSTATUS STDCALL
add_root(device_extension
* Vcb
, UINT64 id
, UINT64 addr
, traverse_ptr
* tp
) {
2426 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2428 ERR("out of memory\n");
2429 return STATUS_INSUFFICIENT_RESOURCES
;
2433 r
->treeholder
.address
= addr
;
2434 r
->treeholder
.tree
= NULL
;
2435 init_tree_holder(&r
->treeholder
);
2437 r
->next
= Vcb
->roots
;
2439 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2441 ERR("out of memory\n");
2443 return STATUS_INSUFFICIENT_RESOURCES
;
2446 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2451 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2452 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2453 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2457 Vcb
->roots
->prev
= r
;
2462 case BTRFS_ROOT_ROOT
:
2466 case BTRFS_ROOT_EXTENT
:
2467 Vcb
->extent_root
= r
;
2470 case BTRFS_ROOT_CHUNK
:
2471 Vcb
->chunk_root
= r
;
2474 case BTRFS_ROOT_DEVTREE
:
2478 case BTRFS_ROOT_CHECKSUM
:
2479 Vcb
->checksum_root
= r
;
2483 return STATUS_SUCCESS
;
2486 static NTSTATUS STDCALL
look_for_roots(device_extension
* Vcb
) {
2487 traverse_ptr tp
, next_tp
;
2492 searchkey
.obj_id
= 0;
2493 searchkey
.obj_type
= 0;
2494 searchkey
.offset
= 0;
2496 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
2497 if (!NT_SUCCESS(Status
)) {
2498 ERR("error - find_tree returned %08x\n", Status
);
2503 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2505 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2506 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2508 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2509 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
, offsetof(ROOT_ITEM
, byte_limit
));
2511 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2513 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, &tp
);
2514 if (!NT_SUCCESS(Status
)) {
2515 ERR("add_root returned %08x\n", Status
);
2521 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2524 free_traverse_ptr(&tp
);
2529 free_traverse_ptr(&tp
);
2531 return STATUS_SUCCESS
;
2534 static NTSTATUS
add_disk_hole(LIST_ENTRY
* disk_holes
, UINT64 address
, UINT64 size
) {
2535 disk_hole
* dh
= ExAllocatePoolWithTag(PagedPool
, sizeof(disk_hole
), ALLOC_TAG
);
2538 ERR("out of memory\n");
2539 return STATUS_INSUFFICIENT_RESOURCES
;
2542 dh
->address
= address
;
2544 dh
->provisional
= FALSE
;
2546 InsertTailList(disk_holes
, &dh
->listentry
);
2548 return STATUS_SUCCESS
;
2551 static NTSTATUS
find_disk_holes(device_extension
* Vcb
, device
* dev
) {
2553 traverse_ptr tp
, next_tp
;
2558 InitializeListHead(&dev
->disk_holes
);
2560 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2561 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2562 searchkey
.offset
= 0;
2564 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
);
2565 if (!NT_SUCCESS(Status
)) {
2566 ERR("error - find_tree returned %08x\n", Status
);
2573 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2574 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2575 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2577 if (tp
.item
->key
.offset
> lastaddr
) {
2578 Status
= add_disk_hole(&dev
->disk_holes
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2579 if (!NT_SUCCESS(Status
)) {
2580 ERR("add_disk_hole returned %08x\n", Status
);
2585 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2587 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(DEV_EXTENT
));
2591 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2594 free_traverse_ptr(&tp
);
2596 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2601 free_traverse_ptr(&tp
);
2603 if (lastaddr
< dev
->devitem
.num_bytes
) {
2604 Status
= add_disk_hole(&dev
->disk_holes
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2605 if (!NT_SUCCESS(Status
)) {
2606 ERR("add_disk_hole returned %08x\n", Status
);
2611 // FIXME - free disk_holes when unmounting
2613 return STATUS_SUCCESS
;
2616 device
* find_device_from_uuid(device_extension
* Vcb
, BTRFS_UUID
* uuid
) {
2619 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2620 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i
,
2621 Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[0], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[1], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[2], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[3], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[4], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[5], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[6], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[7],
2622 Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[8], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[9], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[10], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[11], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[12], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[13], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[14], Vcb
->devices
[i
].devitem
.device_uuid
.uuid
[15]);
2624 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2625 TRACE("returning device %llx\n", i
);
2626 return &Vcb
->devices
[i
];
2630 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2631 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
2632 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
2637 static NTSTATUS STDCALL
load_chunk_root(device_extension
* Vcb
) {
2638 traverse_ptr tp
, next_tp
;
2645 searchkey
.obj_id
= 0;
2646 searchkey
.obj_type
= 0;
2647 searchkey
.offset
= 0;
2649 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
2650 if (!NT_SUCCESS(Status
)) {
2651 ERR("error - find_item returned %08x\n", Status
);
2656 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2658 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
&& tp
.item
->key
.offset
== 1) {
2659 // FIXME - this is a hack; make this work with multiple devices!
2660 if (tp
.item
->size
> 0)
2661 RtlCopyMemory(&Vcb
->devices
[0].devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2662 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
2663 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
2664 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(CHUNK_ITEM
));
2666 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
2669 ERR("out of memory\n");
2670 return STATUS_INSUFFICIENT_RESOURCES
;
2673 c
->size
= tp
.item
->size
;
2674 c
->offset
= tp
.item
->key
.offset
;
2675 c
->used
= c
->oldused
= 0;
2676 c
->space_changed
= FALSE
;
2678 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2680 if (!c
->chunk_item
) {
2681 ERR("out of memory\n");
2682 return STATUS_INSUFFICIENT_RESOURCES
;
2685 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
2687 if (c
->chunk_item
->num_stripes
> 0) {
2688 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
2690 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
2693 ERR("out of memory\n");
2694 return STATUS_INSUFFICIENT_RESOURCES
;
2697 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
2698 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
2699 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
2704 InitializeListHead(&c
->space
);
2706 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
2710 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2713 free_traverse_ptr(&tp
);
2718 free_traverse_ptr(&tp
);
2720 Vcb
->log_to_phys_loaded
= TRUE
;
2722 return STATUS_SUCCESS
;
2725 static BOOL
load_stored_free_space_cache(device_extension
* Vcb
, chunk
* c
) {
2727 traverse_ptr tp
, tp2
;
2728 FREE_SPACE_ITEM
* fsi
;
2729 UINT64 inode
, num_sectors
, i
, generation
;
2733 UINT32
*checksums
, crc32
;
2735 FREE_SPACE_ENTRY
* fse
;
2739 TRACE("(%p, %llx)\n", Vcb
, c
->offset
);
2741 if (Vcb
->superblock
.generation
!= Vcb
->superblock
.cache_generation
)
2744 searchkey
.obj_id
= FREE_SPACE_CACHE_ID
;
2745 searchkey
.obj_type
= 0;
2746 searchkey
.offset
= c
->offset
;
2748 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
2749 if (!NT_SUCCESS(Status
)) {
2750 ERR("error - find_item returned %08x\n", Status
);
2754 if (keycmp(&tp
.item
->key
, &searchkey
)) {
2755 WARN("(%llx,%x,%llx) not found\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2756 free_traverse_ptr(&tp
);
2760 if (tp
.item
->size
< sizeof(FREE_SPACE_ITEM
)) {
2761 WARN("(%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(FREE_SPACE_ITEM
));
2762 free_traverse_ptr(&tp
);
2766 fsi
= (FREE_SPACE_ITEM
*)tp
.item
->data
;
2768 if (fsi
->generation
!= Vcb
->superblock
.cache_generation
) {
2769 WARN("cache had generation %llx, expecting %llx\n", fsi
->generation
, Vcb
->superblock
.cache_generation
);
2770 free_traverse_ptr(&tp
);
2774 if (fsi
->key
.obj_type
!= TYPE_INODE_ITEM
) {
2775 WARN("cache pointed to something other than an INODE_ITEM\n");
2776 free_traverse_ptr(&tp
);
2780 if (fsi
->num_bitmaps
> 0) {
2781 WARN("cache had bitmaps, unsure of how to deal with these\n");
2782 free_traverse_ptr(&tp
);
2786 inode
= fsi
->key
.obj_id
;
2788 searchkey
= fsi
->key
;
2790 num_entries
= fsi
->num_entries
;
2793 Status
= find_item(Vcb
, Vcb
->root_root
, &tp2
, &searchkey
, FALSE
);
2794 if (!NT_SUCCESS(Status
)) {
2795 ERR("error - find_item returned %08x\n", Status
);
2796 free_traverse_ptr(&tp
);
2800 free_traverse_ptr(&tp
);
2802 if (keycmp(&tp2
.item
->key
, &searchkey
)) {
2803 WARN("(%llx,%x,%llx) not found\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
2804 free_traverse_ptr(&tp2
);
2808 if (tp2
.item
->size
< sizeof(INODE_ITEM
)) {
2809 WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp2
.item
->key
.obj_id
, tp2
.item
->key
.obj_type
, tp2
.item
->key
.offset
, tp2
.item
->size
, sizeof(INODE_ITEM
));
2810 free_traverse_ptr(&tp2
);
2814 ii
= (INODE_ITEM
*)tp2
.item
->data
;
2816 data
= ExAllocatePoolWithTag(PagedPool
, ii
->st_size
, ALLOC_TAG
);
2819 ERR("out of memory\n");
2820 free_traverse_ptr(&tp2
);
2824 Status
= read_file(Vcb
, Vcb
->root_root
, inode
, data
, 0, ii
->st_size
, NULL
);
2825 if (!NT_SUCCESS(Status
)) {
2826 ERR("read_file returned %08x\n", Status
);
2828 free_traverse_ptr(&tp2
);
2832 num_sectors
= ii
->st_size
/ Vcb
->superblock
.sector_size
;
2834 generation
= *(data
+ (num_sectors
* sizeof(UINT32
)));
2836 if (generation
!= Vcb
->superblock
.cache_generation
) {
2837 ERR("generation was %llx, expected %llx\n", generation
, Vcb
->superblock
.cache_generation
);
2839 free_traverse_ptr(&tp2
);
2843 checksums
= ExAllocatePoolWithTag(PagedPool
, sizeof(UINT32
) * num_sectors
, ALLOC_TAG
); // FIXME - get rid of this
2846 ERR("out of memory\n");
2848 free_traverse_ptr(&tp2
);
2852 RtlCopyMemory(checksums
, data
, sizeof(UINT32
) * num_sectors
);
2854 for (i
= 0; i
< num_sectors
; i
++) {
2855 if (i
* Vcb
->superblock
.sector_size
> sizeof(UINT32
) * num_sectors
)
2856 crc32
= ~calc_crc32c(0xffffffff, &data
[i
* Vcb
->superblock
.sector_size
], Vcb
->superblock
.sector_size
);
2857 else if ((i
+ 1) * Vcb
->superblock
.sector_size
< sizeof(UINT32
) * num_sectors
)
2858 crc32
= 0; // FIXME - test this
2860 crc32
= ~calc_crc32c(0xffffffff, &data
[sizeof(UINT32
) * num_sectors
], ((i
+ 1) * Vcb
->superblock
.sector_size
) - (sizeof(UINT32
) * num_sectors
));
2862 if (crc32
!= checksums
[i
]) {
2863 WARN("checksum %llu was %08x, expected %08x\n", i
, crc32
, checksums
[i
]);
2864 ExFreePool(checksums
);
2866 free_traverse_ptr(&tp2
);
2871 ExFreePool(checksums
);
2874 fse
= (FREE_SPACE_ENTRY
*)&data
[(sizeof(UINT32
) * num_sectors
) + sizeof(UINT64
)];
2876 for (i
= 0; i
< num_entries
; i
++) {
2877 TRACE("(%llx,%llx,%x)\n", fse
[i
].offset
, fse
[i
].size
, fse
[i
].type
);
2881 FIXME("FIXME - read cache\n");
2884 free_traverse_ptr(&tp2
);
2889 static NTSTATUS
load_free_space_cache(device_extension
* Vcb
, chunk
* c
) {
2890 traverse_ptr tp
, next_tp
;
2898 load_stored_free_space_cache(Vcb
, c
);
2900 TRACE("generating free space cache for chunk %llx\n", c
->offset
);
2902 searchkey
.obj_id
= c
->offset
;
2903 searchkey
.obj_type
= TYPE_EXTENT_ITEM
;
2904 searchkey
.offset
= 0;
2906 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
2907 if (!NT_SUCCESS(Status
)) {
2908 ERR("error - find_item returned %08x\n", Status
);
2912 lastaddr
= c
->offset
;
2915 if (tp
.item
->key
.obj_id
>= c
->offset
+ c
->chunk_item
->size
)
2918 if (tp
.item
->key
.obj_id
>= c
->offset
&& (tp
.item
->key
.obj_type
== TYPE_EXTENT_ITEM
|| tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)) {
2919 if (tp
.item
->key
.obj_id
> lastaddr
) {
2920 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
2923 ERR("out of memory\n");
2924 return STATUS_INSUFFICIENT_RESOURCES
;
2927 s
->offset
= lastaddr
;
2928 s
->size
= tp
.item
->key
.obj_id
- lastaddr
;
2929 s
->type
= SPACE_TYPE_FREE
;
2930 InsertTailList(&c
->space
, &s
->list_entry
);
2932 TRACE("(%llx,%llx)\n", s
->offset
, s
->size
);
2935 if (tp
.item
->key
.obj_type
== TYPE_METADATA_ITEM
)
2936 lastaddr
= tp
.item
->key
.obj_id
+ Vcb
->superblock
.node_size
;
2938 lastaddr
= tp
.item
->key
.obj_id
+ tp
.item
->key
.offset
;
2941 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2943 free_traverse_ptr(&tp
);
2948 if (lastaddr
< c
->offset
+ c
->chunk_item
->size
) {
2949 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
2952 ERR("out of memory\n");
2953 return STATUS_INSUFFICIENT_RESOURCES
;
2956 s
->offset
= lastaddr
;
2957 s
->size
= c
->offset
+ c
->chunk_item
->size
- lastaddr
;
2958 s
->type
= SPACE_TYPE_FREE
;
2959 InsertTailList(&c
->space
, &s
->list_entry
);
2961 TRACE("(%llx,%llx)\n", s
->offset
, s
->size
);
2964 free_traverse_ptr(&tp
);
2966 // add allocated space
2968 lastaddr
= c
->offset
;
2970 le
= c
->space
.Flink
;
2971 while (le
!= &c
->space
) {
2972 s
= CONTAINING_RECORD(le
, space
, list_entry
);
2974 if (s
->offset
> lastaddr
) {
2975 s2
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
2978 ERR("out of memory\n");
2979 return STATUS_INSUFFICIENT_RESOURCES
;
2982 s2
->offset
= lastaddr
;
2983 s2
->size
= s
->offset
- lastaddr
;
2984 s2
->type
= SPACE_TYPE_USED
;
2986 InsertTailList(&s
->list_entry
, &s2
->list_entry
);
2989 lastaddr
= s
->offset
+ s
->size
;
2994 if (lastaddr
< c
->offset
+ c
->chunk_item
->size
) {
2995 s
= ExAllocatePoolWithTag(PagedPool
, sizeof(space
), ALLOC_TAG
);
2998 ERR("out of memory\n");
2999 return STATUS_INSUFFICIENT_RESOURCES
;
3002 s
->offset
= lastaddr
;
3003 s
->size
= c
->offset
+ c
->chunk_item
->size
- lastaddr
;
3004 s
->type
= SPACE_TYPE_USED
;
3005 InsertTailList(&c
->space
, &s
->list_entry
);
3008 le
= c
->space
.Flink
;
3009 while (le
!= &c
->space
) {
3010 s
= CONTAINING_RECORD(le
, space
, list_entry
);
3012 TRACE("%llx,%llx,%u\n", s
->offset
, s
->size
, s
->type
);
3017 return STATUS_SUCCESS
;
3020 void protect_superblocks(device_extension
* Vcb
, chunk
* c
) {
3024 // FIXME - this will need modifying for RAID
3026 while (superblock_addrs
[i
] != 0) {
3027 CHUNK_ITEM
* ci
= c
->chunk_item
;
3028 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3030 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3031 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3034 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3036 addr
= (superblock_addrs
[i
] - cis
[j
].offset
) + c
->offset
;
3037 TRACE("addr %llx\n", addr
);
3039 // This prevents trees from spanning a stripe boundary, which btrfs check complains
3040 // about. It also prevents the chunk tree being placed at 0x11000, which for some
3041 // reason makes the FS unmountable on Linux (it tries to read 0x10000, i.e. the
3042 // superblock, instead).
3043 if (ci
->type
& BLOCK_FLAG_SYSTEM
|| ci
->type
& BLOCK_FLAG_METADATA
)
3044 size
= max(sizeof(superblock
), Vcb
->superblock
.node_size
);
3046 size
= sizeof(superblock
);
3048 add_to_space_list(c
, addr
, size
, SPACE_TYPE_USED
);
3056 static NTSTATUS STDCALL
find_chunk_usage(device_extension
* Vcb
) {
3057 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3061 BLOCK_GROUP_ITEM
* bgi
;
3065 // block_group_item size=7f0000 chunktreeid=100 flags=1
3067 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3069 while (le
!= &Vcb
->chunks
) {
3070 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3072 searchkey
.obj_id
= c
->offset
;
3073 searchkey
.offset
= c
->chunk_item
->size
;
3075 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3076 if (!NT_SUCCESS(Status
)) {
3077 ERR("error - find_item returned %08x\n", Status
);
3081 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
3082 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3083 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3085 c
->used
= c
->oldused
= bgi
->used
;
3087 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3089 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3090 Vcb
->extent_root
->id
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(BLOCK_GROUP_ITEM
));
3094 free_traverse_ptr(&tp
);
3095 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3096 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3098 // return (addr - c->offset) + cis->offset;
3101 // FIXME - make sure we free occasionally after doing one of these, or we
3102 // might use up a lot of memory with a big disk.
3104 Status
= load_free_space_cache(Vcb
, c
);
3105 if (!NT_SUCCESS(Status
)) {
3106 ERR("load_free_space_cache returned %08x\n", Status
);
3110 protect_superblocks(Vcb
, c
);
3115 return STATUS_SUCCESS
;
3118 // static void STDCALL root_test(device_extension* Vcb) {
3121 // traverse_ptr tp, next_tp;
3126 // if (r->id == 0x102)
3132 // ERR("Could not find root tree.\n");
3136 // searchkey.obj_id = 0x1b6;
3137 // searchkey.obj_type = 0xb;
3138 // searchkey.offset = 0;
3140 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3141 // ERR("Could not find first item.\n");
3147 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3149 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3152 // free_traverse_ptr(&tp);
3157 // free_traverse_ptr(&tp);
3160 static NTSTATUS
load_sys_chunks(device_extension
* Vcb
) {
3162 ULONG n
= Vcb
->superblock
.n
;
3165 if (n
> sizeof(KEY
)) {
3166 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3169 return STATUS_SUCCESS
;
3171 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3173 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3178 if (n
< sizeof(CHUNK_ITEM
))
3179 return STATUS_SUCCESS
;
3181 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3182 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3185 return STATUS_SUCCESS
;
3187 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3190 ERR("out of memory\n");
3191 return STATUS_INSUFFICIENT_RESOURCES
;
3196 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3199 ERR("out of memory\n");
3200 return STATUS_INSUFFICIENT_RESOURCES
;
3203 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3204 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3208 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3209 return STATUS_INTERNAL_ERROR
;
3213 return STATUS_SUCCESS
;
3216 static root
* find_default_subvol(device_extension
* Vcb
) {
3220 UNICODE_STRING filename
;
3222 static WCHAR fn
[] = L
"default";
3223 static UINT32 crc32
= 0x8dbfc2d2;
3225 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3226 filename
.Buffer
= fn
;
3227 filename
.Length
= filename
.MaximumLength
= (USHORT
)wcslen(fn
) * sizeof(WCHAR
);
3229 if (!find_file_in_dir_with_crc32(Vcb
, &filename
, crc32
, Vcb
->root_root
, Vcb
->superblock
.root_dir_objectid
, &subvol
, &inode
, &type
, NULL
))
3230 WARN("couldn't find default subvol DIR_ITEM, using default tree\n");
3235 subvol
= Vcb
->roots
;
3236 while (subvol
&& subvol
->id
!= BTRFS_ROOT_FSTREE
)
3237 subvol
= subvol
->next
;
3242 static NTSTATUS STDCALL
mount_vol(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
3243 PIO_STACK_LOCATION Stack
;
3244 PDEVICE_OBJECT NewDeviceObject
= NULL
;
3245 PDEVICE_OBJECT DeviceToMount
;
3247 device_extension
* Vcb
= NULL
;
3248 PARTITION_INFORMATION_EX piex
;
3254 TRACE("mount_vol called\n");
3256 if (DeviceObject
!= devobj
)
3258 Status
= STATUS_INVALID_DEVICE_REQUEST
;
3262 Stack
= IoGetCurrentIrpStackLocation(Irp
);
3263 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
3265 // Status = NtfsHasFileSystem(DeviceToMount);
3266 // if (!NT_SUCCESS(Status))
3271 Status
= dev_ioctl(DeviceToMount
, IOCTL_DISK_GET_PARTITION_INFO_EX
, NULL
, 0,
3272 &piex
, sizeof(piex
), TRUE
);
3273 if (!NT_SUCCESS(Status
)) {
3274 ERR("error reading partition information: %08x\n", Status
);
3278 Status
= IoCreateDevice(drvobj
,
3279 sizeof(device_extension
),
3281 FILE_DEVICE_DISK_FILE_SYSTEM
,
3285 if (!NT_SUCCESS(Status
))
3288 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3290 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
3291 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
3292 RtlZeroMemory(Vcb
, sizeof(device_extension
));
3294 InitializeListHead(&Vcb
->tree_cache
);
3296 ExInitializeResourceLite(&Vcb
->tree_lock
);
3297 Vcb
->tree_lock_counter
= 0;
3298 Vcb
->open_trees
= 0;
3299 Vcb
->write_trees
= 0;
3301 ExInitializeResourceLite(&Vcb
->fcb_lock
);
3302 ExInitializeResourceLite(&Vcb
->DirResource
);
3304 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
3305 InsertTailList(&VcbList
, &Vcb
->list_entry
);
3306 ExReleaseResourceLite(&global_loading_lock
);
3308 ExInitializeResourceLite(&Vcb
->load_lock
);
3309 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
3311 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3312 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3314 // Status = NtfsGetVolumeData(DeviceToMount,
3316 // if (!NT_SUCCESS(Status))
3319 // Vcb->device = DeviceToMount;
3320 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
3322 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3323 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3324 // if (!NT_SUCCESS(Status)) {
3325 // ERR("error reading disk geometry: %08x\n", Status);
3328 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3329 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3330 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3333 Vcb
->length
= piex
.PartitionLength
.QuadPart
;
3334 TRACE("partition length = %u\n", piex
.PartitionLength
);
3336 Status
= read_superblock(Vcb
, DeviceToMount
);
3337 if (!NT_SUCCESS(Status
)) {
3338 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3342 if (Vcb
->superblock
.magic
!= BTRFS_MAGIC
) {
3343 ERR("not a BTRFS volume\n");
3344 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3347 TRACE("btrfs magic found\n");
3350 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
3351 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
3352 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3357 while (le
!= &volumes
) {
3358 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3360 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) && v
->devnum
< Vcb
->superblock
.dev_item
.dev_id
) {
3361 // skipping over device in RAID which isn't the first one
3362 // FIXME - hide this in My Computer
3363 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3370 // FIXME - remove this when we can
3371 if (Vcb
->superblock
.num_devices
> 1) {
3372 WARN("not mounting - multiple devices not yet implemented\n");
3373 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3377 Vcb
->readonly
= FALSE
;
3378 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
3379 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
3380 Vcb
->readonly
= TRUE
;
3383 Vcb
->superblock
.generation
++;
3384 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
3386 Vcb
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
) * Vcb
->superblock
.num_devices
, ALLOC_TAG
);
3387 if (!Vcb
->devices
) {
3388 ERR("out of memory\n");
3389 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3393 Vcb
->devices
[0].devobj
= DeviceToMount
;
3394 RtlCopyMemory(&Vcb
->devices
[0].devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
3396 if (Vcb
->superblock
.num_devices
> 1)
3397 RtlZeroMemory(&Vcb
->devices
[1], sizeof(DEV_ITEM
) * (Vcb
->superblock
.num_devices
- 1));
3399 // FIXME - find other devices, if there are any
3401 TRACE("DeviceToMount = %p\n", DeviceToMount
);
3402 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack
->Parameters
.MountVolume
.Vpb
);
3404 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
3405 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
3407 // find_chunk_root(Vcb);
3408 // Vcb->chunk_root_phys_addr = Vcb->superblock.chunk_tree_addr; // FIXME - map from logical to physical (bootstrapped)
3410 // Vcb->root_tree_phys_addr = logical_to_physical(Vcb, Vcb->superblock.root_tree_addr);
3413 Vcb
->log_to_phys_loaded
= FALSE
;
3415 Vcb
->max_inline
= Vcb
->superblock
.node_size
/ 2;
3417 // Vcb->write_trees = NULL;
3419 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, NULL
);
3421 if (!Vcb
->chunk_root
) {
3422 ERR("Could not load chunk root.\n");
3423 Status
= STATUS_INTERNAL_ERROR
;
3427 InitializeListHead(&Vcb
->sys_chunks
);
3428 Status
= load_sys_chunks(Vcb
);
3429 if (!NT_SUCCESS(Status
)) {
3430 ERR("load_sys_chunks returned %08x\n", Status
);
3434 InitializeListHead(&Vcb
->chunks
);
3435 InitializeListHead(&Vcb
->trees
);
3437 InitializeListHead(&Vcb
->DirNotifyList
);
3439 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
3441 Status
= load_chunk_root(Vcb
);
3442 if (!NT_SUCCESS(Status
)) {
3443 ERR("load_chunk_root returned %08x\n", Status
);
3447 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, NULL
);
3449 if (!Vcb
->root_root
) {
3450 ERR("Could not load root of roots.\n");
3451 Status
= STATUS_INTERNAL_ERROR
;
3455 Status
= look_for_roots(Vcb
);
3456 if (!NT_SUCCESS(Status
)) {
3457 ERR("look_for_roots returned %08x\n", Status
);
3461 Status
= find_chunk_usage(Vcb
);
3462 if (!NT_SUCCESS(Status
)) {
3463 ERR("find_chunk_usage returned %08x\n", Status
);
3467 Vcb
->volume_fcb
= create_fcb();
3468 if (!Vcb
->volume_fcb
) {
3469 ERR("out of memory\n");
3470 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3474 Vcb
->volume_fcb
->Vcb
= Vcb
;
3475 Vcb
->volume_fcb
->sd
= NULL
;
3477 Vcb
->root_fcb
= create_fcb();
3478 if (!Vcb
->root_fcb
) {
3479 ERR("out of memory\n");
3480 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3484 Vcb
->root_fcb
->Vcb
= Vcb
;
3485 Vcb
->root_fcb
->inode
= SUBVOL_ROOT_INODE
;
3486 Vcb
->root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
3488 Vcb
->root_fcb
->full_filename
.Buffer
= ExAllocatePoolWithTag(PagedPool
, sizeof(WCHAR
), ALLOC_TAG
);
3490 if (!Vcb
->root_fcb
->full_filename
.Buffer
) {
3491 ERR("out of memory\n");
3492 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3496 Vcb
->root_fcb
->full_filename
.Buffer
[0] = '\\';
3497 Vcb
->root_fcb
->full_filename
.Length
= Vcb
->root_fcb
->full_filename
.MaximumLength
= sizeof(WCHAR
);
3499 #ifdef DEBUG_FCB_REFCOUNTS
3500 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
3501 WARN("root FCB = %p\n", Vcb
->root_fcb
);
3504 Vcb
->root_fcb
->subvol
= find_default_subvol(Vcb
);
3506 if (!Vcb
->root_fcb
->subvol
) {
3507 ERR("could not find top subvol\n");
3508 Status
= STATUS_INTERNAL_ERROR
;
3512 Vcb
->fcbs
= Vcb
->root_fcb
;
3514 searchkey
.obj_id
= Vcb
->root_fcb
->inode
;
3515 searchkey
.obj_type
= TYPE_INODE_ITEM
;
3516 searchkey
.offset
= 0xffffffffffffffff;
3518 Status
= find_item(Vcb
, Vcb
->root_fcb
->subvol
, &tp
, &searchkey
, FALSE
);
3519 if (!NT_SUCCESS(Status
)) {
3520 ERR("error - find_item returned %08x\n", Status
);
3524 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
3525 ERR("couldn't find INODE_ITEM for root directory\n");
3526 Status
= STATUS_INTERNAL_ERROR
;
3527 free_traverse_ptr(&tp
);
3531 if (tp
.item
->size
> 0)
3532 RtlCopyMemory(&Vcb
->root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
3534 free_traverse_ptr(&tp
);
3536 fcb_get_sd(Vcb
->root_fcb
);
3538 Vcb
->root_fcb
->atts
= get_file_attributes(Vcb
, &Vcb
->root_fcb
->inode_item
, Vcb
->root_fcb
->subvol
, Vcb
->root_fcb
->inode
, Vcb
->root_fcb
->type
,
3541 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3542 Status
= find_disk_holes(Vcb
, &Vcb
->devices
[i
]);
3543 if (!NT_SUCCESS(Status
)) {
3544 ERR("find_disk_holes returned %08x\n", Status
);
3551 // Vcb->StreamFileObject = IoCreateStreamFileObject(NULL,
3552 // Vcb->StorageDevice);
3554 // InitializeListHead(&Vcb->FcbListHead);
3556 // Fcb = NtfsCreateFCB(NULL, Vcb);
3559 // Status = STATUS_INSUFFICIENT_RESOURCES;
3563 // Ccb = ExAllocatePoolWithTag(NonPagedPool,
3564 // sizeof(NTFS_CCB),
3568 // Status = STATUS_INSUFFICIENT_RESOURCES;
3572 // RtlZeroMemory(Ccb, sizeof(NTFS_CCB));
3574 // Ccb->Identifier.Type = NTFS_TYPE_CCB;
3575 // Ccb->Identifier.Size = sizeof(NTFS_TYPE_CCB);
3577 // Vcb->StreamFileObject->FsContext = Fcb;
3578 // Vcb->StreamFileObject->FsContext2 = Ccb;
3579 // Vcb->StreamFileObject->SectionObjectPointer = &Fcb->SectionObjectPointers;
3580 // Vcb->StreamFileObject->PrivateCacheMap = NULL;
3581 // Vcb->StreamFileObject->Vpb = Vcb->Vpb;
3582 // Ccb->PtrFileObject = Vcb->StreamFileObject;
3583 // Fcb->FileObject = Vcb->StreamFileObject;
3584 // Fcb->Vcb = (PDEVICE_EXTENSION)Vcb->StorageDevice;
3586 // Fcb->Flags = FCB_IS_VOLUME_STREAM;
3588 // Fcb->RFCB.FileSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
3589 // Fcb->RFCB.ValidDataLength.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector;
3590 // Fcb->RFCB.AllocationSize.QuadPart = Vcb->NtfsInfo.SectorCount * Vcb->NtfsInfo.BytesPerSector; /* Correct? */
3592 // CcInitializeCacheMap(Vcb->StreamFileObject,
3593 // (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
3595 // &(NtfsGlobalData->CacheMgrCallbacks),
3598 // ExInitializeResourceLite(&Vcb->LogToPhysLock);
3600 KeInitializeSpinLock(&Vcb
->FcbListLock
);
3602 // /* Get serial number */
3603 // NewDeviceObject->Vpb->SerialNumber = Vcb->NtfsInfo.SerialNumber;
3605 // /* Get volume label */
3606 // NewDeviceObject->Vpb->VolumeLabelLength = Vcb->NtfsInfo.VolumeLabelLength;
3607 // RtlCopyMemory(NewDeviceObject->Vpb->VolumeLabel,
3608 // Vcb->NtfsInfo.VolumeLabel,
3609 // Vcb->NtfsInfo.VolumeLabelLength);
3611 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, Vcb
);
3612 if (!NT_SUCCESS(Status
)) {
3613 ERR("PsCreateSystemThread returned %08x\n", Status
);
3617 NewDeviceObject
->Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
3618 Stack
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
3619 Stack
->Parameters
.MountVolume
.Vpb
->RealDevice
= DeviceToMount
;
3620 Stack
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
3621 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
3622 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
3623 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
3624 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
3626 Status
= STATUS_SUCCESS
;
3629 // if (!NT_SUCCESS(Status))
3632 // if (Vcb && Vcb->StreamFileObject)
3633 // ObDereferenceObject(Vcb->StreamFileObject);
3641 // if (NewDeviceObject)
3642 // IoDeleteDevice(NewDeviceObject);
3646 ExReleaseResourceLite(&Vcb
->load_lock
);
3649 if (!NT_SUCCESS(Status
)) {
3652 free_fcb(Vcb
->root_fcb
);
3654 if (Vcb
->volume_fcb
)
3655 free_fcb(Vcb
->volume_fcb
);
3657 ExDeleteResourceLite(&Vcb
->tree_lock
);
3658 ExDeleteResourceLite(&Vcb
->load_lock
);
3659 ExDeleteResourceLite(&Vcb
->fcb_lock
);
3660 ExDeleteResourceLite(&Vcb
->DirResource
);
3663 ExFreePoolWithTag(Vcb
->devices
, ALLOC_TAG
);
3665 RemoveEntryList(&Vcb
->list_entry
);
3668 if (NewDeviceObject
)
3669 IoDeleteDevice(NewDeviceObject
);
3672 TRACE("mount_vol done (status: %lx)\n", Status
);
3677 static NTSTATUS STDCALL
drv_file_system_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3678 PIO_STACK_LOCATION IrpSp
;
3682 TRACE("file system control\n");
3684 FsRtlEnterFileSystem();
3686 top_level
= is_top_level(Irp
);
3688 status
= STATUS_NOT_IMPLEMENTED
;
3690 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3692 Irp
->IoStatus
.Information
= 0;
3694 switch (IrpSp
->MinorFunction
) {
3695 case IRP_MN_MOUNT_VOLUME
:
3696 TRACE("IRP_MN_MOUNT_VOLUME\n");
3698 // Irp->IoStatus.Status = STATUS_SUCCESS;
3699 status
= mount_vol(DeviceObject
, Irp
);
3700 // IrpSp->Parameters.MountVolume.DeviceObject = 0x0badc0de;
3701 // IrpSp->Parameters.MountVolume.Vpb = 0xdeadbeef;
3703 // IoCompleteRequest( Irp, IO_DISK_INCREMENT );
3705 // return Irp->IoStatus.Status;
3708 case IRP_MN_KERNEL_CALL
:
3709 TRACE("IRP_MN_KERNEL_CALL\n");
3712 case IRP_MN_LOAD_FILE_SYSTEM
:
3713 TRACE("IRP_MN_LOAD_FILE_SYSTEM\n");
3716 case IRP_MN_USER_FS_REQUEST
:
3717 TRACE("IRP_MN_USER_FS_REQUEST\n");
3719 status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, TRUE
);
3722 case IRP_MN_VERIFY_VOLUME
:
3723 TRACE("IRP_MN_VERIFY_VOLUME\n");
3727 WARN("unknown minor %u\n", IrpSp
->MinorFunction
);
3732 Irp
->IoStatus
.Status
= status
;
3734 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3737 IoSetTopLevelIrp(NULL
);
3739 FsRtlExitFileSystem();
3744 static NTSTATUS STDCALL
drv_lock_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3746 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3747 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
3750 FsRtlEnterFileSystem();
3752 top_level
= is_top_level(Irp
);
3754 TRACE("lock control\n");
3756 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
3759 IoSetTopLevelIrp(NULL
);
3761 FsRtlExitFileSystem();
3766 static NTSTATUS STDCALL
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3768 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3769 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
3770 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3774 FIXME("STUB: device control\n");
3776 FsRtlEnterFileSystem();
3778 top_level
= is_top_level(Irp
);
3780 Irp
->IoStatus
.Information
= 0;
3782 WARN("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
3785 ERR("FileObject was NULL\n");
3786 Status
= STATUS_INVALID_PARAMETER
;
3791 fcb
= FileObject
->FsContext
;
3794 ERR("FCB was NULL\n");
3795 Status
= STATUS_INVALID_PARAMETER
;
3799 if (fcb
== Vcb
->volume_fcb
) {
3800 FIXME("FIXME - pass through\n");
3801 Status
= STATUS_NOT_IMPLEMENTED
;
3803 TRACE("filename = %.*S\n", fcb
->full_filename
.Length
/ sizeof(WCHAR
), fcb
->full_filename
.Buffer
);
3805 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
3806 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
:
3807 TRACE("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n");
3808 Status
= STATUS_INVALID_PARAMETER
;
3812 WARN("unknown control code %x (DeviceType = %x, Access = %x, Function = %x, Method = %x)\n",
3813 IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
, (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
& 0xff0000) >> 16,
3814 (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
& 0xc000) >> 14, (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
& 0x3ffc) >> 2,
3815 IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
& 0x3);
3816 Status
= STATUS_INVALID_PARAMETER
;
3822 Irp
->IoStatus
.Status
= Status
;
3824 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
3827 IoSetTopLevelIrp(NULL
);
3829 FsRtlExitFileSystem();
3834 static NTSTATUS STDCALL
drv_shutdown(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3840 FsRtlEnterFileSystem();
3842 top_level
= is_top_level(Irp
);
3844 Status
= STATUS_SUCCESS
;
3846 while (!IsListEmpty(&VcbList
)) {
3847 LIST_ENTRY
* le
= RemoveHeadList(&VcbList
);
3848 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
3850 TRACE("shutting down Vcb %p\n", Vcb
);
3855 Irp
->IoStatus
.Status
= Status
;
3856 Irp
->IoStatus
.Information
= 0;
3858 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
3861 IoSetTopLevelIrp(NULL
);
3863 FsRtlExitFileSystem();
3868 static NTSTATUS STDCALL
drv_pnp(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3869 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3870 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3874 FIXME("STUB: pnp\n");
3876 FsRtlEnterFileSystem();
3878 top_level
= is_top_level(Irp
);
3880 Status
= STATUS_NOT_IMPLEMENTED
;
3882 switch (IrpSp
->MinorFunction
) {
3883 case IRP_MN_CANCEL_REMOVE_DEVICE
:
3884 TRACE(" IRP_MN_CANCEL_REMOVE_DEVICE\n");
3887 case IRP_MN_QUERY_REMOVE_DEVICE
:
3888 TRACE(" IRP_MN_QUERY_REMOVE_DEVICE\n");
3891 case IRP_MN_REMOVE_DEVICE
:
3892 TRACE(" IRP_MN_REMOVE_DEVICE\n");
3895 case IRP_MN_START_DEVICE
:
3896 TRACE(" IRP_MN_START_DEVICE\n");
3899 case IRP_MN_SURPRISE_REMOVAL
:
3900 TRACE(" IRP_MN_SURPRISE_REMOVAL\n");
3903 case IRP_MN_QUERY_DEVICE_RELATIONS
:
3904 TRACE(" IRP_MN_QUERY_DEVICE_RELATIONS\n");
3908 WARN("Unrecognized minor function 0x%x\n", IrpSp
->MinorFunction
);
3912 // Irp->IoStatus.Status = Status;
3913 // Irp->IoStatus.Information = 0;
3915 IoSkipCurrentIrpStackLocation(Irp
);
3917 Status
= IoCallDriver(Vcb
->devices
[0].devobj
, Irp
);
3919 // IoCompleteRequest(Irp, IO_NO_INCREMENT);
3922 IoSetTopLevelIrp(NULL
);
3924 FsRtlExitFileSystem();
3930 static void STDCALL
init_serial() {
3933 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
3934 if (!NT_SUCCESS(Status
)) {
3935 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
3941 static void STDCALL
check_cpu() {
3942 unsigned int cpuInfo
[4];
3944 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
3945 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
3947 __cpuid(cpuInfo
, 1);
3948 have_sse42
= cpuInfo
[2] & (1 << 20);
3952 TRACE("SSE4.2 is supported\n");
3954 TRACE("SSE4.2 not supported\n");
3958 static void STDCALL
read_registry(PUNICODE_STRING regpath
) {
3961 OBJECT_ATTRIBUTES oa
;
3965 ULONG kvfilen
, retlen
, i
;
3966 KEY_VALUE_FULL_INFORMATION
* kvfi
;
3968 const WCHAR mappings
[] = L
"\\Mappings";
3970 static WCHAR def_log_file
[] = L
"\\??\\C:\\btrfs.log";
3973 path
= ExAllocatePoolWithTag(PagedPool
, regpath
->Length
+ (wcslen(mappings
) * sizeof(WCHAR
)), ALLOC_TAG
);
3975 ERR("out of memory\n");
3979 RtlCopyMemory(path
, regpath
->Buffer
, regpath
->Length
);
3980 RtlCopyMemory((UINT8
*)path
+ regpath
->Length
, mappings
, wcslen(mappings
) * sizeof(WCHAR
));
3983 us
.Length
= us
.MaximumLength
= regpath
->Length
+ ((USHORT
)wcslen(mappings
) * sizeof(WCHAR
));
3985 InitializeObjectAttributes(&oa
, &us
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
3987 // FIXME - keep open and do notify for changes
3988 Status
= ZwCreateKey(&h
, KEY_QUERY_VALUE
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
3990 if (!NT_SUCCESS(Status
)) {
3991 ERR("ZwCreateKey returned %08x\n", Status
);
3996 if (dispos
== REG_OPENED_EXISTING_KEY
) {
3997 kvfilen
= sizeof(KEY_VALUE_FULL_INFORMATION
) + 256;
3998 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
4001 ERR("out of memory\n");
4009 Status
= ZwEnumerateValueKey(h
, i
, KeyValueFullInformation
, kvfi
, kvfilen
, &retlen
);
4011 if (NT_SUCCESS(Status
) && kvfi
->DataLength
> 0) {
4014 RtlCopyMemory(&val
, (UINT8
*)kvfi
+ kvfi
->DataOffset
, min(kvfi
->DataLength
, sizeof(UINT32
)));
4016 TRACE("entry %u = %.*S = %u\n", i
, kvfi
->NameLength
/ sizeof(WCHAR
), kvfi
->Name
, val
);
4018 add_user_mapping(kvfi
->Name
, kvfi
->NameLength
/ sizeof(WCHAR
), val
);
4022 } while (Status
!= STATUS_NO_MORE_ENTRIES
);
4030 InitializeObjectAttributes(&oa
, regpath
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4032 Status
= ZwCreateKey(&h
, KEY_QUERY_VALUE
, &oa
, 0, NULL
, REG_OPTION_NON_VOLATILE
, &dispos
);
4034 if (!NT_SUCCESS(Status
)) {
4035 ERR("ZwCreateKey returned %08x\n", Status
);
4039 RtlInitUnicodeString(&us
, L
"DebugLogLevel");
4043 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4045 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
4046 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
4049 ERR("out of memory\n");
4054 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4056 if (NT_SUCCESS(Status
)) {
4057 if (kvfi
->Type
== REG_DWORD
&& kvfi
->DataLength
>= sizeof(UINT32
)) {
4058 RtlCopyMemory(&debug_log_level
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, sizeof(UINT32
));
4060 Status
= ZwDeleteValueKey(h
, &us
);
4061 if (!NT_SUCCESS(Status
)) {
4062 ERR("ZwDeleteValueKey returned %08x\n", Status
);
4065 Status
= ZwSetValueKey(h
, &us
, 0, REG_DWORD
, &debug_log_level
, sizeof(debug_log_level
));
4066 if (!NT_SUCCESS(Status
)) {
4067 ERR("ZwSetValueKey reutrned %08x\n", Status
);
4073 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
4074 Status
= ZwSetValueKey(h
, &us
, 0, REG_DWORD
, &debug_log_level
, sizeof(debug_log_level
));
4076 if (!NT_SUCCESS(Status
)) {
4077 ERR("ZwSetValueKey reutrned %08x\n", Status
);
4080 ERR("ZwQueryValueKey returned %08x\n", Status
);
4083 RtlInitUnicodeString(&us
, L
"LogDevice");
4087 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4089 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
4090 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
4093 ERR("out of memory\n");
4098 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4100 if (NT_SUCCESS(Status
)) {
4101 if ((kvfi
->Type
== REG_SZ
|| kvfi
->Type
== REG_EXPAND_SZ
) && kvfi
->DataLength
>= sizeof(WCHAR
)) {
4102 log_device
.Length
= log_device
.MaximumLength
= kvfi
->DataLength
;
4103 log_device
.Buffer
= ExAllocatePoolWithTag(PagedPool
, kvfi
->DataLength
, ALLOC_TAG
);
4105 if (!log_device
.Buffer
) {
4106 ERR("out of memory\n");
4112 RtlCopyMemory(log_device
.Buffer
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, kvfi
->DataLength
);
4114 if (log_device
.Buffer
[(log_device
.Length
/ sizeof(WCHAR
)) - 1] == 0)
4115 log_device
.Length
-= sizeof(WCHAR
);
4117 ERR("LogDevice was type %u, length %u\n", kvfi
->Type
, kvfi
->DataLength
);
4119 Status
= ZwDeleteValueKey(h
, &us
);
4120 if (!NT_SUCCESS(Status
)) {
4121 ERR("ZwDeleteValueKey returned %08x\n", Status
);
4127 } else if (Status
!= STATUS_OBJECT_NAME_NOT_FOUND
) {
4128 ERR("ZwQueryValueKey returned %08x\n", Status
);
4131 RtlInitUnicodeString(&us
, L
"LogFile");
4135 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4137 if ((Status
== STATUS_BUFFER_TOO_SMALL
|| Status
== STATUS_BUFFER_OVERFLOW
) && kvfilen
> 0) {
4138 kvfi
= ExAllocatePoolWithTag(PagedPool
, kvfilen
, ALLOC_TAG
);
4141 ERR("out of memory\n");
4146 Status
= ZwQueryValueKey(h
, &us
, KeyValueFullInformation
, kvfi
, kvfilen
, &kvfilen
);
4148 if (NT_SUCCESS(Status
)) {
4149 if ((kvfi
->Type
== REG_SZ
|| kvfi
->Type
== REG_EXPAND_SZ
) && kvfi
->DataLength
>= sizeof(WCHAR
)) {
4150 log_file
.Length
= log_file
.MaximumLength
= kvfi
->DataLength
;
4151 log_file
.Buffer
= ExAllocatePoolWithTag(PagedPool
, kvfi
->DataLength
, ALLOC_TAG
);
4153 if (!log_file
.Buffer
) {
4154 ERR("out of memory\n");
4160 RtlCopyMemory(log_file
.Buffer
, ((UINT8
*)kvfi
) + kvfi
->DataOffset
, kvfi
->DataLength
);
4162 if (log_file
.Buffer
[(log_file
.Length
/ sizeof(WCHAR
)) - 1] == 0)
4163 log_file
.Length
-= sizeof(WCHAR
);
4165 ERR("LogFile was type %u, length %u\n", kvfi
->Type
, kvfi
->DataLength
);
4167 Status
= ZwDeleteValueKey(h
, &us
);
4168 if (!NT_SUCCESS(Status
)) {
4169 ERR("ZwDeleteValueKey returned %08x\n", Status
);
4175 } else if (Status
== STATUS_OBJECT_NAME_NOT_FOUND
) {
4176 Status
= ZwSetValueKey(h
, &us
, 0, REG_SZ
, def_log_file
, (wcslen(def_log_file
) + 1) * sizeof(WCHAR
));
4178 if (!NT_SUCCESS(Status
)) {
4179 ERR("ZwSetValueKey returned %08x\n", Status
);
4182 ERR("ZwQueryValueKey returned %08x\n", Status
);
4185 if (log_file
.Length
== 0) {
4186 log_file
.Length
= log_file
.MaximumLength
= wcslen(def_log_file
) * sizeof(WCHAR
);
4187 log_file
.Buffer
= ExAllocatePoolWithTag(PagedPool
, log_file
.MaximumLength
, ALLOC_TAG
);
4189 if (!log_file
.Buffer
) {
4190 ERR("out of memory\n");
4195 RtlCopyMemory(log_file
.Buffer
, def_log_file
, log_file
.Length
);
4203 static void init_logging() {
4204 if (log_device
.Length
> 0)
4206 else if (log_file
.Length
> 0) {
4208 OBJECT_ATTRIBUTES oa
;
4209 IO_STATUS_BLOCK iosb
;
4214 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4216 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
4217 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
4219 if (!NT_SUCCESS(Status
)) {
4220 ERR("ZwCreateFile returned %08x\n", Status
);
4224 if (iosb
.Information
== FILE_OPENED
) { // already exists
4225 FILE_STANDARD_INFORMATION fsi
;
4226 FILE_POSITION_INFORMATION fpi
;
4228 static char delim
[] = "\n---\n";
4230 // move to end of file
4232 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
4234 if (!NT_SUCCESS(Status
)) {
4235 ERR("ZwQueryInformationFile returned %08x\n", Status
);
4239 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
4241 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
4243 if (!NT_SUCCESS(Status
)) {
4244 ERR("ZwSetInformationFile returned %08x\n", Status
);
4248 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, delim
, strlen(delim
), NULL
, NULL
);
4250 if (!NT_SUCCESS(Status
)) {
4251 ERR("ZwWriteFile returned %08x\n", Status
);
4256 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
4259 ERR("out of memory\n");
4263 KeQuerySystemTime(&time
);
4265 RtlTimeToTimeFields(&time
, &tf
);
4267 sprintf(dateline
, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
4269 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, strlen(dateline
), NULL
, NULL
);
4271 if (!NT_SUCCESS(Status
)) {
4272 ERR("ZwWriteFile returned %08x\n", Status
);
4276 ExFreePool(dateline
);
4281 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
) {
4283 PDEVICE_OBJECT DeviceObject
;
4284 UNICODE_STRING device_nameW
;
4285 UNICODE_STRING dosdevice_nameW
;
4287 InitializeListHead(&uid_map_list
);
4289 log_device
.Buffer
= NULL
;
4290 log_device
.Length
= log_device
.MaximumLength
= 0;
4291 log_file
.Buffer
= NULL
;
4292 log_file
.Length
= log_file
.MaximumLength
= 0;
4294 read_registry(RegistryPath
);
4297 if (debug_log_level
> 0)
4303 TRACE("DriverEntry\n");
4309 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4311 drvobj
= DriverObject
;
4313 DriverObject
->DriverUnload
= DriverUnload
;
4315 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
4316 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
4317 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
4318 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
4319 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
4320 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
4321 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
4322 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
4323 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
4324 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
4325 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
4326 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
4327 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
4328 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
4329 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
4330 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
4331 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
4332 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
4333 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
4334 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
4336 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
4338 device_nameW
.Buffer
= device_name
;
4339 device_nameW
.Length
= device_nameW
.MaximumLength
= (USHORT
)wcslen(device_name
) * sizeof(WCHAR
);
4340 dosdevice_nameW
.Buffer
= dosdevice_name
;
4341 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
4343 Status
= IoCreateDevice(DriverObject
, 0, &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
, 0, FALSE
, &DeviceObject
);
4344 if (!NT_SUCCESS(Status
)) {
4345 ERR("IoCreateDevice returned %08x\n", Status
);
4349 devobj
= DeviceObject
;
4351 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4353 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
4354 if (!NT_SUCCESS(Status
)) {
4355 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
4359 Status
= init_cache();
4360 if (!NT_SUCCESS(Status
)) {
4361 ERR("init_cache returned %08x\n", Status
);
4365 InitializeListHead(&volumes
);
4366 look_for_vols(&volumes
);
4368 InitializeListHead(&VcbList
);
4369 ExInitializeResourceLite(&global_loading_lock
);
4371 IoRegisterFileSystem(DeviceObject
);
4373 return STATUS_SUCCESS
;