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_MIXED_GROUPS | \
39 BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | \
40 BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | BTRFS_INCOMPAT_FLAGS_NO_HOLES)
41 #define COMPAT_RO_SUPPORTED 0
43 static WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
44 static WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
46 PDRIVER_OBJECT drvobj
;
47 PDEVICE_OBJECT devobj
;
49 BOOL have_sse42
= FALSE
;
52 LIST_ENTRY uid_map_list
;
55 ERESOURCE global_loading_lock
;
56 UINT32 debug_log_level
= 0;
57 UINT32 mount_compress
= 0;
58 UINT32 mount_compress_force
= 0;
59 UINT32 mount_compress_type
= 0;
60 UINT32 mount_zlib_level
= 3;
61 UINT32 mount_flush_interval
= 30;
62 UINT32 mount_max_inline
= 2048;
63 BOOL log_started
= FALSE
;
64 UNICODE_STRING log_device
, log_file
, registry_path
;
67 PFILE_OBJECT comfo
= NULL
;
68 PDEVICE_OBJECT comdo
= NULL
;
69 HANDLE log_handle
= NULL
;
72 int __security_cookie
= __LINE__
;
74 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
);
82 static NTSTATUS STDCALL
dbg_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
83 read_context
* context
= conptr
;
85 // DbgPrint("dbg_completion\n");
87 context
->iosb
= Irp
->IoStatus
;
88 KeSetEvent(&context
->Event
, 0, FALSE
);
90 // return STATUS_SUCCESS;
91 return STATUS_MORE_PROCESSING_REQUIRED
;
94 #ifdef DEBUG_LONG_MESSAGES
95 void STDCALL
_debug_message(const char* func
, const char* file
, unsigned int line
, char* s
, ...) {
97 void STDCALL
_debug_message(const char* func
, char* s
, ...) {
100 PIO_STACK_LOCATION IrpSp
;
104 char *buf2
= NULL
, *buf
;
105 read_context
* context
= NULL
;
108 buf2
= ExAllocatePoolWithTag(NonPagedPool
, 1024, ALLOC_TAG
);
111 DbgPrint("Couldn't allocate buffer in debug_message\n");
115 #ifdef DEBUG_LONG_MESSAGES
116 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func
, file
, line
);
118 sprintf(buf2
, "%p:%s:", PsGetCurrentThreadId(), func
);
120 buf
= &buf2
[strlen(buf2
)];
123 vsprintf(buf
, s
, ap
);
125 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
127 } else if (log_device
.Length
> 0) {
129 DbgPrint("comdo is NULL :-(\n");
134 length
= (UINT32
)strlen(buf2
);
136 offset
.u
.LowPart
= 0;
137 offset
.u
.HighPart
= 0;
139 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
141 DbgPrint("Couldn't allocate context in debug_message\n");
145 RtlZeroMemory(context
, sizeof(read_context
));
147 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
149 // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
151 Irp
= IoAllocateIrp(comdo
->StackSize
, FALSE
);
154 DbgPrint("IoAllocateIrp failed\n");
158 IrpSp
= IoGetNextIrpStackLocation(Irp
);
159 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
161 if (comdo
->Flags
& DO_BUFFERED_IO
) {
162 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
164 Irp
->Flags
= IRP_BUFFERED_IO
;
165 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
166 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, FALSE
, FALSE
, NULL
);
167 if (!Irp
->MdlAddress
) {
168 DbgPrint("IoAllocateMdl failed\n");
172 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
174 Irp
->UserBuffer
= buf2
;
177 IrpSp
->Parameters
.Write
.Length
= length
;
178 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
180 Irp
->UserIosb
= &context
->iosb
;
182 Irp
->UserEvent
= &context
->Event
;
184 IoSetCompletionRoutine(Irp
, dbg_completion
, context
, TRUE
, TRUE
, TRUE
);
186 Status
= IoCallDriver(comdo
, Irp
);
188 if (Status
== STATUS_PENDING
) {
189 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
190 Status
= context
->iosb
.Status
;
193 if (comdo
->Flags
& DO_DIRECT_IO
) {
194 MmUnlockPages(Irp
->MdlAddress
);
195 IoFreeMdl(Irp
->MdlAddress
);
198 if (!NT_SUCCESS(Status
)) {
199 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
205 } else if (log_handle
!= NULL
) {
206 IO_STATUS_BLOCK iosb
;
208 length
= (UINT32
)strlen(buf2
);
210 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
212 if (!NT_SUCCESS(Status
)) {
213 DbgPrint("failed to write to file - error %08x\n", Status
);
228 UINT64
sector_align( UINT64 NumberToBeAligned
, UINT64 Alignment
)
230 if( Alignment
& ( Alignment
- 1 ) )
233 // Alignment not a power of 2
236 return NumberToBeAligned
;
238 if( ( NumberToBeAligned
& ( Alignment
- 1 ) ) != 0 )
240 NumberToBeAligned
= NumberToBeAligned
+ Alignment
;
241 NumberToBeAligned
= NumberToBeAligned
& ( ~ (Alignment
-1) );
243 return NumberToBeAligned
;
246 int keycmp(const KEY
* key1
, const KEY
* key2
) {
247 if (key1
->obj_id
< key2
->obj_id
) {
249 } else if (key1
->obj_id
> key2
->obj_id
) {
253 if (key1
->obj_type
< key2
->obj_type
) {
255 } else if (key1
->obj_type
> key2
->obj_type
) {
259 if (key1
->offset
< key2
->offset
) {
261 } else if (key1
->offset
> key2
->offset
) {
268 BOOL
is_top_level(PIRP Irp
) {
269 if (!IoGetTopLevelIrp()) {
270 IoSetTopLevelIrp(Irp
);
277 static void STDCALL
DriverUnload(PDRIVER_OBJECT DriverObject
) {
278 UNICODE_STRING dosdevice_nameW
;
280 ERR("DriverUnload\n");
284 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
286 dosdevice_nameW
.Buffer
= dosdevice_name
;
287 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
289 IoDeleteSymbolicLink(&dosdevice_nameW
);
290 IoDeleteDevice(DriverObject
->DeviceObject
);
292 while (!IsListEmpty(&uid_map_list
)) {
293 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
294 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
301 // FIXME - free volumes and their devpaths
305 ObDereferenceObject(comfo
);
311 ExDeleteResourceLite(&global_loading_lock
);
313 if (log_device
.Buffer
)
314 ExFreePool(log_device
.Buffer
);
317 ExFreePool(log_file
.Buffer
);
319 if (registry_path
.Buffer
)
320 ExFreePool(registry_path
.Buffer
);
323 BOOL STDCALL
get_last_inode(device_extension
* Vcb
, root
* r
, PIRP Irp
) {
325 traverse_ptr tp
, prev_tp
;
329 searchkey
.obj_id
= 0xffffffffffffffff;
330 searchkey
.obj_type
= 0xff;
331 searchkey
.offset
= 0xffffffffffffffff;
333 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
, Irp
);
334 if (!NT_SUCCESS(Status
)) {
335 ERR("error - find_item returned %08x\n", Status
);
339 while (find_prev_item(Vcb
, &tp
, &prev_tp
, FALSE
, Irp
)) {
342 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
344 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
345 r
->lastinode
= tp
.item
->key
.obj_id
;
346 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
351 r
->lastinode
= SUBVOL_ROOT_INODE
;
353 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
358 BOOL STDCALL
get_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
** data
, UINT16
* datalen
, PIRP Irp
) {
365 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
367 searchkey
.obj_id
= inode
;
368 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
369 searchkey
.offset
= crc32
;
371 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
372 if (!NT_SUCCESS(Status
)) {
373 ERR("error - find_item returned %08x\n", Status
);
377 if (keycmp(&tp
.item
->key
, &searchkey
)) {
378 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
382 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
383 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
));
387 xa
= (DIR_ITEM
*)tp
.item
->data
;
388 size
= tp
.item
->size
;
391 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
392 WARN("(%llx,%x,%llx) is truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
396 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
397 TRACE("found xattr %s in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
402 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
404 ERR("out of memory\n");
408 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
415 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
419 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
424 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
429 NTSTATUS
add_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT32 crc32
, DIR_ITEM
* di
, ULONG disize
, PIRP Irp
, LIST_ENTRY
* rollback
) {
435 searchkey
.obj_id
= inode
;
436 searchkey
.obj_type
= TYPE_DIR_ITEM
;
437 searchkey
.offset
= crc32
;
439 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
440 if (!NT_SUCCESS(Status
)) {
441 ERR("error - find_item returned %08x\n", Status
);
445 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
446 ULONG maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
448 if (tp
.item
->size
+ disize
> maxlen
) {
449 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp
.item
->size
, disize
, maxlen
);
450 return STATUS_INTERNAL_ERROR
;
453 di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ disize
, ALLOC_TAG
);
455 ERR("out of memory\n");
456 return STATUS_INSUFFICIENT_RESOURCES
;
459 if (tp
.item
->size
> 0)
460 RtlCopyMemory(di2
, tp
.item
->data
, tp
.item
->size
);
462 RtlCopyMemory(di2
+ tp
.item
->size
, di
, disize
);
464 delete_tree_item(Vcb
, &tp
, rollback
);
466 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di2
, tp
.item
->size
+ disize
, NULL
, Irp
, rollback
);
470 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di
, disize
, NULL
, Irp
, rollback
);
473 return STATUS_SUCCESS
;
476 static NTSTATUS STDCALL
drv_close(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
478 PIO_STACK_LOCATION IrpSp
;
479 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
484 FsRtlEnterFileSystem();
486 top_level
= is_top_level(Irp
);
488 if (DeviceObject
== devobj
|| (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
)) {
489 TRACE("Closing file system\n");
490 Status
= STATUS_SUCCESS
;
494 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
496 // FIXME - unmount if called for volume
497 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
499 Status
= close_file(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
502 Irp
->IoStatus
.Status
= Status
;
503 Irp
->IoStatus
.Information
= 0;
505 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
508 IoSetTopLevelIrp(NULL
);
510 FsRtlExitFileSystem();
512 TRACE("returning %08x\n", Status
);
517 static NTSTATUS STDCALL
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
520 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
522 FsRtlEnterFileSystem();
524 top_level
= is_top_level(Irp
);
526 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
527 Status
= part0_passthrough(DeviceObject
, Irp
);
531 FIXME("STUB: query ea\n");
532 Status
= STATUS_NOT_IMPLEMENTED
;
534 Irp
->IoStatus
.Status
= Status
;
535 Irp
->IoStatus
.Information
= 0;
537 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
541 IoSetTopLevelIrp(NULL
);
543 FsRtlExitFileSystem();
548 static NTSTATUS STDCALL
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
550 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
553 FsRtlEnterFileSystem();
555 top_level
= is_top_level(Irp
);
557 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
558 Status
= part0_passthrough(DeviceObject
, Irp
);
562 FIXME("STUB: set ea\n");
563 Status
= STATUS_NOT_IMPLEMENTED
;
566 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
568 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
570 Irp
->IoStatus
.Status
= Status
;
571 Irp
->IoStatus
.Information
= 0;
573 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
577 IoSetTopLevelIrp(NULL
);
579 FsRtlExitFileSystem();
584 static NTSTATUS STDCALL
drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
586 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
587 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
588 fcb
* fcb
= FileObject
->FsContext
;
589 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
592 TRACE("flush buffers\n");
594 FsRtlEnterFileSystem();
596 top_level
= is_top_level(Irp
);
598 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
599 Status
= part0_passthrough(DeviceObject
, Irp
);
603 Status
= STATUS_SUCCESS
;
604 Irp
->IoStatus
.Status
= Status
;
605 Irp
->IoStatus
.Information
= 0;
607 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
608 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
610 if (fcb
->Header
.PagingIoResource
) {
611 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
612 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
615 Status
= Irp
->IoStatus
.Status
;
618 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
622 IoSetTopLevelIrp(NULL
);
624 FsRtlExitFileSystem();
629 static void calculate_total_space(device_extension
* Vcb
, LONGLONG
* totalsize
, LONGLONG
* freespace
) {
632 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
)
637 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) / factor
;
638 *freespace
= ((Vcb
->superblock
.total_bytes
- Vcb
->superblock
.bytes_used
) / Vcb
->superblock
.sector_size
) / factor
;
641 static NTSTATUS STDCALL
drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
642 PIO_STACK_LOCATION IrpSp
;
644 ULONG BytesCopied
= 0;
645 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
649 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
650 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
651 // which prevents UAC from working.
652 // FIXME - only lie if we detect that we're being called by mpr.dll
654 WCHAR
* fs_name
= L
"NTFS";
655 ULONG fs_name_len
= 4 * sizeof(WCHAR
);
657 WCHAR
* fs_name
= L
"Btrfs";
658 ULONG fs_name_len
= 5 * sizeof(WCHAR
);
661 TRACE("query volume information\n");
663 FsRtlEnterFileSystem();
664 top_level
= is_top_level(Irp
);
666 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
667 Status
= part0_passthrough(DeviceObject
, Irp
);
671 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
673 Status
= STATUS_NOT_IMPLEMENTED
;
675 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
676 case FileFsAttributeInformation
:
678 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
679 BOOL overflow
= FALSE
;
680 ULONG orig_fs_name_len
= fs_name_len
;
682 TRACE("FileFsAttributeInformation\n");
684 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
685 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
686 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
693 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
694 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
695 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
;
697 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
699 // should also be FILE_FILE_COMPRESSION when supported
700 data
->MaximumComponentNameLength
= 255; // FIXME - check
701 data
->FileSystemNameLength
= orig_fs_name_len
;
702 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
704 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
705 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
709 case FileFsControlInformation
:
710 FIXME("STUB: FileFsControlInformation\n");
713 case FileFsDeviceInformation
:
715 FILE_FS_DEVICE_INFORMATION
* ffdi
= Irp
->AssociatedIrp
.SystemBuffer
;
717 TRACE("FileFsDeviceInformation\n");
719 ffdi
->DeviceType
= FILE_DEVICE_DISK
;
720 ffdi
->Characteristics
= Vcb
->devices
[0].devobj
->Characteristics
;
723 ffdi
->Characteristics
|= FILE_READ_ONLY_DEVICE
;
725 ffdi
->Characteristics
&= ~FILE_READ_ONLY_DEVICE
;
727 BytesCopied
= sizeof(FILE_FS_DEVICE_INFORMATION
);
728 Status
= STATUS_SUCCESS
;
733 case FileFsDriverPathInformation
:
734 FIXME("STUB: FileFsDriverPathInformation\n");
737 case FileFsFullSizeInformation
:
739 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
741 TRACE("FileFsFullSizeInformation\n");
743 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
744 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
745 ffsi
->SectorsPerAllocationUnit
= 1;
746 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
748 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
749 Status
= STATUS_SUCCESS
;
754 case FileFsObjectIdInformation
:
756 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
758 TRACE("FileFsObjectIdInformation\n");
760 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
761 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
763 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
764 Status
= STATUS_SUCCESS
;
769 case FileFsSizeInformation
:
771 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
773 TRACE("FileFsSizeInformation\n");
775 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->AvailableAllocationUnits
.QuadPart
);
776 ffsi
->SectorsPerAllocationUnit
= 1;
777 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
779 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
780 Status
= STATUS_SUCCESS
;
785 case FileFsVolumeInformation
:
787 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
788 FILE_FS_VOLUME_INFORMATION ffvi
;
789 BOOL overflow
= FALSE
;
790 ULONG label_len
, orig_label_len
;
792 TRACE("FileFsVolumeInformation\n");
793 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
795 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
797 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
798 RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
799 orig_label_len
= label_len
;
801 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
802 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
803 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
810 TRACE("label_len = %u\n", label_len
);
812 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
813 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];
814 ffvi
.VolumeLabelLength
= orig_label_len
;
815 ffvi
.SupportsObjects
= FALSE
;
817 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
822 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
823 RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
824 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
827 ExReleaseResourceLite(&Vcb
->tree_lock
);
829 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
830 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
835 Status
= STATUS_INVALID_PARAMETER
;
836 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
840 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
841 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
842 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
843 // Status = STATUS_BUFFER_OVERFLOW;
846 Irp
->IoStatus
.Status
= Status
;
848 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
849 Irp
->IoStatus
.Information
= 0;
851 Irp
->IoStatus
.Information
= BytesCopied
;
853 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
857 IoSetTopLevelIrp(NULL
);
859 FsRtlExitFileSystem();
861 TRACE("query volume information returning %08x\n", Status
);
866 static NTSTATUS STDCALL
read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
867 read_context
* context
= conptr
;
869 // DbgPrint("read_completion\n");
871 context
->iosb
= Irp
->IoStatus
;
872 KeSetEvent(&context
->Event
, 0, FALSE
);
874 // return STATUS_SUCCESS;
875 return STATUS_MORE_PROCESSING_REQUIRED
;
878 // static void test_tree_deletion(device_extension* Vcb) {
879 // KEY searchkey/*, endkey*/;
880 // traverse_ptr tp, next_tp;
883 // searchkey.obj_id = 0x100;
884 // searchkey.obj_type = 0x54;
885 // searchkey.offset = 0xca4ab2f5;
887 // // endkey.obj_id = 0x100;
888 // // endkey.obj_type = 0x60;
889 // // endkey.offset = 0x15a;
892 // while (r && r->id != 0x102)
896 // ERR("error - could not find root\n");
900 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
901 // ERR("error - could not find key\n");
905 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
906 // tp.item->ignore = TRUE;
907 // add_to_tree_cache(tc, tp.tree);
909 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
910 // free_traverse_ptr(&tp);
916 // free_traverse_ptr(&tp);
919 // static void test_tree_splitting(device_extension* Vcb) {
922 // for (i = 0; i < 1000; i++) {
923 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
925 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
929 // static void test_dropping_tree(device_extension* Vcb) {
930 // LIST_ENTRY* le = Vcb->roots.Flink;
932 // while (le != &Vcb->roots) {
933 // root* r = CONTAINING_RECORD(le, root, list_entry);
935 // if (r->id == 0x101) {
936 // RemoveEntryList(&r->list_entry);
937 // InsertTailList(&Vcb->drop_roots, &r->list_entry);
945 NTSTATUS
create_root(device_extension
* Vcb
, UINT64 id
, root
** rootptr
, BOOL no_tree
, UINT64 offset
, PIRP Irp
, LIST_ENTRY
* rollback
) {
951 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
953 ERR("out of memory\n");
954 return STATUS_INSUFFICIENT_RESOURCES
;
957 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
959 ERR("out of memory\n");
961 return STATUS_INSUFFICIENT_RESOURCES
;
965 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
967 ERR("out of memory\n");
968 ExFreePool(r
->nonpaged
);
970 return STATUS_INSUFFICIENT_RESOURCES
;
974 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
976 ERR("out of memory\n");
981 ExFreePool(r
->nonpaged
);
983 return STATUS_INSUFFICIENT_RESOURCES
;
987 r
->treeholder
.address
= 0;
988 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
989 r
->treeholder
.tree
= no_tree
? NULL
: t
;
991 r
->path
.Buffer
= NULL
;
992 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
993 r
->root_item
.num_references
= 1;
994 InitializeListHead(&r
->fcbs
);
996 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
998 // We ask here for a traverse_ptr to the item we're inserting, so we can
999 // copy some of the tree's variables
1001 if (!insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, Irp
, rollback
)) {
1002 ERR("insert_tree_item failed\n");
1008 ExFreePool(r
->nonpaged
);
1010 return STATUS_INTERNAL_ERROR
;
1013 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
1015 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
1018 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
1019 t
->header
.address
= 0;
1020 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
1021 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
1022 t
->header
.generation
= Vcb
->superblock
.generation
;
1023 t
->header
.tree_id
= id
;
1024 t
->header
.num_items
= 0;
1025 t
->header
.level
= 0;
1027 t
->has_address
= FALSE
;
1034 InitializeListHead(&t
->itemlist
);
1037 t
->has_new_address
= FALSE
;
1038 t
->flags
= tp
.tree
->flags
;
1040 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1043 Vcb
->need_write
= TRUE
;
1048 return STATUS_SUCCESS
;
1051 // static void test_creating_root(device_extension* Vcb) {
1053 // LIST_ENTRY rollback;
1057 // InitializeListHead(&rollback);
1059 // if (Vcb->root_root->lastinode == 0)
1060 // get_last_inode(Vcb, Vcb->root_root);
1062 // id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
1063 // Status = create_root(Vcb, id, &r, &rollback);
1065 // if (!NT_SUCCESS(Status)) {
1066 // ERR("create_root returned %08x\n", Status);
1067 // do_rollback(Vcb, &rollback);
1069 // Vcb->root_root->lastinode = id;
1070 // clear_rollback(&rollback);
1074 // static void test_alloc_chunk(device_extension* Vcb) {
1075 // LIST_ENTRY rollback;
1078 // InitializeListHead(&rollback);
1080 // c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
1082 // ERR("alloc_chunk failed\n");
1083 // do_rollback(Vcb, &rollback);
1085 // clear_rollback(&rollback);
1089 // static void test_space_list(device_extension* Vcb) {
1100 // static const space_test entries[] = {
1101 // { 0x1000, 0x1000 },
1102 // { 0x3000, 0x2000 },
1103 // { 0x6000, 0x1000 },
1107 // static const space_test tests[] = {
1108 // { 0x0, 0x800, TRUE },
1109 // { 0x1800, 0x400, TRUE },
1110 // { 0x800, 0x2000, TRUE },
1111 // { 0x1000, 0x2000, TRUE },
1112 // { 0x2000, 0x3800, TRUE },
1113 // { 0x800, 0x1000, TRUE },
1114 // { 0x1800, 0x1000, TRUE },
1115 // { 0x5000, 0x800, TRUE },
1116 // { 0x5000, 0x1000, TRUE },
1117 // { 0x7000, 0x1000, TRUE },
1118 // { 0x8000, 0x1000, TRUE },
1119 // { 0x800, 0x800, TRUE },
1120 // { 0x0, 0x3800, TRUE },
1121 // { 0x1000, 0x2800, TRUE },
1122 // { 0x1000, 0x1000, FALSE },
1123 // { 0x800, 0x2000, FALSE },
1124 // { 0x0, 0x3800, FALSE },
1125 // { 0x2800, 0x1000, FALSE },
1126 // { 0x1800, 0x2000, FALSE },
1127 // { 0x3800, 0x1000, FALSE },
1131 // c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
1134 // while (tests[i].length > 0) {
1135 // InitializeListHead(&c->space);
1136 // InitializeListHead(&c->space_size);
1137 // ERR("test %u\n", i);
1140 // while (entries[j].length > 0) {
1141 // space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
1142 // s->address = entries[j].address;
1143 // s->size = entries[j].length;
1144 // InsertTailList(&c->space, &s->list_entry);
1146 // order_space_entry(s, &c->space_size);
1151 // if (tests[i].add)
1152 // space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1154 // space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1156 // le = c->space.Flink;
1157 // while (le != &c->space) {
1158 // space* s = CONTAINING_RECORD(le, space, list_entry);
1160 // ERR("(%llx,%llx)\n", s->address, s->size);
1167 // le = c->space_size.Flink;
1168 // while (le != &c->space_size) {
1169 // space* s = CONTAINING_RECORD(le, space, list_entry_size);
1171 // ERR("(%llx,%llx)\n", s->address, s->size);
1182 static NTSTATUS STDCALL
set_label(device_extension
* Vcb
, FILE_FS_LABEL_INFORMATION
* ffli
) {
1187 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1189 vollen
= ffli
->VolumeLabelLength
;
1191 for (i
= 0; i
< ffli
->VolumeLabelLength
/ sizeof(WCHAR
); i
++) {
1192 if (ffli
->VolumeLabel
[i
] == 0) {
1193 vollen
= i
* sizeof(WCHAR
);
1195 } else if (ffli
->VolumeLabel
[i
] == '/' || ffli
->VolumeLabel
[i
] == '\\') {
1196 Status
= STATUS_INVALID_VOLUME_LABEL
;
1204 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, vollen
);
1205 if (!NT_SUCCESS(Status
))
1208 if (utf8len
> MAX_LABEL_SIZE
) {
1209 Status
= STATUS_INVALID_VOLUME_LABEL
;
1214 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1217 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
, &utf8len
, ffli
->VolumeLabel
, vollen
);
1218 if (!NT_SUCCESS(Status
))
1221 Status
= STATUS_SUCCESS
;
1223 if (utf8len
< MAX_LABEL_SIZE
)
1224 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, MAX_LABEL_SIZE
- utf8len
);
1226 // test_tree_deletion(Vcb); // TESTING
1227 // test_tree_splitting(Vcb);
1228 // test_dropping_tree(Vcb);
1229 // test_creating_root(Vcb);
1230 // test_alloc_chunk(Vcb);
1231 // test_space_list(Vcb);
1233 Vcb
->need_write
= TRUE
;
1236 ExReleaseResourceLite(&Vcb
->tree_lock
);
1239 TRACE("returning %08x\n", Status
);
1244 static NTSTATUS STDCALL
drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1245 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1246 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1250 TRACE("set volume information\n");
1252 FsRtlEnterFileSystem();
1254 top_level
= is_top_level(Irp
);
1256 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
1257 Status
= part0_passthrough(DeviceObject
, Irp
);
1261 Status
= STATUS_NOT_IMPLEMENTED
;
1263 if (Vcb
->readonly
) {
1264 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1268 if (Vcb
->removing
|| Vcb
->locked
) {
1269 Status
= STATUS_ACCESS_DENIED
;
1273 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1274 case FileFsControlInformation
:
1275 FIXME("STUB: FileFsControlInformation\n");
1278 case FileFsLabelInformation
:
1279 TRACE("FileFsLabelInformation\n");
1281 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1284 case FileFsObjectIdInformation
:
1285 FIXME("STUB: FileFsObjectIdInformation\n");
1289 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1294 Irp
->IoStatus
.Status
= Status
;
1295 Irp
->IoStatus
.Information
= 0;
1297 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1301 IoSetTopLevelIrp(NULL
);
1303 FsRtlExitFileSystem();
1308 NTSTATUS
delete_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 parinode
, UINT32 crc32
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1313 searchkey
.obj_id
= parinode
;
1314 searchkey
.obj_type
= TYPE_DIR_ITEM
;
1315 searchkey
.offset
= crc32
;
1317 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1318 if (!NT_SUCCESS(Status
)) {
1319 ERR("error - find_item returned %08x\n", Status
);
1323 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1324 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
1325 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
));
1330 di
= (DIR_ITEM
*)tp
.item
->data
;
1331 len
= tp
.item
->size
;
1334 if (di
->n
== utf8
->Length
&& RtlCompareMemory(di
->name
, utf8
->Buffer
, di
->n
) == di
->n
) {
1335 ULONG newlen
= tp
.item
->size
- (sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
);
1337 delete_tree_item(Vcb
, &tp
, rollback
);
1340 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1342 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1345 ERR("out of memory\n");
1346 return STATUS_INSUFFICIENT_RESOURCES
;
1349 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1351 if ((UINT8
*)di
> tp
.item
->data
) {
1352 RtlCopyMemory(newdi
, tp
.item
->data
, (UINT8
*)di
- tp
.item
->data
);
1353 dioff
= newdi
+ ((UINT8
*)di
- tp
.item
->data
);
1358 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
< tp
.item
->size
)
1359 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], tp
.item
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
));
1361 insert_tree_item(Vcb
, subvol
, parinode
, TYPE_DIR_ITEM
, crc32
, newdi
, newlen
, NULL
, Irp
, rollback
);
1367 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1368 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1372 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32
);
1375 return STATUS_SUCCESS
;
1378 NTSTATUS
delete_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, PANSI_STRING utf8
, PIRP Irp
, LIST_ENTRY
* rollback
) {
1381 BOOL changed
= FALSE
;
1384 searchkey
.obj_id
= inode
;
1385 searchkey
.obj_type
= TYPE_INODE_REF
;
1386 searchkey
.offset
= parinode
;
1388 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1389 if (!NT_SUCCESS(Status
)) {
1390 ERR("error - find_item returned %08x\n", Status
);
1394 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1395 if (tp
.item
->size
< sizeof(INODE_REF
)) {
1396 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
));
1401 ir
= (INODE_REF
*)tp
.item
->data
;
1402 len
= tp
.item
->size
;
1407 if (len
< sizeof(INODE_REF
) || len
< sizeof(INODE_REF
) - 1 + ir
->n
) {
1408 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1412 itemlen
= sizeof(INODE_REF
) - sizeof(char) + ir
->n
;
1414 if (ir
->n
== utf8
->Length
&& RtlCompareMemory(ir
->name
, utf8
->Buffer
, ir
->n
) == ir
->n
) {
1415 ULONG newlen
= tp
.item
->size
- itemlen
;
1417 delete_tree_item(Vcb
, &tp
, rollback
);
1421 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1423 UINT8
*newir
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *iroff
;
1426 ERR("out of memory\n");
1427 return STATUS_INSUFFICIENT_RESOURCES
;
1430 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1432 if ((UINT8
*)ir
> tp
.item
->data
) {
1433 RtlCopyMemory(newir
, tp
.item
->data
, (UINT8
*)ir
- tp
.item
->data
);
1434 iroff
= newir
+ ((UINT8
*)ir
- tp
.item
->data
);
1439 if ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
< tp
.item
->size
)
1440 RtlCopyMemory(iroff
, &ir
->name
[ir
->n
], tp
.item
->size
- ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
));
1442 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newir
, newlen
, NULL
, Irp
, rollback
);
1448 if (len
> itemlen
) {
1450 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
1456 WARN("found INODE_REF entry, but couldn't find filename\n");
1460 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey
.obj_id
, searchkey
.offset
);
1464 return STATUS_SUCCESS
;
1466 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
))
1467 return STATUS_INTERNAL_ERROR
;
1469 searchkey
.obj_id
= inode
;
1470 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1471 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
1473 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
, Irp
);
1474 if (!NT_SUCCESS(Status
)) {
1475 ERR("error - find_item returned %08x\n", Status
);
1479 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1480 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
1481 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
));
1486 ier
= (INODE_EXTREF
*)tp
.item
->data
;
1487 len
= tp
.item
->size
;
1492 if (len
< sizeof(INODE_EXTREF
) || len
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
1493 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1497 itemlen
= sizeof(INODE_EXTREF
) - sizeof(char) + ier
->n
;
1499 if (ier
->dir
== parinode
&& ier
->n
== utf8
->Length
&& RtlCompareMemory(ier
->name
, utf8
->Buffer
, ier
->n
) == ier
->n
) {
1500 ULONG newlen
= tp
.item
->size
- itemlen
;
1502 delete_tree_item(Vcb
, &tp
, rollback
);
1506 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1508 UINT8
*newier
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *ieroff
;
1511 ERR("out of memory\n");
1512 return STATUS_INSUFFICIENT_RESOURCES
;
1515 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1517 if ((UINT8
*)ier
> tp
.item
->data
) {
1518 RtlCopyMemory(newier
, tp
.item
->data
, (UINT8
*)ier
- tp
.item
->data
);
1519 ieroff
= newier
+ ((UINT8
*)ier
- tp
.item
->data
);
1524 if ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
< tp
.item
->size
)
1525 RtlCopyMemory(ieroff
, &ier
->name
[ier
->n
], tp
.item
->size
- ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
));
1527 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newier
, newlen
, NULL
, Irp
, rollback
);
1533 if (len
> itemlen
) {
1535 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
1541 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32
)searchkey
.offset
);
1544 return changed
? STATUS_SUCCESS
: STATUS_INTERNAL_ERROR
;
1547 static WCHAR
* file_desc_fcb(fcb
* fcb
) {
1552 if (fcb
->debug_desc
)
1553 return fcb
->debug_desc
;
1555 if (fcb
== fcb
->Vcb
->volume_fcb
)
1556 return L
"volume FCB";
1558 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1559 if (!fcb
->debug_desc
)
1560 return L
"(memory error)";
1562 // I know this is pretty hackish...
1563 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1564 // without the CRT, which breaks drivers.
1566 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1569 as
.Length
= as
.MaximumLength
= strlen(s
);
1571 us
.Buffer
= fcb
->debug_desc
;
1572 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1575 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1577 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1579 return fcb
->debug_desc
;
1582 WCHAR
* file_desc_fileref(file_ref
* fileref
) {
1586 if (fileref
->debug_desc
)
1587 return fileref
->debug_desc
;
1589 Status
= fileref_get_filename(fileref
, &fn
, NULL
);
1590 if (!NT_SUCCESS(Status
)) {
1594 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, fn
.Length
+ sizeof(WCHAR
), ALLOC_TAG
);
1595 if (!fileref
->debug_desc
) {
1596 ExFreePool(fn
.Buffer
);
1597 return L
"(memory error)";
1600 RtlCopyMemory(fileref
->debug_desc
, fn
.Buffer
, fn
.Length
);
1601 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1603 ExFreePool(fn
.Buffer
);
1605 return fileref
->debug_desc
;
1608 WCHAR
* file_desc(PFILE_OBJECT FileObject
) {
1609 fcb
* fcb
= FileObject
->FsContext
;
1610 ccb
* ccb
= FileObject
->FsContext2
;
1611 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1614 return file_desc_fileref(fileref
);
1616 return file_desc_fcb(fcb
);
1619 void send_notification_fileref(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1623 fcb
* fcb
= fileref
->fcb
;
1625 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
);
1626 if (!NT_SUCCESS(Status
)) {
1627 ERR("fileref_get_filename returned %08x\n", Status
);
1631 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1632 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1633 ExFreePool(fn
.Buffer
);
1636 void send_notification_fcb(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1637 fcb
* fcb
= fileref
->fcb
;
1641 // no point looking for hardlinks if st_nlink == 1
1642 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1643 send_notification_fileref(fileref
, filter_match
, action
);
1647 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1649 le
= fcb
->hardlinks
.Flink
;
1650 while (le
!= &fcb
->hardlinks
) {
1651 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1654 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
, NULL
);
1656 if (!NT_SUCCESS(Status
)) {
1657 ERR("open_fileref_by_inode returned %08x\n", Status
);
1658 } else if (!parfr
->deleted
) {
1660 BOOL found
= FALSE
, deleted
= FALSE
;
1663 le2
= parfr
->children
.Flink
;
1664 while (le2
!= &parfr
->children
) {
1665 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
1667 if (fr2
->index
== hl
->index
) {
1669 deleted
= fr2
->deleted
;
1672 fn
= &fr2
->filepart
;
1684 UNICODE_STRING path
;
1686 Status
= fileref_get_filename(parfr
, &path
, NULL
);
1687 if (!NT_SUCCESS(Status
)) {
1688 ERR("fileref_get_filename returned %08x\n", Status
);
1693 name_offset
= path
.Length
;
1694 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) name_offset
+= sizeof(WCHAR
);
1696 fn2
.Length
= fn2
.MaximumLength
= fn
->Length
+ name_offset
;
1697 fn2
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn2
.MaximumLength
, ALLOC_TAG
);
1699 RtlCopyMemory(fn2
.Buffer
, path
.Buffer
, path
.Length
);
1700 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) fn2
.Buffer
[path
.Length
/ sizeof(WCHAR
)] = '\\';
1701 RtlCopyMemory(&fn2
.Buffer
[name_offset
/ sizeof(WCHAR
)], fn
->Buffer
, fn
->Length
);
1703 TRACE("%.*S\n", fn2
.Length
/ sizeof(WCHAR
), fn2
.Buffer
);
1705 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn2
, name_offset
,
1706 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1708 ExFreePool(fn2
.Buffer
);
1709 ExFreePool(path
.Buffer
);
1713 free_fileref(parfr
);
1719 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1722 void mark_fcb_dirty(fcb
* fcb
) {
1724 #ifdef DEBUG_FCB_REFCOUNTS
1727 dirty_fcb
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fcb
), ALLOC_TAG
);
1730 ExFreePool("out of memory\n");
1736 #ifdef DEBUG_FCB_REFCOUNTS
1737 rc
= InterlockedIncrement(&fcb
->refcount
);
1738 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1740 InterlockedIncrement(&fcb
->refcount
);
1745 ExInterlockedInsertTailList(&fcb
->Vcb
->dirty_fcbs
, &dirt
->list_entry
, &fcb
->Vcb
->dirty_fcbs_lock
);
1748 fcb
->Vcb
->need_write
= TRUE
;
1751 void mark_fileref_dirty(file_ref
* fileref
) {
1752 if (!fileref
->dirty
) {
1753 dirty_fileref
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fileref
), ALLOC_TAG
);
1756 ExFreePool("out of memory\n");
1760 fileref
->dirty
= TRUE
;
1761 increase_fileref_refcount(fileref
);
1763 dirt
->fileref
= fileref
;
1765 ExInterlockedInsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &dirt
->list_entry
, &fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1768 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1771 void _free_fcb(fcb
* fcb
, const char* func
, const char* file
, unsigned int line
) {
1775 // if (!ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fcb->Vcb->tree_lock)) {
1776 // ERR("fcb_lock not acquired exclusively\n");
1781 rc
= InterlockedDecrement(&fcb
->refcount
);
1783 #ifdef DEBUG_FCB_REFCOUNTS
1784 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1785 #ifdef DEBUG_LONG_MESSAGES
1786 _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
);
1788 _debug_message(func
, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1795 // ExAcquireResourceExclusiveLite(&fcb->Vcb->fcb_lock, TRUE);
1797 if (fcb
->list_entry
.Flink
)
1798 RemoveEntryList(&fcb
->list_entry
);
1800 if (fcb
->list_entry_all
.Flink
)
1801 RemoveEntryList(&fcb
->list_entry_all
);
1803 // ExReleaseResourceLite(&fcb->Vcb->fcb_lock);
1805 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1806 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1807 ExDeleteResourceLite(&fcb
->nonpaged
->index_lock
);
1808 ExFreePool(fcb
->nonpaged
);
1811 ExFreePool(fcb
->sd
);
1813 if (fcb
->adsxattr
.Buffer
)
1814 ExFreePool(fcb
->adsxattr
.Buffer
);
1816 if (fcb
->reparse_xattr
.Buffer
)
1817 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1819 if (fcb
->adsdata
.Buffer
)
1820 ExFreePool(fcb
->adsdata
.Buffer
);
1822 if (fcb
->debug_desc
)
1823 ExFreePool(fcb
->debug_desc
);
1825 while (!IsListEmpty(&fcb
->extents
)) {
1826 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1827 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1829 ExFreePool(ext
->data
);
1833 while (!IsListEmpty(&fcb
->index_list
)) {
1834 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->index_list
);
1835 index_entry
* ie
= CONTAINING_RECORD(le
, index_entry
, list_entry
);
1837 if (ie
->utf8
.Buffer
) ExFreePool(ie
->utf8
.Buffer
);
1838 if (ie
->filepart_uc
.Buffer
) ExFreePool(ie
->filepart_uc
.Buffer
);
1842 while (!IsListEmpty(&fcb
->hardlinks
)) {
1843 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1844 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1846 if (hl
->name
.Buffer
)
1847 ExFreePool(hl
->name
.Buffer
);
1849 if (hl
->utf8
.Buffer
)
1850 ExFreePool(hl
->utf8
.Buffer
);
1855 FsRtlUninitializeFileLock(&fcb
->lock
);
1858 #ifdef DEBUG_FCB_REFCOUNTS
1859 #ifdef DEBUG_LONG_MESSAGES
1860 _debug_message(func
, file
, line
, "freeing fcb %p\n", fcb
);
1862 _debug_message(func
, "freeing fcb %p\n", fcb
);
1867 void _free_fileref(file_ref
* fr
, const char* func
, const char* file
, unsigned int line
) {
1871 // if (!ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->fcb_lock) && !ExIsResourceAcquiredExclusiveLite(&fr->fcb->Vcb->tree_lock) && !fr->dirty) {
1872 // ERR("fcb_lock not acquired exclusively\n");
1877 rc
= InterlockedDecrement(&fr
->refcount
);
1879 #ifdef DEBUG_FCB_REFCOUNTS
1880 #ifdef DEBUG_LONG_MESSAGES
1881 _debug_message(func
, file
, line
, "fileref %p: refcount now %i\n", fr
, rc
);
1883 _debug_message(func
, "fileref %p: refcount now %i\n", fr
, rc
);
1889 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1898 ExAcquireResourceExclusiveLite(&fr
->parent
->nonpaged
->children_lock
, TRUE
);
1900 // FIXME - do we need a file_ref lock?
1902 // FIXME - do delete if needed
1904 if (fr
->filepart
.Buffer
)
1905 ExFreePool(fr
->filepart
.Buffer
);
1907 if (fr
->filepart_uc
.Buffer
)
1908 ExFreePool(fr
->filepart_uc
.Buffer
);
1910 if (fr
->utf8
.Buffer
)
1911 ExFreePool(fr
->utf8
.Buffer
);
1914 ExFreePool(fr
->debug_desc
);
1916 ExDeleteResourceLite(&fr
->nonpaged
->children_lock
);
1918 ExFreePool(fr
->nonpaged
);
1920 // FIXME - throw error if children not empty
1922 if (fr
->fcb
->fileref
== fr
)
1923 fr
->fcb
->fileref
= NULL
;
1925 if (fr
->list_entry
.Flink
)
1926 RemoveEntryList(&fr
->list_entry
);
1929 ExReleaseResourceLite(&fr
->parent
->nonpaged
->children_lock
);
1930 free_fileref(fr
->parent
);
1937 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
1940 file_ref
* fileref
= NULL
;
1942 TRACE("FileObject = %p\n", FileObject
);
1944 fcb
= FileObject
->FsContext
;
1946 TRACE("FCB was NULL, returning success\n");
1947 return STATUS_SUCCESS
;
1950 ccb
= FileObject
->FsContext2
;
1952 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1954 // FIXME - make sure notification gets sent if file is being deleted
1957 if (ccb
->query_string
.Buffer
)
1958 RtlFreeUnicodeString(&ccb
->query_string
);
1960 if (ccb
->filename
.Buffer
)
1961 ExFreePool(ccb
->filename
.Buffer
);
1963 // FIXME - use refcounts for fileref
1964 fileref
= ccb
->fileref
;
1969 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1971 if (!(Vcb
->Vpb
->Flags
& VPB_MOUNTED
))
1972 return STATUS_SUCCESS
;
1974 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1977 free_fileref(fileref
);
1981 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1983 return STATUS_SUCCESS
;
1986 void STDCALL
uninit(device_extension
* Vcb
, BOOL flush
) {
1989 LIST_ENTRY rollback
;
1994 Vcb
->removing
= TRUE
;
1996 RemoveEntryList(&Vcb
->list_entry
);
1998 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1999 if (!NT_SUCCESS(Status
))
2000 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
2003 InitializeListHead(&rollback
);
2005 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
2007 if (Vcb
->need_write
&& !Vcb
->readonly
)
2008 do_write(Vcb
, NULL
, &rollback
);
2012 clear_rollback(&rollback
);
2014 ExReleaseResourceLite(&Vcb
->tree_lock
);
2017 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
2018 Vcb
->threads
.threads
[i
].quit
= TRUE
;
2019 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
2021 KeWaitForSingleObject(&Vcb
->threads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
2023 ZwClose(Vcb
->threads
.threads
[i
].handle
);
2026 ExFreePool(Vcb
->threads
.threads
);
2029 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
2030 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
2032 free_fcb(Vcb
->volume_fcb
);
2035 ObDereferenceObject(Vcb
->root_file
);
2037 le
= Vcb
->chunks
.Flink
;
2038 while (le
!= &Vcb
->chunks
) {
2039 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2049 while (!IsListEmpty(&Vcb
->roots
)) {
2050 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->roots
);
2051 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
2053 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
2054 ExFreePool(r
->nonpaged
);
2058 while (!IsListEmpty(&Vcb
->chunks
)) {
2061 le
= RemoveHeadList(&Vcb
->chunks
);
2062 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2064 while (!IsListEmpty(&c
->space
)) {
2065 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2066 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2071 while (!IsListEmpty(&c
->deleting
)) {
2072 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
2073 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2079 ExFreePool(c
->devices
);
2084 ExDeleteResourceLite(&c
->lock
);
2085 ExDeleteResourceLite(&c
->changed_extents_lock
);
2087 ExFreePool(c
->chunk_item
);
2091 // FIXME - free any open fcbs?
2093 while (!IsListEmpty(&Vcb
->sector_checksums
)) {
2094 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->sector_checksums
);
2095 changed_sector
* cs
= (changed_sector
*)le
;
2100 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2101 while (!IsListEmpty(&Vcb
->devices
[i
].space
)) {
2102 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->devices
[i
].space
);
2103 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
2109 ExFreePool(Vcb
->devices
);
2111 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2112 ExDeleteResourceLite(&Vcb
->load_lock
);
2113 ExDeleteResourceLite(&Vcb
->tree_lock
);
2114 ExDeleteResourceLite(&Vcb
->checksum_lock
);
2115 ExDeleteResourceLite(&Vcb
->chunk_lock
);
2117 ZwClose(Vcb
->flush_thread_handle
);
2120 NTSTATUS
delete_fileref(file_ref
* fileref
, PFILE_OBJECT FileObject
, PIRP Irp
, LIST_ENTRY
* rollback
) {
2121 LARGE_INTEGER newlength
, time
;
2125 KeQuerySystemTime(&time
);
2126 win_time_to_unix(time
, &now
);
2128 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2130 if (fileref
->deleted
) {
2131 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2132 return STATUS_SUCCESS
;
2135 fileref
->deleted
= TRUE
;
2136 mark_fileref_dirty(fileref
);
2138 // delete INODE_ITEM (0x1)
2140 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2142 if (!fileref
->fcb
->ads
) {
2143 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2146 mark_fcb_dirty(fileref
->fcb
);
2148 if (fileref
->fcb
->inode_item
.st_nlink
> 1) {
2149 fileref
->fcb
->inode_item
.st_nlink
--;
2150 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2151 fileref
->fcb
->inode_item
.sequence
++;
2152 fileref
->fcb
->inode_item
.st_ctime
= now
;
2154 fileref
->fcb
->deleted
= TRUE
;
2158 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2159 Status
= excise_extents(fileref
->fcb
->Vcb
, fileref
->fcb
, 0, sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
), Irp
, rollback
);
2160 if (!NT_SUCCESS(Status
)) {
2161 ERR("excise_extents returned %08x\n", Status
);
2162 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2167 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2168 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2169 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2174 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2175 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2176 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2178 CcSetFileSizes(FileObject
, &ccfs
);
2182 le
= fileref
->fcb
->hardlinks
.Flink
;
2183 while (le
!= &fileref
->fcb
->hardlinks
) {
2184 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2186 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->index
) {
2187 RemoveEntryList(&hl
->list_entry
);
2189 if (hl
->name
.Buffer
)
2190 ExFreePool(hl
->name
.Buffer
);
2192 if (hl
->utf8
.Buffer
)
2193 ExFreePool(hl
->utf8
.Buffer
);
2201 } else { // subvolume
2202 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2203 fileref
->fcb
->subvol
->root_item
.num_references
--;
2205 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2207 // FIXME - we need a lock here
2209 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2211 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2215 fileref
->fcb
->deleted
= TRUE
;
2216 mark_fcb_dirty(fileref
->fcb
);
2219 // update INODE_ITEM of parent
2221 TRACE("delete file %.*S\n", fileref
->filepart
.Length
/ sizeof(WCHAR
), fileref
->filepart
.Buffer
);
2222 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2223 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2224 fileref
->parent
->fcb
->inode_item
.st_size
-= fileref
->utf8
.Length
* 2;
2225 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2226 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2227 fileref
->parent
->fcb
->inode_item
.sequence
++;
2228 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2229 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2230 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2232 mark_fcb_dirty(fileref
->parent
->fcb
);
2234 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
2236 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2237 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2239 if (FileObject
&& FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fileref
->fcb
->nonpaged
->segment_object
.DataSectionObject
)
2240 CcPurgeCacheSection(&fileref
->fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2242 newlength
.QuadPart
= 0;
2244 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2245 TRACE("CcUninitializeCacheMap failed\n");
2247 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2249 return STATUS_SUCCESS
;
2252 static NTSTATUS STDCALL
drv_cleanup(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2254 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2255 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2256 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2262 FsRtlEnterFileSystem();
2264 top_level
= is_top_level(Irp
);
2266 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
2267 Status
= part0_passthrough(DeviceObject
, Irp
);
2271 if (DeviceObject
== devobj
) {
2272 TRACE("closing file system\n");
2273 Status
= STATUS_SUCCESS
;
2277 if (FileObject
&& FileObject
->FsContext
) {
2282 fcb
= FileObject
->FsContext
;
2283 ccb
= FileObject
->FsContext2
;
2284 fileref
= ccb
? ccb
->fileref
: NULL
;
2286 TRACE("cleanup called for FileObject %p\n", FileObject
);
2287 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb
, file_desc(FileObject
), fcb
->refcount
, fcb
->open_count
);
2289 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2291 FsRtlNotifyCleanup(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, ccb
);
2293 oc
= InterlockedDecrement(&fcb
->open_count
);
2294 #ifdef DEBUG_FCB_REFCOUNTS
2295 ERR("fcb %p: open_count now %i\n", fcb
, oc
);
2298 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2299 fileref
->delete_on_close
= TRUE
;
2301 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0)
2302 fileref
->delete_on_close
= FALSE
;
2304 if (Vcb
->locked
&& Vcb
->locked_fileobj
== FileObject
) {
2305 TRACE("unlocking volume\n");
2306 do_unlock_volume(Vcb
);
2307 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2311 if (!Vcb
->removing
) {
2312 LIST_ENTRY rollback
;
2314 InitializeListHead(&rollback
);
2316 if (fileref
&& fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2317 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
);
2319 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2321 Status
= delete_fileref(fileref
, FileObject
, Irp
, &rollback
);
2322 if (!NT_SUCCESS(Status
)) {
2323 ERR("delete_fileref returned %08x\n", Status
);
2324 do_rollback(Vcb
, &rollback
);
2325 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2329 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2330 clear_rollback(&rollback
);
2331 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2332 IO_STATUS_BLOCK iosb
;
2333 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2335 if (!NT_SUCCESS(iosb
.Status
)) {
2336 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2339 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2340 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2341 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2344 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2346 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2347 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2351 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2352 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2355 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2358 Status
= STATUS_SUCCESS
;
2361 Irp
->IoStatus
.Status
= Status
;
2362 Irp
->IoStatus
.Information
= 0;
2364 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2368 IoSetTopLevelIrp(NULL
);
2370 FsRtlExitFileSystem();
2375 ULONG STDCALL
get_file_attributes(device_extension
* Vcb
, INODE_ITEM
* ii
, root
* r
, UINT64 inode
, UINT8 type
, BOOL dotfile
, BOOL ignore_xa
, PIRP Irp
) {
2382 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
, Irp
)) {
2384 if (eaval
[0] == '0' && eaval
[1] == 'x') {
2388 for (i
= 2; i
< ealen
; i
++) {
2391 if (eaval
[i
] >= '0' && eaval
[i
] <= '9')
2392 dosnum
|= eaval
[i
] - '0';
2393 else if (eaval
[i
] >= 'a' && eaval
[i
] <= 'f')
2394 dosnum
|= eaval
[i
] + 10 - 'a';
2395 else if (eaval
[i
] >= 'A' && eaval
[i
] <= 'F')
2396 dosnum
|= eaval
[i
] + 10 - 'a';
2399 TRACE("DOSATTRIB: %08x\n", dosnum
);
2403 if (type
== BTRFS_TYPE_DIRECTORY
)
2404 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2405 else if (type
== BTRFS_TYPE_SYMLINK
)
2406 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2416 case BTRFS_TYPE_DIRECTORY
:
2417 att
= FILE_ATTRIBUTE_DIRECTORY
;
2420 case BTRFS_TYPE_SYMLINK
:
2421 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2430 att
|= FILE_ATTRIBUTE_HIDDEN
;
2433 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2435 // FIXME - get READONLY from ii->st_mode
2436 // FIXME - return SYSTEM for block/char devices?
2439 att
= FILE_ATTRIBUTE_NORMAL
;
2444 static NTSTATUS
sync_read_phys(PDEVICE_OBJECT DeviceObject
, LONGLONG StartingOffset
, ULONG Length
, PUCHAR Buffer
, BOOL override
) {
2445 IO_STATUS_BLOCK
* IoStatus
;
2446 LARGE_INTEGER Offset
;
2448 PIO_STACK_LOCATION IrpSp
;
2450 read_context
* context
;
2454 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
2456 ERR("out of memory\n");
2457 return STATUS_INSUFFICIENT_RESOURCES
;
2460 RtlZeroMemory(context
, sizeof(read_context
));
2461 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
2463 IoStatus
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), ALLOC_TAG
);
2465 ERR("out of memory\n");
2466 ExFreePool(context
);
2467 return STATUS_INSUFFICIENT_RESOURCES
;
2470 Offset
.QuadPart
= StartingOffset
;
2472 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2473 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2476 ERR("IoAllocateIrp failed\n");
2477 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2481 Irp
->Flags
|= IRP_NOCACHE
;
2482 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2483 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2486 IrpSp
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2488 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2489 FIXME("FIXME - buffered IO\n");
2490 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2491 // TRACE("direct IO\n");
2493 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2494 if (!Irp
->MdlAddress
) {
2495 ERR("IoAllocateMdl failed\n");
2496 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2500 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2503 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2505 // TRACE("neither buffered nor direct IO\n");
2506 Irp
->UserBuffer
= Buffer
;
2509 IrpSp
->Parameters
.Read
.Length
= Length
;
2510 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2512 Irp
->UserIosb
= IoStatus
;
2513 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2515 Irp
->UserEvent
= &context
->Event
;
2517 // IoQueueThreadIrp(Irp);
2519 IoSetCompletionRoutine(Irp
, read_completion
, context
, TRUE
, TRUE
, TRUE
);
2521 Status
= IoCallDriver(DeviceObject
, Irp
);
2523 if (Status
== STATUS_PENDING
) {
2524 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2525 Status
= context
->iosb
.Status
;
2528 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2529 MmUnlockPages(Irp
->MdlAddress
);
2530 IoFreeMdl(Irp
->MdlAddress
);
2536 ExFreePool(IoStatus
);
2537 ExFreePool(context
);
2542 static NTSTATUS STDCALL
read_superblock(device_extension
* Vcb
, PDEVICE_OBJECT device
, UINT64 length
) {
2545 unsigned int i
, to_read
;
2548 to_read
= sector_align(sizeof(superblock
), device
->SectorSize
);
2550 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2552 ERR("out of memory\n");
2553 return STATUS_INSUFFICIENT_RESOURCES
;
2558 while (superblock_addrs
[i
] > 0) {
2559 if (i
> 0 && superblock_addrs
[i
] + sizeof(superblock
) > length
)
2562 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
, FALSE
);
2563 if (!NT_SUCCESS(Status
)) {
2564 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2569 // FIXME - check checksum before accepting?
2571 TRACE("got superblock %u!\n", i
);
2573 if (i
== 0 || sb
->generation
> Vcb
->superblock
.generation
)
2574 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2581 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
2583 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)Vcb
->superblock
.checksum
));
2585 if (crc32
!= *((UINT32
*)Vcb
->superblock
.checksum
))
2586 return STATUS_INTERNAL_ERROR
; // FIXME - correct error?
2588 TRACE("label is %s\n", Vcb
->superblock
.label
);
2589 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2591 return STATUS_SUCCESS
;
2594 NTSTATUS STDCALL
dev_ioctl(PDEVICE_OBJECT DeviceObject
, ULONG ControlCode
, PVOID InputBuffer
, ULONG InputBufferSize
,
2595 PVOID OutputBuffer
, ULONG OutputBufferSize
, BOOLEAN Override
, IO_STATUS_BLOCK
* iosb
)
2600 PIO_STACK_LOCATION Stack
;
2601 IO_STATUS_BLOCK IoStatus
;
2603 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2605 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2615 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2618 Stack
= IoGetNextIrpStackLocation(Irp
);
2619 Stack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2622 Status
= IoCallDriver(DeviceObject
, Irp
);
2624 if (Status
== STATUS_PENDING
) {
2625 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2626 Status
= IoStatus
.Status
;
2635 static NTSTATUS STDCALL
add_root(device_extension
* Vcb
, UINT64 id
, UINT64 addr
, traverse_ptr
* tp
) {
2636 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2638 ERR("out of memory\n");
2639 return STATUS_INSUFFICIENT_RESOURCES
;
2643 r
->path
.Buffer
= NULL
;
2644 r
->treeholder
.address
= addr
;
2645 r
->treeholder
.tree
= NULL
;
2646 InitializeListHead(&r
->fcbs
);
2648 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2650 ERR("out of memory\n");
2652 return STATUS_INSUFFICIENT_RESOURCES
;
2655 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2660 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2661 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2662 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2665 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2668 case BTRFS_ROOT_ROOT
:
2672 case BTRFS_ROOT_EXTENT
:
2673 Vcb
->extent_root
= r
;
2676 case BTRFS_ROOT_CHUNK
:
2677 Vcb
->chunk_root
= r
;
2680 case BTRFS_ROOT_DEVTREE
:
2684 case BTRFS_ROOT_CHECKSUM
:
2685 Vcb
->checksum_root
= r
;
2688 case BTRFS_ROOT_UUID
:
2693 return STATUS_SUCCESS
;
2696 static NTSTATUS STDCALL
look_for_roots(device_extension
* Vcb
, PIRP Irp
) {
2697 traverse_ptr tp
, next_tp
;
2702 searchkey
.obj_id
= 0;
2703 searchkey
.obj_type
= 0;
2704 searchkey
.offset
= 0;
2706 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
2707 if (!NT_SUCCESS(Status
)) {
2708 ERR("error - find_tree returned %08x\n", Status
);
2713 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2715 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2716 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2718 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2719 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
));
2721 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2723 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, &tp
);
2724 if (!NT_SUCCESS(Status
)) {
2725 ERR("add_root returned %08x\n", Status
);
2731 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2737 return STATUS_SUCCESS
;
2740 static NTSTATUS
find_disk_holes(device_extension
* Vcb
, device
* dev
, PIRP Irp
) {
2742 traverse_ptr tp
, next_tp
;
2747 InitializeListHead(&dev
->space
);
2749 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2750 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2751 searchkey
.offset
= 0;
2753 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
, Irp
);
2754 if (!NT_SUCCESS(Status
)) {
2755 ERR("error - find_tree returned %08x\n", Status
);
2762 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2763 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2764 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2766 if (tp
.item
->key
.offset
> lastaddr
) {
2767 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2768 if (!NT_SUCCESS(Status
)) {
2769 ERR("add_space_entry returned %08x\n", Status
);
2774 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2776 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
));
2780 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
2784 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2789 if (lastaddr
< dev
->devitem
.num_bytes
) {
2790 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2791 if (!NT_SUCCESS(Status
)) {
2792 ERR("add_space_entry returned %08x\n", Status
);
2797 return STATUS_SUCCESS
;
2800 device
* find_device_from_uuid(device_extension
* Vcb
, BTRFS_UUID
* uuid
) {
2803 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2804 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i
,
2805 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],
2806 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]);
2808 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2809 TRACE("returning device %llx\n", i
);
2810 return &Vcb
->devices
[i
];
2814 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& !IsListEmpty(&volumes
)) {
2815 LIST_ENTRY
* le
= volumes
.Flink
;
2817 while (le
!= &volumes
) {
2818 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2820 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2821 RtlCompareMemory(uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2824 PFILE_OBJECT FileObject
;
2825 PDEVICE_OBJECT DeviceObject
;
2827 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
2828 if (!NT_SUCCESS(Status
)) {
2829 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2833 DeviceObject
= FileObject
->DeviceObject
;
2835 ObReferenceObject(DeviceObject
);
2836 ObDereferenceObject(FileObject
);
2838 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2839 Vcb
->devices
[Vcb
->devices_loaded
].devitem
.device_uuid
= *uuid
;
2840 Vcb
->devices_loaded
++;
2842 return &Vcb
->devices
[Vcb
->devices_loaded
- 1];
2849 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2850 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
2851 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
2856 static BOOL
is_device_removable(PDEVICE_OBJECT devobj
) {
2858 STORAGE_HOTPLUG_INFO shi
;
2860 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
2862 if (!NT_SUCCESS(Status
)) {
2863 ERR("dev_ioctl returned %08x\n", Status
);
2867 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
2870 static ULONG
get_device_change_count(PDEVICE_OBJECT devobj
) {
2873 IO_STATUS_BLOCK iosb
;
2875 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
2877 if (!NT_SUCCESS(Status
)) {
2878 ERR("dev_ioctl returned %08x\n", Status
);
2882 if (iosb
.Information
< sizeof(ULONG
)) {
2883 ERR("iosb.Information was too short\n");
2890 static void init_device(device_extension
* Vcb
, device
* dev
, BOOL get_length
) {
2892 GET_LENGTH_INFORMATION gli
;
2894 dev
->removable
= is_device_removable(dev
->devobj
);
2895 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
2898 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2899 &gli
, sizeof(gli
), TRUE
, NULL
);
2900 if (!NT_SUCCESS(Status
)) {
2901 ERR("error reading length information: %08x\n", Status
);
2904 dev
->length
= gli
.Length
.QuadPart
;
2908 static NTSTATUS STDCALL
load_chunk_root(device_extension
* Vcb
, PIRP Irp
) {
2909 traverse_ptr tp
, next_tp
;
2916 searchkey
.obj_id
= 0;
2917 searchkey
.obj_type
= 0;
2918 searchkey
.offset
= 0;
2920 Vcb
->data_flags
= 0;
2922 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
, Irp
);
2923 if (!NT_SUCCESS(Status
)) {
2924 ERR("error - find_item returned %08x\n", Status
);
2929 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2931 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
2932 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
2933 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
));
2935 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
2938 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2939 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2940 RtlCopyMemory(&Vcb
->devices
[i
].devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2943 init_device(Vcb
, &Vcb
->devices
[i
], TRUE
);
2951 if (!IsListEmpty(&volumes
) && Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
2952 LIST_ENTRY
* le
= volumes
.Flink
;
2954 while (le
!= &volumes
) {
2955 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2957 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2958 RtlCompareMemory(&di
->device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2960 PFILE_OBJECT FileObject
;
2961 PDEVICE_OBJECT DeviceObject
;
2963 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_DATA
| FILE_WRITE_DATA
, &FileObject
, &DeviceObject
);
2964 if (!NT_SUCCESS(Status
)) {
2965 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2969 DeviceObject
= FileObject
->DeviceObject
;
2971 ObReferenceObject(DeviceObject
);
2972 ObDereferenceObject(FileObject
);
2974 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2975 RtlCopyMemory(&Vcb
->devices
[Vcb
->devices_loaded
].devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2976 init_device(Vcb
, &Vcb
->devices
[i
], FALSE
);
2977 Vcb
->devices
[i
].length
= v
->length
;
2978 Vcb
->devices_loaded
++;
2988 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
,
2989 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],
2990 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]);
2993 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
2996 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
2997 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
2998 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
));
3000 c
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk
), ALLOC_TAG
);
3003 ERR("out of memory\n");
3004 return STATUS_INSUFFICIENT_RESOURCES
;
3007 c
->size
= tp
.item
->size
;
3008 c
->offset
= tp
.item
->key
.offset
;
3009 c
->used
= c
->oldused
= 0;
3013 c
->chunk_item
= ExAllocatePoolWithTag(NonPagedPool
, tp
.item
->size
, ALLOC_TAG
);
3015 if (!c
->chunk_item
) {
3016 ERR("out of memory\n");
3018 return STATUS_INSUFFICIENT_RESOURCES
;
3021 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
3023 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
3024 Vcb
->data_flags
= c
->chunk_item
->type
;
3026 if (c
->chunk_item
->num_stripes
> 0) {
3027 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
3029 c
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
3032 ERR("out of memory\n");
3034 ExFreePool(c
->chunk_item
);
3035 return STATUS_INSUFFICIENT_RESOURCES
;
3038 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3039 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3040 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3045 ExInitializeResourceLite(&c
->lock
);
3046 ExInitializeResourceLite(&c
->changed_extents_lock
);
3048 InitializeListHead(&c
->space
);
3049 InitializeListHead(&c
->space_size
);
3050 InitializeListHead(&c
->deleting
);
3051 InitializeListHead(&c
->changed_extents
);
3053 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3055 c
->list_entry_changed
.Flink
= NULL
;
3059 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
, Irp
);
3065 Vcb
->log_to_phys_loaded
= TRUE
;
3067 if (Vcb
->data_flags
== 0)
3068 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3070 return STATUS_SUCCESS
;
3073 void protect_superblocks(device_extension
* Vcb
, chunk
* c
) {
3075 UINT64 off_start
, off_end
;
3077 // The Linux driver also protects all the space before the first superblock.
3078 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3079 // evidently Linux assumes the chunk at 0 is always SINGLE.
3080 if (c
->offset
< superblock_addrs
[0])
3081 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3083 while (superblock_addrs
[i
] != 0) {
3084 CHUNK_ITEM
* ci
= c
->chunk_item
;
3085 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3087 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3088 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3089 ULONG sub_stripes
= max(ci
->sub_stripes
, 1);
3091 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3094 UINT16 startoffstripe
;
3097 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3099 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3100 off_start
-= off_start
% ci
->stripe_length
;
3101 off_start
*= ci
->num_stripes
/ sub_stripes
;
3102 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3104 off_end
= off_start
+ ci
->stripe_length
;
3107 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3108 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3109 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3112 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3115 } else { // SINGLE, DUPLICATE, RAID1
3116 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3117 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3118 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3120 // The Linux driver protects the whole stripe in which the superblock lives
3122 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3123 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3125 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3134 static NTSTATUS STDCALL
find_chunk_usage(device_extension
* Vcb
, PIRP Irp
) {
3135 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3139 BLOCK_GROUP_ITEM
* bgi
;
3143 // block_group_item size=7f0000 chunktreeid=100 flags=1
3145 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3147 while (le
!= &Vcb
->chunks
) {
3148 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3150 searchkey
.obj_id
= c
->offset
;
3151 searchkey
.offset
= c
->chunk_item
->size
;
3153 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
, Irp
);
3154 if (!NT_SUCCESS(Status
)) {
3155 ERR("error - find_item returned %08x\n", Status
);
3159 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
3160 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3161 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3163 c
->used
= c
->oldused
= bgi
->used
;
3165 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3167 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3168 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
));
3172 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3173 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3175 // return (addr - c->offset) + cis->offset;
3178 // FIXME - make sure we free occasionally after doing one of these, or we
3179 // might use up a lot of memory with a big disk.
3181 Status
= load_free_space_cache(Vcb
, c
, Irp
);
3182 if (!NT_SUCCESS(Status
)) {
3183 ERR("load_free_space_cache returned %08x\n", Status
);
3187 protect_superblocks(Vcb
, c
);
3192 return STATUS_SUCCESS
;
3195 // static void STDCALL root_test(device_extension* Vcb) {
3198 // traverse_ptr tp, next_tp;
3203 // if (r->id == 0x102)
3209 // ERR("Could not find root tree.\n");
3213 // searchkey.obj_id = 0x1b6;
3214 // searchkey.obj_type = 0xb;
3215 // searchkey.offset = 0;
3217 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3218 // ERR("Could not find first item.\n");
3224 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3226 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3229 // free_traverse_ptr(&tp);
3234 // free_traverse_ptr(&tp);
3237 static NTSTATUS
load_sys_chunks(device_extension
* Vcb
) {
3239 ULONG n
= Vcb
->superblock
.n
;
3242 if (n
> sizeof(KEY
)) {
3243 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3246 return STATUS_SUCCESS
;
3248 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3250 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3255 if (n
< sizeof(CHUNK_ITEM
))
3256 return STATUS_SUCCESS
;
3258 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3259 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3262 return STATUS_SUCCESS
;
3264 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3267 ERR("out of memory\n");
3268 return STATUS_INSUFFICIENT_RESOURCES
;
3273 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3276 ERR("out of memory\n");
3277 return STATUS_INSUFFICIENT_RESOURCES
;
3280 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3281 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3285 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3286 return STATUS_INTERNAL_ERROR
;
3290 return STATUS_SUCCESS
;
3293 static root
* find_default_subvol(device_extension
* Vcb
, PIRP Irp
) {
3296 static char fn
[] = "default";
3297 static UINT32 crc32
= 0x8dbfc2d2;
3299 if (Vcb
->options
.subvol_id
!= 0) {
3300 le
= Vcb
->roots
.Flink
;
3301 while (le
!= &Vcb
->roots
) {
3302 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3304 if (r
->id
== Vcb
->options
.subvol_id
)
3311 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3317 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3318 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3319 searchkey
.offset
= crc32
;
3321 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
, Irp
);
3322 if (!NT_SUCCESS(Status
)) {
3323 ERR("error - find_item returned %08x\n", Status
);
3327 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3328 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3332 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3333 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
));
3337 di
= (DIR_ITEM
*)tp
.item
->data
;
3339 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3340 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
);
3344 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3345 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3349 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3350 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3354 le
= Vcb
->roots
.Flink
;
3355 while (le
!= &Vcb
->roots
) {
3356 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3358 if (r
->id
== di
->key
.obj_id
)
3364 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3368 le
= Vcb
->roots
.Flink
;
3369 while (le
!= &Vcb
->roots
) {
3370 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3372 if (r
->id
== BTRFS_ROOT_FSTREE
)
3381 static NTSTATUS
create_worker_threads(PDEVICE_OBJECT DeviceObject
) {
3382 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3386 Vcb
->threads
.num_threads
= max(3, KeQueryActiveProcessorCount(NULL
));
3388 Vcb
->threads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
, ALLOC_TAG
);
3389 if (!Vcb
->threads
.threads
) {
3390 ERR("out of memory\n");
3391 return STATUS_INSUFFICIENT_RESOURCES
;
3394 RtlZeroMemory(Vcb
->threads
.threads
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
);
3396 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
3397 Vcb
->threads
.threads
[i
].DeviceObject
= DeviceObject
;
3398 KeInitializeEvent(&Vcb
->threads
.threads
[i
].event
, SynchronizationEvent
, FALSE
);
3399 KeInitializeEvent(&Vcb
->threads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3400 InitializeListHead(&Vcb
->threads
.threads
[i
].jobs
);
3401 KeInitializeSpinLock(&Vcb
->threads
.threads
[i
].spin_lock
);
3403 Status
= PsCreateSystemThread(&Vcb
->threads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, worker_thread
, &Vcb
->threads
.threads
[i
]);
3404 if (!NT_SUCCESS(Status
)) {
3407 ERR("PsCreateSystemThread returned %08x\n", Status
);
3409 for (j
= 0; j
< i
; j
++) {
3410 Vcb
->threads
.threads
[i
].quit
= TRUE
;
3411 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
3418 Vcb
->threads
.pending_jobs
= 0;
3420 return STATUS_SUCCESS
;
3423 BOOL
add_thread_job(device_extension
* Vcb
, PIRP Irp
) {
3427 threadnum
= InterlockedIncrement(&Vcb
->threads
.next_thread
) % Vcb
->threads
.num_threads
;
3429 if (Vcb
->threads
.pending_jobs
>= Vcb
->threads
.num_threads
)
3432 if (Vcb
->threads
.threads
[threadnum
].quit
)
3435 tj
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(thread_job
), ALLOC_TAG
);
3437 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
3438 Irp
->IoStatus
.Information
= 0;
3439 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3445 InterlockedIncrement(&Vcb
->threads
.pending_jobs
);
3447 ExInterlockedInsertTailList(&Vcb
->threads
.threads
[threadnum
].jobs
, &tj
->list_entry
, &Vcb
->threads
.threads
[threadnum
].spin_lock
);
3448 KeSetEvent(&Vcb
->threads
.threads
[threadnum
].event
, 0, FALSE
);
3453 static BOOL
raid_generations_okay(device_extension
* Vcb
) {
3456 // FIXME - if the difference between superblocks is small, we should try to recover
3458 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3459 LIST_ENTRY
* le
= volumes
.Flink
;
3460 while (le
!= &volumes
) {
3461 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3463 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
3464 RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
3466 if (v
->gen1
!= Vcb
->superblock
.generation
- 1) {
3467 WARN("device %llu had generation %llx, expected %llx\n", i
, v
->gen1
, Vcb
->superblock
.generation
- 1);
3479 static NTSTATUS STDCALL
mount_vol(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
3480 PIO_STACK_LOCATION Stack
;
3481 PDEVICE_OBJECT NewDeviceObject
= NULL
;
3482 PDEVICE_OBJECT DeviceToMount
;
3484 device_extension
* Vcb
= NULL
;
3485 GET_LENGTH_INFORMATION gli
;
3490 fcb
* root_fcb
= NULL
;
3491 ccb
* root_ccb
= NULL
;
3493 TRACE("mount_vol called\n");
3495 if (DeviceObject
!= devobj
)
3497 Status
= STATUS_INVALID_DEVICE_REQUEST
;
3501 Stack
= IoGetCurrentIrpStackLocation(Irp
);
3502 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
3504 Status
= dev_ioctl(DeviceToMount
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
3505 &gli
, sizeof(gli
), TRUE
, NULL
);
3506 if (!NT_SUCCESS(Status
)) {
3507 ERR("error reading length information: %08x\n", Status
);
3508 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3512 Status
= IoCreateDevice(drvobj
,
3513 sizeof(device_extension
),
3515 FILE_DEVICE_DISK_FILE_SYSTEM
,
3519 if (!NT_SUCCESS(Status
)) {
3520 ERR("IoCreateDevice returned %08x\n", Status
);
3521 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3525 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3527 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
3528 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
3529 RtlZeroMemory(Vcb
, sizeof(device_extension
));
3530 Vcb
->type
= VCB_TYPE_VOLUME
;
3532 ExInitializeResourceLite(&Vcb
->tree_lock
);
3533 Vcb
->open_trees
= 0;
3534 Vcb
->need_write
= FALSE
;
3536 ExInitializeResourceLite(&Vcb
->fcb_lock
);
3537 ExInitializeResourceLite(&Vcb
->DirResource
);
3538 ExInitializeResourceLite(&Vcb
->checksum_lock
);
3539 ExInitializeResourceLite(&Vcb
->chunk_lock
);
3541 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
3542 InsertTailList(&VcbList
, &Vcb
->list_entry
);
3543 ExReleaseResourceLite(&global_loading_lock
);
3545 ExInitializeResourceLite(&Vcb
->load_lock
);
3546 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
3548 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3549 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3551 // Status = NtfsGetVolumeData(DeviceToMount,
3553 // if (!NT_SUCCESS(Status))
3556 // Vcb->device = DeviceToMount;
3557 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
3559 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3560 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3561 // if (!NT_SUCCESS(Status)) {
3562 // ERR("error reading disk geometry: %08x\n", Status);
3565 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3566 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3567 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3570 TRACE("partition length = %llx\n", gli
.Length
.QuadPart
);
3572 Status
= read_superblock(Vcb
, DeviceToMount
, gli
.Length
.QuadPart
);
3573 if (!NT_SUCCESS(Status
)) {
3574 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3578 if (Vcb
->superblock
.magic
!= BTRFS_MAGIC
) {
3579 ERR("not a BTRFS volume\n");
3580 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3583 TRACE("btrfs magic found\n");
3586 Status
= registry_load_volume_options(Vcb
);
3587 if (!NT_SUCCESS(Status
)) {
3588 ERR("registry_load_volume_options returned %08x\n", Status
);
3592 if (Vcb
->options
.ignore
) {
3593 TRACE("ignoring volume\n");
3594 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3598 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
3599 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
3600 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3605 while (le
!= &volumes
) {
3606 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3608 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) && v
->devnum
< Vcb
->superblock
.dev_item
.dev_id
) {
3609 // skipping over device in RAID which isn't the first one
3610 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3617 Vcb
->readonly
= FALSE
;
3618 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
3619 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
3620 Vcb
->readonly
= TRUE
;
3623 if (Vcb
->options
.readonly
)
3624 Vcb
->readonly
= TRUE
;
3626 Vcb
->superblock
.generation
++;
3627 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
3629 Vcb
->devices
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(device
) * Vcb
->superblock
.num_devices
, ALLOC_TAG
);
3630 if (!Vcb
->devices
) {
3631 ERR("out of memory\n");
3632 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3636 Vcb
->devices
[0].devobj
= DeviceToMount
;
3637 RtlCopyMemory(&Vcb
->devices
[0].devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
3638 init_device(Vcb
, &Vcb
->devices
[0], FALSE
);
3639 Vcb
->devices
[0].length
= gli
.Length
.QuadPart
;
3641 if (Vcb
->superblock
.num_devices
> 1)
3642 RtlZeroMemory(&Vcb
->devices
[1], sizeof(DEV_ITEM
) * (Vcb
->superblock
.num_devices
- 1));
3644 Vcb
->devices_loaded
= 1;
3646 TRACE("DeviceToMount = %p\n", DeviceToMount
);
3647 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack
->Parameters
.MountVolume
.Vpb
);
3649 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
3650 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
3652 InitializeListHead(&Vcb
->roots
);
3653 InitializeListHead(&Vcb
->drop_roots
);
3655 Vcb
->log_to_phys_loaded
= FALSE
;
3657 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, NULL
);
3659 if (!Vcb
->chunk_root
) {
3660 ERR("Could not load chunk root.\n");
3661 Status
= STATUS_INTERNAL_ERROR
;
3665 InitializeListHead(&Vcb
->sys_chunks
);
3666 Status
= load_sys_chunks(Vcb
);
3667 if (!NT_SUCCESS(Status
)) {
3668 ERR("load_sys_chunks returned %08x\n", Status
);
3672 InitializeListHead(&Vcb
->chunks
);
3673 InitializeListHead(&Vcb
->chunks_changed
);
3674 InitializeListHead(&Vcb
->trees
);
3675 InitializeListHead(&Vcb
->all_fcbs
);
3676 InitializeListHead(&Vcb
->dirty_fcbs
);
3677 InitializeListHead(&Vcb
->dirty_filerefs
);
3678 InitializeListHead(&Vcb
->shared_extents
);
3679 InitializeListHead(&Vcb
->sector_checksums
);
3681 KeInitializeSpinLock(&Vcb
->dirty_fcbs_lock
);
3682 KeInitializeSpinLock(&Vcb
->dirty_filerefs_lock
);
3683 KeInitializeSpinLock(&Vcb
->shared_extents_lock
);
3685 InitializeListHead(&Vcb
->DirNotifyList
);
3687 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
3689 Status
= load_chunk_root(Vcb
, Irp
);
3690 if (!NT_SUCCESS(Status
)) {
3691 ERR("load_chunk_root returned %08x\n", Status
);
3695 if (Vcb
->superblock
.num_devices
> 1) {
3696 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3697 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
3699 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3701 Status
= STATUS_INTERNAL_ERROR
;
3705 if (!raid_generations_okay(Vcb
)) {
3706 ERR("could not mount as generation mismatch\n");
3708 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3710 Status
= STATUS_INTERNAL_ERROR
;
3715 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, NULL
);
3717 if (!Vcb
->root_root
) {
3718 ERR("Could not load root of roots.\n");
3719 Status
= STATUS_INTERNAL_ERROR
;
3723 Status
= look_for_roots(Vcb
, Irp
);
3724 if (!NT_SUCCESS(Status
)) {
3725 ERR("look_for_roots returned %08x\n", Status
);
3729 if (!Vcb
->readonly
) {
3730 Status
= find_chunk_usage(Vcb
, Irp
);
3731 if (!NT_SUCCESS(Status
)) {
3732 ERR("find_chunk_usage returned %08x\n", Status
);
3737 // We've already increased the generation by one
3738 if (!Vcb
->readonly
&& Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) {
3739 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
3740 Status
= clear_free_space_cache(Vcb
, Irp
);
3741 if (!NT_SUCCESS(Status
)) {
3742 ERR("clear_free_space_cache returned %08x\n", Status
);
3747 Vcb
->volume_fcb
= create_fcb(NonPagedPool
);
3748 if (!Vcb
->volume_fcb
) {
3749 ERR("out of memory\n");
3750 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3754 Vcb
->volume_fcb
->Vcb
= Vcb
;
3755 Vcb
->volume_fcb
->sd
= NULL
;
3757 root_fcb
= create_fcb(NonPagedPool
);
3759 ERR("out of memory\n");
3760 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3764 root_fcb
->Vcb
= Vcb
;
3765 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
3766 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
3768 #ifdef DEBUG_FCB_REFCOUNTS
3769 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
3770 WARN("root FCB = %p\n", root_fcb
);
3773 root_fcb
->subvol
= find_default_subvol(Vcb
, Irp
);
3775 if (!root_fcb
->subvol
) {
3776 ERR("could not find top subvol\n");
3777 Status
= STATUS_INTERNAL_ERROR
;
3781 searchkey
.obj_id
= root_fcb
->inode
;
3782 searchkey
.obj_type
= TYPE_INODE_ITEM
;
3783 searchkey
.offset
= 0xffffffffffffffff;
3785 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
, Irp
);
3786 if (!NT_SUCCESS(Status
)) {
3787 ERR("error - find_item returned %08x\n", Status
);
3791 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
3792 ERR("couldn't find INODE_ITEM for root directory\n");
3793 Status
= STATUS_INTERNAL_ERROR
;
3797 if (tp
.item
->size
> 0)
3798 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
3800 fcb_get_sd(root_fcb
, NULL
, Irp
);
3802 root_fcb
->atts
= get_file_attributes(Vcb
, &root_fcb
->inode_item
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
, Irp
);
3804 Vcb
->root_fileref
= create_fileref();
3805 if (!Vcb
->root_fileref
) {
3806 ERR("out of memory\n");
3807 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3811 Vcb
->root_fileref
->fcb
= root_fcb
;
3812 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
3813 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
3815 root_fcb
->fileref
= Vcb
->root_fileref
;
3817 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
3819 ERR("out of memory\n");
3820 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3824 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
3825 Vcb
->root_file
->FsContext
= root_fcb
;
3826 Vcb
->root_file
->SectionObjectPointer
= &root_fcb
->nonpaged
->segment_object
;
3827 Vcb
->root_file
->Vpb
= DeviceObject
->Vpb
;
3829 RtlZeroMemory(root_ccb
, sizeof(ccb
));
3830 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3831 root_ccb
->NodeSize
= sizeof(ccb
);
3833 Vcb
->root_file
->FsContext2
= root_ccb
;
3836 CcInitializeCacheMap(Vcb
->root_file
, (PCC_FILE_SIZES
)(&root_fcb
->Header
.AllocationSize
), FALSE
, cache_callbacks
, Vcb
->root_file
);
3837 } _SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER
) {
3838 Status
= _SEH2_GetExceptionCode();
3842 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3843 Status
= find_disk_holes(Vcb
, &Vcb
->devices
[i
], Irp
);
3844 if (!NT_SUCCESS(Status
)) {
3845 ERR("find_disk_holes returned %08x\n", Status
);
3852 KeInitializeSpinLock(&Vcb
->FcbListLock
);
3854 NewDeviceObject
->Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
3855 Stack
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
3856 Stack
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
3857 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
3858 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
3859 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
3860 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
3861 Vcb
->Vpb
= NewDeviceObject
->Vpb
;
3863 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
3865 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
3866 if (!NT_SUCCESS(Status
)) {
3867 ERR("PsCreateSystemThread returned %08x\n", Status
);
3871 Status
= create_worker_threads(NewDeviceObject
);
3872 if (!NT_SUCCESS(Status
)) {
3873 ERR("create_worker_threads returned %08x\n", Status
);
3877 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
3878 if (!NT_SUCCESS(Status
))
3879 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
3881 Status
= STATUS_SUCCESS
;
3885 ExReleaseResourceLite(&Vcb
->load_lock
);
3888 if (!NT_SUCCESS(Status
)) {
3891 ObDereferenceObject(Vcb
->root_file
);
3892 else if (Vcb
->root_fileref
)
3893 free_fileref(Vcb
->root_fileref
);
3897 if (Vcb
->volume_fcb
)
3898 free_fcb(Vcb
->volume_fcb
);
3900 ExDeleteResourceLite(&Vcb
->tree_lock
);
3901 ExDeleteResourceLite(&Vcb
->load_lock
);
3902 ExDeleteResourceLite(&Vcb
->fcb_lock
);
3903 ExDeleteResourceLite(&Vcb
->DirResource
);
3904 ExDeleteResourceLite(&Vcb
->checksum_lock
);
3905 ExDeleteResourceLite(&Vcb
->chunk_lock
);
3908 ExFreePoolWithTag(Vcb
->devices
, ALLOC_TAG
);
3910 RemoveEntryList(&Vcb
->list_entry
);
3913 if (NewDeviceObject
)
3914 IoDeleteDevice(NewDeviceObject
);
3916 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
3918 TRACE("mount_vol done (status: %lx)\n", Status
);
3923 static NTSTATUS
verify_volume(PDEVICE_OBJECT device
) {
3924 device_extension
* Vcb
= device
->DeviceExtension
;
3926 IO_STATUS_BLOCK iosb
;
3933 return STATUS_WRONG_VOLUME
;
3935 Status
= dev_ioctl(Vcb
->Vpb
->RealDevice
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3937 if (!NT_SUCCESS(Status
)) {
3938 ERR("dev_ioctl returned %08x\n", Status
);
3942 to_read
= sector_align(sizeof(superblock
), device
->SectorSize
);
3944 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
3946 ERR("out of memory\n");
3947 return STATUS_INSUFFICIENT_RESOURCES
;
3950 Status
= sync_read_phys(Vcb
->Vpb
->RealDevice
, superblock_addrs
[0], to_read
, (PUCHAR
)sb
, TRUE
);
3951 if (!NT_SUCCESS(Status
)) {
3952 ERR("Failed to read superblock: %08x\n", Status
);
3957 if (sb
->magic
!= BTRFS_MAGIC
) {
3958 ERR("not a BTRFS volume\n");
3960 return STATUS_WRONG_VOLUME
;
3963 if (RtlCompareMemory(&sb
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
)) != sizeof(BTRFS_UUID
)) {
3964 ERR("different UUIDs\n");
3966 return STATUS_WRONG_VOLUME
;
3969 crc32
= ~calc_crc32c(0xffffffff, (UINT8
*)&sb
->uuid
, (ULONG
)sizeof(superblock
) - sizeof(sb
->checksum
));
3970 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)sb
->checksum
));
3972 if (crc32
!= *((UINT32
*)sb
->checksum
)) {
3973 ERR("different UUIDs\n");
3975 return STATUS_WRONG_VOLUME
;
3980 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3981 if (Vcb
->devices
[i
].removable
) {
3984 IO_STATUS_BLOCK iosb
;
3986 Status
= dev_ioctl(Vcb
->devices
[i
].devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
3988 if (!NT_SUCCESS(Status
)) {
3989 ERR("dev_ioctl returned %08x\n", Status
);
3993 if (iosb
.Information
< sizeof(ULONG
)) {
3994 ERR("iosb.Information was too short\n");
3995 return STATUS_INTERNAL_ERROR
;
3998 Vcb
->devices
[i
].change_count
= cc
;
4001 Vcb
->devices
[i
].devobj
->Flags
&= ~DO_VERIFY_VOLUME
;
4004 Vcb
->Vpb
->RealDevice
->Flags
&= ~DO_VERIFY_VOLUME
;
4006 return STATUS_SUCCESS
;
4009 static NTSTATUS STDCALL
drv_file_system_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4010 PIO_STACK_LOCATION IrpSp
;
4012 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4015 TRACE("file system control\n");
4017 FsRtlEnterFileSystem();
4019 top_level
= is_top_level(Irp
);
4021 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4022 Status
= part0_passthrough(DeviceObject
, Irp
);
4026 Status
= STATUS_NOT_IMPLEMENTED
;
4028 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
4030 Irp
->IoStatus
.Information
= 0;
4032 switch (IrpSp
->MinorFunction
) {
4033 case IRP_MN_MOUNT_VOLUME
:
4034 TRACE("IRP_MN_MOUNT_VOLUME\n");
4036 Status
= mount_vol(DeviceObject
, Irp
);
4039 case IRP_MN_KERNEL_CALL
:
4040 TRACE("IRP_MN_KERNEL_CALL\n");
4042 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, FALSE
);
4045 case IRP_MN_USER_FS_REQUEST
:
4046 TRACE("IRP_MN_USER_FS_REQUEST\n");
4048 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, TRUE
);
4051 case IRP_MN_VERIFY_VOLUME
:
4052 TRACE("IRP_MN_VERIFY_VOLUME\n");
4054 Status
= verify_volume(DeviceObject
);
4056 if (!NT_SUCCESS(Status
) && Vcb
->Vpb
->Flags
& VPB_MOUNTED
) {
4058 // Vcb->Vpb->Flags &= ~VPB_MOUNTED;
4067 Irp
->IoStatus
.Status
= Status
;
4069 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4073 IoSetTopLevelIrp(NULL
);
4075 FsRtlExitFileSystem();
4080 static NTSTATUS STDCALL
drv_lock_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4082 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4083 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
4084 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4087 FsRtlEnterFileSystem();
4089 top_level
= is_top_level(Irp
);
4091 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4092 Status
= part0_passthrough(DeviceObject
, Irp
);
4096 TRACE("lock control\n");
4098 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
4100 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
4104 IoSetTopLevelIrp(NULL
);
4106 FsRtlExitFileSystem();
4111 NTSTATUS
part0_passthrough(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4113 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
4115 IoSkipCurrentIrpStackLocation(Irp
);
4117 Status
= IoCallDriver(p0de
->devobj
, Irp
);
4122 static NTSTATUS
part0_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4124 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
4125 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4127 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
4129 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
4130 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
:
4132 MOUNTDEV_UNIQUE_ID
* mduid
;
4134 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
)) {
4135 Status
= STATUS_BUFFER_TOO_SMALL
;
4136 Irp
->IoStatus
.Status
= Status
;
4137 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4138 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4142 mduid
= Irp
->AssociatedIrp
.SystemBuffer
;
4143 mduid
->UniqueIdLength
= sizeof(BTRFS_UUID
);
4145 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
) {
4146 Status
= STATUS_BUFFER_OVERFLOW
;
4147 Irp
->IoStatus
.Status
= Status
;
4148 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
4149 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4153 RtlCopyMemory(mduid
->UniqueId
, &p0de
->uuid
, sizeof(BTRFS_UUID
));
4155 Status
= STATUS_SUCCESS
;
4156 Irp
->IoStatus
.Status
= Status
;
4157 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
;
4158 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4163 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
:
4165 PMOUNTDEV_NAME name
;
4167 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
)) {
4168 Status
= STATUS_BUFFER_TOO_SMALL
;
4169 Irp
->IoStatus
.Status
= Status
;
4170 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4171 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4175 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4176 name
->NameLength
= p0de
->name
.Length
;
4178 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
) {
4179 Status
= STATUS_BUFFER_OVERFLOW
;
4180 Irp
->IoStatus
.Status
= Status
;
4181 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4182 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4186 RtlCopyMemory(name
->Name
, p0de
->name
.Buffer
, p0de
->name
.Length
);
4188 Status
= STATUS_SUCCESS
;
4189 Irp
->IoStatus
.Status
= Status
;
4190 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
;
4191 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4197 IoSkipCurrentIrpStackLocation(Irp
);
4199 Status
= IoCallDriver(p0de
->devobj
, Irp
);
4201 TRACE("returning %08x\n", Status
);
4206 static NTSTATUS STDCALL
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4208 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4209 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4210 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4214 FsRtlEnterFileSystem();
4216 top_level
= is_top_level(Irp
);
4218 Irp
->IoStatus
.Information
= 0;
4220 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4221 Status
= part0_device_control(DeviceObject
, Irp
);
4225 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
4228 ERR("FileObject was NULL\n");
4229 Status
= STATUS_INVALID_PARAMETER
;
4233 fcb
= FileObject
->FsContext
;
4236 ERR("FCB was NULL\n");
4237 Status
= STATUS_INVALID_PARAMETER
;
4241 if (fcb
!= Vcb
->volume_fcb
) {
4242 Status
= STATUS_NOT_IMPLEMENTED
;
4246 IoSkipCurrentIrpStackLocation(Irp
);
4248 Status
= IoCallDriver(Vcb
->devices
[0].devobj
, Irp
);
4253 Irp
->IoStatus
.Status
= Status
;
4255 if (Status
!= STATUS_PENDING
)
4256 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4260 IoSetTopLevelIrp(NULL
);
4262 FsRtlExitFileSystem();
4267 static NTSTATUS STDCALL
drv_shutdown(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4270 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4272 TRACE("shutdown\n");
4274 FsRtlEnterFileSystem();
4276 top_level
= is_top_level(Irp
);
4278 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4279 Status
= part0_passthrough(DeviceObject
, Irp
);
4283 Status
= STATUS_SUCCESS
;
4285 while (!IsListEmpty(&VcbList
)) {
4286 Vcb
= CONTAINING_RECORD(VcbList
.Flink
, device_extension
, list_entry
);
4288 TRACE("shutting down Vcb %p\n", Vcb
);
4293 Irp
->IoStatus
.Status
= Status
;
4294 Irp
->IoStatus
.Information
= 0;
4296 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4300 IoSetTopLevelIrp(NULL
);
4302 FsRtlExitFileSystem();
4307 BOOL
is_file_name_valid(PUNICODE_STRING us
) {
4310 if (us
->Length
< sizeof(WCHAR
))
4313 if (us
->Length
> 255 * sizeof(WCHAR
))
4316 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
4317 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
4318 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))
4322 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
4329 static void STDCALL
init_serial() {
4332 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
4333 if (!NT_SUCCESS(Status
)) {
4334 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
4340 static void STDCALL
check_cpu() {
4341 unsigned int cpuInfo
[4];
4343 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
4344 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
4346 __cpuid(cpuInfo
, 1);
4347 have_sse42
= cpuInfo
[2] & (1 << 20);
4351 TRACE("SSE4.2 is supported\n");
4353 TRACE("SSE4.2 not supported\n");
4358 static void init_logging() {
4359 if (log_device
.Length
> 0)
4361 else if (log_file
.Length
> 0) {
4363 OBJECT_ATTRIBUTES oa
;
4364 IO_STATUS_BLOCK iosb
;
4369 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4371 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
4372 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
4374 if (!NT_SUCCESS(Status
)) {
4375 ERR("ZwCreateFile returned %08x\n", Status
);
4379 if (iosb
.Information
== FILE_OPENED
) { // already exists
4380 FILE_STANDARD_INFORMATION fsi
;
4381 FILE_POSITION_INFORMATION fpi
;
4383 static char delim
[] = "\n---\n";
4385 // move to end of file
4387 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
4389 if (!NT_SUCCESS(Status
)) {
4390 ERR("ZwQueryInformationFile returned %08x\n", Status
);
4394 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
4396 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
4398 if (!NT_SUCCESS(Status
)) {
4399 ERR("ZwSetInformationFile returned %08x\n", Status
);
4403 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, delim
, strlen(delim
), NULL
, NULL
);
4405 if (!NT_SUCCESS(Status
)) {
4406 ERR("ZwWriteFile returned %08x\n", Status
);
4411 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
4414 ERR("out of memory\n");
4418 KeQuerySystemTime(&time
);
4420 RtlTimeToTimeFields(&time
, &tf
);
4422 sprintf(dateline
, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
4424 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, strlen(dateline
), NULL
, NULL
);
4426 if (!NT_SUCCESS(Status
)) {
4427 ERR("ZwWriteFile returned %08x\n", Status
);
4431 ExFreePool(dateline
);
4436 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
) {
4438 PDEVICE_OBJECT DeviceObject
;
4439 UNICODE_STRING device_nameW
;
4440 UNICODE_STRING dosdevice_nameW
;
4442 InitializeListHead(&uid_map_list
);
4444 log_device
.Buffer
= NULL
;
4445 log_device
.Length
= log_device
.MaximumLength
= 0;
4446 log_file
.Buffer
= NULL
;
4447 log_file
.Length
= log_file
.MaximumLength
= 0;
4449 read_registry(RegistryPath
);
4452 if (debug_log_level
> 0)
4458 TRACE("DriverEntry\n");
4460 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
4461 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
4463 if (!registry_path
.Buffer
) {
4464 ERR("out of memory\n");
4465 return STATUS_INSUFFICIENT_RESOURCES
;
4468 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
4474 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4476 drvobj
= DriverObject
;
4478 DriverObject
->DriverUnload
= DriverUnload
;
4480 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
4481 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
4482 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
4483 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
4484 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
4485 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
4486 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
4487 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
4488 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
4489 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
4490 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
4491 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
4492 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
4493 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
4494 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
4495 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
4496 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
4497 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
4498 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
4499 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
4501 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
4503 device_nameW
.Buffer
= device_name
;
4504 device_nameW
.Length
= device_nameW
.MaximumLength
= (USHORT
)wcslen(device_name
) * sizeof(WCHAR
);
4505 dosdevice_nameW
.Buffer
= dosdevice_name
;
4506 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
4508 Status
= IoCreateDevice(DriverObject
, 0, &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
, FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
4509 if (!NT_SUCCESS(Status
)) {
4510 ERR("IoCreateDevice returned %08x\n", Status
);
4514 devobj
= DeviceObject
;
4516 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4518 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
4519 if (!NT_SUCCESS(Status
)) {
4520 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
4524 Status
= init_cache();
4525 if (!NT_SUCCESS(Status
)) {
4526 ERR("init_cache returned %08x\n", Status
);
4530 InitializeListHead(&volumes
);
4531 look_for_vols(DriverObject
, &volumes
);
4533 InitializeListHead(&VcbList
);
4534 ExInitializeResourceLite(&global_loading_lock
);
4536 IoRegisterFileSystem(DeviceObject
);
4538 return STATUS_SUCCESS
;