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"
38 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
39 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
40 #define COMPAT_RO_SUPPORTED 0
42 static WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
43 static WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
45 PDRIVER_OBJECT drvobj
;
46 PDEVICE_OBJECT devobj
;
48 BOOL have_sse42
= FALSE
;
51 LIST_ENTRY uid_map_list
;
54 ERESOURCE global_loading_lock
;
55 UINT32 debug_log_level
= 0;
56 BOOL log_started
= FALSE
;
57 UNICODE_STRING log_device
, log_file
, registry_path
;
60 PFILE_OBJECT comfo
= NULL
;
61 PDEVICE_OBJECT comdo
= NULL
;
62 HANDLE log_handle
= NULL
;
65 int __security_cookie
= __LINE__
;
67 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
);
75 static NTSTATUS STDCALL
dbg_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
76 read_context
* context
= conptr
;
78 // DbgPrint("dbg_completion\n");
80 context
->iosb
= Irp
->IoStatus
;
81 KeSetEvent(&context
->Event
, 0, FALSE
);
83 // return STATUS_SUCCESS;
84 return STATUS_MORE_PROCESSING_REQUIRED
;
87 #ifdef DEBUG_LONG_MESSAGES
88 void STDCALL
_debug_message(const char* func
, const char* file
, unsigned int line
, char* s
, ...) {
90 void STDCALL
_debug_message(const char* func
, char* s
, ...) {
93 PIO_STACK_LOCATION IrpSp
;
97 char *buf2
= NULL
, *buf
;
98 read_context
* context
= NULL
;
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 UINT64
sector_align( UINT64 NumberToBeAligned
, UINT64 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
);
312 if (registry_path
.Buffer
)
313 ExFreePool(registry_path
.Buffer
);
316 BOOL STDCALL
get_last_inode(device_extension
* Vcb
, root
* r
) {
318 traverse_ptr tp
, prev_tp
;
322 searchkey
.obj_id
= 0xffffffffffffffff;
323 searchkey
.obj_type
= 0xff;
324 searchkey
.offset
= 0xffffffffffffffff;
326 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
);
327 if (!NT_SUCCESS(Status
)) {
328 ERR("error - find_item returned %08x\n", Status
);
332 while (find_prev_item(Vcb
, &tp
, &prev_tp
, FALSE
)) {
335 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
337 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
338 r
->lastinode
= tp
.item
->key
.obj_id
;
339 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
344 r
->lastinode
= SUBVOL_ROOT_INODE
;
346 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
351 BOOL STDCALL
get_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
** data
, UINT16
* datalen
) {
358 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
360 searchkey
.obj_id
= inode
;
361 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
362 searchkey
.offset
= crc32
;
364 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
365 if (!NT_SUCCESS(Status
)) {
366 ERR("error - find_item returned %08x\n", Status
);
370 if (keycmp(&tp
.item
->key
, &searchkey
)) {
371 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
375 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
376 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
));
380 xa
= (DIR_ITEM
*)tp
.item
->data
;
381 size
= tp
.item
->size
;
384 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
385 WARN("(%llx,%x,%llx) is truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
389 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
390 TRACE("found xattr %s in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
395 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
397 ERR("out of memory\n");
401 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
408 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
412 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
417 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
422 NTSTATUS
add_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT32 crc32
, DIR_ITEM
* di
, ULONG disize
, LIST_ENTRY
* rollback
) {
428 searchkey
.obj_id
= inode
;
429 searchkey
.obj_type
= TYPE_DIR_ITEM
;
430 searchkey
.offset
= crc32
;
432 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
433 if (!NT_SUCCESS(Status
)) {
434 ERR("error - find_item returned %08x\n", Status
);
438 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
439 ULONG maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
441 if (tp
.item
->size
+ disize
> maxlen
) {
442 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp
.item
->size
, disize
, maxlen
);
443 return STATUS_INTERNAL_ERROR
;
446 di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ disize
, ALLOC_TAG
);
448 ERR("out of memory\n");
449 return STATUS_INSUFFICIENT_RESOURCES
;
452 if (tp
.item
->size
> 0)
453 RtlCopyMemory(di2
, tp
.item
->data
, tp
.item
->size
);
455 RtlCopyMemory(di2
+ tp
.item
->size
, di
, disize
);
457 delete_tree_item(Vcb
, &tp
, rollback
);
459 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di2
, tp
.item
->size
+ disize
, NULL
, rollback
);
463 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di
, disize
, NULL
, rollback
);
466 return STATUS_SUCCESS
;
469 static NTSTATUS STDCALL
drv_close(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
471 PIO_STACK_LOCATION IrpSp
;
472 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
477 FsRtlEnterFileSystem();
479 top_level
= is_top_level(Irp
);
481 if (DeviceObject
== devobj
|| (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
)) {
482 TRACE("Closing file system\n");
483 Status
= STATUS_SUCCESS
;
487 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
489 // FIXME - unmount if called for volume
490 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
492 Status
= close_file(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
495 Irp
->IoStatus
.Status
= Status
;
496 Irp
->IoStatus
.Information
= 0;
498 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
501 IoSetTopLevelIrp(NULL
);
503 FsRtlExitFileSystem();
505 TRACE("returning %08x\n", Status
);
510 static NTSTATUS STDCALL
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
513 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
515 FsRtlEnterFileSystem();
517 top_level
= is_top_level(Irp
);
519 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
520 Status
= part0_passthrough(DeviceObject
, Irp
);
524 FIXME("STUB: query ea\n");
525 Status
= STATUS_NOT_IMPLEMENTED
;
527 Irp
->IoStatus
.Status
= Status
;
528 Irp
->IoStatus
.Information
= 0;
530 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
534 IoSetTopLevelIrp(NULL
);
536 FsRtlExitFileSystem();
541 static NTSTATUS STDCALL
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
543 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
546 FsRtlEnterFileSystem();
548 top_level
= is_top_level(Irp
);
550 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
551 Status
= part0_passthrough(DeviceObject
, Irp
);
555 FIXME("STUB: set ea\n");
556 Status
= STATUS_NOT_IMPLEMENTED
;
559 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
561 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
563 Irp
->IoStatus
.Status
= Status
;
564 Irp
->IoStatus
.Information
= 0;
566 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
570 IoSetTopLevelIrp(NULL
);
572 FsRtlExitFileSystem();
577 static NTSTATUS STDCALL
drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
579 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
580 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
581 fcb
* fcb
= FileObject
->FsContext
;
582 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
585 TRACE("flush buffers\n");
587 FsRtlEnterFileSystem();
589 top_level
= is_top_level(Irp
);
591 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
592 Status
= part0_passthrough(DeviceObject
, Irp
);
596 Status
= STATUS_SUCCESS
;
597 Irp
->IoStatus
.Status
= Status
;
598 Irp
->IoStatus
.Information
= 0;
600 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
601 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
603 if (fcb
->Header
.PagingIoResource
) {
604 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
605 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
608 Status
= Irp
->IoStatus
.Status
;
611 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
615 IoSetTopLevelIrp(NULL
);
617 FsRtlExitFileSystem();
622 static void calculate_total_space(device_extension
* Vcb
, LONGLONG
* totalsize
, LONGLONG
* freespace
) {
625 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
)
630 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) / factor
;
631 *freespace
= ((Vcb
->superblock
.total_bytes
- Vcb
->superblock
.bytes_used
) / Vcb
->superblock
.sector_size
) / factor
;
634 static NTSTATUS STDCALL
drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
635 PIO_STACK_LOCATION IrpSp
;
637 ULONG BytesCopied
= 0;
638 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
642 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
643 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
644 // which prevents UAC from working.
645 // FIXME - only lie if we detect that we're being called by mpr.dll
647 WCHAR
* fs_name
= L
"NTFS";
648 ULONG fs_name_len
= 4 * sizeof(WCHAR
);
650 WCHAR
* fs_name
= L
"Btrfs";
651 ULONG fs_name_len
= 5 * sizeof(WCHAR
);
654 TRACE("query volume information\n");
656 FsRtlEnterFileSystem();
657 top_level
= is_top_level(Irp
);
659 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
660 Status
= part0_passthrough(DeviceObject
, Irp
);
664 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
666 Status
= STATUS_NOT_IMPLEMENTED
;
668 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
669 case FileFsAttributeInformation
:
671 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
672 BOOL overflow
= FALSE
;
673 ULONG orig_fs_name_len
= fs_name_len
;
675 TRACE("FileFsAttributeInformation\n");
677 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
678 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
679 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
686 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
687 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
688 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
;
690 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
692 // should also be FILE_FILE_COMPRESSION when supported
693 data
->MaximumComponentNameLength
= 255; // FIXME - check
694 data
->FileSystemNameLength
= orig_fs_name_len
;
695 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
697 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
698 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
702 case FileFsControlInformation
:
703 FIXME("STUB: FileFsControlInformation\n");
706 case FileFsDeviceInformation
:
707 FIXME("STUB: FileFsDeviceInformation\n");
710 case FileFsDriverPathInformation
:
711 FIXME("STUB: FileFsDriverPathInformation\n");
714 case FileFsFullSizeInformation
:
716 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
718 TRACE("FileFsFullSizeInformation\n");
720 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
721 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
722 ffsi
->SectorsPerAllocationUnit
= 1;
723 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
725 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
726 Status
= STATUS_SUCCESS
;
731 case FileFsObjectIdInformation
:
733 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
735 TRACE("FileFsObjectIdInformation\n");
737 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
738 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
740 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
741 Status
= STATUS_SUCCESS
;
746 case FileFsSizeInformation
:
748 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
750 TRACE("FileFsSizeInformation\n");
752 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->AvailableAllocationUnits
.QuadPart
);
753 ffsi
->SectorsPerAllocationUnit
= 1;
754 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
756 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
757 Status
= STATUS_SUCCESS
;
762 case FileFsVolumeInformation
:
764 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
765 FILE_FS_VOLUME_INFORMATION ffvi
;
766 BOOL overflow
= FALSE
;
767 ULONG label_len
, orig_label_len
;
769 TRACE("FileFsVolumeInformation\n");
770 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
772 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
774 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
775 RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
776 orig_label_len
= label_len
;
778 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
779 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
780 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
787 TRACE("label_len = %u\n", label_len
);
789 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
790 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];
791 ffvi
.VolumeLabelLength
= orig_label_len
;
792 ffvi
.SupportsObjects
= FALSE
;
794 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
799 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
800 RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
801 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
804 ExReleaseResourceLite(&Vcb
->tree_lock
);
806 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
807 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
812 Status
= STATUS_INVALID_PARAMETER
;
813 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
817 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
818 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
819 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
820 // Status = STATUS_BUFFER_OVERFLOW;
823 Irp
->IoStatus
.Status
= Status
;
825 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
826 Irp
->IoStatus
.Information
= 0;
828 Irp
->IoStatus
.Information
= BytesCopied
;
830 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
834 IoSetTopLevelIrp(NULL
);
836 FsRtlExitFileSystem();
838 TRACE("query volume information returning %08x\n", Status
);
843 static NTSTATUS STDCALL
read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
844 read_context
* context
= conptr
;
846 // DbgPrint("read_completion\n");
848 context
->iosb
= Irp
->IoStatus
;
849 KeSetEvent(&context
->Event
, 0, FALSE
);
851 // return STATUS_SUCCESS;
852 return STATUS_MORE_PROCESSING_REQUIRED
;
855 // static void test_tree_deletion(device_extension* Vcb) {
856 // KEY searchkey/*, endkey*/;
857 // traverse_ptr tp, next_tp;
860 // searchkey.obj_id = 0x100;
861 // searchkey.obj_type = 0x54;
862 // searchkey.offset = 0xca4ab2f5;
864 // // endkey.obj_id = 0x100;
865 // // endkey.obj_type = 0x60;
866 // // endkey.offset = 0x15a;
869 // while (r && r->id != 0x102)
873 // ERR("error - could not find root\n");
877 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
878 // ERR("error - could not find key\n");
882 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
883 // tp.item->ignore = TRUE;
884 // add_to_tree_cache(tc, tp.tree);
886 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
887 // free_traverse_ptr(&tp);
893 // free_traverse_ptr(&tp);
896 // static void test_tree_splitting(device_extension* Vcb) {
899 // for (i = 0; i < 1000; i++) {
900 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
902 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
906 // static void test_dropping_tree(device_extension* Vcb) {
907 // LIST_ENTRY* le = Vcb->roots.Flink;
909 // while (le != &Vcb->roots) {
910 // root* r = CONTAINING_RECORD(le, root, list_entry);
912 // if (r->id == 0x101) {
913 // RemoveEntryList(&r->list_entry);
914 // InsertTailList(&Vcb->drop_roots, &r->list_entry);
922 NTSTATUS
create_root(device_extension
* Vcb
, UINT64 id
, root
** rootptr
, BOOL no_tree
, UINT64 offset
, LIST_ENTRY
* rollback
) {
928 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
930 ERR("out of memory\n");
931 return STATUS_INSUFFICIENT_RESOURCES
;
934 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
936 ERR("out of memory\n");
938 return STATUS_INSUFFICIENT_RESOURCES
;
942 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
944 ERR("out of memory\n");
945 ExFreePool(r
->nonpaged
);
947 return STATUS_INSUFFICIENT_RESOURCES
;
951 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
953 ERR("out of memory\n");
958 ExFreePool(r
->nonpaged
);
960 return STATUS_INSUFFICIENT_RESOURCES
;
964 r
->treeholder
.address
= 0;
965 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
966 r
->treeholder
.tree
= no_tree
? NULL
: t
;
968 r
->path
.Buffer
= NULL
;
969 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
970 r
->root_item
.num_references
= 1;
971 InitializeListHead(&r
->fcbs
);
973 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
975 // We ask here for a traverse_ptr to the item we're inserting, so we can
976 // copy some of the tree's variables
978 if (!insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, rollback
)) {
979 ERR("insert_tree_item failed\n");
985 ExFreePool(r
->nonpaged
);
987 return STATUS_INTERNAL_ERROR
;
990 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
992 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
995 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
996 t
->header
.address
= 0;
997 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
998 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
999 t
->header
.generation
= Vcb
->superblock
.generation
;
1000 t
->header
.tree_id
= id
;
1001 t
->header
.num_items
= 0;
1002 t
->header
.level
= 0;
1004 t
->has_address
= FALSE
;
1011 InitializeListHead(&t
->itemlist
);
1014 t
->has_new_address
= FALSE
;
1015 t
->flags
= tp
.tree
->flags
;
1017 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1020 Vcb
->need_write
= TRUE
;
1025 return STATUS_SUCCESS
;
1028 // static void test_creating_root(device_extension* Vcb) {
1030 // LIST_ENTRY rollback;
1034 // InitializeListHead(&rollback);
1036 // if (Vcb->root_root->lastinode == 0)
1037 // get_last_inode(Vcb, Vcb->root_root);
1039 // id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
1040 // Status = create_root(Vcb, id, &r, &rollback);
1042 // if (!NT_SUCCESS(Status)) {
1043 // ERR("create_root returned %08x\n", Status);
1044 // do_rollback(Vcb, &rollback);
1046 // Vcb->root_root->lastinode = id;
1047 // clear_rollback(&rollback);
1051 // static void test_alloc_chunk(device_extension* Vcb) {
1052 // LIST_ENTRY rollback;
1055 // InitializeListHead(&rollback);
1057 // c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
1059 // ERR("alloc_chunk failed\n");
1060 // do_rollback(Vcb, &rollback);
1062 // clear_rollback(&rollback);
1066 // static void test_space_list(device_extension* Vcb) {
1077 // static const space_test entries[] = {
1078 // { 0x1000, 0x1000 },
1079 // { 0x3000, 0x2000 },
1080 // { 0x6000, 0x1000 },
1084 // static const space_test tests[] = {
1085 // { 0x0, 0x800, TRUE },
1086 // { 0x1800, 0x400, TRUE },
1087 // { 0x800, 0x2000, TRUE },
1088 // { 0x1000, 0x2000, TRUE },
1089 // { 0x2000, 0x3800, TRUE },
1090 // { 0x800, 0x1000, TRUE },
1091 // { 0x1800, 0x1000, TRUE },
1092 // { 0x5000, 0x800, TRUE },
1093 // { 0x5000, 0x1000, TRUE },
1094 // { 0x7000, 0x1000, TRUE },
1095 // { 0x8000, 0x1000, TRUE },
1096 // { 0x800, 0x800, TRUE },
1097 // { 0x0, 0x3800, TRUE },
1098 // { 0x1000, 0x2800, TRUE },
1099 // { 0x1000, 0x1000, FALSE },
1100 // { 0x800, 0x2000, FALSE },
1101 // { 0x0, 0x3800, FALSE },
1102 // { 0x2800, 0x1000, FALSE },
1103 // { 0x1800, 0x2000, FALSE },
1104 // { 0x3800, 0x1000, FALSE },
1108 // c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
1111 // while (tests[i].length > 0) {
1112 // InitializeListHead(&c->space);
1113 // InitializeListHead(&c->space_size);
1114 // ERR("test %u\n", i);
1117 // while (entries[j].length > 0) {
1118 // space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
1119 // s->address = entries[j].address;
1120 // s->size = entries[j].length;
1121 // InsertTailList(&c->space, &s->list_entry);
1123 // order_space_entry(s, &c->space_size);
1128 // if (tests[i].add)
1129 // space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1131 // space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1133 // le = c->space.Flink;
1134 // while (le != &c->space) {
1135 // space* s = CONTAINING_RECORD(le, space, list_entry);
1137 // ERR("(%llx,%llx)\n", s->address, s->size);
1144 // le = c->space_size.Flink;
1145 // while (le != &c->space_size) {
1146 // space* s = CONTAINING_RECORD(le, space, list_entry_size);
1148 // ERR("(%llx,%llx)\n", s->address, s->size);
1159 static NTSTATUS STDCALL
set_label(device_extension
* Vcb
, FILE_FS_LABEL_INFORMATION
* ffli
) {
1163 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1165 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1166 if (!NT_SUCCESS(Status
))
1169 if (utf8len
> MAX_LABEL_SIZE
) {
1170 Status
= STATUS_INVALID_VOLUME_LABEL
;
1174 // FIXME - check for '/' and '\\' and reject
1176 // utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1178 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
* sizeof(WCHAR
), &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1179 if (!NT_SUCCESS(Status
))
1182 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1184 if (utf8len
< MAX_LABEL_SIZE
* sizeof(WCHAR
))
1185 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, (MAX_LABEL_SIZE
* sizeof(WCHAR
)) - utf8len
);
1187 // test_tree_deletion(Vcb); // TESTING
1188 // test_tree_splitting(Vcb);
1189 // test_dropping_tree(Vcb);
1190 // test_creating_root(Vcb);
1191 // test_alloc_chunk(Vcb);
1192 // test_space_list(Vcb);
1194 Vcb
->need_write
= TRUE
;
1197 ExReleaseResourceLite(&Vcb
->tree_lock
);
1200 TRACE("returning %08x\n", Status
);
1205 static NTSTATUS STDCALL
drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1206 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1207 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1211 TRACE("set volume information\n");
1213 FsRtlEnterFileSystem();
1215 top_level
= is_top_level(Irp
);
1217 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
1218 Status
= part0_passthrough(DeviceObject
, Irp
);
1222 Status
= STATUS_NOT_IMPLEMENTED
;
1224 if (Vcb
->readonly
) {
1225 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1229 if (Vcb
->removing
|| Vcb
->locked
) {
1230 Status
= STATUS_ACCESS_DENIED
;
1234 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1235 case FileFsControlInformation
:
1236 FIXME("STUB: FileFsControlInformation\n");
1239 case FileFsLabelInformation
:
1240 TRACE("FileFsLabelInformation\n");
1242 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1245 case FileFsObjectIdInformation
:
1246 FIXME("STUB: FileFsObjectIdInformation\n");
1250 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1255 Irp
->IoStatus
.Status
= Status
;
1256 Irp
->IoStatus
.Information
= 0;
1258 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1262 IoSetTopLevelIrp(NULL
);
1264 FsRtlExitFileSystem();
1269 NTSTATUS
delete_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 parinode
, UINT32 crc32
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
1274 searchkey
.obj_id
= parinode
;
1275 searchkey
.obj_type
= TYPE_DIR_ITEM
;
1276 searchkey
.offset
= crc32
;
1278 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1279 if (!NT_SUCCESS(Status
)) {
1280 ERR("error - find_item returned %08x\n", Status
);
1284 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1285 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
1286 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
));
1291 di
= (DIR_ITEM
*)tp
.item
->data
;
1292 len
= tp
.item
->size
;
1295 if (di
->n
== utf8
->Length
&& RtlCompareMemory(di
->name
, utf8
->Buffer
, di
->n
) == di
->n
) {
1296 ULONG newlen
= tp
.item
->size
- (sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
);
1298 delete_tree_item(Vcb
, &tp
, rollback
);
1301 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1303 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1306 ERR("out of memory\n");
1307 return STATUS_INSUFFICIENT_RESOURCES
;
1310 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1312 if ((UINT8
*)di
> tp
.item
->data
) {
1313 RtlCopyMemory(newdi
, tp
.item
->data
, (UINT8
*)di
- tp
.item
->data
);
1314 dioff
= newdi
+ ((UINT8
*)di
- tp
.item
->data
);
1319 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
< tp
.item
->size
)
1320 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], tp
.item
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
));
1322 insert_tree_item(Vcb
, subvol
, parinode
, TYPE_DIR_ITEM
, crc32
, newdi
, newlen
, NULL
, rollback
);
1328 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1329 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1333 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32
);
1336 return STATUS_SUCCESS
;
1339 NTSTATUS
delete_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
1342 BOOL changed
= FALSE
;
1345 searchkey
.obj_id
= inode
;
1346 searchkey
.obj_type
= TYPE_INODE_REF
;
1347 searchkey
.offset
= parinode
;
1349 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1350 if (!NT_SUCCESS(Status
)) {
1351 ERR("error - find_item returned %08x\n", Status
);
1355 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1356 if (tp
.item
->size
< sizeof(INODE_REF
)) {
1357 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
));
1362 ir
= (INODE_REF
*)tp
.item
->data
;
1363 len
= tp
.item
->size
;
1368 if (len
< sizeof(INODE_REF
) || len
< sizeof(INODE_REF
) - 1 + ir
->n
) {
1369 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1373 itemlen
= sizeof(INODE_REF
) - sizeof(char) + ir
->n
;
1375 if (ir
->n
== utf8
->Length
&& RtlCompareMemory(ir
->name
, utf8
->Buffer
, ir
->n
) == ir
->n
) {
1376 ULONG newlen
= tp
.item
->size
- itemlen
;
1378 delete_tree_item(Vcb
, &tp
, rollback
);
1382 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1384 UINT8
*newir
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *iroff
;
1387 ERR("out of memory\n");
1388 return STATUS_INSUFFICIENT_RESOURCES
;
1391 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1393 if ((UINT8
*)ir
> tp
.item
->data
) {
1394 RtlCopyMemory(newir
, tp
.item
->data
, (UINT8
*)ir
- tp
.item
->data
);
1395 iroff
= newir
+ ((UINT8
*)ir
- tp
.item
->data
);
1400 if ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
< tp
.item
->size
)
1401 RtlCopyMemory(iroff
, &ir
->name
[ir
->n
], tp
.item
->size
- ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
));
1403 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newir
, newlen
, NULL
, rollback
);
1409 if (len
> itemlen
) {
1411 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
1417 WARN("found INODE_REF entry, but couldn't find filename\n");
1421 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey
.obj_id
, searchkey
.offset
);
1425 return STATUS_SUCCESS
;
1427 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
))
1428 return STATUS_INTERNAL_ERROR
;
1430 searchkey
.obj_id
= inode
;
1431 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1432 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
1434 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1435 if (!NT_SUCCESS(Status
)) {
1436 ERR("error - find_item returned %08x\n", Status
);
1440 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1441 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
1442 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
));
1447 ier
= (INODE_EXTREF
*)tp
.item
->data
;
1448 len
= tp
.item
->size
;
1453 if (len
< sizeof(INODE_EXTREF
) || len
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
1454 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1458 itemlen
= sizeof(INODE_EXTREF
) - sizeof(char) + ier
->n
;
1460 if (ier
->dir
== parinode
&& ier
->n
== utf8
->Length
&& RtlCompareMemory(ier
->name
, utf8
->Buffer
, ier
->n
) == ier
->n
) {
1461 ULONG newlen
= tp
.item
->size
- itemlen
;
1463 delete_tree_item(Vcb
, &tp
, rollback
);
1467 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1469 UINT8
*newier
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *ieroff
;
1472 ERR("out of memory\n");
1473 return STATUS_INSUFFICIENT_RESOURCES
;
1476 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1478 if ((UINT8
*)ier
> tp
.item
->data
) {
1479 RtlCopyMemory(newier
, tp
.item
->data
, (UINT8
*)ier
- tp
.item
->data
);
1480 ieroff
= newier
+ ((UINT8
*)ier
- tp
.item
->data
);
1485 if ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
< tp
.item
->size
)
1486 RtlCopyMemory(ieroff
, &ier
->name
[ier
->n
], tp
.item
->size
- ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
));
1488 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newier
, newlen
, NULL
, rollback
);
1494 if (len
> itemlen
) {
1496 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
1502 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32
)searchkey
.offset
);
1505 return changed
? STATUS_SUCCESS
: STATUS_INTERNAL_ERROR
;
1508 static WCHAR
* file_desc_fcb(fcb
* fcb
) {
1513 if (fcb
->debug_desc
)
1514 return fcb
->debug_desc
;
1516 if (fcb
== fcb
->Vcb
->volume_fcb
)
1517 return L
"volume FCB";
1519 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1520 if (!fcb
->debug_desc
)
1521 return L
"(memory error)";
1523 // I know this is pretty hackish...
1524 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1525 // without the CRT, which breaks drivers.
1527 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1530 as
.Length
= as
.MaximumLength
= strlen(s
);
1532 us
.Buffer
= fcb
->debug_desc
;
1533 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1536 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1538 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1540 return fcb
->debug_desc
;
1543 WCHAR
* file_desc_fileref(file_ref
* fileref
) {
1547 if (fileref
->debug_desc
)
1548 return fileref
->debug_desc
;
1550 Status
= fileref_get_filename(fileref
, &fn
, NULL
);
1551 if (!NT_SUCCESS(Status
)) {
1555 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, fn
.Length
+ sizeof(WCHAR
), ALLOC_TAG
);
1556 if (!fileref
->debug_desc
) {
1557 ExFreePool(fn
.Buffer
);
1558 return L
"(memory error)";
1561 RtlCopyMemory(fileref
->debug_desc
, fn
.Buffer
, fn
.Length
);
1562 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1564 ExFreePool(fn
.Buffer
);
1566 return fileref
->debug_desc
;
1569 WCHAR
* file_desc(PFILE_OBJECT FileObject
) {
1570 fcb
* fcb
= FileObject
->FsContext
;
1571 ccb
* ccb
= FileObject
->FsContext2
;
1572 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1575 return file_desc_fileref(fileref
);
1577 return file_desc_fcb(fcb
);
1580 void send_notification_fileref(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1584 fcb
* fcb
= fileref
->fcb
;
1586 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
);
1587 if (!NT_SUCCESS(Status
)) {
1588 ERR("fileref_get_filename returned %08x\n", Status
);
1592 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1593 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1594 ExFreePool(fn
.Buffer
);
1597 void send_notification_fcb(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1598 fcb
* fcb
= fileref
->fcb
;
1602 // no point looking for hardlinks if st_nlink == 1
1603 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1604 send_notification_fileref(fileref
, filter_match
, action
);
1608 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1610 le
= fcb
->hardlinks
.Flink
;
1611 while (le
!= &fcb
->hardlinks
) {
1612 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1615 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
);
1617 if (!NT_SUCCESS(Status
)) {
1618 ERR("open_fileref_by_inode returned %08x\n", Status
);
1619 } else if (!parfr
->deleted
) {
1621 BOOL found
= FALSE
, deleted
= FALSE
;
1624 le2
= parfr
->children
.Flink
;
1625 while (le2
!= &parfr
->children
) {
1626 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
1628 if (fr2
->index
== hl
->index
) {
1630 deleted
= fr2
->deleted
;
1633 fn
= &fr2
->filepart
;
1645 UNICODE_STRING path
;
1647 Status
= fileref_get_filename(parfr
, &path
, NULL
);
1648 if (!NT_SUCCESS(Status
)) {
1649 ERR("fileref_get_filename returned %08x\n", Status
);
1654 name_offset
= path
.Length
;
1655 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) name_offset
+= sizeof(WCHAR
);
1657 fn2
.Length
= fn2
.MaximumLength
= fn
->Length
+ name_offset
;
1658 fn2
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn2
.MaximumLength
, ALLOC_TAG
);
1660 RtlCopyMemory(fn2
.Buffer
, path
.Buffer
, path
.Length
);
1661 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) fn2
.Buffer
[path
.Length
/ sizeof(WCHAR
)] = '\\';
1662 RtlCopyMemory(&fn2
.Buffer
[name_offset
/ sizeof(WCHAR
)], fn
->Buffer
, fn
->Length
);
1664 TRACE("%.*S\n", fn2
.Length
/ sizeof(WCHAR
), fn2
.Buffer
);
1666 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn2
, name_offset
,
1667 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1669 ExFreePool(fn2
.Buffer
);
1670 ExFreePool(path
.Buffer
);
1674 free_fileref(parfr
);
1680 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1683 void mark_fcb_dirty(fcb
* fcb
) {
1685 #ifdef DEBUG_FCB_REFCOUNTS
1688 dirty_fcb
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fcb
), ALLOC_TAG
);
1691 ExFreePool("out of memory\n");
1697 #ifdef DEBUG_FCB_REFCOUNTS
1698 rc
= InterlockedIncrement(&fcb
->refcount
);
1699 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1701 InterlockedIncrement(&fcb
->refcount
);
1706 ExInterlockedInsertTailList(&fcb
->Vcb
->dirty_fcbs
, &dirt
->list_entry
, &fcb
->Vcb
->dirty_fcbs_lock
);
1709 fcb
->Vcb
->need_write
= TRUE
;
1712 void mark_fileref_dirty(file_ref
* fileref
) {
1713 if (!fileref
->dirty
) {
1714 dirty_fileref
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fileref
), ALLOC_TAG
);
1717 ExFreePool("out of memory\n");
1721 fileref
->dirty
= TRUE
;
1722 increase_fileref_refcount(fileref
);
1724 dirt
->fileref
= fileref
;
1726 ExInterlockedInsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &dirt
->list_entry
, &fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1729 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1732 void _free_fcb(fcb
* fcb
, const char* func
, const char* file
, unsigned int line
) {
1735 rc
= InterlockedDecrement(&fcb
->refcount
);
1737 #ifdef DEBUG_FCB_REFCOUNTS
1738 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1739 #ifdef DEBUG_LONG_MESSAGES
1740 _debug_message(func
, file
, line
, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1742 _debug_message(func
, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1749 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1751 if (fcb
->list_entry
.Flink
)
1752 RemoveEntryList(&fcb
->list_entry
);
1754 if (fcb
->list_entry_all
.Flink
)
1755 RemoveEntryList(&fcb
->list_entry_all
);
1757 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1759 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1760 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1761 ExDeleteResourceLite(&fcb
->nonpaged
->index_lock
);
1762 ExFreePool(fcb
->nonpaged
);
1765 ExFreePool(fcb
->sd
);
1767 if (fcb
->adsxattr
.Buffer
)
1768 ExFreePool(fcb
->adsxattr
.Buffer
);
1770 if (fcb
->reparse_xattr
.Buffer
)
1771 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1773 if (fcb
->adsdata
.Buffer
)
1774 ExFreePool(fcb
->adsdata
.Buffer
);
1776 if (fcb
->debug_desc
)
1777 ExFreePool(fcb
->debug_desc
);
1779 while (!IsListEmpty(&fcb
->extents
)) {
1780 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1781 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1783 ExFreePool(ext
->data
);
1787 while (!IsListEmpty(&fcb
->index_list
)) {
1788 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->index_list
);
1789 index_entry
* ie
= CONTAINING_RECORD(le
, index_entry
, list_entry
);
1791 if (ie
->utf8
.Buffer
) ExFreePool(ie
->utf8
.Buffer
);
1792 if (ie
->filepart_uc
.Buffer
) ExFreePool(ie
->filepart_uc
.Buffer
);
1796 while (!IsListEmpty(&fcb
->hardlinks
)) {
1797 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1798 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1800 if (hl
->name
.Buffer
)
1801 ExFreePool(hl
->name
.Buffer
);
1803 if (hl
->utf8
.Buffer
)
1804 ExFreePool(hl
->utf8
.Buffer
);
1809 FsRtlUninitializeFileLock(&fcb
->lock
);
1812 #ifdef DEBUG_FCB_REFCOUNTS
1813 #ifdef DEBUG_LONG_MESSAGES
1814 _debug_message(func
, file
, line
, "freeing fcb %p\n", fcb
);
1816 _debug_message(func
, "freeing fcb %p\n", fcb
);
1821 void _free_fileref(file_ref
* fr
, const char* func
, const char* file
, unsigned int line
) {
1824 rc
= InterlockedDecrement(&fr
->refcount
);
1826 #ifdef DEBUG_FCB_REFCOUNTS
1827 #ifdef DEBUG_LONG_MESSAGES
1828 _debug_message(func
, file
, line
, "fileref %p: refcount now %i\n", fr
, rc
);
1830 _debug_message(func
, "fileref %p: refcount now %i\n", fr
, rc
);
1836 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1845 ExAcquireResourceExclusiveLite(&fr
->parent
->nonpaged
->children_lock
, TRUE
);
1847 // FIXME - do we need a file_ref lock?
1849 // FIXME - do delete if needed
1851 if (fr
->filepart
.Buffer
)
1852 ExFreePool(fr
->filepart
.Buffer
);
1854 if (fr
->filepart_uc
.Buffer
)
1855 ExFreePool(fr
->filepart_uc
.Buffer
);
1857 if (fr
->utf8
.Buffer
)
1858 ExFreePool(fr
->utf8
.Buffer
);
1861 ExFreePool(fr
->debug_desc
);
1863 ExDeleteResourceLite(&fr
->nonpaged
->children_lock
);
1865 ExFreePool(fr
->nonpaged
);
1867 // FIXME - throw error if children not empty
1869 if (fr
->fcb
->fileref
== fr
)
1870 fr
->fcb
->fileref
= NULL
;
1872 if (fr
->list_entry
.Flink
)
1873 RemoveEntryList(&fr
->list_entry
);
1876 ExReleaseResourceLite(&fr
->parent
->nonpaged
->children_lock
);
1877 free_fileref(fr
->parent
);
1884 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
1887 file_ref
* fileref
= NULL
;
1889 TRACE("FileObject = %p\n", FileObject
);
1891 fcb
= FileObject
->FsContext
;
1893 TRACE("FCB was NULL, returning success\n");
1894 return STATUS_SUCCESS
;
1897 ccb
= FileObject
->FsContext2
;
1899 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1901 // FIXME - make sure notification gets sent if file is being deleted
1904 if (ccb
->query_string
.Buffer
)
1905 RtlFreeUnicodeString(&ccb
->query_string
);
1907 if (ccb
->filename
.Buffer
)
1908 ExFreePool(ccb
->filename
.Buffer
);
1910 // FIXME - use refcounts for fileref
1911 fileref
= ccb
->fileref
;
1916 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1918 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1921 free_fileref(fileref
);
1925 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1927 return STATUS_SUCCESS
;
1930 void STDCALL
uninit(device_extension
* Vcb
, BOOL flush
) {
1933 LIST_ENTRY rollback
;
1939 RemoveEntryList(&Vcb
->list_entry
);
1942 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1943 if (!NT_SUCCESS(Status
))
1944 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
1947 InitializeListHead(&rollback
);
1949 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1951 if (Vcb
->need_write
)
1952 do_write(Vcb
, &rollback
);
1956 clear_rollback(&rollback
);
1958 ExReleaseResourceLite(&Vcb
->tree_lock
);
1961 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
1962 Vcb
->threads
.threads
[i
].quit
= TRUE
;
1963 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
1965 KeWaitForSingleObject(&Vcb
->threads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
1967 ZwClose(Vcb
->threads
.threads
[i
].handle
);
1970 ExFreePool(Vcb
->threads
.threads
);
1972 Vcb
->removing
= TRUE
;
1975 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
1976 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
1978 free_fcb(Vcb
->volume_fcb
);
1981 ObDereferenceObject(Vcb
->root_file
);
1983 le
= Vcb
->chunks
.Flink
;
1984 while (le
!= &Vcb
->chunks
) {
1985 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1995 while (!IsListEmpty(&Vcb
->roots
)) {
1996 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->roots
);
1997 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
1999 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
2000 ExFreePool(r
->nonpaged
);
2004 while (!IsListEmpty(&Vcb
->chunks
)) {
2007 le
= RemoveHeadList(&Vcb
->chunks
);
2008 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2010 while (!IsListEmpty(&c
->space
)) {
2011 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2012 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2017 while (!IsListEmpty(&c
->deleting
)) {
2018 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
2019 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2025 ExFreePool(c
->devices
);
2030 ExDeleteResourceLite(&c
->nonpaged
->lock
);
2031 ExDeleteResourceLite(&c
->nonpaged
->changed_extents_lock
);
2033 ExFreePool(c
->nonpaged
);
2034 ExFreePool(c
->chunk_item
);
2038 // FIXME - free any open fcbs?
2040 while (!IsListEmpty(&Vcb
->sector_checksums
)) {
2041 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->sector_checksums
);
2042 changed_sector
* cs
= (changed_sector
*)le
;
2047 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2048 while (!IsListEmpty(&Vcb
->devices
[i
].space
)) {
2049 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->devices
[i
].space
);
2050 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
2056 ExFreePool(Vcb
->devices
);
2058 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2059 ExDeleteResourceLite(&Vcb
->load_lock
);
2060 ExDeleteResourceLite(&Vcb
->tree_lock
);
2061 ExDeleteResourceLite(&Vcb
->checksum_lock
);
2062 ExDeleteResourceLite(&Vcb
->chunk_lock
);
2064 ZwClose(Vcb
->flush_thread_handle
);
2067 NTSTATUS
delete_fileref(file_ref
* fileref
, PFILE_OBJECT FileObject
, LIST_ENTRY
* rollback
) {
2068 LARGE_INTEGER newlength
, time
;
2072 KeQuerySystemTime(&time
);
2073 win_time_to_unix(time
, &now
);
2075 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2077 if (fileref
->deleted
) {
2078 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2079 return STATUS_SUCCESS
;
2082 fileref
->deleted
= TRUE
;
2083 mark_fileref_dirty(fileref
);
2085 // delete INODE_ITEM (0x1)
2087 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2089 if (!fileref
->fcb
->ads
) {
2090 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2093 mark_fcb_dirty(fileref
->fcb
);
2095 if (fileref
->fcb
->inode_item
.st_nlink
> 1) {
2096 fileref
->fcb
->inode_item
.st_nlink
--;
2097 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2098 fileref
->fcb
->inode_item
.sequence
++;
2099 fileref
->fcb
->inode_item
.st_ctime
= now
;
2101 fileref
->fcb
->deleted
= TRUE
;
2105 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2106 Status
= excise_extents(fileref
->fcb
->Vcb
, fileref
->fcb
, 0, sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
), rollback
);
2107 if (!NT_SUCCESS(Status
)) {
2108 ERR("excise_extents returned %08x\n", Status
);
2109 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2114 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2115 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2116 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2121 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2122 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2123 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2125 CcSetFileSizes(FileObject
, &ccfs
);
2129 le
= fileref
->fcb
->hardlinks
.Flink
;
2130 while (le
!= &fileref
->fcb
->hardlinks
) {
2131 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2133 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->index
) {
2134 RemoveEntryList(&hl
->list_entry
);
2136 if (hl
->name
.Buffer
)
2137 ExFreePool(hl
->name
.Buffer
);
2139 if (hl
->utf8
.Buffer
)
2140 ExFreePool(hl
->utf8
.Buffer
);
2148 } else { // subvolume
2149 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2150 fileref
->fcb
->subvol
->root_item
.num_references
--;
2152 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2154 // FIXME - we need a lock here
2156 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2158 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2162 fileref
->fcb
->deleted
= TRUE
;
2163 mark_fcb_dirty(fileref
->fcb
);
2166 // update INODE_ITEM of parent
2168 TRACE("delete file %.*S\n", fileref
->filepart
.Length
/ sizeof(WCHAR
), fileref
->filepart
.Buffer
);
2169 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2170 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2171 fileref
->parent
->fcb
->inode_item
.st_size
-= fileref
->utf8
.Length
* 2;
2172 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2173 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2174 fileref
->parent
->fcb
->inode_item
.sequence
++;
2175 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2176 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2177 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2179 mark_fcb_dirty(fileref
->parent
->fcb
);
2181 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
2183 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2184 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2186 if (FileObject
&& FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fileref
->fcb
->nonpaged
->segment_object
.DataSectionObject
)
2187 CcPurgeCacheSection(&fileref
->fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2189 newlength
.QuadPart
= 0;
2191 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2192 TRACE("CcUninitializeCacheMap failed\n");
2194 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2196 return STATUS_SUCCESS
;
2199 static NTSTATUS STDCALL
drv_cleanup(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2201 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2202 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2203 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2209 FsRtlEnterFileSystem();
2211 top_level
= is_top_level(Irp
);
2213 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
2214 Status
= part0_passthrough(DeviceObject
, Irp
);
2218 if (DeviceObject
== devobj
) {
2219 TRACE("closing file system\n");
2220 Status
= STATUS_SUCCESS
;
2224 if (FileObject
&& FileObject
->FsContext
) {
2229 fcb
= FileObject
->FsContext
;
2230 ccb
= FileObject
->FsContext2
;
2231 fileref
= ccb
? ccb
->fileref
: NULL
;
2233 TRACE("cleanup called for FileObject %p\n", FileObject
);
2234 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb
, file_desc(FileObject
), fcb
->refcount
, fcb
->open_count
);
2236 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2238 FsRtlNotifyCleanup(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, ccb
);
2240 oc
= InterlockedDecrement(&fcb
->open_count
);
2241 #ifdef DEBUG_FCB_REFCOUNTS
2242 ERR("fcb %p: open_count now %i\n", fcb
, oc
);
2245 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2246 fileref
->delete_on_close
= TRUE
;
2248 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0)
2249 fileref
->delete_on_close
= FALSE
;
2251 if (Vcb
->locked
&& Vcb
->locked_fileobj
== FileObject
) {
2252 TRACE("unlocking volume\n");
2253 do_unlock_volume(Vcb
);
2254 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2258 if (!Vcb
->removing
) {
2259 LIST_ENTRY rollback
;
2261 InitializeListHead(&rollback
);
2263 if (fileref
&& fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2264 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
);
2266 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2268 Status
= delete_fileref(fileref
, FileObject
, &rollback
);
2269 if (!NT_SUCCESS(Status
)) {
2270 ERR("delete_fileref returned %08x\n", Status
);
2271 do_rollback(Vcb
, &rollback
);
2272 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2276 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2277 clear_rollback(&rollback
);
2278 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2279 IO_STATUS_BLOCK iosb
;
2280 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2282 if (!NT_SUCCESS(iosb
.Status
)) {
2283 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2286 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2287 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2288 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2291 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2293 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2294 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2298 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2299 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2302 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2305 Status
= STATUS_SUCCESS
;
2308 Irp
->IoStatus
.Status
= Status
;
2309 Irp
->IoStatus
.Information
= 0;
2311 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2315 IoSetTopLevelIrp(NULL
);
2317 FsRtlExitFileSystem();
2322 ULONG STDCALL
get_file_attributes(device_extension
* Vcb
, INODE_ITEM
* ii
, root
* r
, UINT64 inode
, UINT8 type
, BOOL dotfile
, BOOL ignore_xa
) {
2329 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
)) {
2331 if (eaval
[0] == '0' && eaval
[1] == 'x') {
2335 for (i
= 2; i
< ealen
; i
++) {
2338 if (eaval
[i
] >= '0' && eaval
[i
] <= '9')
2339 dosnum
|= eaval
[i
] - '0';
2340 else if (eaval
[i
] >= 'a' && eaval
[i
] <= 'f')
2341 dosnum
|= eaval
[i
] + 10 - 'a';
2342 else if (eaval
[i
] >= 'A' && eaval
[i
] <= 'F')
2343 dosnum
|= eaval
[i
] + 10 - 'a';
2346 TRACE("DOSATTRIB: %08x\n", dosnum
);
2350 if (type
== BTRFS_TYPE_DIRECTORY
)
2351 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2352 else if (type
== BTRFS_TYPE_SYMLINK
)
2353 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2363 case BTRFS_TYPE_DIRECTORY
:
2364 att
= FILE_ATTRIBUTE_DIRECTORY
;
2367 case BTRFS_TYPE_SYMLINK
:
2368 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2377 att
|= FILE_ATTRIBUTE_HIDDEN
;
2380 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2382 // FIXME - get READONLY from ii->st_mode
2383 // FIXME - return SYSTEM for block/char devices?
2386 att
= FILE_ATTRIBUTE_NORMAL
;
2391 NTSTATUS
sync_read_phys(PDEVICE_OBJECT DeviceObject
, LONGLONG StartingOffset
, ULONG Length
, PUCHAR Buffer
) {
2392 IO_STATUS_BLOCK
* IoStatus
;
2393 LARGE_INTEGER Offset
;
2395 PIO_STACK_LOCATION IrpSp
;
2397 read_context
* context
;
2401 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
2403 ERR("out of memory\n");
2404 return STATUS_INSUFFICIENT_RESOURCES
;
2407 RtlZeroMemory(context
, sizeof(read_context
));
2408 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
2410 IoStatus
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), ALLOC_TAG
);
2412 ERR("out of memory\n");
2413 ExFreePool(context
);
2414 return STATUS_INSUFFICIENT_RESOURCES
;
2417 Offset
.QuadPart
= StartingOffset
;
2419 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2420 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2423 ERR("IoAllocateIrp failed\n");
2424 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2428 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2429 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2431 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2432 FIXME("FIXME - buffered IO\n");
2433 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2434 // TRACE("direct IO\n");
2436 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2437 if (!Irp
->MdlAddress
) {
2438 ERR("IoAllocateMdl failed\n");
2439 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2443 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2446 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2448 // TRACE("neither buffered nor direct IO\n");
2449 Irp
->UserBuffer
= Buffer
;
2452 IrpSp
->Parameters
.Read
.Length
= Length
;
2453 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2455 Irp
->UserIosb
= IoStatus
;
2456 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2458 Irp
->UserEvent
= &context
->Event
;
2460 // IoQueueThreadIrp(Irp);
2462 IoSetCompletionRoutine(Irp
, read_completion
, context
, TRUE
, TRUE
, TRUE
);
2466 // Stack = IoGetNextIrpStackLocation(Irp);
2467 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2470 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2471 Status
= IoCallDriver(DeviceObject
, Irp
);
2473 // TRACE("Waiting for IO Operation for %p\n", Irp);
2474 if (Status
== STATUS_PENDING
) {
2475 // TRACE("Operation pending\n");
2476 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2477 // TRACE("Getting IO Status... for %p\n", Irp);
2478 Status
= context
->iosb
.Status
;
2481 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2482 MmUnlockPages(Irp
->MdlAddress
);
2483 IoFreeMdl(Irp
->MdlAddress
);
2489 ExFreePool(IoStatus
);
2490 ExFreePool(context
);
2495 static NTSTATUS STDCALL
read_superblock(device_extension
* Vcb
, PDEVICE_OBJECT device
, UINT64 length
) {
2498 unsigned int i
, to_read
;
2501 to_read
= sector_align(sizeof(superblock
), device
->SectorSize
);
2503 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2505 ERR("out of memory\n");
2506 return STATUS_INSUFFICIENT_RESOURCES
;
2511 while (superblock_addrs
[i
] > 0) {
2512 if (i
> 0 && superblock_addrs
[i
] + sizeof(superblock
) > length
)
2515 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
);
2516 if (!NT_SUCCESS(Status
)) {
2517 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2522 // FIXME - check checksum before accepting?
2524 TRACE("got superblock %u!\n", i
);
2526 if (i
== 0 || sb
->generation
> Vcb
->superblock
.generation
)
2527 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2534 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
2536 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)Vcb
->superblock
.checksum
));
2538 if (crc32
!= *((UINT32
*)Vcb
->superblock
.checksum
))
2539 return STATUS_INTERNAL_ERROR
; // FIXME - correct error?
2541 TRACE("label is %s\n", Vcb
->superblock
.label
);
2542 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2544 return STATUS_SUCCESS
;
2547 NTSTATUS STDCALL
dev_ioctl(PDEVICE_OBJECT DeviceObject
, ULONG ControlCode
, PVOID InputBuffer
, ULONG InputBufferSize
,
2548 PVOID OutputBuffer
, ULONG OutputBufferSize
, BOOLEAN Override
, IO_STATUS_BLOCK
* iosb
)
2553 PIO_STACK_LOCATION Stack
;
2554 IO_STATUS_BLOCK IoStatus
;
2556 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2558 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2568 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2571 Stack
= IoGetNextIrpStackLocation(Irp
);
2572 Stack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2575 Status
= IoCallDriver(DeviceObject
, Irp
);
2577 if (Status
== STATUS_PENDING
) {
2578 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2579 Status
= IoStatus
.Status
;
2588 static NTSTATUS STDCALL
add_root(device_extension
* Vcb
, UINT64 id
, UINT64 addr
, traverse_ptr
* tp
) {
2589 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2591 ERR("out of memory\n");
2592 return STATUS_INSUFFICIENT_RESOURCES
;
2596 r
->path
.Buffer
= NULL
;
2597 r
->treeholder
.address
= addr
;
2598 r
->treeholder
.tree
= NULL
;
2599 InitializeListHead(&r
->fcbs
);
2601 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2603 ERR("out of memory\n");
2605 return STATUS_INSUFFICIENT_RESOURCES
;
2608 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2613 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2614 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2615 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2618 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2621 case BTRFS_ROOT_ROOT
:
2625 case BTRFS_ROOT_EXTENT
:
2626 Vcb
->extent_root
= r
;
2629 case BTRFS_ROOT_CHUNK
:
2630 Vcb
->chunk_root
= r
;
2633 case BTRFS_ROOT_DEVTREE
:
2637 case BTRFS_ROOT_CHECKSUM
:
2638 Vcb
->checksum_root
= r
;
2641 case BTRFS_ROOT_UUID
:
2646 return STATUS_SUCCESS
;
2649 static NTSTATUS STDCALL
look_for_roots(device_extension
* Vcb
) {
2650 traverse_ptr tp
, next_tp
;
2655 searchkey
.obj_id
= 0;
2656 searchkey
.obj_type
= 0;
2657 searchkey
.offset
= 0;
2659 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
2660 if (!NT_SUCCESS(Status
)) {
2661 ERR("error - find_tree returned %08x\n", Status
);
2666 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2668 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2669 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2671 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2672 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
));
2674 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2676 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, &tp
);
2677 if (!NT_SUCCESS(Status
)) {
2678 ERR("add_root returned %08x\n", Status
);
2684 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2690 return STATUS_SUCCESS
;
2693 static NTSTATUS
find_disk_holes(device_extension
* Vcb
, device
* dev
) {
2695 traverse_ptr tp
, next_tp
;
2700 InitializeListHead(&dev
->space
);
2702 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2703 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2704 searchkey
.offset
= 0;
2706 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
);
2707 if (!NT_SUCCESS(Status
)) {
2708 ERR("error - find_tree returned %08x\n", Status
);
2715 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2716 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2717 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2719 if (tp
.item
->key
.offset
> lastaddr
) {
2720 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2721 if (!NT_SUCCESS(Status
)) {
2722 ERR("add_space_entry returned %08x\n", Status
);
2727 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2729 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
));
2733 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2737 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2742 if (lastaddr
< dev
->devitem
.num_bytes
) {
2743 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2744 if (!NT_SUCCESS(Status
)) {
2745 ERR("add_space_entry returned %08x\n", Status
);
2750 return STATUS_SUCCESS
;
2753 device
* find_device_from_uuid(device_extension
* Vcb
, BTRFS_UUID
* uuid
) {
2756 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2757 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i
,
2758 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],
2759 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]);
2761 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2762 TRACE("returning device %llx\n", i
);
2763 return &Vcb
->devices
[i
];
2767 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& !IsListEmpty(&volumes
)) {
2768 LIST_ENTRY
* le
= volumes
.Flink
;
2770 while (le
!= &volumes
) {
2771 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2773 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2774 RtlCompareMemory(uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2777 PFILE_OBJECT FileObject
;
2778 PDEVICE_OBJECT DeviceObject
;
2780 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
2781 if (!NT_SUCCESS(Status
)) {
2782 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2786 DeviceObject
= FileObject
->DeviceObject
;
2788 ObReferenceObject(DeviceObject
);
2789 ObDereferenceObject(FileObject
);
2791 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2792 Vcb
->devices
[Vcb
->devices_loaded
].devitem
.device_uuid
= *uuid
;
2793 Vcb
->devices_loaded
++;
2795 return &Vcb
->devices
[Vcb
->devices_loaded
- 1];
2802 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2803 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
2804 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
2809 static BOOL
is_device_removable(PDEVICE_OBJECT devobj
) {
2811 STORAGE_HOTPLUG_INFO shi
;
2813 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
2815 if (!NT_SUCCESS(Status
)) {
2816 ERR("dev_ioctl returned %08x\n", Status
);
2820 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
2823 static ULONG
get_device_change_count(PDEVICE_OBJECT devobj
) {
2826 IO_STATUS_BLOCK iosb
;
2828 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
2830 if (!NT_SUCCESS(Status
)) {
2831 ERR("dev_ioctl returned %08x\n", Status
);
2835 if (iosb
.Information
< sizeof(ULONG
)) {
2836 ERR("iosb.Information was too short\n");
2843 static void init_device(device_extension
* Vcb
, device
* dev
, BOOL get_length
) {
2845 GET_LENGTH_INFORMATION gli
;
2847 dev
->removable
= is_device_removable(dev
->devobj
);
2848 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
2851 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2852 &gli
, sizeof(gli
), TRUE
, NULL
);
2853 if (!NT_SUCCESS(Status
)) {
2854 ERR("error reading length information: %08x\n", Status
);
2857 dev
->length
= gli
.Length
.QuadPart
;
2861 static NTSTATUS STDCALL
load_chunk_root(device_extension
* Vcb
) {
2862 traverse_ptr tp
, next_tp
;
2869 searchkey
.obj_id
= 0;
2870 searchkey
.obj_type
= 0;
2871 searchkey
.offset
= 0;
2873 Vcb
->data_flags
= 0;
2875 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
2876 if (!NT_SUCCESS(Status
)) {
2877 ERR("error - find_item returned %08x\n", Status
);
2882 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2884 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
2885 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
2886 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_ITEM
));
2888 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
2891 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2892 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2893 RtlCopyMemory(&Vcb
->devices
[i
].devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2896 init_device(Vcb
, &Vcb
->devices
[i
], TRUE
);
2904 if (!IsListEmpty(&volumes
) && Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
2905 LIST_ENTRY
* le
= volumes
.Flink
;
2907 while (le
!= &volumes
) {
2908 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2910 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2911 RtlCompareMemory(&di
->device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2913 PFILE_OBJECT FileObject
;
2914 PDEVICE_OBJECT DeviceObject
;
2916 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_DATA
| FILE_WRITE_DATA
, &FileObject
, &DeviceObject
);
2917 if (!NT_SUCCESS(Status
)) {
2918 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2922 DeviceObject
= FileObject
->DeviceObject
;
2924 ObReferenceObject(DeviceObject
);
2925 ObDereferenceObject(FileObject
);
2927 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2928 RtlCopyMemory(&Vcb
->devices
[Vcb
->devices_loaded
].devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2929 init_device(Vcb
, &Vcb
->devices
[i
], FALSE
);
2930 Vcb
->devices
[i
].length
= v
->length
;
2931 Vcb
->devices_loaded
++;
2941 ERR("volume not found: device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", tp
.item
->key
.offset
,
2942 di
->device_uuid
.uuid
[0], di
->device_uuid
.uuid
[1], di
->device_uuid
.uuid
[2], di
->device_uuid
.uuid
[3], di
->device_uuid
.uuid
[4], di
->device_uuid
.uuid
[5], di
->device_uuid
.uuid
[6], di
->device_uuid
.uuid
[7],
2943 di
->device_uuid
.uuid
[8], di
->device_uuid
.uuid
[9], di
->device_uuid
.uuid
[10], di
->device_uuid
.uuid
[11], di
->device_uuid
.uuid
[12], di
->device_uuid
.uuid
[13], di
->device_uuid
.uuid
[14], di
->device_uuid
.uuid
[15]);
2946 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
2949 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
2950 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
2951 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
));
2953 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
2956 ERR("out of memory\n");
2957 return STATUS_INSUFFICIENT_RESOURCES
;
2960 c
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk_nonpaged
), ALLOC_TAG
);
2963 ERR("out of memory\n");
2965 return STATUS_INSUFFICIENT_RESOURCES
;
2968 c
->size
= tp
.item
->size
;
2969 c
->offset
= tp
.item
->key
.offset
;
2970 c
->used
= c
->oldused
= 0;
2974 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2976 if (!c
->chunk_item
) {
2977 ERR("out of memory\n");
2979 ExFreePool(c
->nonpaged
);
2980 return STATUS_INSUFFICIENT_RESOURCES
;
2983 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
2985 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
2986 Vcb
->data_flags
= c
->chunk_item
->type
;
2988 if (c
->chunk_item
->num_stripes
> 0) {
2989 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
2991 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
2994 ERR("out of memory\n");
2996 ExFreePool(c
->nonpaged
);
2997 ExFreePool(c
->chunk_item
);
2998 return STATUS_INSUFFICIENT_RESOURCES
;
3001 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3002 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3003 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3008 ExInitializeResourceLite(&c
->nonpaged
->lock
);
3009 ExInitializeResourceLite(&c
->nonpaged
->changed_extents_lock
);
3011 InitializeListHead(&c
->space
);
3012 InitializeListHead(&c
->space_size
);
3013 InitializeListHead(&c
->deleting
);
3014 InitializeListHead(&c
->changed_extents
);
3016 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3018 c
->list_entry_changed
.Flink
= NULL
;
3022 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
3028 Vcb
->log_to_phys_loaded
= TRUE
;
3030 if (Vcb
->data_flags
== 0)
3031 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3033 return STATUS_SUCCESS
;
3036 void protect_superblocks(device_extension
* Vcb
, chunk
* c
) {
3038 UINT64 off_start
, off_end
;
3040 // The Linux driver also protects all the space before the first superblock.
3041 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3042 // evidently Linux assumes the chunk at 0 is always SINGLE.
3043 if (c
->offset
< superblock_addrs
[0])
3044 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3046 while (superblock_addrs
[i
] != 0) {
3047 CHUNK_ITEM
* ci
= c
->chunk_item
;
3048 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3050 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3051 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3052 ULONG sub_stripes
= max(ci
->sub_stripes
, 1);
3054 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3057 UINT16 startoffstripe
;
3060 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3062 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3063 off_start
-= off_start
% ci
->stripe_length
;
3064 off_start
*= ci
->num_stripes
/ sub_stripes
;
3065 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3067 off_end
= off_start
+ ci
->stripe_length
;
3070 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3071 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3072 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3075 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3078 } else { // SINGLE, DUPLICATE, RAID1
3079 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3080 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3081 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3083 // The Linux driver protects the whole stripe in which the superblock lives
3085 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3086 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3088 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3097 static NTSTATUS STDCALL
find_chunk_usage(device_extension
* Vcb
) {
3098 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3102 BLOCK_GROUP_ITEM
* bgi
;
3106 // block_group_item size=7f0000 chunktreeid=100 flags=1
3108 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3110 while (le
!= &Vcb
->chunks
) {
3111 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3113 searchkey
.obj_id
= c
->offset
;
3114 searchkey
.offset
= c
->chunk_item
->size
;
3116 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3117 if (!NT_SUCCESS(Status
)) {
3118 ERR("error - find_item returned %08x\n", Status
);
3122 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
3123 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3124 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3126 c
->used
= c
->oldused
= bgi
->used
;
3128 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3130 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3131 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
));
3135 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3136 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3138 // return (addr - c->offset) + cis->offset;
3141 // FIXME - make sure we free occasionally after doing one of these, or we
3142 // might use up a lot of memory with a big disk.
3144 Status
= load_free_space_cache(Vcb
, c
);
3145 if (!NT_SUCCESS(Status
)) {
3146 ERR("load_free_space_cache returned %08x\n", Status
);
3150 protect_superblocks(Vcb
, c
);
3155 return STATUS_SUCCESS
;
3158 // static void STDCALL root_test(device_extension* Vcb) {
3161 // traverse_ptr tp, next_tp;
3166 // if (r->id == 0x102)
3172 // ERR("Could not find root tree.\n");
3176 // searchkey.obj_id = 0x1b6;
3177 // searchkey.obj_type = 0xb;
3178 // searchkey.offset = 0;
3180 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3181 // ERR("Could not find first item.\n");
3187 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3189 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3192 // free_traverse_ptr(&tp);
3197 // free_traverse_ptr(&tp);
3200 static NTSTATUS
load_sys_chunks(device_extension
* Vcb
) {
3202 ULONG n
= Vcb
->superblock
.n
;
3205 if (n
> sizeof(KEY
)) {
3206 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3209 return STATUS_SUCCESS
;
3211 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3213 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3218 if (n
< sizeof(CHUNK_ITEM
))
3219 return STATUS_SUCCESS
;
3221 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3222 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3225 return STATUS_SUCCESS
;
3227 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3230 ERR("out of memory\n");
3231 return STATUS_INSUFFICIENT_RESOURCES
;
3236 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3239 ERR("out of memory\n");
3240 return STATUS_INSUFFICIENT_RESOURCES
;
3243 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3244 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3248 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3249 return STATUS_INTERNAL_ERROR
;
3253 return STATUS_SUCCESS
;
3256 static root
* find_default_subvol(device_extension
* Vcb
) {
3259 static char fn
[] = "default";
3260 static UINT32 crc32
= 0x8dbfc2d2;
3262 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3268 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3269 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3270 searchkey
.offset
= crc32
;
3272 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3273 if (!NT_SUCCESS(Status
)) {
3274 ERR("error - find_item returned %08x\n", Status
);
3278 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3279 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3283 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3284 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
));
3288 di
= (DIR_ITEM
*)tp
.item
->data
;
3290 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3291 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(DIR_ITEM
) - 1 + di
->n
);
3295 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3296 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3300 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3301 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3305 le
= Vcb
->roots
.Flink
;
3306 while (le
!= &Vcb
->roots
) {
3307 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3309 if (r
->id
== di
->key
.obj_id
)
3315 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3319 le
= Vcb
->roots
.Flink
;
3320 while (le
!= &Vcb
->roots
) {
3321 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3323 if (r
->id
== BTRFS_ROOT_FSTREE
)
3332 static NTSTATUS
create_worker_threads(PDEVICE_OBJECT DeviceObject
) {
3333 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3337 Vcb
->threads
.num_threads
= max(3, KeQueryActiveProcessorCount(NULL
)); // FIXME - number of processors?
3339 Vcb
->threads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
, ALLOC_TAG
);
3340 if (!Vcb
->threads
.threads
) {
3341 ERR("out of memory\n");
3342 return STATUS_INSUFFICIENT_RESOURCES
;
3345 RtlZeroMemory(Vcb
->threads
.threads
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
);
3347 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
3348 Vcb
->threads
.threads
[i
].DeviceObject
= DeviceObject
;
3349 KeInitializeEvent(&Vcb
->threads
.threads
[i
].event
, SynchronizationEvent
, FALSE
);
3350 KeInitializeEvent(&Vcb
->threads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3351 InitializeListHead(&Vcb
->threads
.threads
[i
].jobs
);
3352 KeInitializeSpinLock(&Vcb
->threads
.threads
[i
].spin_lock
);
3354 Status
= PsCreateSystemThread(&Vcb
->threads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, worker_thread
, &Vcb
->threads
.threads
[i
]);
3355 if (!NT_SUCCESS(Status
)) {
3358 ERR("PsCreateSystemThread returned %08x\n", Status
);
3360 for (j
= 0; j
< i
; j
++) {
3361 Vcb
->threads
.threads
[i
].quit
= TRUE
;
3362 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
3369 return STATUS_SUCCESS
;
3372 BOOL
add_thread_job(device_extension
* Vcb
, PIRP Irp
) {
3376 threadnum
= InterlockedIncrement(&Vcb
->threads
.next_thread
) % Vcb
->threads
.num_threads
;
3378 if (Vcb
->threads
.threads
[threadnum
].quit
)
3381 tj
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(thread_job
), ALLOC_TAG
);
3383 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
3384 Irp
->IoStatus
.Information
= 0;
3385 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3391 ExInterlockedInsertTailList(&Vcb
->threads
.threads
[threadnum
].jobs
, &tj
->list_entry
, &Vcb
->threads
.threads
[threadnum
].spin_lock
);
3392 KeSetEvent(&Vcb
->threads
.threads
[threadnum
].event
, 0, FALSE
);
3397 static BOOL
raid_generations_okay(device_extension
* Vcb
) {
3400 // FIXME - if the difference between superblocks is small, we should try to recover
3402 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3403 LIST_ENTRY
* le
= volumes
.Flink
;
3404 while (le
!= &volumes
) {
3405 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3407 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
3408 RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
3410 if (v
->gen1
!= Vcb
->superblock
.generation
- 1) {
3411 WARN("device %llu had generation %llx, expected %llx\n", i
, v
->gen1
, Vcb
->superblock
.generation
- 1);
3423 static NTSTATUS STDCALL
mount_vol(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
3424 PIO_STACK_LOCATION Stack
;
3425 PDEVICE_OBJECT NewDeviceObject
= NULL
;
3426 PDEVICE_OBJECT DeviceToMount
;
3428 device_extension
* Vcb
= NULL
;
3429 GET_LENGTH_INFORMATION gli
;
3434 fcb
* root_fcb
= NULL
;
3435 ccb
* root_ccb
= NULL
;
3437 TRACE("mount_vol called\n");
3439 if (DeviceObject
!= devobj
)
3441 Status
= STATUS_INVALID_DEVICE_REQUEST
;
3445 Stack
= IoGetCurrentIrpStackLocation(Irp
);
3446 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
3448 Status
= dev_ioctl(DeviceToMount
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
3449 &gli
, sizeof(gli
), TRUE
, NULL
);
3450 if (!NT_SUCCESS(Status
)) {
3451 ERR("error reading length information: %08x\n", Status
);
3452 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3456 Status
= IoCreateDevice(drvobj
,
3457 sizeof(device_extension
),
3459 FILE_DEVICE_DISK_FILE_SYSTEM
,
3463 if (!NT_SUCCESS(Status
)) {
3464 ERR("IoCreateDevice returned %08x\n", Status
);
3465 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3469 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3471 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
3472 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
3473 RtlZeroMemory(Vcb
, sizeof(device_extension
));
3474 Vcb
->type
= VCB_TYPE_VOLUME
;
3476 ExInitializeResourceLite(&Vcb
->tree_lock
);
3477 Vcb
->open_trees
= 0;
3478 Vcb
->need_write
= FALSE
;
3480 ExInitializeResourceLite(&Vcb
->fcb_lock
);
3481 ExInitializeResourceLite(&Vcb
->DirResource
);
3482 ExInitializeResourceLite(&Vcb
->checksum_lock
);
3483 ExInitializeResourceLite(&Vcb
->chunk_lock
);
3485 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
3486 InsertTailList(&VcbList
, &Vcb
->list_entry
);
3487 ExReleaseResourceLite(&global_loading_lock
);
3489 ExInitializeResourceLite(&Vcb
->load_lock
);
3490 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
3492 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3493 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3495 // Status = NtfsGetVolumeData(DeviceToMount,
3497 // if (!NT_SUCCESS(Status))
3500 // Vcb->device = DeviceToMount;
3501 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
3503 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3504 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3505 // if (!NT_SUCCESS(Status)) {
3506 // ERR("error reading disk geometry: %08x\n", Status);
3509 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3510 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3511 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3514 TRACE("partition length = %llx\n", gli
.Length
.QuadPart
);
3516 Status
= read_superblock(Vcb
, DeviceToMount
, gli
.Length
.QuadPart
);
3517 if (!NT_SUCCESS(Status
)) {
3518 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3522 if (Vcb
->superblock
.magic
!= BTRFS_MAGIC
) {
3523 ERR("not a BTRFS volume\n");
3524 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3527 TRACE("btrfs magic found\n");
3530 Status
= registry_load_volume_options(&Vcb
->superblock
.uuid
, &Vcb
->options
);
3531 if (!NT_SUCCESS(Status
)) {
3532 ERR("registry_load_volume_options returned %08x\n", Status
);
3536 if (Vcb
->options
.ignore
) {
3537 TRACE("ignoring volume\n");
3538 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3542 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
3543 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
3544 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3549 while (le
!= &volumes
) {
3550 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3552 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) && v
->devnum
< Vcb
->superblock
.dev_item
.dev_id
) {
3553 // skipping over device in RAID which isn't the first one
3554 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3561 Vcb
->readonly
= FALSE
;
3562 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
3563 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
3564 Vcb
->readonly
= TRUE
;
3567 Vcb
->superblock
.generation
++;
3568 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
3570 Vcb
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
) * Vcb
->superblock
.num_devices
, ALLOC_TAG
);
3571 if (!Vcb
->devices
) {
3572 ERR("out of memory\n");
3573 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3577 Vcb
->devices
[0].devobj
= DeviceToMount
;
3578 RtlCopyMemory(&Vcb
->devices
[0].devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
3579 init_device(Vcb
, &Vcb
->devices
[0], FALSE
);
3580 Vcb
->devices
[0].length
= gli
.Length
.QuadPart
;
3582 if (Vcb
->superblock
.num_devices
> 1)
3583 RtlZeroMemory(&Vcb
->devices
[1], sizeof(DEV_ITEM
) * (Vcb
->superblock
.num_devices
- 1));
3585 Vcb
->devices_loaded
= 1;
3587 TRACE("DeviceToMount = %p\n", DeviceToMount
);
3588 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack
->Parameters
.MountVolume
.Vpb
);
3590 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
3591 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
3593 InitializeListHead(&Vcb
->roots
);
3594 InitializeListHead(&Vcb
->drop_roots
);
3596 Vcb
->log_to_phys_loaded
= FALSE
;
3598 Vcb
->max_inline
= Vcb
->superblock
.node_size
/ 2;
3600 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, NULL
);
3602 if (!Vcb
->chunk_root
) {
3603 ERR("Could not load chunk root.\n");
3604 Status
= STATUS_INTERNAL_ERROR
;
3608 InitializeListHead(&Vcb
->sys_chunks
);
3609 Status
= load_sys_chunks(Vcb
);
3610 if (!NT_SUCCESS(Status
)) {
3611 ERR("load_sys_chunks returned %08x\n", Status
);
3615 InitializeListHead(&Vcb
->chunks
);
3616 InitializeListHead(&Vcb
->chunks_changed
);
3617 InitializeListHead(&Vcb
->trees
);
3618 InitializeListHead(&Vcb
->all_fcbs
);
3619 InitializeListHead(&Vcb
->dirty_fcbs
);
3620 InitializeListHead(&Vcb
->dirty_filerefs
);
3621 InitializeListHead(&Vcb
->shared_extents
);
3622 InitializeListHead(&Vcb
->sector_checksums
);
3624 KeInitializeSpinLock(&Vcb
->dirty_fcbs_lock
);
3625 KeInitializeSpinLock(&Vcb
->dirty_filerefs_lock
);
3626 KeInitializeSpinLock(&Vcb
->shared_extents_lock
);
3628 InitializeListHead(&Vcb
->DirNotifyList
);
3630 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
3632 Status
= load_chunk_root(Vcb
);
3633 if (!NT_SUCCESS(Status
)) {
3634 ERR("load_chunk_root returned %08x\n", Status
);
3638 if (Vcb
->superblock
.num_devices
> 1) {
3639 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3640 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
3642 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3644 Status
= STATUS_INTERNAL_ERROR
;
3648 if (!raid_generations_okay(Vcb
)) {
3649 ERR("could not mount as generation mismatch\n");
3651 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3653 Status
= STATUS_INTERNAL_ERROR
;
3658 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, NULL
);
3660 if (!Vcb
->root_root
) {
3661 ERR("Could not load root of roots.\n");
3662 Status
= STATUS_INTERNAL_ERROR
;
3666 Status
= look_for_roots(Vcb
);
3667 if (!NT_SUCCESS(Status
)) {
3668 ERR("look_for_roots returned %08x\n", Status
);
3672 Status
= find_chunk_usage(Vcb
);
3673 if (!NT_SUCCESS(Status
)) {
3674 ERR("find_chunk_usage returned %08x\n", Status
);
3678 // We've already increased the generation by one
3679 if (!Vcb
->readonly
&& Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) {
3680 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
3681 Status
= clear_free_space_cache(Vcb
);
3682 if (!NT_SUCCESS(Status
)) {
3683 ERR("clear_free_space_cache returned %08x\n", Status
);
3688 Vcb
->volume_fcb
= create_fcb();
3689 if (!Vcb
->volume_fcb
) {
3690 ERR("out of memory\n");
3691 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3695 Vcb
->volume_fcb
->Vcb
= Vcb
;
3696 Vcb
->volume_fcb
->sd
= NULL
;
3698 root_fcb
= create_fcb();
3700 ERR("out of memory\n");
3701 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3705 root_fcb
->Vcb
= Vcb
;
3706 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
3707 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
3709 #ifdef DEBUG_FCB_REFCOUNTS
3710 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
3711 WARN("root FCB = %p\n", root_fcb
);
3714 root_fcb
->subvol
= find_default_subvol(Vcb
);
3716 if (!root_fcb
->subvol
) {
3717 ERR("could not find top subvol\n");
3718 Status
= STATUS_INTERNAL_ERROR
;
3722 searchkey
.obj_id
= root_fcb
->inode
;
3723 searchkey
.obj_type
= TYPE_INODE_ITEM
;
3724 searchkey
.offset
= 0xffffffffffffffff;
3726 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
);
3727 if (!NT_SUCCESS(Status
)) {
3728 ERR("error - find_item returned %08x\n", Status
);
3732 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
3733 ERR("couldn't find INODE_ITEM for root directory\n");
3734 Status
= STATUS_INTERNAL_ERROR
;
3738 if (tp
.item
->size
> 0)
3739 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
3741 fcb_get_sd(root_fcb
, NULL
);
3743 root_fcb
->atts
= get_file_attributes(Vcb
, &root_fcb
->inode_item
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
);
3745 Vcb
->root_fileref
= create_fileref();
3746 if (!Vcb
->root_fileref
) {
3747 ERR("out of memory\n");
3748 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3752 Vcb
->root_fileref
->fcb
= root_fcb
;
3753 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
3754 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
3756 root_fcb
->fileref
= Vcb
->root_fileref
;
3758 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
3760 ERR("out of memory\n");
3761 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3765 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
3766 Vcb
->root_file
->FsContext
= root_fcb
;
3768 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
3769 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
3772 RtlZeroMemory(root_ccb
, sizeof(ccb
));
3773 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3774 root_ccb
->NodeSize
= sizeof(ccb
);
3777 Vcb
->root_file
->FsContext
= root_ccb
;
3779 Vcb
->root_file
->FsContext2
= root_ccb
;
3782 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), FALSE
, cache_callbacks
, Vcb
->root_file
);
3783 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
3784 Status
= _SEH2_GetExceptionCode();
3789 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3790 Status
= find_disk_holes(Vcb
, &Vcb
->devices
[i
]);
3791 if (!NT_SUCCESS(Status
)) {
3792 ERR("find_disk_holes returned %08x\n", Status
);
3799 KeInitializeSpinLock(&Vcb
->FcbListLock
);
3801 NewDeviceObject
->Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
3802 Stack
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
3803 Stack
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
3804 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
3805 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
3806 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
3807 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
3808 Vcb
->Vpb
= NewDeviceObject
->Vpb
;
3810 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
3812 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
3813 if (!NT_SUCCESS(Status
)) {
3814 ERR("PsCreateSystemThread returned %08x\n", Status
);
3818 Status
= create_worker_threads(NewDeviceObject
);
3819 if (!NT_SUCCESS(Status
)) {
3820 ERR("create_worker_threads returned %08x\n", Status
);
3824 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
3825 if (!NT_SUCCESS(Status
))
3826 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
3828 Status
= STATUS_SUCCESS
;
3832 ExReleaseResourceLite(&Vcb
->load_lock
);
3835 if (!NT_SUCCESS(Status
)) {
3838 ObDereferenceObject(Vcb
->root_file
);
3839 else if (Vcb
->root_fileref
)
3840 free_fileref(Vcb
->root_fileref
);
3844 if (Vcb
->volume_fcb
)
3845 free_fcb(Vcb
->volume_fcb
);
3847 ExDeleteResourceLite(&Vcb
->tree_lock
);
3848 ExDeleteResourceLite(&Vcb
->load_lock
);
3849 ExDeleteResourceLite(&Vcb
->fcb_lock
);
3850 ExDeleteResourceLite(&Vcb
->DirResource
);
3851 ExDeleteResourceLite(&Vcb
->checksum_lock
);
3852 ExDeleteResourceLite(&Vcb
->chunk_lock
);
3855 ExFreePoolWithTag(Vcb
->devices
, ALLOC_TAG
);
3857 RemoveEntryList(&Vcb
->list_entry
);
3860 if (NewDeviceObject
)
3861 IoDeleteDevice(NewDeviceObject
);
3863 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
3865 TRACE("mount_vol done (status: %lx)\n", Status
);
3870 static NTSTATUS STDCALL
drv_file_system_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3871 PIO_STACK_LOCATION IrpSp
;
3873 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3876 TRACE("file system control\n");
3878 FsRtlEnterFileSystem();
3880 top_level
= is_top_level(Irp
);
3882 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
3883 Status
= part0_passthrough(DeviceObject
, Irp
);
3887 Status
= STATUS_NOT_IMPLEMENTED
;
3889 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3891 Irp
->IoStatus
.Information
= 0;
3893 switch (IrpSp
->MinorFunction
) {
3894 case IRP_MN_MOUNT_VOLUME
:
3895 TRACE("IRP_MN_MOUNT_VOLUME\n");
3897 Status
= mount_vol(DeviceObject
, Irp
);
3900 case IRP_MN_KERNEL_CALL
:
3901 TRACE("IRP_MN_KERNEL_CALL\n");
3903 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, FALSE
);
3906 case IRP_MN_USER_FS_REQUEST
:
3907 TRACE("IRP_MN_USER_FS_REQUEST\n");
3909 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, TRUE
);
3912 case IRP_MN_VERIFY_VOLUME
:
3913 FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
3920 Irp
->IoStatus
.Status
= Status
;
3922 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3926 IoSetTopLevelIrp(NULL
);
3928 FsRtlExitFileSystem();
3933 static NTSTATUS STDCALL
drv_lock_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3935 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3936 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
3937 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3940 FsRtlEnterFileSystem();
3942 top_level
= is_top_level(Irp
);
3944 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
3945 Status
= part0_passthrough(DeviceObject
, Irp
);
3949 TRACE("lock control\n");
3951 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
3953 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3957 IoSetTopLevelIrp(NULL
);
3959 FsRtlExitFileSystem();
3964 NTSTATUS
part0_passthrough(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3966 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
3968 IoSkipCurrentIrpStackLocation(Irp
);
3970 Status
= IoCallDriver(p0de
->devobj
, Irp
);
3975 static NTSTATUS
part0_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3977 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
3978 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3980 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
3982 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
3983 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
:
3985 MOUNTDEV_UNIQUE_ID
* mduid
;
3987 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
)) {
3988 Status
= STATUS_BUFFER_TOO_SMALL
;
3989 Irp
->IoStatus
.Status
= Status
;
3990 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
3991 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3995 mduid
= Irp
->AssociatedIrp
.SystemBuffer
;
3996 mduid
->UniqueIdLength
= sizeof(BTRFS_UUID
);
3998 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
) {
3999 Status
= STATUS_BUFFER_OVERFLOW
;
4000 Irp
->IoStatus
.Status
= Status
;
4001 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4002 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4006 RtlCopyMemory(mduid
->UniqueId
, &p0de
->uuid
, sizeof(BTRFS_UUID
));
4008 Status
= STATUS_SUCCESS
;
4009 Irp
->IoStatus
.Status
= Status
;
4010 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
;
4011 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4016 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
:
4018 PMOUNTDEV_NAME name
;
4020 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
)) {
4021 Status
= STATUS_BUFFER_TOO_SMALL
;
4022 Irp
->IoStatus
.Status
= Status
;
4023 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4024 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4028 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4029 name
->NameLength
= p0de
->name
.Length
;
4031 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
) {
4032 Status
= STATUS_BUFFER_OVERFLOW
;
4033 Irp
->IoStatus
.Status
= Status
;
4034 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4035 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4039 RtlCopyMemory(name
->Name
, p0de
->name
.Buffer
, p0de
->name
.Length
);
4041 Status
= STATUS_SUCCESS
;
4042 Irp
->IoStatus
.Status
= Status
;
4043 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
;
4044 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4050 IoSkipCurrentIrpStackLocation(Irp
);
4052 Status
= IoCallDriver(p0de
->devobj
, Irp
);
4054 TRACE("returning %08x\n", Status
);
4059 static NTSTATUS STDCALL
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4061 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4062 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4063 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4067 FsRtlEnterFileSystem();
4069 top_level
= is_top_level(Irp
);
4071 Irp
->IoStatus
.Information
= 0;
4073 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4074 Status
= part0_device_control(DeviceObject
, Irp
);
4078 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
4081 ERR("FileObject was NULL\n");
4082 Status
= STATUS_INVALID_PARAMETER
;
4086 fcb
= FileObject
->FsContext
;
4089 ERR("FCB was NULL\n");
4090 Status
= STATUS_INVALID_PARAMETER
;
4094 if (fcb
!= Vcb
->volume_fcb
) {
4095 Status
= STATUS_NOT_IMPLEMENTED
;
4099 IoSkipCurrentIrpStackLocation(Irp
);
4101 Status
= IoCallDriver(Vcb
->devices
[0].devobj
, Irp
);
4106 Irp
->IoStatus
.Status
= Status
;
4108 if (Status
!= STATUS_PENDING
)
4109 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4113 IoSetTopLevelIrp(NULL
);
4115 FsRtlExitFileSystem();
4120 static NTSTATUS STDCALL
drv_shutdown(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4123 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4125 TRACE("shutdown\n");
4127 FsRtlEnterFileSystem();
4129 top_level
= is_top_level(Irp
);
4131 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4132 Status
= part0_passthrough(DeviceObject
, Irp
);
4136 Status
= STATUS_SUCCESS
;
4138 while (!IsListEmpty(&VcbList
)) {
4139 LIST_ENTRY
* le
= RemoveHeadList(&VcbList
);
4140 Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
4142 TRACE("shutting down Vcb %p\n", Vcb
);
4147 Irp
->IoStatus
.Status
= Status
;
4148 Irp
->IoStatus
.Information
= 0;
4150 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4154 IoSetTopLevelIrp(NULL
);
4156 FsRtlExitFileSystem();
4161 BOOL
is_file_name_valid(PUNICODE_STRING us
) {
4164 if (us
->Length
< sizeof(WCHAR
))
4167 if (us
->Length
> 255 * sizeof(WCHAR
))
4170 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
4171 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
4172 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))
4176 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
4183 static void STDCALL
init_serial() {
4186 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
4187 if (!NT_SUCCESS(Status
)) {
4188 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
4194 static void STDCALL
check_cpu() {
4195 unsigned int cpuInfo
[4];
4197 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
4198 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
4200 __cpuid(cpuInfo
, 1);
4201 have_sse42
= cpuInfo
[2] & (1 << 20);
4205 TRACE("SSE4.2 is supported\n");
4207 TRACE("SSE4.2 not supported\n");
4212 static void init_logging() {
4213 if (log_device
.Length
> 0)
4215 else if (log_file
.Length
> 0) {
4217 OBJECT_ATTRIBUTES oa
;
4218 IO_STATUS_BLOCK iosb
;
4223 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4225 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
4226 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
4228 if (!NT_SUCCESS(Status
)) {
4229 ERR("ZwCreateFile returned %08x\n", Status
);
4233 if (iosb
.Information
== FILE_OPENED
) { // already exists
4234 FILE_STANDARD_INFORMATION fsi
;
4235 FILE_POSITION_INFORMATION fpi
;
4237 static char delim
[] = "\n---\n";
4239 // move to end of file
4241 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
4243 if (!NT_SUCCESS(Status
)) {
4244 ERR("ZwQueryInformationFile returned %08x\n", Status
);
4248 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
4250 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
4252 if (!NT_SUCCESS(Status
)) {
4253 ERR("ZwSetInformationFile returned %08x\n", Status
);
4257 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, delim
, strlen(delim
), NULL
, NULL
);
4259 if (!NT_SUCCESS(Status
)) {
4260 ERR("ZwWriteFile returned %08x\n", Status
);
4265 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
4268 ERR("out of memory\n");
4272 KeQuerySystemTime(&time
);
4274 RtlTimeToTimeFields(&time
, &tf
);
4276 sprintf(dateline
, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
4278 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, strlen(dateline
), NULL
, NULL
);
4280 if (!NT_SUCCESS(Status
)) {
4281 ERR("ZwWriteFile returned %08x\n", Status
);
4285 ExFreePool(dateline
);
4290 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
) {
4292 PDEVICE_OBJECT DeviceObject
;
4293 UNICODE_STRING device_nameW
;
4294 UNICODE_STRING dosdevice_nameW
;
4296 InitializeListHead(&uid_map_list
);
4298 log_device
.Buffer
= NULL
;
4299 log_device
.Length
= log_device
.MaximumLength
= 0;
4300 log_file
.Buffer
= NULL
;
4301 log_file
.Length
= log_file
.MaximumLength
= 0;
4303 read_registry(RegistryPath
);
4306 if (debug_log_level
> 0)
4312 TRACE("DriverEntry\n");
4314 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
4315 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
4317 if (!registry_path
.Buffer
) {
4318 ERR("out of memory\n");
4319 return STATUS_INSUFFICIENT_RESOURCES
;
4322 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
4328 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4330 drvobj
= DriverObject
;
4332 DriverObject
->DriverUnload
= DriverUnload
;
4334 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
4335 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
4336 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
4337 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
4338 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
4339 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
4340 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
4341 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
4342 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
4343 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
4344 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
4345 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
4346 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
4347 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
4348 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
4349 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
4350 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
4351 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
4352 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
4353 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
4355 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
4357 device_nameW
.Buffer
= device_name
;
4358 device_nameW
.Length
= device_nameW
.MaximumLength
= (USHORT
)wcslen(device_name
) * sizeof(WCHAR
);
4359 dosdevice_nameW
.Buffer
= dosdevice_name
;
4360 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
4362 Status
= IoCreateDevice(DriverObject
, 0, &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
, FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
4363 if (!NT_SUCCESS(Status
)) {
4364 ERR("IoCreateDevice returned %08x\n", Status
);
4368 devobj
= DeviceObject
;
4370 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4372 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
4373 if (!NT_SUCCESS(Status
)) {
4374 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
4378 Status
= init_cache();
4379 if (!NT_SUCCESS(Status
)) {
4380 ERR("init_cache returned %08x\n", Status
);
4384 InitializeListHead(&volumes
);
4385 look_for_vols(DriverObject
, &volumes
);
4387 InitializeListHead(&VcbList
);
4388 ExInitializeResourceLite(&global_loading_lock
);
4390 IoRegisterFileSystem(DeviceObject
);
4392 return STATUS_SUCCESS
;