1 /* Copyright (c) Mark Harmstone 2016
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
22 #include "btrfs_drv.h"
38 #define INCOMPAT_SUPPORTED (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \
39 BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA)
40 #define COMPAT_RO_SUPPORTED 0
42 static WCHAR device_name
[] = {'\\','B','t','r','f','s',0};
43 static WCHAR dosdevice_name
[] = {'\\','D','o','s','D','e','v','i','c','e','s','\\','B','t','r','f','s',0};
45 PDRIVER_OBJECT drvobj
;
46 PDEVICE_OBJECT devobj
;
48 BOOL have_sse42
= FALSE
;
51 LIST_ENTRY uid_map_list
;
54 ERESOURCE global_loading_lock
;
55 UINT32 debug_log_level
= 0;
56 BOOL log_started
= FALSE
;
57 UNICODE_STRING log_device
, log_file
, registry_path
;
60 PFILE_OBJECT comfo
= NULL
;
61 PDEVICE_OBJECT comdo
= NULL
;
62 HANDLE log_handle
= NULL
;
65 int __security_cookie
= __LINE__
;
67 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
);
75 static NTSTATUS STDCALL
dbg_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
76 read_context
* context
= conptr
;
78 // DbgPrint("dbg_completion\n");
80 context
->iosb
= Irp
->IoStatus
;
81 KeSetEvent(&context
->Event
, 0, FALSE
);
83 // return STATUS_SUCCESS;
84 return STATUS_MORE_PROCESSING_REQUIRED
;
87 #ifdef DEBUG_LONG_MESSAGES
88 void STDCALL
_debug_message(const char* func
, const char* file
, unsigned int line
, char* s
, ...) {
90 void STDCALL
_debug_message(const char* func
, char* s
, ...) {
93 PIO_STACK_LOCATION IrpSp
;
97 char *buf2
= NULL
, *buf
;
98 read_context
* context
= NULL
;
101 buf2
= ExAllocatePoolWithTag(NonPagedPool
, 1024, ALLOC_TAG
);
104 DbgPrint("Couldn't allocate buffer in debug_message\n");
108 #ifdef DEBUG_LONG_MESSAGES
109 sprintf(buf2
, "%p:%s:%s:%u:", PsGetCurrentThreadId(), func
, file
, line
);
111 sprintf(buf2
, "%p:%s:", PsGetCurrentThreadId(), func
);
113 buf
= &buf2
[strlen(buf2
)];
116 vsprintf(buf
, s
, ap
);
118 if (!log_started
|| (log_device
.Length
== 0 && log_file
.Length
== 0)) {
120 } else if (log_device
.Length
> 0) {
122 DbgPrint("comdo is NULL :-(\n");
127 length
= (UINT32
)strlen(buf2
);
129 offset
.u
.LowPart
= 0;
130 offset
.u
.HighPart
= 0;
132 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
134 DbgPrint("Couldn't allocate context in debug_message\n");
138 RtlZeroMemory(context
, sizeof(read_context
));
140 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
142 // status = ZwWriteFile(comh, NULL, NULL, NULL, &io, buf2, strlen(buf2), &offset, NULL);
144 Irp
= IoAllocateIrp(comdo
->StackSize
, FALSE
);
147 DbgPrint("IoAllocateIrp failed\n");
151 IrpSp
= IoGetNextIrpStackLocation(Irp
);
152 IrpSp
->MajorFunction
= IRP_MJ_WRITE
;
154 if (comdo
->Flags
& DO_BUFFERED_IO
) {
155 Irp
->AssociatedIrp
.SystemBuffer
= buf2
;
157 Irp
->Flags
= IRP_BUFFERED_IO
;
158 } else if (comdo
->Flags
& DO_DIRECT_IO
) {
159 Irp
->MdlAddress
= IoAllocateMdl(buf2
, length
, FALSE
, FALSE
, NULL
);
160 if (!Irp
->MdlAddress
) {
161 DbgPrint("IoAllocateMdl failed\n");
165 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
167 Irp
->UserBuffer
= buf2
;
170 IrpSp
->Parameters
.Write
.Length
= length
;
171 IrpSp
->Parameters
.Write
.ByteOffset
= offset
;
173 Irp
->UserIosb
= &context
->iosb
;
175 Irp
->UserEvent
= &context
->Event
;
177 IoSetCompletionRoutine(Irp
, dbg_completion
, context
, TRUE
, TRUE
, TRUE
);
179 Status
= IoCallDriver(comdo
, Irp
);
181 if (Status
== STATUS_PENDING
) {
182 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
183 Status
= context
->iosb
.Status
;
186 if (comdo
->Flags
& DO_DIRECT_IO
) {
187 MmUnlockPages(Irp
->MdlAddress
);
188 IoFreeMdl(Irp
->MdlAddress
);
191 if (!NT_SUCCESS(Status
)) {
192 DbgPrint("failed to write to COM1 - error %08x\n", Status
);
198 } else if (log_handle
!= NULL
) {
199 IO_STATUS_BLOCK iosb
;
201 length
= (UINT32
)strlen(buf2
);
203 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, buf2
, length
, NULL
, NULL
);
205 if (!NT_SUCCESS(Status
)) {
206 DbgPrint("failed to write to file - error %08x\n", Status
);
221 UINT64
sector_align( UINT64 NumberToBeAligned
, UINT64 Alignment
)
223 if( Alignment
& ( Alignment
- 1 ) )
226 // Alignment not a power of 2
229 return NumberToBeAligned
;
231 if( ( NumberToBeAligned
& ( Alignment
- 1 ) ) != 0 )
233 NumberToBeAligned
= NumberToBeAligned
+ Alignment
;
234 NumberToBeAligned
= NumberToBeAligned
& ( ~ (Alignment
-1) );
236 return NumberToBeAligned
;
239 int keycmp(const KEY
* key1
, const KEY
* key2
) {
240 if (key1
->obj_id
< key2
->obj_id
) {
242 } else if (key1
->obj_id
> key2
->obj_id
) {
246 if (key1
->obj_type
< key2
->obj_type
) {
248 } else if (key1
->obj_type
> key2
->obj_type
) {
252 if (key1
->offset
< key2
->offset
) {
254 } else if (key1
->offset
> key2
->offset
) {
261 BOOL
is_top_level(PIRP Irp
) {
262 if (!IoGetTopLevelIrp()) {
263 IoSetTopLevelIrp(Irp
);
270 static void STDCALL
DriverUnload(PDRIVER_OBJECT DriverObject
) {
271 UNICODE_STRING dosdevice_nameW
;
273 ERR("DriverUnload\n");
277 IoUnregisterFileSystem(DriverObject
->DeviceObject
);
279 dosdevice_nameW
.Buffer
= dosdevice_name
;
280 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
282 IoDeleteSymbolicLink(&dosdevice_nameW
);
283 IoDeleteDevice(DriverObject
->DeviceObject
);
285 while (!IsListEmpty(&uid_map_list
)) {
286 LIST_ENTRY
* le
= RemoveHeadList(&uid_map_list
);
287 uid_map
* um
= CONTAINING_RECORD(le
, uid_map
, listentry
);
294 // FIXME - free volumes and their devpaths
298 ObDereferenceObject(comfo
);
304 ExDeleteResourceLite(&global_loading_lock
);
306 if (log_device
.Buffer
)
307 ExFreePool(log_device
.Buffer
);
310 ExFreePool(log_file
.Buffer
);
312 if (registry_path
.Buffer
)
313 ExFreePool(registry_path
.Buffer
);
316 BOOL STDCALL
get_last_inode(device_extension
* Vcb
, root
* r
) {
318 traverse_ptr tp
, prev_tp
;
322 searchkey
.obj_id
= 0xffffffffffffffff;
323 searchkey
.obj_type
= 0xff;
324 searchkey
.offset
= 0xffffffffffffffff;
326 Status
= find_item(Vcb
, r
, &tp
, &searchkey
, FALSE
);
327 if (!NT_SUCCESS(Status
)) {
328 ERR("error - find_item returned %08x\n", Status
);
332 while (find_prev_item(Vcb
, &tp
, &prev_tp
, FALSE
)) {
335 TRACE("moving on to %llx,%x,%llx\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
337 if (tp
.item
->key
.obj_type
== TYPE_INODE_ITEM
|| (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
&& !(tp
.item
->key
.obj_id
& 0x8000000000000000))) {
338 r
->lastinode
= tp
.item
->key
.obj_id
;
339 TRACE("last inode for tree %llx is %llx\n", r
->id
, r
->lastinode
);
344 r
->lastinode
= SUBVOL_ROOT_INODE
;
346 WARN("no INODE_ITEMs in tree %llx\n", r
->id
);
351 BOOL STDCALL
get_xattr(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, char* name
, UINT32 crc32
, UINT8
** data
, UINT16
* datalen
) {
358 TRACE("(%p, %llx, %llx, %s, %08x, %p, %p)\n", Vcb
, subvol
->id
, inode
, name
, crc32
, data
, datalen
);
360 searchkey
.obj_id
= inode
;
361 searchkey
.obj_type
= TYPE_XATTR_ITEM
;
362 searchkey
.offset
= crc32
;
364 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
365 if (!NT_SUCCESS(Status
)) {
366 ERR("error - find_item returned %08x\n", Status
);
370 if (keycmp(&tp
.item
->key
, &searchkey
)) {
371 TRACE("could not find item (%llx,%x,%llx)\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
375 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
376 ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
380 xa
= (DIR_ITEM
*)tp
.item
->data
;
381 size
= tp
.item
->size
;
384 if (size
< sizeof(DIR_ITEM
) || size
< (sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
)) {
385 WARN("(%llx,%x,%llx) is truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
389 if (xa
->n
== strlen(name
) && RtlCompareMemory(name
, xa
->name
, xa
->n
) == xa
->n
) {
390 TRACE("found xattr %s in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
395 *data
= ExAllocatePoolWithTag(PagedPool
, xa
->m
, ALLOC_TAG
);
397 ERR("out of memory\n");
401 RtlCopyMemory(*data
, &xa
->name
[xa
->n
], xa
->m
);
408 xasize
= sizeof(DIR_ITEM
) - 1 + xa
->m
+ xa
->n
;
412 xa
= (DIR_ITEM
*)&xa
->name
[xa
->m
+ xa
->n
];
417 TRACE("xattr %s not found in (%llx,%x,%llx)\n", name
, searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
422 NTSTATUS
add_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT32 crc32
, DIR_ITEM
* di
, ULONG disize
, LIST_ENTRY
* rollback
) {
428 searchkey
.obj_id
= inode
;
429 searchkey
.obj_type
= TYPE_DIR_ITEM
;
430 searchkey
.offset
= crc32
;
432 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
433 if (!NT_SUCCESS(Status
)) {
434 ERR("error - find_item returned %08x\n", Status
);
438 if (!keycmp(&tp
.item
->key
, &searchkey
)) {
439 ULONG maxlen
= Vcb
->superblock
.node_size
- sizeof(tree_header
) - sizeof(leaf_node
);
441 if (tp
.item
->size
+ disize
> maxlen
) {
442 WARN("DIR_ITEM was longer than maxlen (%u + %u > %u)\n", tp
.item
->size
, disize
, maxlen
);
443 return STATUS_INTERNAL_ERROR
;
446 di2
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
+ disize
, ALLOC_TAG
);
448 ERR("out of memory\n");
449 return STATUS_INSUFFICIENT_RESOURCES
;
452 if (tp
.item
->size
> 0)
453 RtlCopyMemory(di2
, tp
.item
->data
, tp
.item
->size
);
455 RtlCopyMemory(di2
+ tp
.item
->size
, di
, disize
);
457 delete_tree_item(Vcb
, &tp
, rollback
);
459 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di2
, tp
.item
->size
+ disize
, NULL
, rollback
);
463 insert_tree_item(Vcb
, subvol
, inode
, TYPE_DIR_ITEM
, crc32
, di
, disize
, NULL
, rollback
);
466 return STATUS_SUCCESS
;
469 static NTSTATUS STDCALL
drv_close(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
471 PIO_STACK_LOCATION IrpSp
;
472 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
477 FsRtlEnterFileSystem();
479 top_level
= is_top_level(Irp
);
481 if (DeviceObject
== devobj
|| (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
)) {
482 TRACE("Closing file system\n");
483 Status
= STATUS_SUCCESS
;
487 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
489 // FIXME - unmount if called for volume
490 // FIXME - call FsRtlNotifyUninitializeSync(&Vcb->NotifySync) if unmounting
492 Status
= close_file(DeviceObject
->DeviceExtension
, IrpSp
->FileObject
);
495 Irp
->IoStatus
.Status
= Status
;
496 Irp
->IoStatus
.Information
= 0;
498 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
501 IoSetTopLevelIrp(NULL
);
503 FsRtlExitFileSystem();
505 TRACE("returning %08x\n", Status
);
510 static NTSTATUS STDCALL
drv_query_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
513 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
515 FsRtlEnterFileSystem();
517 top_level
= is_top_level(Irp
);
519 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
520 Status
= part0_passthrough(DeviceObject
, Irp
);
524 FIXME("STUB: query ea\n");
525 Status
= STATUS_NOT_IMPLEMENTED
;
527 Irp
->IoStatus
.Status
= Status
;
528 Irp
->IoStatus
.Information
= 0;
530 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
534 IoSetTopLevelIrp(NULL
);
536 FsRtlExitFileSystem();
541 static NTSTATUS STDCALL
drv_set_ea(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
543 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
546 FsRtlEnterFileSystem();
548 top_level
= is_top_level(Irp
);
550 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
551 Status
= part0_passthrough(DeviceObject
, Irp
);
555 FIXME("STUB: set ea\n");
556 Status
= STATUS_NOT_IMPLEMENTED
;
559 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
561 // FIXME - return STATUS_ACCESS_DENIED if subvol readonly
563 Irp
->IoStatus
.Status
= Status
;
564 Irp
->IoStatus
.Information
= 0;
566 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
570 IoSetTopLevelIrp(NULL
);
572 FsRtlExitFileSystem();
577 static NTSTATUS STDCALL
drv_flush_buffers(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
579 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
580 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
581 fcb
* fcb
= FileObject
->FsContext
;
582 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
585 TRACE("flush buffers\n");
587 FsRtlEnterFileSystem();
589 top_level
= is_top_level(Irp
);
591 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
592 Status
= part0_passthrough(DeviceObject
, Irp
);
596 Status
= STATUS_SUCCESS
;
597 Irp
->IoStatus
.Status
= Status
;
598 Irp
->IoStatus
.Information
= 0;
600 if (fcb
->type
!= BTRFS_TYPE_DIRECTORY
) {
601 CcFlushCache(&fcb
->nonpaged
->segment_object
, NULL
, 0, &Irp
->IoStatus
);
603 if (fcb
->Header
.PagingIoResource
) {
604 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
605 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
608 Status
= Irp
->IoStatus
.Status
;
611 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
615 IoSetTopLevelIrp(NULL
);
617 FsRtlExitFileSystem();
622 static void calculate_total_space(device_extension
* Vcb
, LONGLONG
* totalsize
, LONGLONG
* freespace
) {
625 if (Vcb
->data_flags
& BLOCK_FLAG_DUPLICATE
|| Vcb
->data_flags
& BLOCK_FLAG_RAID1
|| Vcb
->data_flags
& BLOCK_FLAG_RAID10
)
630 *totalsize
= (Vcb
->superblock
.total_bytes
/ Vcb
->superblock
.sector_size
) / factor
;
631 *freespace
= ((Vcb
->superblock
.total_bytes
- Vcb
->superblock
.bytes_used
) / Vcb
->superblock
.sector_size
) / factor
;
634 static NTSTATUS STDCALL
drv_query_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
635 PIO_STACK_LOCATION IrpSp
;
637 ULONG BytesCopied
= 0;
638 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
642 // An unfortunate necessity - we have to lie about our FS type. MPR!MprGetConnection polls for this,
643 // and compares it to a whitelist. If it doesn't match, it will return ERROR_NO_NET_OR_BAD_PATH,
644 // which prevents UAC from working.
645 // FIXME - only lie if we detect that we're being called by mpr.dll
647 WCHAR
* fs_name
= L
"NTFS";
648 ULONG fs_name_len
= 4 * sizeof(WCHAR
);
650 WCHAR
* fs_name
= L
"Btrfs";
651 ULONG fs_name_len
= 5 * sizeof(WCHAR
);
654 TRACE("query volume information\n");
656 FsRtlEnterFileSystem();
657 top_level
= is_top_level(Irp
);
659 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
660 Status
= part0_passthrough(DeviceObject
, Irp
);
664 IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
666 Status
= STATUS_NOT_IMPLEMENTED
;
668 switch (IrpSp
->Parameters
.QueryVolume
.FsInformationClass
) {
669 case FileFsAttributeInformation
:
671 FILE_FS_ATTRIBUTE_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
672 BOOL overflow
= FALSE
;
673 ULONG orig_fs_name_len
= fs_name_len
;
675 TRACE("FileFsAttributeInformation\n");
677 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
) {
678 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
))
679 fs_name_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) + sizeof(WCHAR
);
686 data
->FileSystemAttributes
= FILE_CASE_PRESERVED_NAMES
| FILE_CASE_SENSITIVE_SEARCH
|
687 FILE_UNICODE_ON_DISK
| FILE_NAMED_STREAMS
| FILE_SUPPORTS_HARD_LINKS
| FILE_PERSISTENT_ACLS
|
688 FILE_SUPPORTS_REPARSE_POINTS
| FILE_SUPPORTS_SPARSE_FILES
| FILE_SUPPORTS_OBJECT_IDS
;
690 data
->FileSystemAttributes
|= FILE_READ_ONLY_VOLUME
;
692 // should also be FILE_FILE_COMPRESSION when supported
693 data
->MaximumComponentNameLength
= 255; // FIXME - check
694 data
->FileSystemNameLength
= orig_fs_name_len
;
695 RtlCopyMemory(data
->FileSystemName
, fs_name
, fs_name_len
);
697 BytesCopied
= sizeof(FILE_FS_ATTRIBUTE_INFORMATION
) - sizeof(WCHAR
) + fs_name_len
;
698 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
702 case FileFsControlInformation
:
703 FIXME("STUB: FileFsControlInformation\n");
706 case FileFsDeviceInformation
:
707 FIXME("STUB: FileFsDeviceInformation\n");
710 case FileFsDriverPathInformation
:
711 FIXME("STUB: FileFsDriverPathInformation\n");
714 case FileFsFullSizeInformation
:
716 FILE_FS_FULL_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
718 TRACE("FileFsFullSizeInformation\n");
720 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->ActualAvailableAllocationUnits
.QuadPart
);
721 ffsi
->CallerAvailableAllocationUnits
.QuadPart
= ffsi
->ActualAvailableAllocationUnits
.QuadPart
;
722 ffsi
->SectorsPerAllocationUnit
= 1;
723 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
725 BytesCopied
= sizeof(FILE_FS_FULL_SIZE_INFORMATION
);
726 Status
= STATUS_SUCCESS
;
731 case FileFsObjectIdInformation
:
733 FILE_FS_OBJECTID_INFORMATION
* ffoi
= Irp
->AssociatedIrp
.SystemBuffer
;
735 TRACE("FileFsObjectIdInformation\n");
737 RtlCopyMemory(ffoi
->ObjectId
, &Vcb
->superblock
.uuid
.uuid
[0], sizeof(UCHAR
) * 16);
738 RtlZeroMemory(ffoi
->ExtendedInfo
, sizeof(ffoi
->ExtendedInfo
));
740 BytesCopied
= sizeof(FILE_FS_OBJECTID_INFORMATION
);
741 Status
= STATUS_SUCCESS
;
746 case FileFsSizeInformation
:
748 FILE_FS_SIZE_INFORMATION
* ffsi
= Irp
->AssociatedIrp
.SystemBuffer
;
750 TRACE("FileFsSizeInformation\n");
752 calculate_total_space(Vcb
, &ffsi
->TotalAllocationUnits
.QuadPart
, &ffsi
->AvailableAllocationUnits
.QuadPart
);
753 ffsi
->SectorsPerAllocationUnit
= 1;
754 ffsi
->BytesPerSector
= Vcb
->superblock
.sector_size
;
756 BytesCopied
= sizeof(FILE_FS_SIZE_INFORMATION
);
757 Status
= STATUS_SUCCESS
;
762 case FileFsVolumeInformation
:
764 FILE_FS_VOLUME_INFORMATION
* data
= Irp
->AssociatedIrp
.SystemBuffer
;
765 FILE_FS_VOLUME_INFORMATION ffvi
;
766 BOOL overflow
= FALSE
;
767 ULONG label_len
, orig_label_len
;
769 TRACE("FileFsVolumeInformation\n");
770 TRACE("max length = %u\n", IrpSp
->Parameters
.QueryVolume
.Length
);
772 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
774 // orig_label_len = label_len = (ULONG)(wcslen(Vcb->label) * sizeof(WCHAR));
775 RtlUTF8ToUnicodeN(NULL
, 0, &label_len
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
776 orig_label_len
= label_len
;
778 if (IrpSp
->Parameters
.QueryVolume
.Length
< sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
) {
779 if (IrpSp
->Parameters
.QueryVolume
.Length
> sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
))
780 label_len
= IrpSp
->Parameters
.QueryVolume
.Length
- sizeof(FILE_FS_VOLUME_INFORMATION
) + sizeof(WCHAR
);
787 TRACE("label_len = %u\n", label_len
);
789 ffvi
.VolumeCreationTime
.QuadPart
= 0; // FIXME
790 ffvi
.VolumeSerialNumber
= Vcb
->superblock
.uuid
.uuid
[12] << 24 | Vcb
->superblock
.uuid
.uuid
[13] << 16 | Vcb
->superblock
.uuid
.uuid
[14] << 8 | Vcb
->superblock
.uuid
.uuid
[15];
791 ffvi
.VolumeLabelLength
= orig_label_len
;
792 ffvi
.SupportsObjects
= FALSE
;
794 RtlCopyMemory(data
, &ffvi
, min(sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
), IrpSp
->Parameters
.QueryVolume
.Length
));
799 // RtlCopyMemory(&data->VolumeLabel[0], Vcb->label, label_len);
800 RtlUTF8ToUnicodeN(&data
->VolumeLabel
[0], label_len
, &bytecount
, Vcb
->superblock
.label
, (ULONG
)strlen(Vcb
->superblock
.label
));
801 TRACE("label = %.*S\n", label_len
/ sizeof(WCHAR
), data
->VolumeLabel
);
804 ExReleaseResourceLite(&Vcb
->tree_lock
);
806 BytesCopied
= sizeof(FILE_FS_VOLUME_INFORMATION
) - sizeof(WCHAR
) + label_len
;
807 Status
= overflow
? STATUS_BUFFER_OVERFLOW
: STATUS_SUCCESS
;
812 Status
= STATUS_INVALID_PARAMETER
;
813 WARN("unknown FsInformationClass %u\n", IrpSp
->Parameters
.QueryVolume
.FsInformationClass
);
817 // if (NT_SUCCESS(Status) && IrpSp->Parameters.QueryVolume.Length < BytesCopied) { // FIXME - should not copy anything if overflow
818 // WARN("overflow: %u < %u\n", IrpSp->Parameters.QueryVolume.Length, BytesCopied);
819 // BytesCopied = IrpSp->Parameters.QueryVolume.Length;
820 // Status = STATUS_BUFFER_OVERFLOW;
823 Irp
->IoStatus
.Status
= Status
;
825 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
)
826 Irp
->IoStatus
.Information
= 0;
828 Irp
->IoStatus
.Information
= BytesCopied
;
830 IoCompleteRequest( Irp
, IO_DISK_INCREMENT
);
834 IoSetTopLevelIrp(NULL
);
836 FsRtlExitFileSystem();
838 TRACE("query volume information returning %08x\n", Status
);
843 static NTSTATUS STDCALL
read_completion(PDEVICE_OBJECT DeviceObject
, PIRP Irp
, PVOID conptr
) {
844 read_context
* context
= conptr
;
846 // DbgPrint("read_completion\n");
848 context
->iosb
= Irp
->IoStatus
;
849 KeSetEvent(&context
->Event
, 0, FALSE
);
851 // return STATUS_SUCCESS;
852 return STATUS_MORE_PROCESSING_REQUIRED
;
855 // static void test_tree_deletion(device_extension* Vcb) {
856 // KEY searchkey/*, endkey*/;
857 // traverse_ptr tp, next_tp;
860 // searchkey.obj_id = 0x100;
861 // searchkey.obj_type = 0x54;
862 // searchkey.offset = 0xca4ab2f5;
864 // // endkey.obj_id = 0x100;
865 // // endkey.obj_type = 0x60;
866 // // endkey.offset = 0x15a;
869 // while (r && r->id != 0x102)
873 // ERR("error - could not find root\n");
877 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
878 // ERR("error - could not find key\n");
882 // while (TRUE/*keycmp(&tp.item->key, &endkey) < 1*/) {
883 // tp.item->ignore = TRUE;
884 // add_to_tree_cache(tc, tp.tree);
886 // if (find_next_item(Vcb, &tp, &next_tp, NULL, FALSE)) {
887 // free_traverse_ptr(&tp);
893 // free_traverse_ptr(&tp);
896 // static void test_tree_splitting(device_extension* Vcb) {
899 // for (i = 0; i < 1000; i++) {
900 // char* data = ExAllocatePoolWithTag(PagedPool, 4, ALLOC_TAG);
902 // insert_tree_item(Vcb, Vcb->extent_root, 0, 0xfd, i, data, 4, NULL);
906 // static void test_dropping_tree(device_extension* Vcb) {
907 // LIST_ENTRY* le = Vcb->roots.Flink;
909 // while (le != &Vcb->roots) {
910 // root* r = CONTAINING_RECORD(le, root, list_entry);
912 // if (r->id == 0x101) {
913 // RemoveEntryList(&r->list_entry);
914 // InsertTailList(&Vcb->drop_roots, &r->list_entry);
922 NTSTATUS
create_root(device_extension
* Vcb
, UINT64 id
, root
** rootptr
, BOOL no_tree
, UINT64 offset
, LIST_ENTRY
* rollback
) {
928 r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
930 ERR("out of memory\n");
931 return STATUS_INSUFFICIENT_RESOURCES
;
934 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
936 ERR("out of memory\n");
938 return STATUS_INSUFFICIENT_RESOURCES
;
942 t
= ExAllocatePoolWithTag(PagedPool
, sizeof(tree
), ALLOC_TAG
);
944 ERR("out of memory\n");
945 ExFreePool(r
->nonpaged
);
947 return STATUS_INSUFFICIENT_RESOURCES
;
951 ri
= ExAllocatePoolWithTag(PagedPool
, sizeof(ROOT_ITEM
), ALLOC_TAG
);
953 ERR("out of memory\n");
958 ExFreePool(r
->nonpaged
);
960 return STATUS_INSUFFICIENT_RESOURCES
;
964 r
->treeholder
.address
= 0;
965 r
->treeholder
.generation
= Vcb
->superblock
.generation
;
966 r
->treeholder
.tree
= no_tree
? NULL
: t
;
968 r
->path
.Buffer
= NULL
;
969 RtlZeroMemory(&r
->root_item
, sizeof(ROOT_ITEM
));
970 r
->root_item
.num_references
= 1;
971 InitializeListHead(&r
->fcbs
);
973 RtlCopyMemory(ri
, &r
->root_item
, sizeof(ROOT_ITEM
));
975 // We ask here for a traverse_ptr to the item we're inserting, so we can
976 // copy some of the tree's variables
978 if (!insert_tree_item(Vcb
, Vcb
->root_root
, id
, TYPE_ROOT_ITEM
, offset
, ri
, sizeof(ROOT_ITEM
), &tp
, rollback
)) {
979 ERR("insert_tree_item failed\n");
985 ExFreePool(r
->nonpaged
);
987 return STATUS_INTERNAL_ERROR
;
990 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
992 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
995 t
->header
.fs_uuid
= tp
.tree
->header
.fs_uuid
;
996 t
->header
.address
= 0;
997 t
->header
.flags
= HEADER_FLAG_MIXED_BACKREF
| 1; // 1 == "written"? Why does the Linux driver record this?
998 t
->header
.chunk_tree_uuid
= tp
.tree
->header
.chunk_tree_uuid
;
999 t
->header
.generation
= Vcb
->superblock
.generation
;
1000 t
->header
.tree_id
= id
;
1001 t
->header
.num_items
= 0;
1002 t
->header
.level
= 0;
1004 t
->has_address
= FALSE
;
1011 InitializeListHead(&t
->itemlist
);
1014 t
->has_new_address
= FALSE
;
1015 t
->flags
= tp
.tree
->flags
;
1017 InsertTailList(&Vcb
->trees
, &t
->list_entry
);
1020 Vcb
->need_write
= TRUE
;
1025 return STATUS_SUCCESS
;
1028 // static void test_creating_root(device_extension* Vcb) {
1030 // LIST_ENTRY rollback;
1034 // InitializeListHead(&rollback);
1036 // if (Vcb->root_root->lastinode == 0)
1037 // get_last_inode(Vcb, Vcb->root_root);
1039 // id = Vcb->root_root->lastinode > 0x100 ? (Vcb->root_root->lastinode + 1) : 0x101;
1040 // Status = create_root(Vcb, id, &r, &rollback);
1042 // if (!NT_SUCCESS(Status)) {
1043 // ERR("create_root returned %08x\n", Status);
1044 // do_rollback(Vcb, &rollback);
1046 // Vcb->root_root->lastinode = id;
1047 // clear_rollback(&rollback);
1051 // static void test_alloc_chunk(device_extension* Vcb) {
1052 // LIST_ENTRY rollback;
1055 // InitializeListHead(&rollback);
1057 // c = alloc_chunk(Vcb, BLOCK_FLAG_DATA | BLOCK_FLAG_RAID10, &rollback);
1059 // ERR("alloc_chunk failed\n");
1060 // do_rollback(Vcb, &rollback);
1062 // clear_rollback(&rollback);
1066 // static void test_space_list(device_extension* Vcb) {
1077 // static const space_test entries[] = {
1078 // { 0x1000, 0x1000 },
1079 // { 0x3000, 0x2000 },
1080 // { 0x6000, 0x1000 },
1084 // static const space_test tests[] = {
1085 // { 0x0, 0x800, TRUE },
1086 // { 0x1800, 0x400, TRUE },
1087 // { 0x800, 0x2000, TRUE },
1088 // { 0x1000, 0x2000, TRUE },
1089 // { 0x2000, 0x3800, TRUE },
1090 // { 0x800, 0x1000, TRUE },
1091 // { 0x1800, 0x1000, TRUE },
1092 // { 0x5000, 0x800, TRUE },
1093 // { 0x5000, 0x1000, TRUE },
1094 // { 0x7000, 0x1000, TRUE },
1095 // { 0x8000, 0x1000, TRUE },
1096 // { 0x800, 0x800, TRUE },
1097 // { 0x0, 0x3800, TRUE },
1098 // { 0x1000, 0x2800, TRUE },
1099 // { 0x1000, 0x1000, FALSE },
1100 // { 0x800, 0x2000, FALSE },
1101 // { 0x0, 0x3800, FALSE },
1102 // { 0x2800, 0x1000, FALSE },
1103 // { 0x1800, 0x2000, FALSE },
1104 // { 0x3800, 0x1000, FALSE },
1108 // c = CONTAINING_RECORD(Vcb->chunks.Flink, chunk, list_entry);
1111 // while (tests[i].length > 0) {
1112 // InitializeListHead(&c->space);
1113 // InitializeListHead(&c->space_size);
1114 // ERR("test %u\n", i);
1117 // while (entries[j].length > 0) {
1118 // space* s = ExAllocatePoolWithTag(PagedPool, sizeof(space), ALLOC_TAG);
1119 // s->address = entries[j].address;
1120 // s->size = entries[j].length;
1121 // InsertTailList(&c->space, &s->list_entry);
1123 // order_space_entry(s, &c->space_size);
1128 // if (tests[i].add)
1129 // space_list_add(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1131 // space_list_subtract(Vcb, c, FALSE, tests[i].address, tests[i].length, NULL);
1133 // le = c->space.Flink;
1134 // while (le != &c->space) {
1135 // space* s = CONTAINING_RECORD(le, space, list_entry);
1137 // ERR("(%llx,%llx)\n", s->address, s->size);
1144 // le = c->space_size.Flink;
1145 // while (le != &c->space_size) {
1146 // space* s = CONTAINING_RECORD(le, space, list_entry_size);
1148 // ERR("(%llx,%llx)\n", s->address, s->size);
1159 static NTSTATUS STDCALL
set_label(device_extension
* Vcb
, FILE_FS_LABEL_INFORMATION
* ffli
) {
1163 TRACE("label = %.*S\n", ffli
->VolumeLabelLength
/ sizeof(WCHAR
), ffli
->VolumeLabel
);
1165 Status
= RtlUnicodeToUTF8N(NULL
, 0, &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1166 if (!NT_SUCCESS(Status
))
1169 if (utf8len
> MAX_LABEL_SIZE
) {
1170 Status
= STATUS_INVALID_VOLUME_LABEL
;
1174 // FIXME - check for '/' and '\\' and reject
1176 // utf8 = ExAllocatePoolWithTag(PagedPool, utf8len + 1, ALLOC_TAG);
1178 Status
= RtlUnicodeToUTF8N((PCHAR
)&Vcb
->superblock
.label
, MAX_LABEL_SIZE
* sizeof(WCHAR
), &utf8len
, ffli
->VolumeLabel
, ffli
->VolumeLabelLength
);
1179 if (!NT_SUCCESS(Status
))
1182 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1184 if (utf8len
< MAX_LABEL_SIZE
* sizeof(WCHAR
))
1185 RtlZeroMemory(Vcb
->superblock
.label
+ utf8len
, (MAX_LABEL_SIZE
* sizeof(WCHAR
)) - utf8len
);
1187 // test_tree_deletion(Vcb); // TESTING
1188 // test_tree_splitting(Vcb);
1189 // test_dropping_tree(Vcb);
1190 // test_creating_root(Vcb);
1191 // test_alloc_chunk(Vcb);
1192 // test_space_list(Vcb);
1194 Vcb
->need_write
= TRUE
;
1197 ExReleaseResourceLite(&Vcb
->tree_lock
);
1200 TRACE("returning %08x\n", Status
);
1205 static NTSTATUS STDCALL
drv_set_volume_information(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
1206 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
1207 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
1211 TRACE("set volume information\n");
1213 FsRtlEnterFileSystem();
1215 top_level
= is_top_level(Irp
);
1217 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
1218 Status
= part0_passthrough(DeviceObject
, Irp
);
1222 Status
= STATUS_NOT_IMPLEMENTED
;
1224 if (Vcb
->readonly
) {
1225 Status
= STATUS_MEDIA_WRITE_PROTECTED
;
1229 if (Vcb
->removing
|| Vcb
->locked
) {
1230 Status
= STATUS_ACCESS_DENIED
;
1234 switch (IrpSp
->Parameters
.SetVolume
.FsInformationClass
) {
1235 case FileFsControlInformation
:
1236 FIXME("STUB: FileFsControlInformation\n");
1239 case FileFsLabelInformation
:
1240 TRACE("FileFsLabelInformation\n");
1242 Status
= set_label(Vcb
, Irp
->AssociatedIrp
.SystemBuffer
);
1245 case FileFsObjectIdInformation
:
1246 FIXME("STUB: FileFsObjectIdInformation\n");
1250 WARN("Unrecognized FsInformationClass 0x%x\n", IrpSp
->Parameters
.SetVolume
.FsInformationClass
);
1255 Irp
->IoStatus
.Status
= Status
;
1256 Irp
->IoStatus
.Information
= 0;
1258 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
1262 IoSetTopLevelIrp(NULL
);
1264 FsRtlExitFileSystem();
1269 NTSTATUS
delete_dir_item(device_extension
* Vcb
, root
* subvol
, UINT64 parinode
, UINT32 crc32
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
1274 searchkey
.obj_id
= parinode
;
1275 searchkey
.obj_type
= TYPE_DIR_ITEM
;
1276 searchkey
.offset
= crc32
;
1278 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1279 if (!NT_SUCCESS(Status
)) {
1280 ERR("error - find_item returned %08x\n", Status
);
1284 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1285 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
1286 WARN("(%llx,%x,%llx) was %u bytes, expected %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(DIR_ITEM
));
1291 di
= (DIR_ITEM
*)tp
.item
->data
;
1292 len
= tp
.item
->size
;
1295 if (di
->n
== utf8
->Length
&& RtlCompareMemory(di
->name
, utf8
->Buffer
, di
->n
) == di
->n
) {
1296 ULONG newlen
= tp
.item
->size
- (sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
);
1298 delete_tree_item(Vcb
, &tp
, rollback
);
1301 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1303 UINT8
*newdi
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *dioff
;
1306 ERR("out of memory\n");
1307 return STATUS_INSUFFICIENT_RESOURCES
;
1310 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1312 if ((UINT8
*)di
> tp
.item
->data
) {
1313 RtlCopyMemory(newdi
, tp
.item
->data
, (UINT8
*)di
- tp
.item
->data
);
1314 dioff
= newdi
+ ((UINT8
*)di
- tp
.item
->data
);
1319 if ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
< tp
.item
->size
)
1320 RtlCopyMemory(dioff
, &di
->name
[di
->n
+ di
->m
], tp
.item
->size
- ((UINT8
*)&di
->name
[di
->n
+ di
->m
] - tp
.item
->data
));
1322 insert_tree_item(Vcb
, subvol
, parinode
, TYPE_DIR_ITEM
, crc32
, newdi
, newlen
, NULL
, rollback
);
1328 len
-= sizeof(DIR_ITEM
) - sizeof(char) + di
->n
+ di
->m
;
1329 di
= (DIR_ITEM
*)&di
->name
[di
->n
+ di
->m
];
1333 WARN("could not find DIR_ITEM for crc32 %08x\n", crc32
);
1336 return STATUS_SUCCESS
;
1339 NTSTATUS
delete_inode_ref(device_extension
* Vcb
, root
* subvol
, UINT64 inode
, UINT64 parinode
, PANSI_STRING utf8
, LIST_ENTRY
* rollback
) {
1342 BOOL changed
= FALSE
;
1345 searchkey
.obj_id
= inode
;
1346 searchkey
.obj_type
= TYPE_INODE_REF
;
1347 searchkey
.offset
= parinode
;
1349 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1350 if (!NT_SUCCESS(Status
)) {
1351 ERR("error - find_item returned %08x\n", Status
);
1355 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1356 if (tp
.item
->size
< sizeof(INODE_REF
)) {
1357 WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(INODE_REF
));
1362 ir
= (INODE_REF
*)tp
.item
->data
;
1363 len
= tp
.item
->size
;
1368 if (len
< sizeof(INODE_REF
) || len
< sizeof(INODE_REF
) - 1 + ir
->n
) {
1369 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1373 itemlen
= sizeof(INODE_REF
) - sizeof(char) + ir
->n
;
1375 if (ir
->n
== utf8
->Length
&& RtlCompareMemory(ir
->name
, utf8
->Buffer
, ir
->n
) == ir
->n
) {
1376 ULONG newlen
= tp
.item
->size
- itemlen
;
1378 delete_tree_item(Vcb
, &tp
, rollback
);
1382 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1384 UINT8
*newir
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *iroff
;
1387 ERR("out of memory\n");
1388 return STATUS_INSUFFICIENT_RESOURCES
;
1391 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1393 if ((UINT8
*)ir
> tp
.item
->data
) {
1394 RtlCopyMemory(newir
, tp
.item
->data
, (UINT8
*)ir
- tp
.item
->data
);
1395 iroff
= newir
+ ((UINT8
*)ir
- tp
.item
->data
);
1400 if ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
< tp
.item
->size
)
1401 RtlCopyMemory(iroff
, &ir
->name
[ir
->n
], tp
.item
->size
- ((UINT8
*)&ir
->name
[ir
->n
] - tp
.item
->data
));
1403 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newir
, newlen
, NULL
, rollback
);
1409 if (len
> itemlen
) {
1411 ir
= (INODE_REF
*)&ir
->name
[ir
->n
];
1417 WARN("found INODE_REF entry, but couldn't find filename\n");
1421 WARN("could not find INODE_REF entry for inode %llx in %llx\n", searchkey
.obj_id
, searchkey
.offset
);
1425 return STATUS_SUCCESS
;
1427 if (!(Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF
))
1428 return STATUS_INTERNAL_ERROR
;
1430 searchkey
.obj_id
= inode
;
1431 searchkey
.obj_type
= TYPE_INODE_EXTREF
;
1432 searchkey
.offset
= calc_crc32c((UINT32
)parinode
, (UINT8
*)utf8
->Buffer
, utf8
->Length
);
1434 Status
= find_item(Vcb
, subvol
, &tp
, &searchkey
, FALSE
);
1435 if (!NT_SUCCESS(Status
)) {
1436 ERR("error - find_item returned %08x\n", Status
);
1440 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
1441 if (tp
.item
->size
< sizeof(INODE_EXTREF
)) {
1442 WARN("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, tp
.item
->size
, sizeof(INODE_EXTREF
));
1447 ier
= (INODE_EXTREF
*)tp
.item
->data
;
1448 len
= tp
.item
->size
;
1453 if (len
< sizeof(INODE_EXTREF
) || len
< sizeof(INODE_EXTREF
) - 1 + ier
->n
) {
1454 ERR("(%llx,%x,%llx) was truncated\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1458 itemlen
= sizeof(INODE_EXTREF
) - sizeof(char) + ier
->n
;
1460 if (ier
->dir
== parinode
&& ier
->n
== utf8
->Length
&& RtlCompareMemory(ier
->name
, utf8
->Buffer
, ier
->n
) == ier
->n
) {
1461 ULONG newlen
= tp
.item
->size
- itemlen
;
1463 delete_tree_item(Vcb
, &tp
, rollback
);
1467 TRACE("deleting (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1469 UINT8
*newier
= ExAllocatePoolWithTag(PagedPool
, newlen
, ALLOC_TAG
), *ieroff
;
1472 ERR("out of memory\n");
1473 return STATUS_INSUFFICIENT_RESOURCES
;
1476 TRACE("modifying (%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
1478 if ((UINT8
*)ier
> tp
.item
->data
) {
1479 RtlCopyMemory(newier
, tp
.item
->data
, (UINT8
*)ier
- tp
.item
->data
);
1480 ieroff
= newier
+ ((UINT8
*)ier
- tp
.item
->data
);
1485 if ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
< tp
.item
->size
)
1486 RtlCopyMemory(ieroff
, &ier
->name
[ier
->n
], tp
.item
->size
- ((UINT8
*)&ier
->name
[ier
->n
] - tp
.item
->data
));
1488 insert_tree_item(Vcb
, subvol
, tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
, newier
, newlen
, NULL
, rollback
);
1494 if (len
> itemlen
) {
1496 ier
= (INODE_EXTREF
*)&ier
->name
[ier
->n
];
1502 WARN("couldn't find INODE_EXTREF entry either (offset = %08x)\n", (UINT32
)searchkey
.offset
);
1505 return changed
? STATUS_SUCCESS
: STATUS_INTERNAL_ERROR
;
1508 static WCHAR
* file_desc_fcb(fcb
* fcb
) {
1513 if (fcb
->debug_desc
)
1514 return fcb
->debug_desc
;
1516 if (fcb
== fcb
->Vcb
->volume_fcb
)
1517 return L
"volume FCB";
1519 fcb
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, 60 * sizeof(WCHAR
), ALLOC_TAG
);
1520 if (!fcb
->debug_desc
)
1521 return L
"(memory error)";
1523 // I know this is pretty hackish...
1524 // GCC doesn't like %llx in sprintf, and MSVC won't let us use swprintf
1525 // without the CRT, which breaks drivers.
1527 sprintf(s
, "subvol %x, inode %x", (UINT32
)fcb
->subvol
->id
, (UINT32
)fcb
->inode
);
1530 as
.Length
= as
.MaximumLength
= strlen(s
);
1532 us
.Buffer
= fcb
->debug_desc
;
1533 us
.MaximumLength
= 60 * sizeof(WCHAR
);
1536 RtlAnsiStringToUnicodeString(&us
, &as
, FALSE
);
1538 us
.Buffer
[us
.Length
/ sizeof(WCHAR
)] = 0;
1540 return fcb
->debug_desc
;
1543 WCHAR
* file_desc_fileref(file_ref
* fileref
) {
1547 if (fileref
->debug_desc
)
1548 return fileref
->debug_desc
;
1550 Status
= fileref_get_filename(fileref
, &fn
, NULL
);
1551 if (!NT_SUCCESS(Status
)) {
1555 fileref
->debug_desc
= ExAllocatePoolWithTag(PagedPool
, fn
.Length
+ sizeof(WCHAR
), ALLOC_TAG
);
1556 if (!fileref
->debug_desc
) {
1557 ExFreePool(fn
.Buffer
);
1558 return L
"(memory error)";
1561 RtlCopyMemory(fileref
->debug_desc
, fn
.Buffer
, fn
.Length
);
1562 fileref
->debug_desc
[fn
.Length
/ sizeof(WCHAR
)] = 0;
1564 ExFreePool(fn
.Buffer
);
1566 return fileref
->debug_desc
;
1569 WCHAR
* file_desc(PFILE_OBJECT FileObject
) {
1570 fcb
* fcb
= FileObject
->FsContext
;
1571 ccb
* ccb
= FileObject
->FsContext2
;
1572 file_ref
* fileref
= ccb
? ccb
->fileref
: NULL
;
1575 return file_desc_fileref(fileref
);
1577 return file_desc_fcb(fcb
);
1580 void send_notification_fileref(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1584 fcb
* fcb
= fileref
->fcb
;
1586 Status
= fileref_get_filename(fileref
, &fn
, &name_offset
);
1587 if (!NT_SUCCESS(Status
)) {
1588 ERR("fileref_get_filename returned %08x\n", Status
);
1592 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn
, name_offset
,
1593 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1594 ExFreePool(fn
.Buffer
);
1597 void send_notification_fcb(file_ref
* fileref
, ULONG filter_match
, ULONG action
) {
1598 fcb
* fcb
= fileref
->fcb
;
1602 // no point looking for hardlinks if st_nlink == 1
1603 if (fileref
->fcb
->inode_item
.st_nlink
== 1) {
1604 send_notification_fileref(fileref
, filter_match
, action
);
1608 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1610 le
= fcb
->hardlinks
.Flink
;
1611 while (le
!= &fcb
->hardlinks
) {
1612 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1615 Status
= open_fileref_by_inode(fcb
->Vcb
, fcb
->subvol
, hl
->parent
, &parfr
);
1617 if (!NT_SUCCESS(Status
)) {
1618 ERR("open_fileref_by_inode returned %08x\n", Status
);
1619 } else if (!parfr
->deleted
) {
1621 BOOL found
= FALSE
, deleted
= FALSE
;
1624 le2
= parfr
->children
.Flink
;
1625 while (le2
!= &parfr
->children
) {
1626 file_ref
* fr2
= CONTAINING_RECORD(le2
, file_ref
, list_entry
);
1628 if (fr2
->index
== hl
->index
) {
1630 deleted
= fr2
->deleted
;
1633 fn
= &fr2
->filepart
;
1645 UNICODE_STRING path
;
1647 Status
= fileref_get_filename(parfr
, &path
, NULL
);
1648 if (!NT_SUCCESS(Status
)) {
1649 ERR("fileref_get_filename returned %08x\n", Status
);
1654 name_offset
= path
.Length
;
1655 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) name_offset
+= sizeof(WCHAR
);
1657 fn2
.Length
= fn2
.MaximumLength
= fn
->Length
+ name_offset
;
1658 fn2
.Buffer
= ExAllocatePoolWithTag(PagedPool
, fn2
.MaximumLength
, ALLOC_TAG
);
1660 RtlCopyMemory(fn2
.Buffer
, path
.Buffer
, path
.Length
);
1661 if (parfr
!= fileref
->fcb
->Vcb
->root_fileref
) fn2
.Buffer
[path
.Length
/ sizeof(WCHAR
)] = '\\';
1662 RtlCopyMemory(&fn2
.Buffer
[name_offset
/ sizeof(WCHAR
)], fn
->Buffer
, fn
->Length
);
1664 TRACE("%.*S\n", fn2
.Length
/ sizeof(WCHAR
), fn2
.Buffer
);
1666 FsRtlNotifyFilterReportChange(fcb
->Vcb
->NotifySync
, &fcb
->Vcb
->DirNotifyList
, (PSTRING
)&fn2
, name_offset
,
1667 NULL
, NULL
, filter_match
, action
, NULL
, NULL
);
1669 ExFreePool(fn2
.Buffer
);
1670 ExFreePool(path
.Buffer
);
1674 free_fileref(parfr
);
1680 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1683 void mark_fcb_dirty(fcb
* fcb
) {
1685 #ifdef DEBUG_FCB_REFCOUNTS
1688 dirty_fcb
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fcb
), ALLOC_TAG
);
1691 ExFreePool("out of memory\n");
1697 #ifdef DEBUG_FCB_REFCOUNTS
1698 rc
= InterlockedIncrement(&fcb
->refcount
);
1699 WARN("fcb %p: refcount now %i\n", fcb
, rc
);
1701 InterlockedIncrement(&fcb
->refcount
);
1706 ExInterlockedInsertTailList(&fcb
->Vcb
->dirty_fcbs
, &dirt
->list_entry
, &fcb
->Vcb
->dirty_fcbs_lock
);
1709 fcb
->Vcb
->need_write
= TRUE
;
1712 void mark_fileref_dirty(file_ref
* fileref
) {
1713 if (!fileref
->dirty
) {
1714 dirty_fileref
* dirt
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(dirty_fileref
), ALLOC_TAG
);
1717 ExFreePool("out of memory\n");
1721 fileref
->dirty
= TRUE
;
1722 increase_fileref_refcount(fileref
);
1724 dirt
->fileref
= fileref
;
1726 ExInterlockedInsertTailList(&fileref
->fcb
->Vcb
->dirty_filerefs
, &dirt
->list_entry
, &fileref
->fcb
->Vcb
->dirty_filerefs_lock
);
1729 fileref
->fcb
->Vcb
->need_write
= TRUE
;
1732 void _free_fcb(fcb
* fcb
, const char* func
, const char* file
, unsigned int line
) {
1735 rc
= InterlockedDecrement(&fcb
->refcount
);
1737 #ifdef DEBUG_FCB_REFCOUNTS
1738 // WARN("fcb %p: refcount now %i (%.*S)\n", fcb, rc, fcb->full_filename.Length / sizeof(WCHAR), fcb->full_filename.Buffer);
1739 #ifdef DEBUG_LONG_MESSAGES
1740 _debug_message(func
, file
, line
, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1742 _debug_message(func
, "fcb %p: refcount now %i (subvol %llx, inode %llx)\n", fcb
, rc
, fcb
->subvol
? fcb
->subvol
->id
: 0, fcb
->inode
);
1749 ExAcquireResourceExclusiveLite(&fcb
->Vcb
->fcb_lock
, TRUE
);
1751 if (fcb
->list_entry
.Flink
)
1752 RemoveEntryList(&fcb
->list_entry
);
1754 if (fcb
->list_entry_all
.Flink
)
1755 RemoveEntryList(&fcb
->list_entry_all
);
1757 ExReleaseResourceLite(&fcb
->Vcb
->fcb_lock
);
1759 ExDeleteResourceLite(&fcb
->nonpaged
->resource
);
1760 ExDeleteResourceLite(&fcb
->nonpaged
->paging_resource
);
1761 ExDeleteResourceLite(&fcb
->nonpaged
->index_lock
);
1762 ExFreePool(fcb
->nonpaged
);
1765 ExFreePool(fcb
->sd
);
1767 if (fcb
->adsxattr
.Buffer
)
1768 ExFreePool(fcb
->adsxattr
.Buffer
);
1770 if (fcb
->reparse_xattr
.Buffer
)
1771 ExFreePool(fcb
->reparse_xattr
.Buffer
);
1773 if (fcb
->adsdata
.Buffer
)
1774 ExFreePool(fcb
->adsdata
.Buffer
);
1776 if (fcb
->debug_desc
)
1777 ExFreePool(fcb
->debug_desc
);
1779 while (!IsListEmpty(&fcb
->extents
)) {
1780 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->extents
);
1781 extent
* ext
= CONTAINING_RECORD(le
, extent
, list_entry
);
1783 ExFreePool(ext
->data
);
1787 while (!IsListEmpty(&fcb
->index_list
)) {
1788 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->index_list
);
1789 index_entry
* ie
= CONTAINING_RECORD(le
, index_entry
, list_entry
);
1791 if (ie
->utf8
.Buffer
) ExFreePool(ie
->utf8
.Buffer
);
1792 if (ie
->filepart_uc
.Buffer
) ExFreePool(ie
->filepart_uc
.Buffer
);
1796 while (!IsListEmpty(&fcb
->hardlinks
)) {
1797 LIST_ENTRY
* le
= RemoveHeadList(&fcb
->hardlinks
);
1798 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
1800 if (hl
->name
.Buffer
)
1801 ExFreePool(hl
->name
.Buffer
);
1803 if (hl
->utf8
.Buffer
)
1804 ExFreePool(hl
->utf8
.Buffer
);
1809 FsRtlUninitializeFileLock(&fcb
->lock
);
1812 #ifdef DEBUG_FCB_REFCOUNTS
1813 #ifdef DEBUG_LONG_MESSAGES
1814 _debug_message(func
, file
, line
, "freeing fcb %p\n", fcb
);
1816 _debug_message(func
, "freeing fcb %p\n", fcb
);
1821 void _free_fileref(file_ref
* fr
, const char* func
, const char* file
, unsigned int line
) {
1824 rc
= InterlockedDecrement(&fr
->refcount
);
1826 #ifdef DEBUG_FCB_REFCOUNTS
1827 #ifdef DEBUG_LONG_MESSAGES
1828 _debug_message(func
, file
, line
, "fileref %p: refcount now %i\n", fr
, rc
);
1830 _debug_message(func
, "fileref %p: refcount now %i\n", fr
, rc
);
1836 ERR("fileref %p: refcount now %i\n", fr
, rc
);
1845 ExAcquireResourceExclusiveLite(&fr
->parent
->nonpaged
->children_lock
, TRUE
);
1847 // FIXME - do we need a file_ref lock?
1849 // FIXME - do delete if needed
1851 if (fr
->filepart
.Buffer
)
1852 ExFreePool(fr
->filepart
.Buffer
);
1854 if (fr
->filepart_uc
.Buffer
)
1855 ExFreePool(fr
->filepart_uc
.Buffer
);
1857 if (fr
->utf8
.Buffer
)
1858 ExFreePool(fr
->utf8
.Buffer
);
1861 ExFreePool(fr
->debug_desc
);
1863 ExDeleteResourceLite(&fr
->nonpaged
->children_lock
);
1865 ExFreePool(fr
->nonpaged
);
1867 // FIXME - throw error if children not empty
1869 if (fr
->fcb
->fileref
== fr
)
1870 fr
->fcb
->fileref
= NULL
;
1872 if (fr
->list_entry
.Flink
)
1873 RemoveEntryList(&fr
->list_entry
);
1876 ExReleaseResourceLite(&fr
->parent
->nonpaged
->children_lock
);
1877 free_fileref(fr
->parent
);
1884 static NTSTATUS STDCALL
close_file(device_extension
* Vcb
, PFILE_OBJECT FileObject
) {
1887 file_ref
* fileref
= NULL
;
1889 TRACE("FileObject = %p\n", FileObject
);
1891 fcb
= FileObject
->FsContext
;
1893 TRACE("FCB was NULL, returning success\n");
1894 return STATUS_SUCCESS
;
1897 ccb
= FileObject
->FsContext2
;
1899 TRACE("close called for %S (fcb == %p)\n", file_desc(FileObject
), fcb
);
1901 // FIXME - make sure notification gets sent if file is being deleted
1904 if (ccb
->query_string
.Buffer
)
1905 RtlFreeUnicodeString(&ccb
->query_string
);
1907 if (ccb
->filename
.Buffer
)
1908 ExFreePool(ccb
->filename
.Buffer
);
1910 // FIXME - use refcounts for fileref
1911 fileref
= ccb
->fileref
;
1916 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
1918 ExAcquireResourceExclusiveLite(&Vcb
->fcb_lock
, TRUE
);
1921 free_fileref(fileref
);
1925 ExReleaseResourceLite(&Vcb
->fcb_lock
);
1927 return STATUS_SUCCESS
;
1930 void STDCALL
uninit(device_extension
* Vcb
, BOOL flush
) {
1933 LIST_ENTRY rollback
;
1938 RemoveEntryList(&Vcb
->list_entry
);
1940 Status
= registry_mark_volume_unmounted(&Vcb
->superblock
.uuid
);
1941 if (!NT_SUCCESS(Status
))
1942 WARN("registry_mark_volume_unmounted returned %08x\n", Status
);
1945 InitializeListHead(&rollback
);
1947 ExAcquireResourceExclusiveLite(&Vcb
->tree_lock
, TRUE
);
1949 if (Vcb
->need_write
)
1950 do_write(Vcb
, &rollback
);
1954 clear_rollback(&rollback
);
1956 ExReleaseResourceLite(&Vcb
->tree_lock
);
1959 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
1960 Vcb
->threads
.threads
[i
].quit
= TRUE
;
1961 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
1963 KeWaitForSingleObject(&Vcb
->threads
.threads
[i
].finished
, Executive
, KernelMode
, FALSE
, NULL
);
1965 ZwClose(Vcb
->threads
.threads
[i
].handle
);
1968 ExFreePool(Vcb
->threads
.threads
);
1970 Vcb
->removing
= TRUE
;
1973 KeSetTimer(&Vcb
->flush_thread_timer
, time
, NULL
); // trigger the timer early
1974 KeWaitForSingleObject(&Vcb
->flush_thread_finished
, Executive
, KernelMode
, FALSE
, NULL
);
1976 free_fcb(Vcb
->volume_fcb
);
1979 ObDereferenceObject(Vcb
->root_file
);
1981 le
= Vcb
->chunks
.Flink
;
1982 while (le
!= &Vcb
->chunks
) {
1983 chunk
* c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
1993 while (!IsListEmpty(&Vcb
->roots
)) {
1994 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->roots
);
1995 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
1997 ExDeleteResourceLite(&r
->nonpaged
->load_tree_lock
);
1998 ExFreePool(r
->nonpaged
);
2002 while (!IsListEmpty(&Vcb
->chunks
)) {
2005 le
= RemoveHeadList(&Vcb
->chunks
);
2006 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
2008 while (!IsListEmpty(&c
->space
)) {
2009 LIST_ENTRY
* le2
= RemoveHeadList(&c
->space
);
2010 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2015 while (!IsListEmpty(&c
->deleting
)) {
2016 LIST_ENTRY
* le2
= RemoveHeadList(&c
->deleting
);
2017 s
= CONTAINING_RECORD(le2
, space
, list_entry
);
2023 ExFreePool(c
->devices
);
2028 ExDeleteResourceLite(&c
->nonpaged
->lock
);
2029 ExDeleteResourceLite(&c
->nonpaged
->changed_extents_lock
);
2031 ExFreePool(c
->nonpaged
);
2032 ExFreePool(c
->chunk_item
);
2036 // FIXME - free any open fcbs?
2038 while (!IsListEmpty(&Vcb
->sector_checksums
)) {
2039 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->sector_checksums
);
2040 changed_sector
* cs
= (changed_sector
*)le
;
2045 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
2046 while (!IsListEmpty(&Vcb
->devices
[i
].space
)) {
2047 LIST_ENTRY
* le
= RemoveHeadList(&Vcb
->devices
[i
].space
);
2048 space
* s
= CONTAINING_RECORD(le
, space
, list_entry
);
2054 ExFreePool(Vcb
->devices
);
2056 ExDeleteResourceLite(&Vcb
->fcb_lock
);
2057 ExDeleteResourceLite(&Vcb
->load_lock
);
2058 ExDeleteResourceLite(&Vcb
->tree_lock
);
2059 ExDeleteResourceLite(&Vcb
->checksum_lock
);
2060 ExDeleteResourceLite(&Vcb
->chunk_lock
);
2062 ZwClose(Vcb
->flush_thread_handle
);
2065 NTSTATUS
delete_fileref(file_ref
* fileref
, PFILE_OBJECT FileObject
, LIST_ENTRY
* rollback
) {
2066 LARGE_INTEGER newlength
, time
;
2070 KeQuerySystemTime(&time
);
2071 win_time_to_unix(time
, &now
);
2073 ExAcquireResourceExclusiveLite(fileref
->fcb
->Header
.Resource
, TRUE
);
2075 if (fileref
->deleted
) {
2076 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2077 return STATUS_SUCCESS
;
2080 fileref
->deleted
= TRUE
;
2081 mark_fileref_dirty(fileref
);
2083 // delete INODE_ITEM (0x1)
2085 TRACE("nlink = %u\n", fileref
->fcb
->inode_item
.st_nlink
);
2087 if (!fileref
->fcb
->ads
) {
2088 if (fileref
->parent
->fcb
->subvol
== fileref
->fcb
->subvol
) {
2091 mark_fcb_dirty(fileref
->fcb
);
2093 if (fileref
->fcb
->inode_item
.st_nlink
> 1) {
2094 fileref
->fcb
->inode_item
.st_nlink
--;
2095 fileref
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2096 fileref
->fcb
->inode_item
.sequence
++;
2097 fileref
->fcb
->inode_item
.st_ctime
= now
;
2099 fileref
->fcb
->deleted
= TRUE
;
2103 if (fileref
->fcb
->type
!= BTRFS_TYPE_DIRECTORY
&& fileref
->fcb
->inode_item
.st_size
> 0) {
2104 Status
= excise_extents(fileref
->fcb
->Vcb
, fileref
->fcb
, 0, sector_align(fileref
->fcb
->inode_item
.st_size
, fileref
->fcb
->Vcb
->superblock
.sector_size
), rollback
);
2105 if (!NT_SUCCESS(Status
)) {
2106 ERR("excise_extents returned %08x\n", Status
);
2107 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2112 fileref
->fcb
->Header
.AllocationSize
.QuadPart
= 0;
2113 fileref
->fcb
->Header
.FileSize
.QuadPart
= 0;
2114 fileref
->fcb
->Header
.ValidDataLength
.QuadPart
= 0;
2119 ccfs
.AllocationSize
= fileref
->fcb
->Header
.AllocationSize
;
2120 ccfs
.FileSize
= fileref
->fcb
->Header
.FileSize
;
2121 ccfs
.ValidDataLength
= fileref
->fcb
->Header
.ValidDataLength
;
2123 CcSetFileSizes(FileObject
, &ccfs
);
2127 le
= fileref
->fcb
->hardlinks
.Flink
;
2128 while (le
!= &fileref
->fcb
->hardlinks
) {
2129 hardlink
* hl
= CONTAINING_RECORD(le
, hardlink
, list_entry
);
2131 if (hl
->parent
== fileref
->parent
->fcb
->inode
&& hl
->index
== fileref
->index
) {
2132 RemoveEntryList(&hl
->list_entry
);
2134 if (hl
->name
.Buffer
)
2135 ExFreePool(hl
->name
.Buffer
);
2137 if (hl
->utf8
.Buffer
)
2138 ExFreePool(hl
->utf8
.Buffer
);
2146 } else { // subvolume
2147 if (fileref
->fcb
->subvol
->root_item
.num_references
> 1) {
2148 fileref
->fcb
->subvol
->root_item
.num_references
--;
2150 mark_fcb_dirty(fileref
->fcb
); // so ROOT_ITEM gets updated
2152 // FIXME - we need a lock here
2154 RemoveEntryList(&fileref
->fcb
->subvol
->list_entry
);
2156 InsertTailList(&fileref
->fcb
->Vcb
->drop_roots
, &fileref
->fcb
->subvol
->list_entry
);
2160 fileref
->fcb
->deleted
= TRUE
;
2161 mark_fcb_dirty(fileref
->fcb
);
2164 // update INODE_ITEM of parent
2166 TRACE("delete file %.*S\n", fileref
->filepart
.Length
/ sizeof(WCHAR
), fileref
->filepart
.Buffer
);
2167 ExAcquireResourceExclusiveLite(fileref
->parent
->fcb
->Header
.Resource
, TRUE
);
2168 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) was %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2169 fileref
->parent
->fcb
->inode_item
.st_size
-= fileref
->utf8
.Length
* 2;
2170 TRACE("fileref->parent->fcb->inode_item.st_size (inode %llx) now %llx\n", fileref
->parent
->fcb
->inode
, fileref
->parent
->fcb
->inode_item
.st_size
);
2171 fileref
->parent
->fcb
->inode_item
.transid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2172 fileref
->parent
->fcb
->inode_item
.sequence
++;
2173 fileref
->parent
->fcb
->inode_item
.st_ctime
= now
;
2174 fileref
->parent
->fcb
->inode_item
.st_mtime
= now
;
2175 ExReleaseResourceLite(fileref
->parent
->fcb
->Header
.Resource
);
2177 mark_fcb_dirty(fileref
->parent
->fcb
);
2179 send_notification_fcb(fileref
->parent
, FILE_NOTIFY_CHANGE_LAST_WRITE
, FILE_ACTION_MODIFIED
);
2181 fileref
->fcb
->subvol
->root_item
.ctransid
= fileref
->fcb
->Vcb
->superblock
.generation
;
2182 fileref
->fcb
->subvol
->root_item
.ctime
= now
;
2184 if (FileObject
&& FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fileref
->fcb
->nonpaged
->segment_object
.DataSectionObject
)
2185 CcPurgeCacheSection(&fileref
->fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2187 newlength
.QuadPart
= 0;
2189 if (FileObject
&& !CcUninitializeCacheMap(FileObject
, &newlength
, NULL
))
2190 TRACE("CcUninitializeCacheMap failed\n");
2192 ExReleaseResourceLite(fileref
->fcb
->Header
.Resource
);
2194 return STATUS_SUCCESS
;
2197 static NTSTATUS STDCALL
drv_cleanup(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
2199 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
2200 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
2201 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
2207 FsRtlEnterFileSystem();
2209 top_level
= is_top_level(Irp
);
2211 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
2212 Status
= part0_passthrough(DeviceObject
, Irp
);
2216 if (DeviceObject
== devobj
) {
2217 TRACE("closing file system\n");
2218 Status
= STATUS_SUCCESS
;
2222 if (FileObject
&& FileObject
->FsContext
) {
2227 fcb
= FileObject
->FsContext
;
2228 ccb
= FileObject
->FsContext2
;
2229 fileref
= ccb
? ccb
->fileref
: NULL
;
2231 TRACE("cleanup called for FileObject %p\n", FileObject
);
2232 TRACE("fcb %p (%S), refcount = %u, open_count = %u\n", fcb
, file_desc(FileObject
), fcb
->refcount
, fcb
->open_count
);
2234 IoRemoveShareAccess(FileObject
, &fcb
->share_access
);
2236 FsRtlNotifyCleanup(Vcb
->NotifySync
, &Vcb
->DirNotifyList
, ccb
);
2238 oc
= InterlockedDecrement(&fcb
->open_count
);
2239 #ifdef DEBUG_FCB_REFCOUNTS
2240 ERR("fcb %p: open_count now %i\n", fcb
, oc
);
2243 if (ccb
&& ccb
->options
& FILE_DELETE_ON_CLOSE
&& fileref
)
2244 fileref
->delete_on_close
= TRUE
;
2246 if (fileref
&& fileref
->delete_on_close
&& fcb
->type
== BTRFS_TYPE_DIRECTORY
&& fcb
->inode_item
.st_size
> 0)
2247 fileref
->delete_on_close
= FALSE
;
2249 if (Vcb
->locked
&& Vcb
->locked_fileobj
== FileObject
) {
2250 TRACE("unlocking volume\n");
2251 do_unlock_volume(Vcb
);
2252 FsRtlNotifyVolumeEvent(FileObject
, FSRTL_VOLUME_UNLOCK
);
2256 if (!Vcb
->removing
) {
2257 LIST_ENTRY rollback
;
2259 InitializeListHead(&rollback
);
2261 if (fileref
&& fileref
->delete_on_close
&& fileref
!= fcb
->Vcb
->root_fileref
&& fcb
!= fcb
->Vcb
->volume_fcb
) {
2262 send_notification_fileref(fileref
, fcb
->type
== BTRFS_TYPE_DIRECTORY
? FILE_NOTIFY_CHANGE_DIR_NAME
: FILE_NOTIFY_CHANGE_FILE_NAME
, FILE_ACTION_REMOVED
);
2264 ExAcquireResourceSharedLite(&fcb
->Vcb
->tree_lock
, TRUE
);
2266 Status
= delete_fileref(fileref
, FileObject
, &rollback
);
2267 if (!NT_SUCCESS(Status
)) {
2268 ERR("delete_fileref returned %08x\n", Status
);
2269 do_rollback(Vcb
, &rollback
);
2270 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2274 ExReleaseResourceLite(&fcb
->Vcb
->tree_lock
);
2275 clear_rollback(&rollback
);
2276 } else if (FileObject
->Flags
& FO_CACHE_SUPPORTED
&& fcb
->nonpaged
->segment_object
.DataSectionObject
) {
2277 IO_STATUS_BLOCK iosb
;
2278 CcFlushCache(FileObject
->SectionObjectPointer
, NULL
, 0, &iosb
);
2280 if (!NT_SUCCESS(iosb
.Status
)) {
2281 ERR("CcFlushCache returned %08x\n", iosb
.Status
);
2284 if (!ExIsResourceAcquiredSharedLite(fcb
->Header
.PagingIoResource
)) {
2285 ExAcquireResourceExclusiveLite(fcb
->Header
.PagingIoResource
, TRUE
);
2286 ExReleaseResourceLite(fcb
->Header
.PagingIoResource
);
2289 CcPurgeCacheSection(&fcb
->nonpaged
->segment_object
, NULL
, 0, FALSE
);
2291 TRACE("flushed cache on close (FileObject = %p, fcb = %p, AllocationSize = %llx, FileSize = %llx, ValidDataLength = %llx)\n",
2292 FileObject
, fcb
, fcb
->Header
.AllocationSize
.QuadPart
, fcb
->Header
.FileSize
.QuadPart
, fcb
->Header
.ValidDataLength
.QuadPart
);
2296 if (fcb
->Vcb
&& fcb
!= fcb
->Vcb
->volume_fcb
)
2297 CcUninitializeCacheMap(FileObject
, NULL
, NULL
);
2300 FileObject
->Flags
|= FO_CLEANUP_COMPLETE
;
2303 Status
= STATUS_SUCCESS
;
2306 Irp
->IoStatus
.Status
= Status
;
2307 Irp
->IoStatus
.Information
= 0;
2309 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
2313 IoSetTopLevelIrp(NULL
);
2315 FsRtlExitFileSystem();
2320 ULONG STDCALL
get_file_attributes(device_extension
* Vcb
, INODE_ITEM
* ii
, root
* r
, UINT64 inode
, UINT8 type
, BOOL dotfile
, BOOL ignore_xa
) {
2327 if (!ignore_xa
&& get_xattr(Vcb
, r
, inode
, EA_DOSATTRIB
, EA_DOSATTRIB_HASH
, (UINT8
**)&eaval
, &ealen
)) {
2329 if (eaval
[0] == '0' && eaval
[1] == 'x') {
2333 for (i
= 2; i
< ealen
; i
++) {
2336 if (eaval
[i
] >= '0' && eaval
[i
] <= '9')
2337 dosnum
|= eaval
[i
] - '0';
2338 else if (eaval
[i
] >= 'a' && eaval
[i
] <= 'f')
2339 dosnum
|= eaval
[i
] + 10 - 'a';
2340 else if (eaval
[i
] >= 'A' && eaval
[i
] <= 'F')
2341 dosnum
|= eaval
[i
] + 10 - 'a';
2344 TRACE("DOSATTRIB: %08x\n", dosnum
);
2348 if (type
== BTRFS_TYPE_DIRECTORY
)
2349 dosnum
|= FILE_ATTRIBUTE_DIRECTORY
;
2350 else if (type
== BTRFS_TYPE_SYMLINK
)
2351 dosnum
|= FILE_ATTRIBUTE_REPARSE_POINT
;
2361 case BTRFS_TYPE_DIRECTORY
:
2362 att
= FILE_ATTRIBUTE_DIRECTORY
;
2365 case BTRFS_TYPE_SYMLINK
:
2366 att
= FILE_ATTRIBUTE_REPARSE_POINT
;
2375 att
|= FILE_ATTRIBUTE_HIDDEN
;
2378 att
|= FILE_ATTRIBUTE_ARCHIVE
;
2380 // FIXME - get READONLY from ii->st_mode
2381 // FIXME - return SYSTEM for block/char devices?
2384 att
= FILE_ATTRIBUTE_NORMAL
;
2389 NTSTATUS
sync_read_phys(PDEVICE_OBJECT DeviceObject
, LONGLONG StartingOffset
, ULONG Length
, PUCHAR Buffer
) {
2390 IO_STATUS_BLOCK
* IoStatus
;
2391 LARGE_INTEGER Offset
;
2393 PIO_STACK_LOCATION IrpSp
;
2395 read_context
* context
;
2399 context
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(read_context
), ALLOC_TAG
);
2401 ERR("out of memory\n");
2402 return STATUS_INSUFFICIENT_RESOURCES
;
2405 RtlZeroMemory(context
, sizeof(read_context
));
2406 KeInitializeEvent(&context
->Event
, NotificationEvent
, FALSE
);
2408 IoStatus
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(IO_STATUS_BLOCK
), ALLOC_TAG
);
2410 ERR("out of memory\n");
2411 ExFreePool(context
);
2412 return STATUS_INSUFFICIENT_RESOURCES
;
2415 Offset
.QuadPart
= StartingOffset
;
2417 // Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, Buffer, Length, &Offset, /*&Event*/NULL, IoStatus);
2418 Irp
= IoAllocateIrp(DeviceObject
->StackSize
, FALSE
);
2421 ERR("IoAllocateIrp failed\n");
2422 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2426 IrpSp
= IoGetNextIrpStackLocation(Irp
);
2427 IrpSp
->MajorFunction
= IRP_MJ_READ
;
2429 if (DeviceObject
->Flags
& DO_BUFFERED_IO
) {
2430 FIXME("FIXME - buffered IO\n");
2431 } else if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2432 // TRACE("direct IO\n");
2434 Irp
->MdlAddress
= IoAllocateMdl(Buffer
, Length
, FALSE
, FALSE
, NULL
);
2435 if (!Irp
->MdlAddress
) {
2436 ERR("IoAllocateMdl failed\n");
2437 Status
= STATUS_INSUFFICIENT_RESOURCES
;
2441 // TRACE("got MDL %p from buffer %p\n", Irp->MdlAddress, Buffer);
2444 MmProbeAndLockPages(Irp
->MdlAddress
, KernelMode
, IoWriteAccess
);
2446 // TRACE("neither buffered nor direct IO\n");
2447 Irp
->UserBuffer
= Buffer
;
2450 IrpSp
->Parameters
.Read
.Length
= Length
;
2451 IrpSp
->Parameters
.Read
.ByteOffset
= Offset
;
2453 Irp
->UserIosb
= IoStatus
;
2454 // Irp->Tail.Overlay.Thread = PsGetCurrentThread();
2456 Irp
->UserEvent
= &context
->Event
;
2458 // IoQueueThreadIrp(Irp);
2460 IoSetCompletionRoutine(Irp
, read_completion
, context
, TRUE
, TRUE
, TRUE
);
2464 // Stack = IoGetNextIrpStackLocation(Irp);
2465 // Stack->Flags |= SL_OVERRIDE_VERIFY_VOLUME;
2468 // TRACE("Calling IO Driver... with irp %p\n", Irp);
2469 Status
= IoCallDriver(DeviceObject
, Irp
);
2471 // TRACE("Waiting for IO Operation for %p\n", Irp);
2472 if (Status
== STATUS_PENDING
) {
2473 // TRACE("Operation pending\n");
2474 KeWaitForSingleObject(&context
->Event
, Executive
, KernelMode
, FALSE
, NULL
);
2475 // TRACE("Getting IO Status... for %p\n", Irp);
2476 Status
= context
->iosb
.Status
;
2479 if (DeviceObject
->Flags
& DO_DIRECT_IO
) {
2480 MmUnlockPages(Irp
->MdlAddress
);
2481 IoFreeMdl(Irp
->MdlAddress
);
2487 ExFreePool(IoStatus
);
2488 ExFreePool(context
);
2493 static NTSTATUS STDCALL
read_superblock(device_extension
* Vcb
, PDEVICE_OBJECT device
, UINT64 length
) {
2496 unsigned int i
, to_read
;
2499 to_read
= sector_align(sizeof(superblock
), device
->SectorSize
);
2501 sb
= ExAllocatePoolWithTag(NonPagedPool
, to_read
, ALLOC_TAG
);
2503 ERR("out of memory\n");
2504 return STATUS_INSUFFICIENT_RESOURCES
;
2509 while (superblock_addrs
[i
] > 0) {
2510 if (i
> 0 && superblock_addrs
[i
] + sizeof(superblock
) > length
)
2513 Status
= sync_read_phys(device
, superblock_addrs
[i
], to_read
, (PUCHAR
)sb
);
2514 if (!NT_SUCCESS(Status
)) {
2515 ERR("Failed to read superblock %u: %08x\n", i
, Status
);
2520 // FIXME - check checksum before accepting?
2522 TRACE("got superblock %u!\n", i
);
2524 if (i
== 0 || sb
->generation
> Vcb
->superblock
.generation
)
2525 RtlCopyMemory(&Vcb
->superblock
, sb
, sizeof(superblock
));
2532 crc32
= calc_crc32c(0xffffffff, (UINT8
*)&Vcb
->superblock
.uuid
, (ULONG
)sizeof(superblock
) - sizeof(Vcb
->superblock
.checksum
));
2534 TRACE("crc32 was %08x, expected %08x\n", crc32
, *((UINT32
*)Vcb
->superblock
.checksum
));
2536 if (crc32
!= *((UINT32
*)Vcb
->superblock
.checksum
))
2537 return STATUS_INTERNAL_ERROR
; // FIXME - correct error?
2539 TRACE("label is %s\n", Vcb
->superblock
.label
);
2540 // utf8_to_utf16(Vcb->superblock.label, Vcb->label, MAX_LABEL_SIZE * sizeof(WCHAR));
2542 return STATUS_SUCCESS
;
2545 NTSTATUS STDCALL
dev_ioctl(PDEVICE_OBJECT DeviceObject
, ULONG ControlCode
, PVOID InputBuffer
, ULONG InputBufferSize
,
2546 PVOID OutputBuffer
, ULONG OutputBufferSize
, BOOLEAN Override
, IO_STATUS_BLOCK
* iosb
)
2551 PIO_STACK_LOCATION Stack
;
2552 IO_STATUS_BLOCK IoStatus
;
2554 KeInitializeEvent(&Event
, NotificationEvent
, FALSE
);
2556 Irp
= IoBuildDeviceIoControlRequest(ControlCode
,
2566 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
2569 Stack
= IoGetNextIrpStackLocation(Irp
);
2570 Stack
->Flags
|= SL_OVERRIDE_VERIFY_VOLUME
;
2573 Status
= IoCallDriver(DeviceObject
, Irp
);
2575 if (Status
== STATUS_PENDING
) {
2576 KeWaitForSingleObject(&Event
, Executive
, KernelMode
, FALSE
, NULL
);
2577 Status
= IoStatus
.Status
;
2586 static NTSTATUS STDCALL
add_root(device_extension
* Vcb
, UINT64 id
, UINT64 addr
, traverse_ptr
* tp
) {
2587 root
* r
= ExAllocatePoolWithTag(PagedPool
, sizeof(root
), ALLOC_TAG
);
2589 ERR("out of memory\n");
2590 return STATUS_INSUFFICIENT_RESOURCES
;
2594 r
->path
.Buffer
= NULL
;
2595 r
->treeholder
.address
= addr
;
2596 r
->treeholder
.tree
= NULL
;
2597 InitializeListHead(&r
->fcbs
);
2599 r
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(root_nonpaged
), ALLOC_TAG
);
2601 ERR("out of memory\n");
2603 return STATUS_INSUFFICIENT_RESOURCES
;
2606 ExInitializeResourceLite(&r
->nonpaged
->load_tree_lock
);
2611 RtlCopyMemory(&r
->root_item
, tp
->item
->data
, min(sizeof(ROOT_ITEM
), tp
->item
->size
));
2612 if (tp
->item
->size
< sizeof(ROOT_ITEM
))
2613 RtlZeroMemory(((UINT8
*)&r
->root_item
) + tp
->item
->size
, sizeof(ROOT_ITEM
) - tp
->item
->size
);
2616 InsertTailList(&Vcb
->roots
, &r
->list_entry
);
2619 case BTRFS_ROOT_ROOT
:
2623 case BTRFS_ROOT_EXTENT
:
2624 Vcb
->extent_root
= r
;
2627 case BTRFS_ROOT_CHUNK
:
2628 Vcb
->chunk_root
= r
;
2631 case BTRFS_ROOT_DEVTREE
:
2635 case BTRFS_ROOT_CHECKSUM
:
2636 Vcb
->checksum_root
= r
;
2639 case BTRFS_ROOT_UUID
:
2644 return STATUS_SUCCESS
;
2647 static NTSTATUS STDCALL
look_for_roots(device_extension
* Vcb
) {
2648 traverse_ptr tp
, next_tp
;
2653 searchkey
.obj_id
= 0;
2654 searchkey
.obj_type
= 0;
2655 searchkey
.offset
= 0;
2657 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
2658 if (!NT_SUCCESS(Status
)) {
2659 ERR("error - find_tree returned %08x\n", Status
);
2664 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2666 if (tp
.item
->key
.obj_type
== TYPE_ROOT_ITEM
) {
2667 ROOT_ITEM
* ri
= (ROOT_ITEM
*)tp
.item
->data
;
2669 if (tp
.item
->size
< offsetof(ROOT_ITEM
, byte_limit
)) {
2670 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
));
2672 TRACE("root %llx - address %llx\n", tp
.item
->key
.obj_id
, ri
->block_number
);
2674 Status
= add_root(Vcb
, tp
.item
->key
.obj_id
, ri
->block_number
, &tp
);
2675 if (!NT_SUCCESS(Status
)) {
2676 ERR("add_root returned %08x\n", Status
);
2682 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2688 return STATUS_SUCCESS
;
2691 static NTSTATUS
find_disk_holes(device_extension
* Vcb
, device
* dev
) {
2693 traverse_ptr tp
, next_tp
;
2698 InitializeListHead(&dev
->space
);
2700 searchkey
.obj_id
= dev
->devitem
.dev_id
;
2701 searchkey
.obj_type
= TYPE_DEV_EXTENT
;
2702 searchkey
.offset
= 0;
2704 Status
= find_item(Vcb
, Vcb
->dev_root
, &tp
, &searchkey
, FALSE
);
2705 if (!NT_SUCCESS(Status
)) {
2706 ERR("error - find_tree returned %08x\n", Status
);
2713 if (tp
.item
->key
.obj_id
== dev
->devitem
.dev_id
&& tp
.item
->key
.obj_type
== TYPE_DEV_EXTENT
) {
2714 if (tp
.item
->size
>= sizeof(DEV_EXTENT
)) {
2715 DEV_EXTENT
* de
= (DEV_EXTENT
*)tp
.item
->data
;
2717 if (tp
.item
->key
.offset
> lastaddr
) {
2718 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, tp
.item
->key
.offset
- lastaddr
);
2719 if (!NT_SUCCESS(Status
)) {
2720 ERR("add_space_entry returned %08x\n", Status
);
2725 lastaddr
= tp
.item
->key
.offset
+ de
->length
;
2727 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
));
2731 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
2735 if (tp
.item
->key
.obj_id
> searchkey
.obj_id
|| tp
.item
->key
.obj_type
> searchkey
.obj_type
)
2740 if (lastaddr
< dev
->devitem
.num_bytes
) {
2741 Status
= add_space_entry(&dev
->space
, NULL
, lastaddr
, dev
->devitem
.num_bytes
- lastaddr
);
2742 if (!NT_SUCCESS(Status
)) {
2743 ERR("add_space_entry returned %08x\n", Status
);
2748 return STATUS_SUCCESS
;
2751 device
* find_device_from_uuid(device_extension
* Vcb
, BTRFS_UUID
* uuid
) {
2754 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2755 TRACE("device %llx, uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", i
,
2756 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],
2757 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]);
2759 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2760 TRACE("returning device %llx\n", i
);
2761 return &Vcb
->devices
[i
];
2765 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
&& !IsListEmpty(&volumes
)) {
2766 LIST_ENTRY
* le
= volumes
.Flink
;
2768 while (le
!= &volumes
) {
2769 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2771 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2772 RtlCompareMemory(uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2775 PFILE_OBJECT FileObject
;
2776 PDEVICE_OBJECT DeviceObject
;
2778 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
2779 if (!NT_SUCCESS(Status
)) {
2780 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2784 DeviceObject
= FileObject
->DeviceObject
;
2786 ObReferenceObject(DeviceObject
);
2787 ObDereferenceObject(FileObject
);
2789 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2790 Vcb
->devices
[Vcb
->devices_loaded
].devitem
.device_uuid
= *uuid
;
2791 Vcb
->devices_loaded
++;
2793 return &Vcb
->devices
[Vcb
->devices_loaded
- 1];
2800 WARN("could not find device with uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
2801 uuid
->uuid
[0], uuid
->uuid
[1], uuid
->uuid
[2], uuid
->uuid
[3], uuid
->uuid
[4], uuid
->uuid
[5], uuid
->uuid
[6], uuid
->uuid
[7],
2802 uuid
->uuid
[8], uuid
->uuid
[9], uuid
->uuid
[10], uuid
->uuid
[11], uuid
->uuid
[12], uuid
->uuid
[13], uuid
->uuid
[14], uuid
->uuid
[15]);
2807 static BOOL
is_device_removable(PDEVICE_OBJECT devobj
) {
2809 STORAGE_HOTPLUG_INFO shi
;
2811 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_GET_HOTPLUG_INFO
, NULL
, 0, &shi
, sizeof(STORAGE_HOTPLUG_INFO
), TRUE
, NULL
);
2813 if (!NT_SUCCESS(Status
)) {
2814 ERR("dev_ioctl returned %08x\n", Status
);
2818 return shi
.MediaRemovable
!= 0 ? TRUE
: FALSE
;
2821 static ULONG
get_device_change_count(PDEVICE_OBJECT devobj
) {
2824 IO_STATUS_BLOCK iosb
;
2826 Status
= dev_ioctl(devobj
, IOCTL_STORAGE_CHECK_VERIFY
, NULL
, 0, &cc
, sizeof(ULONG
), TRUE
, &iosb
);
2828 if (!NT_SUCCESS(Status
)) {
2829 ERR("dev_ioctl returned %08x\n", Status
);
2833 if (iosb
.Information
< sizeof(ULONG
)) {
2834 ERR("iosb.Information was too short\n");
2841 static void init_device(device_extension
* Vcb
, device
* dev
, BOOL get_length
) {
2843 GET_LENGTH_INFORMATION gli
;
2845 dev
->removable
= is_device_removable(dev
->devobj
);
2846 dev
->change_count
= dev
->removable
? get_device_change_count(dev
->devobj
) : 0;
2849 Status
= dev_ioctl(dev
->devobj
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
2850 &gli
, sizeof(gli
), TRUE
, NULL
);
2851 if (!NT_SUCCESS(Status
)) {
2852 ERR("error reading length information: %08x\n", Status
);
2855 dev
->length
= gli
.Length
.QuadPart
;
2859 static NTSTATUS STDCALL
load_chunk_root(device_extension
* Vcb
) {
2860 traverse_ptr tp
, next_tp
;
2867 searchkey
.obj_id
= 0;
2868 searchkey
.obj_type
= 0;
2869 searchkey
.offset
= 0;
2871 Vcb
->data_flags
= 0;
2873 Status
= find_item(Vcb
, Vcb
->chunk_root
, &tp
, &searchkey
, FALSE
);
2874 if (!NT_SUCCESS(Status
)) {
2875 ERR("error - find_item returned %08x\n", Status
);
2880 TRACE("(%llx,%x,%llx)\n", tp
.item
->key
.obj_id
, tp
.item
->key
.obj_type
, tp
.item
->key
.offset
);
2882 if (tp
.item
->key
.obj_id
== 1 && tp
.item
->key
.obj_type
== TYPE_DEV_ITEM
) {
2883 if (tp
.item
->size
< sizeof(DEV_ITEM
)) {
2884 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
));
2886 DEV_ITEM
* di
= (DEV_ITEM
*)tp
.item
->data
;
2889 for (i
= 0; i
< Vcb
->devices_loaded
; i
++) {
2890 if (Vcb
->devices
[i
].devobj
&& RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &di
->device_uuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)) {
2891 RtlCopyMemory(&Vcb
->devices
[i
].devitem
, tp
.item
->data
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2894 init_device(Vcb
, &Vcb
->devices
[i
], TRUE
);
2902 if (!IsListEmpty(&volumes
) && Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
2903 LIST_ENTRY
* le
= volumes
.Flink
;
2905 while (le
!= &volumes
) {
2906 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
2908 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
2909 RtlCompareMemory(&di
->device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
2911 PFILE_OBJECT FileObject
;
2912 PDEVICE_OBJECT DeviceObject
;
2914 Status
= IoGetDeviceObjectPointer(&v
->devpath
, FILE_READ_DATA
| FILE_WRITE_DATA
, &FileObject
, &DeviceObject
);
2915 if (!NT_SUCCESS(Status
)) {
2916 ERR("IoGetDeviceObjectPointer(%.*S) returned %08x\n", v
->devpath
.Length
/ sizeof(WCHAR
), v
->devpath
.Buffer
, Status
);
2920 DeviceObject
= FileObject
->DeviceObject
;
2922 ObReferenceObject(DeviceObject
);
2923 ObDereferenceObject(FileObject
);
2925 Vcb
->devices
[Vcb
->devices_loaded
].devobj
= DeviceObject
;
2926 RtlCopyMemory(&Vcb
->devices
[Vcb
->devices_loaded
].devitem
, di
, min(tp
.item
->size
, sizeof(DEV_ITEM
)));
2927 init_device(Vcb
, &Vcb
->devices
[i
], FALSE
);
2928 Vcb
->devices
[i
].length
= v
->length
;
2929 Vcb
->devices_loaded
++;
2939 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
,
2940 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],
2941 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]);
2944 ERR("unexpected device %llx found\n", tp
.item
->key
.offset
);
2947 } else if (tp
.item
->key
.obj_type
== TYPE_CHUNK_ITEM
) {
2948 if (tp
.item
->size
< sizeof(CHUNK_ITEM
)) {
2949 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
));
2951 c
= ExAllocatePoolWithTag(PagedPool
, sizeof(chunk
), ALLOC_TAG
);
2954 ERR("out of memory\n");
2955 return STATUS_INSUFFICIENT_RESOURCES
;
2958 c
->nonpaged
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(chunk_nonpaged
), ALLOC_TAG
);
2961 ERR("out of memory\n");
2963 return STATUS_INSUFFICIENT_RESOURCES
;
2966 c
->size
= tp
.item
->size
;
2967 c
->offset
= tp
.item
->key
.offset
;
2968 c
->used
= c
->oldused
= 0;
2972 c
->chunk_item
= ExAllocatePoolWithTag(PagedPool
, tp
.item
->size
, ALLOC_TAG
);
2974 if (!c
->chunk_item
) {
2975 ERR("out of memory\n");
2977 ExFreePool(c
->nonpaged
);
2978 return STATUS_INSUFFICIENT_RESOURCES
;
2981 RtlCopyMemory(c
->chunk_item
, tp
.item
->data
, tp
.item
->size
);
2983 if (c
->chunk_item
->type
& BLOCK_FLAG_DATA
&& c
->chunk_item
->type
> Vcb
->data_flags
)
2984 Vcb
->data_flags
= c
->chunk_item
->type
;
2986 if (c
->chunk_item
->num_stripes
> 0) {
2987 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&c
->chunk_item
[1];
2989 c
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
*) * c
->chunk_item
->num_stripes
, ALLOC_TAG
);
2992 ERR("out of memory\n");
2994 ExFreePool(c
->nonpaged
);
2995 ExFreePool(c
->chunk_item
);
2996 return STATUS_INSUFFICIENT_RESOURCES
;
2999 for (i
= 0; i
< c
->chunk_item
->num_stripes
; i
++) {
3000 c
->devices
[i
] = find_device_from_uuid(Vcb
, &cis
[i
].dev_uuid
);
3001 TRACE("device %llu = %p\n", i
, c
->devices
[i
]);
3006 ExInitializeResourceLite(&c
->nonpaged
->lock
);
3007 ExInitializeResourceLite(&c
->nonpaged
->changed_extents_lock
);
3009 InitializeListHead(&c
->space
);
3010 InitializeListHead(&c
->space_size
);
3011 InitializeListHead(&c
->deleting
);
3012 InitializeListHead(&c
->changed_extents
);
3014 InsertTailList(&Vcb
->chunks
, &c
->list_entry
);
3016 c
->list_entry_changed
.Flink
= NULL
;
3020 b
= find_next_item(Vcb
, &tp
, &next_tp
, FALSE
);
3026 Vcb
->log_to_phys_loaded
= TRUE
;
3028 if (Vcb
->data_flags
== 0)
3029 Vcb
->data_flags
= BLOCK_FLAG_DATA
| (Vcb
->superblock
.num_devices
> 1 ? BLOCK_FLAG_RAID0
: 0);
3031 return STATUS_SUCCESS
;
3034 void protect_superblocks(device_extension
* Vcb
, chunk
* c
) {
3036 UINT64 off_start
, off_end
;
3038 // The Linux driver also protects all the space before the first superblock.
3039 // I realize this confuses physical and logical addresses, but this is what btrfs-progs does -
3040 // evidently Linux assumes the chunk at 0 is always SINGLE.
3041 if (c
->offset
< superblock_addrs
[0])
3042 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
, superblock_addrs
[0] - c
->offset
, NULL
);
3044 while (superblock_addrs
[i
] != 0) {
3045 CHUNK_ITEM
* ci
= c
->chunk_item
;
3046 CHUNK_ITEM_STRIPE
* cis
= (CHUNK_ITEM_STRIPE
*)&ci
[1];
3048 if (ci
->type
& BLOCK_FLAG_RAID0
|| ci
->type
& BLOCK_FLAG_RAID10
) {
3049 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3050 ULONG sub_stripes
= max(ci
->sub_stripes
, 1);
3052 if (cis
[j
].offset
+ (ci
->size
* ci
->num_stripes
/ sub_stripes
) > superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3055 UINT16 startoffstripe
;
3058 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3060 off_start
= superblock_addrs
[i
] - cis
[j
].offset
;
3061 off_start
-= off_start
% ci
->stripe_length
;
3062 off_start
*= ci
->num_stripes
/ sub_stripes
;
3063 off_start
+= (j
/ sub_stripes
) * ci
->stripe_length
;
3065 off_end
= off_start
+ ci
->stripe_length
;
3068 get_raid0_offset(off_start
, ci
->stripe_length
, ci
->num_stripes
/ sub_stripes
, &startoff
, &startoffstripe
);
3069 TRACE("j = %u, startoffstripe = %u\n", j
, startoffstripe
);
3070 TRACE("startoff = %llx, superblock = %llx\n", startoff
+ cis
[j
].offset
, superblock_addrs
[i
]);
3073 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3076 } else { // SINGLE, DUPLICATE, RAID1
3077 for (j
= 0; j
< ci
->num_stripes
; j
++) {
3078 if (cis
[j
].offset
+ ci
->size
> superblock_addrs
[i
] && cis
[j
].offset
<= superblock_addrs
[i
] + sizeof(superblock
)) {
3079 TRACE("cut out superblock in chunk %llx\n", c
->offset
);
3081 // The Linux driver protects the whole stripe in which the superblock lives
3083 off_start
= ((superblock_addrs
[i
] - cis
[j
].offset
) / c
->chunk_item
->stripe_length
) * c
->chunk_item
->stripe_length
;
3084 off_end
= sector_align(superblock_addrs
[i
] - cis
[j
].offset
+ sizeof(superblock
), c
->chunk_item
->stripe_length
);
3086 space_list_subtract(Vcb
, c
, FALSE
, c
->offset
+ off_start
, off_end
- off_start
, NULL
);
3095 static NTSTATUS STDCALL
find_chunk_usage(device_extension
* Vcb
) {
3096 LIST_ENTRY
* le
= Vcb
->chunks
.Flink
;
3100 BLOCK_GROUP_ITEM
* bgi
;
3104 // block_group_item size=7f0000 chunktreeid=100 flags=1
3106 searchkey
.obj_type
= TYPE_BLOCK_GROUP_ITEM
;
3108 while (le
!= &Vcb
->chunks
) {
3109 c
= CONTAINING_RECORD(le
, chunk
, list_entry
);
3111 searchkey
.obj_id
= c
->offset
;
3112 searchkey
.offset
= c
->chunk_item
->size
;
3114 Status
= find_item(Vcb
, Vcb
->extent_root
, &tp
, &searchkey
, FALSE
);
3115 if (!NT_SUCCESS(Status
)) {
3116 ERR("error - find_item returned %08x\n", Status
);
3120 if (!keycmp(&searchkey
, &tp
.item
->key
)) {
3121 if (tp
.item
->size
>= sizeof(BLOCK_GROUP_ITEM
)) {
3122 bgi
= (BLOCK_GROUP_ITEM
*)tp
.item
->data
;
3124 c
->used
= c
->oldused
= bgi
->used
;
3126 TRACE("chunk %llx has %llx bytes used\n", c
->offset
, c
->used
);
3128 ERR("(%llx;%llx,%x,%llx) is %u bytes, expected %u\n",
3129 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
));
3133 // if (addr >= c->offset && (addr - c->offset) < c->chunk_item->size && c->chunk_item->num_stripes > 0) {
3134 // cis = (CHUNK_ITEM_STRIPE*)&c->chunk_item[1];
3136 // return (addr - c->offset) + cis->offset;
3139 // FIXME - make sure we free occasionally after doing one of these, or we
3140 // might use up a lot of memory with a big disk.
3142 Status
= load_free_space_cache(Vcb
, c
);
3143 if (!NT_SUCCESS(Status
)) {
3144 ERR("load_free_space_cache returned %08x\n", Status
);
3148 protect_superblocks(Vcb
, c
);
3153 return STATUS_SUCCESS
;
3156 // static void STDCALL root_test(device_extension* Vcb) {
3159 // traverse_ptr tp, next_tp;
3164 // if (r->id == 0x102)
3170 // ERR("Could not find root tree.\n");
3174 // searchkey.obj_id = 0x1b6;
3175 // searchkey.obj_type = 0xb;
3176 // searchkey.offset = 0;
3178 // if (!find_item(Vcb, r, &tp, &searchkey, NULL, FALSE)) {
3179 // ERR("Could not find first item.\n");
3185 // TRACE("%x,%x,%x\n", (UINT32)tp.item->key.obj_id, tp.item->key.obj_type, (UINT32)tp.item->key.offset);
3187 // b = find_prev_item(Vcb, &tp, &next_tp, NULL, FALSE);
3190 // free_traverse_ptr(&tp);
3195 // free_traverse_ptr(&tp);
3198 static NTSTATUS
load_sys_chunks(device_extension
* Vcb
) {
3200 ULONG n
= Vcb
->superblock
.n
;
3203 if (n
> sizeof(KEY
)) {
3204 RtlCopyMemory(&key
, &Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
], sizeof(KEY
));
3207 return STATUS_SUCCESS
;
3209 TRACE("bootstrap: %llx,%x,%llx\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3211 if (key
.obj_type
== TYPE_CHUNK_ITEM
) {
3216 if (n
< sizeof(CHUNK_ITEM
))
3217 return STATUS_SUCCESS
;
3219 ci
= (CHUNK_ITEM
*)&Vcb
->superblock
.sys_chunk_array
[Vcb
->superblock
.n
- n
];
3220 cisize
= sizeof(CHUNK_ITEM
) + (ci
->num_stripes
* sizeof(CHUNK_ITEM_STRIPE
));
3223 return STATUS_SUCCESS
;
3225 sc
= ExAllocatePoolWithTag(PagedPool
, sizeof(sys_chunk
), ALLOC_TAG
);
3228 ERR("out of memory\n");
3229 return STATUS_INSUFFICIENT_RESOURCES
;
3234 sc
->data
= ExAllocatePoolWithTag(PagedPool
, sc
->size
, ALLOC_TAG
);
3237 ERR("out of memory\n");
3238 return STATUS_INSUFFICIENT_RESOURCES
;
3241 RtlCopyMemory(sc
->data
, ci
, sc
->size
);
3242 InsertTailList(&Vcb
->sys_chunks
, &sc
->list_entry
);
3246 ERR("unexpected item %llx,%x,%llx in bootstrap\n", key
.obj_id
, key
.obj_type
, key
.offset
);
3247 return STATUS_INTERNAL_ERROR
;
3251 return STATUS_SUCCESS
;
3254 static root
* find_default_subvol(device_extension
* Vcb
) {
3257 static char fn
[] = "default";
3258 static UINT32 crc32
= 0x8dbfc2d2;
3260 if (Vcb
->superblock
.incompat_flags
& BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL
) {
3266 searchkey
.obj_id
= Vcb
->superblock
.root_dir_objectid
;
3267 searchkey
.obj_type
= TYPE_DIR_ITEM
;
3268 searchkey
.offset
= crc32
;
3270 Status
= find_item(Vcb
, Vcb
->root_root
, &tp
, &searchkey
, FALSE
);
3271 if (!NT_SUCCESS(Status
)) {
3272 ERR("error - find_item returned %08x\n", Status
);
3276 if (keycmp(&tp
.item
->key
, &searchkey
)) {
3277 ERR("could not find (%llx,%x,%llx) in root tree\n", searchkey
.obj_id
, searchkey
.obj_type
, searchkey
.offset
);
3281 if (tp
.item
->size
< sizeof(DIR_ITEM
)) {
3282 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
));
3286 di
= (DIR_ITEM
*)tp
.item
->data
;
3288 if (tp
.item
->size
< sizeof(DIR_ITEM
) - 1 + di
->n
) {
3289 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
);
3293 if (di
->n
!= strlen(fn
) || RtlCompareMemory(di
->name
, fn
, di
->n
) != di
->n
) {
3294 ERR("root DIR_ITEM had same CRC32, but was not \"default\"\n");
3298 if (di
->key
.obj_type
!= TYPE_ROOT_ITEM
) {
3299 ERR("default root has key (%llx,%x,%llx), expected subvolume\n", di
->key
.obj_id
, di
->key
.obj_type
, di
->key
.offset
);
3303 le
= Vcb
->roots
.Flink
;
3304 while (le
!= &Vcb
->roots
) {
3305 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3307 if (r
->id
== di
->key
.obj_id
)
3313 ERR("could not find root %llx, using default instead\n", di
->key
.obj_id
);
3317 le
= Vcb
->roots
.Flink
;
3318 while (le
!= &Vcb
->roots
) {
3319 root
* r
= CONTAINING_RECORD(le
, root
, list_entry
);
3321 if (r
->id
== BTRFS_ROOT_FSTREE
)
3330 static NTSTATUS
create_worker_threads(PDEVICE_OBJECT DeviceObject
) {
3331 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3335 Vcb
->threads
.num_threads
= max(3, KeQueryActiveProcessorCount(NULL
)); // FIXME - number of processors?
3337 Vcb
->threads
.threads
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
, ALLOC_TAG
);
3338 if (!Vcb
->threads
.threads
) {
3339 ERR("out of memory\n");
3340 return STATUS_INSUFFICIENT_RESOURCES
;
3343 RtlZeroMemory(Vcb
->threads
.threads
, sizeof(drv_thread
) * Vcb
->threads
.num_threads
);
3345 for (i
= 0; i
< Vcb
->threads
.num_threads
; i
++) {
3346 Vcb
->threads
.threads
[i
].DeviceObject
= DeviceObject
;
3347 KeInitializeEvent(&Vcb
->threads
.threads
[i
].event
, SynchronizationEvent
, FALSE
);
3348 KeInitializeEvent(&Vcb
->threads
.threads
[i
].finished
, NotificationEvent
, FALSE
);
3349 InitializeListHead(&Vcb
->threads
.threads
[i
].jobs
);
3350 KeInitializeSpinLock(&Vcb
->threads
.threads
[i
].spin_lock
);
3352 Status
= PsCreateSystemThread(&Vcb
->threads
.threads
[i
].handle
, 0, NULL
, NULL
, NULL
, worker_thread
, &Vcb
->threads
.threads
[i
]);
3353 if (!NT_SUCCESS(Status
)) {
3356 ERR("PsCreateSystemThread returned %08x\n", Status
);
3358 for (j
= 0; j
< i
; j
++) {
3359 Vcb
->threads
.threads
[i
].quit
= TRUE
;
3360 KeSetEvent(&Vcb
->threads
.threads
[i
].event
, 0, FALSE
);
3367 return STATUS_SUCCESS
;
3370 BOOL
add_thread_job(device_extension
* Vcb
, PIRP Irp
) {
3374 threadnum
= InterlockedIncrement(&Vcb
->threads
.next_thread
) % Vcb
->threads
.num_threads
;
3376 if (Vcb
->threads
.threads
[threadnum
].quit
)
3379 tj
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(thread_job
), ALLOC_TAG
);
3381 Irp
->IoStatus
.Status
= STATUS_INSUFFICIENT_RESOURCES
;
3382 Irp
->IoStatus
.Information
= 0;
3383 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3389 ExInterlockedInsertTailList(&Vcb
->threads
.threads
[threadnum
].jobs
, &tj
->list_entry
, &Vcb
->threads
.threads
[threadnum
].spin_lock
);
3390 KeSetEvent(&Vcb
->threads
.threads
[threadnum
].event
, 0, FALSE
);
3395 static BOOL
raid_generations_okay(device_extension
* Vcb
) {
3398 // FIXME - if the difference between superblocks is small, we should try to recover
3400 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3401 LIST_ENTRY
* le
= volumes
.Flink
;
3402 while (le
!= &volumes
) {
3403 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3405 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) &&
3406 RtlCompareMemory(&Vcb
->devices
[i
].devitem
.device_uuid
, &v
->devuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
)
3408 if (v
->gen1
!= Vcb
->superblock
.generation
- 1) {
3409 WARN("device %llu had generation %llx, expected %llx\n", i
, v
->gen1
, Vcb
->superblock
.generation
- 1);
3421 static NTSTATUS STDCALL
mount_vol(PDEVICE_OBJECT DeviceObject
, PIRP Irp
) {
3422 PIO_STACK_LOCATION Stack
;
3423 PDEVICE_OBJECT NewDeviceObject
= NULL
;
3424 PDEVICE_OBJECT DeviceToMount
;
3426 device_extension
* Vcb
= NULL
;
3427 GET_LENGTH_INFORMATION gli
;
3432 fcb
* root_fcb
= NULL
;
3433 ccb
* root_ccb
= NULL
;
3435 TRACE("mount_vol called\n");
3437 if (DeviceObject
!= devobj
)
3439 Status
= STATUS_INVALID_DEVICE_REQUEST
;
3443 Stack
= IoGetCurrentIrpStackLocation(Irp
);
3444 DeviceToMount
= Stack
->Parameters
.MountVolume
.DeviceObject
;
3446 Status
= dev_ioctl(DeviceToMount
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0,
3447 &gli
, sizeof(gli
), TRUE
, NULL
);
3448 if (!NT_SUCCESS(Status
)) {
3449 ERR("error reading length information: %08x\n", Status
);
3450 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3454 Status
= IoCreateDevice(drvobj
,
3455 sizeof(device_extension
),
3457 FILE_DEVICE_DISK_FILE_SYSTEM
,
3461 if (!NT_SUCCESS(Status
)) {
3462 ERR("IoCreateDevice returned %08x\n", Status
);
3463 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3467 // TRACE("DEV_ITEM = %x, superblock = %x\n", sizeof(DEV_ITEM), sizeof(superblock));
3469 NewDeviceObject
->Flags
|= DO_DIRECT_IO
;
3470 Vcb
= (PVOID
)NewDeviceObject
->DeviceExtension
;
3471 RtlZeroMemory(Vcb
, sizeof(device_extension
));
3472 Vcb
->type
= VCB_TYPE_VOLUME
;
3474 ExInitializeResourceLite(&Vcb
->tree_lock
);
3475 Vcb
->open_trees
= 0;
3476 Vcb
->need_write
= FALSE
;
3478 ExInitializeResourceLite(&Vcb
->fcb_lock
);
3479 ExInitializeResourceLite(&Vcb
->DirResource
);
3480 ExInitializeResourceLite(&Vcb
->checksum_lock
);
3481 ExInitializeResourceLite(&Vcb
->chunk_lock
);
3483 ExAcquireResourceExclusiveLite(&global_loading_lock
, TRUE
);
3484 InsertTailList(&VcbList
, &Vcb
->list_entry
);
3485 ExReleaseResourceLite(&global_loading_lock
);
3487 ExInitializeResourceLite(&Vcb
->load_lock
);
3488 ExAcquireResourceExclusiveLite(&Vcb
->load_lock
, TRUE
);
3490 // Vcb->Identifier.Type = NTFS_TYPE_VCB;
3491 // Vcb->Identifier.Size = sizeof(NTFS_TYPE_VCB);
3493 // Status = NtfsGetVolumeData(DeviceToMount,
3495 // if (!NT_SUCCESS(Status))
3498 // Vcb->device = DeviceToMount;
3499 DeviceToMount
->Flags
|= DO_DIRECT_IO
;
3501 // Status = dev_ioctl(DeviceToMount, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
3502 // &Vcb->geometry, sizeof(DISK_GEOMETRY), TRUE);
3503 // if (!NT_SUCCESS(Status)) {
3504 // ERR("error reading disk geometry: %08x\n", Status);
3507 // TRACE("media type = %u, cylinders = %u, tracks per cylinder = %u, sectors per track = %u, bytes per sector = %u\n",
3508 // Vcb->geometry.MediaType, Vcb->geometry.Cylinders, Vcb->geometry.TracksPerCylinder,
3509 // Vcb->geometry.SectorsPerTrack, Vcb->geometry.BytesPerSector);
3512 TRACE("partition length = %llx\n", gli
.Length
.QuadPart
);
3514 Status
= read_superblock(Vcb
, DeviceToMount
, gli
.Length
.QuadPart
);
3515 if (!NT_SUCCESS(Status
)) {
3516 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3520 if (Vcb
->superblock
.magic
!= BTRFS_MAGIC
) {
3521 ERR("not a BTRFS volume\n");
3522 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3525 TRACE("btrfs magic found\n");
3528 Status
= registry_load_volume_options(&Vcb
->superblock
.uuid
, &Vcb
->options
);
3529 if (!NT_SUCCESS(Status
)) {
3530 ERR("registry_load_volume_options returned %08x\n", Status
);
3534 if (Vcb
->options
.ignore
) {
3535 TRACE("ignoring volume\n");
3536 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3540 if (Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
) {
3541 WARN("cannot mount because of unsupported incompat flags (%llx)\n", Vcb
->superblock
.incompat_flags
& ~INCOMPAT_SUPPORTED
);
3542 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3547 while (le
!= &volumes
) {
3548 volume
* v
= CONTAINING_RECORD(le
, volume
, list_entry
);
3550 if (RtlCompareMemory(&Vcb
->superblock
.uuid
, &v
->fsuuid
, sizeof(BTRFS_UUID
)) == sizeof(BTRFS_UUID
) && v
->devnum
< Vcb
->superblock
.dev_item
.dev_id
) {
3551 // skipping over device in RAID which isn't the first one
3552 Status
= STATUS_UNRECOGNIZED_VOLUME
;
3559 Vcb
->readonly
= FALSE
;
3560 if (Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
) {
3561 WARN("mounting read-only because of unsupported flags (%llx)\n", Vcb
->superblock
.compat_ro_flags
& ~COMPAT_RO_SUPPORTED
);
3562 Vcb
->readonly
= TRUE
;
3565 Vcb
->superblock
.generation
++;
3566 Vcb
->superblock
.incompat_flags
|= BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF
;
3568 Vcb
->devices
= ExAllocatePoolWithTag(PagedPool
, sizeof(device
) * Vcb
->superblock
.num_devices
, ALLOC_TAG
);
3569 if (!Vcb
->devices
) {
3570 ERR("out of memory\n");
3571 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3575 Vcb
->devices
[0].devobj
= DeviceToMount
;
3576 RtlCopyMemory(&Vcb
->devices
[0].devitem
, &Vcb
->superblock
.dev_item
, sizeof(DEV_ITEM
));
3577 init_device(Vcb
, &Vcb
->devices
[0], FALSE
);
3578 Vcb
->devices
[0].length
= gli
.Length
.QuadPart
;
3580 if (Vcb
->superblock
.num_devices
> 1)
3581 RtlZeroMemory(&Vcb
->devices
[1], sizeof(DEV_ITEM
) * (Vcb
->superblock
.num_devices
- 1));
3583 Vcb
->devices_loaded
= 1;
3585 TRACE("DeviceToMount = %p\n", DeviceToMount
);
3586 TRACE("Stack->Parameters.MountVolume.Vpb = %p\n", Stack
->Parameters
.MountVolume
.Vpb
);
3588 NewDeviceObject
->StackSize
= DeviceToMount
->StackSize
+ 1;
3589 NewDeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
3591 InitializeListHead(&Vcb
->roots
);
3592 InitializeListHead(&Vcb
->drop_roots
);
3594 Vcb
->log_to_phys_loaded
= FALSE
;
3596 Vcb
->max_inline
= Vcb
->superblock
.node_size
/ 2;
3598 add_root(Vcb
, BTRFS_ROOT_CHUNK
, Vcb
->superblock
.chunk_tree_addr
, NULL
);
3600 if (!Vcb
->chunk_root
) {
3601 ERR("Could not load chunk root.\n");
3602 Status
= STATUS_INTERNAL_ERROR
;
3606 InitializeListHead(&Vcb
->sys_chunks
);
3607 Status
= load_sys_chunks(Vcb
);
3608 if (!NT_SUCCESS(Status
)) {
3609 ERR("load_sys_chunks returned %08x\n", Status
);
3613 InitializeListHead(&Vcb
->chunks
);
3614 InitializeListHead(&Vcb
->chunks_changed
);
3615 InitializeListHead(&Vcb
->trees
);
3616 InitializeListHead(&Vcb
->all_fcbs
);
3617 InitializeListHead(&Vcb
->dirty_fcbs
);
3618 InitializeListHead(&Vcb
->dirty_filerefs
);
3619 InitializeListHead(&Vcb
->shared_extents
);
3620 InitializeListHead(&Vcb
->sector_checksums
);
3622 KeInitializeSpinLock(&Vcb
->dirty_fcbs_lock
);
3623 KeInitializeSpinLock(&Vcb
->dirty_filerefs_lock
);
3624 KeInitializeSpinLock(&Vcb
->shared_extents_lock
);
3626 InitializeListHead(&Vcb
->DirNotifyList
);
3628 FsRtlNotifyInitializeSync(&Vcb
->NotifySync
);
3630 Status
= load_chunk_root(Vcb
);
3631 if (!NT_SUCCESS(Status
)) {
3632 ERR("load_chunk_root returned %08x\n", Status
);
3636 if (Vcb
->superblock
.num_devices
> 1) {
3637 if (Vcb
->devices_loaded
< Vcb
->superblock
.num_devices
) {
3638 ERR("could not mount as %u device(s) missing\n", Vcb
->superblock
.num_devices
- Vcb
->devices_loaded
);
3640 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3642 Status
= STATUS_INTERNAL_ERROR
;
3646 if (!raid_generations_okay(Vcb
)) {
3647 ERR("could not mount as generation mismatch\n");
3649 IoRaiseInformationalHardError(IO_ERR_INTERNAL_ERROR
, NULL
, NULL
);
3651 Status
= STATUS_INTERNAL_ERROR
;
3656 add_root(Vcb
, BTRFS_ROOT_ROOT
, Vcb
->superblock
.root_tree_addr
, NULL
);
3658 if (!Vcb
->root_root
) {
3659 ERR("Could not load root of roots.\n");
3660 Status
= STATUS_INTERNAL_ERROR
;
3664 Status
= look_for_roots(Vcb
);
3665 if (!NT_SUCCESS(Status
)) {
3666 ERR("look_for_roots returned %08x\n", Status
);
3670 Status
= find_chunk_usage(Vcb
);
3671 if (!NT_SUCCESS(Status
)) {
3672 ERR("find_chunk_usage returned %08x\n", Status
);
3676 // We've already increased the generation by one
3677 if (!Vcb
->readonly
&& Vcb
->superblock
.generation
- 1 != Vcb
->superblock
.cache_generation
) {
3678 WARN("generation was %llx, free-space cache generation was %llx; clearing cache...\n", Vcb
->superblock
.generation
- 1, Vcb
->superblock
.cache_generation
);
3679 Status
= clear_free_space_cache(Vcb
);
3680 if (!NT_SUCCESS(Status
)) {
3681 ERR("clear_free_space_cache returned %08x\n", Status
);
3686 Vcb
->volume_fcb
= create_fcb();
3687 if (!Vcb
->volume_fcb
) {
3688 ERR("out of memory\n");
3689 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3693 Vcb
->volume_fcb
->Vcb
= Vcb
;
3694 Vcb
->volume_fcb
->sd
= NULL
;
3696 root_fcb
= create_fcb();
3698 ERR("out of memory\n");
3699 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3703 root_fcb
->Vcb
= Vcb
;
3704 root_fcb
->inode
= SUBVOL_ROOT_INODE
;
3705 root_fcb
->type
= BTRFS_TYPE_DIRECTORY
;
3707 #ifdef DEBUG_FCB_REFCOUNTS
3708 WARN("volume FCB = %p\n", Vcb
->volume_fcb
);
3709 WARN("root FCB = %p\n", root_fcb
);
3712 root_fcb
->subvol
= find_default_subvol(Vcb
);
3714 if (!root_fcb
->subvol
) {
3715 ERR("could not find top subvol\n");
3716 Status
= STATUS_INTERNAL_ERROR
;
3720 searchkey
.obj_id
= root_fcb
->inode
;
3721 searchkey
.obj_type
= TYPE_INODE_ITEM
;
3722 searchkey
.offset
= 0xffffffffffffffff;
3724 Status
= find_item(Vcb
, root_fcb
->subvol
, &tp
, &searchkey
, FALSE
);
3725 if (!NT_SUCCESS(Status
)) {
3726 ERR("error - find_item returned %08x\n", Status
);
3730 if (tp
.item
->key
.obj_id
!= searchkey
.obj_id
|| tp
.item
->key
.obj_type
!= searchkey
.obj_type
) {
3731 ERR("couldn't find INODE_ITEM for root directory\n");
3732 Status
= STATUS_INTERNAL_ERROR
;
3736 if (tp
.item
->size
> 0)
3737 RtlCopyMemory(&root_fcb
->inode_item
, tp
.item
->data
, min(sizeof(INODE_ITEM
), tp
.item
->size
));
3739 fcb_get_sd(root_fcb
, NULL
);
3741 root_fcb
->atts
= get_file_attributes(Vcb
, &root_fcb
->inode_item
, root_fcb
->subvol
, root_fcb
->inode
, root_fcb
->type
, FALSE
, FALSE
);
3743 Vcb
->root_fileref
= create_fileref();
3744 if (!Vcb
->root_fileref
) {
3745 ERR("out of memory\n");
3746 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3750 Vcb
->root_fileref
->fcb
= root_fcb
;
3751 InsertTailList(&root_fcb
->subvol
->fcbs
, &root_fcb
->list_entry
);
3752 InsertTailList(&Vcb
->all_fcbs
, &root_fcb
->list_entry_all
);
3754 root_fcb
->fileref
= Vcb
->root_fileref
;
3756 root_ccb
= ExAllocatePoolWithTag(PagedPool
, sizeof(ccb
), ALLOC_TAG
);
3758 ERR("out of memory\n");
3759 Status
= STATUS_INSUFFICIENT_RESOURCES
;
3763 Vcb
->root_file
= IoCreateStreamFileObject(NULL
, DeviceToMount
);
3764 Vcb
->root_file
->FsContext
= root_fcb
;
3766 RtlZeroMemory(root_ccb
, sizeof(ccb
));
3767 root_ccb
->NodeType
= BTRFS_NODE_TYPE_CCB
;
3768 root_ccb
->NodeSize
= sizeof(ccb
);
3770 Vcb
->root_file
->FsContext
= root_ccb
;
3772 for (i
= 0; i
< Vcb
->superblock
.num_devices
; i
++) {
3773 Status
= find_disk_holes(Vcb
, &Vcb
->devices
[i
]);
3774 if (!NT_SUCCESS(Status
)) {
3775 ERR("find_disk_holes returned %08x\n", Status
);
3782 KeInitializeSpinLock(&Vcb
->FcbListLock
);
3784 NewDeviceObject
->Vpb
= Stack
->Parameters
.MountVolume
.Vpb
;
3785 Stack
->Parameters
.MountVolume
.Vpb
->DeviceObject
= NewDeviceObject
;
3786 Stack
->Parameters
.MountVolume
.Vpb
->Flags
|= VPB_MOUNTED
;
3787 NewDeviceObject
->Vpb
->VolumeLabelLength
= 4; // FIXME
3788 NewDeviceObject
->Vpb
->VolumeLabel
[0] = '?';
3789 NewDeviceObject
->Vpb
->VolumeLabel
[1] = 0;
3790 NewDeviceObject
->Vpb
->ReferenceCount
++; // FIXME - should we deref this at any point?
3791 Vcb
->Vpb
= NewDeviceObject
->Vpb
;
3793 KeInitializeEvent(&Vcb
->flush_thread_finished
, NotificationEvent
, FALSE
);
3795 Status
= PsCreateSystemThread(&Vcb
->flush_thread_handle
, 0, NULL
, NULL
, NULL
, flush_thread
, NewDeviceObject
);
3796 if (!NT_SUCCESS(Status
)) {
3797 ERR("PsCreateSystemThread returned %08x\n", Status
);
3801 Status
= create_worker_threads(NewDeviceObject
);
3802 if (!NT_SUCCESS(Status
)) {
3803 ERR("create_worker_threads returned %08x\n", Status
);
3807 Status
= registry_mark_volume_mounted(&Vcb
->superblock
.uuid
);
3808 if (!NT_SUCCESS(Status
))
3809 WARN("registry_mark_volume_mounted returned %08x\n", Status
);
3811 Status
= STATUS_SUCCESS
;
3815 ExReleaseResourceLite(&Vcb
->load_lock
);
3818 if (!NT_SUCCESS(Status
)) {
3821 ObDereferenceObject(Vcb
->root_file
);
3822 else if (Vcb
->root_fileref
)
3823 free_fileref(Vcb
->root_fileref
);
3827 if (Vcb
->volume_fcb
)
3828 free_fcb(Vcb
->volume_fcb
);
3830 ExDeleteResourceLite(&Vcb
->tree_lock
);
3831 ExDeleteResourceLite(&Vcb
->load_lock
);
3832 ExDeleteResourceLite(&Vcb
->fcb_lock
);
3833 ExDeleteResourceLite(&Vcb
->DirResource
);
3834 ExDeleteResourceLite(&Vcb
->checksum_lock
);
3835 ExDeleteResourceLite(&Vcb
->chunk_lock
);
3838 ExFreePoolWithTag(Vcb
->devices
, ALLOC_TAG
);
3840 RemoveEntryList(&Vcb
->list_entry
);
3843 if (NewDeviceObject
)
3844 IoDeleteDevice(NewDeviceObject
);
3846 FsRtlNotifyVolumeEvent(Vcb
->root_file
, FSRTL_VOLUME_MOUNT
);
3848 TRACE("mount_vol done (status: %lx)\n", Status
);
3853 static NTSTATUS STDCALL
drv_file_system_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3854 PIO_STACK_LOCATION IrpSp
;
3856 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3859 TRACE("file system control\n");
3861 FsRtlEnterFileSystem();
3863 top_level
= is_top_level(Irp
);
3865 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
3866 Status
= part0_passthrough(DeviceObject
, Irp
);
3870 Status
= STATUS_NOT_IMPLEMENTED
;
3872 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
3874 Irp
->IoStatus
.Information
= 0;
3876 switch (IrpSp
->MinorFunction
) {
3877 case IRP_MN_MOUNT_VOLUME
:
3878 TRACE("IRP_MN_MOUNT_VOLUME\n");
3880 Status
= mount_vol(DeviceObject
, Irp
);
3883 case IRP_MN_KERNEL_CALL
:
3884 TRACE("IRP_MN_KERNEL_CALL\n");
3886 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, FALSE
);
3889 case IRP_MN_USER_FS_REQUEST
:
3890 TRACE("IRP_MN_USER_FS_REQUEST\n");
3892 Status
= fsctl_request(DeviceObject
, Irp
, IrpSp
->Parameters
.FileSystemControl
.FsControlCode
, TRUE
);
3895 case IRP_MN_VERIFY_VOLUME
:
3896 FIXME("STUB: IRP_MN_VERIFY_VOLUME\n");
3903 Irp
->IoStatus
.Status
= Status
;
3905 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3909 IoSetTopLevelIrp(NULL
);
3911 FsRtlExitFileSystem();
3916 static NTSTATUS STDCALL
drv_lock_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3918 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3919 fcb
* fcb
= IrpSp
->FileObject
->FsContext
;
3920 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
3923 FsRtlEnterFileSystem();
3925 top_level
= is_top_level(Irp
);
3927 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
3928 Status
= part0_passthrough(DeviceObject
, Irp
);
3932 TRACE("lock control\n");
3934 Status
= FsRtlProcessFileLock(&fcb
->lock
, Irp
, NULL
);
3936 fcb
->Header
.IsFastIoPossible
= fast_io_possible(fcb
);
3940 IoSetTopLevelIrp(NULL
);
3942 FsRtlExitFileSystem();
3947 NTSTATUS
part0_passthrough(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3949 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
3951 IoSkipCurrentIrpStackLocation(Irp
);
3953 Status
= IoCallDriver(p0de
->devobj
, Irp
);
3958 static NTSTATUS
part0_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
3960 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
3961 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
3963 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
3965 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
3966 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
:
3968 MOUNTDEV_UNIQUE_ID
* mduid
;
3970 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
)) {
3971 Status
= STATUS_BUFFER_TOO_SMALL
;
3972 Irp
->IoStatus
.Status
= Status
;
3973 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
3974 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3978 mduid
= Irp
->AssociatedIrp
.SystemBuffer
;
3979 mduid
->UniqueIdLength
= sizeof(BTRFS_UUID
);
3981 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
) {
3982 Status
= STATUS_BUFFER_OVERFLOW
;
3983 Irp
->IoStatus
.Status
= Status
;
3984 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
3985 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3989 RtlCopyMemory(mduid
->UniqueId
, &p0de
->uuid
, sizeof(BTRFS_UUID
));
3991 Status
= STATUS_SUCCESS
;
3992 Irp
->IoStatus
.Status
= Status
;
3993 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
;
3994 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
3999 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
:
4001 PMOUNTDEV_NAME name
;
4003 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
)) {
4004 Status
= STATUS_BUFFER_TOO_SMALL
;
4005 Irp
->IoStatus
.Status
= Status
;
4006 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4007 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4011 name
= Irp
->AssociatedIrp
.SystemBuffer
;
4012 name
->NameLength
= p0de
->name
.Length
;
4014 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
) {
4015 Status
= STATUS_BUFFER_OVERFLOW
;
4016 Irp
->IoStatus
.Status
= Status
;
4017 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
4018 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4022 RtlCopyMemory(name
->Name
, p0de
->name
.Buffer
, p0de
->name
.Length
);
4024 Status
= STATUS_SUCCESS
;
4025 Irp
->IoStatus
.Status
= Status
;
4026 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
) - 1 + name
->NameLength
;
4027 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4033 IoSkipCurrentIrpStackLocation(Irp
);
4035 Status
= IoCallDriver(p0de
->devobj
, Irp
);
4037 TRACE("returning %08x\n", Status
);
4042 static NTSTATUS STDCALL
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4044 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
4045 PFILE_OBJECT FileObject
= IrpSp
->FileObject
;
4046 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4050 FsRtlEnterFileSystem();
4052 top_level
= is_top_level(Irp
);
4054 Irp
->IoStatus
.Information
= 0;
4056 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4057 Status
= part0_device_control(DeviceObject
, Irp
);
4061 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
4064 ERR("FileObject was NULL\n");
4065 Status
= STATUS_INVALID_PARAMETER
;
4069 fcb
= FileObject
->FsContext
;
4072 ERR("FCB was NULL\n");
4073 Status
= STATUS_INVALID_PARAMETER
;
4077 if (fcb
!= Vcb
->volume_fcb
) {
4078 Status
= STATUS_NOT_IMPLEMENTED
;
4082 IoSkipCurrentIrpStackLocation(Irp
);
4084 Status
= IoCallDriver(Vcb
->devices
[0].devobj
, Irp
);
4089 Irp
->IoStatus
.Status
= Status
;
4091 if (Status
!= STATUS_PENDING
)
4092 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
4096 IoSetTopLevelIrp(NULL
);
4098 FsRtlExitFileSystem();
4103 static NTSTATUS STDCALL
drv_shutdown(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
4106 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
4108 TRACE("shutdown\n");
4110 FsRtlEnterFileSystem();
4112 top_level
= is_top_level(Irp
);
4114 if (Vcb
&& Vcb
->type
== VCB_TYPE_PARTITION0
) {
4115 Status
= part0_passthrough(DeviceObject
, Irp
);
4119 Status
= STATUS_SUCCESS
;
4121 while (!IsListEmpty(&VcbList
)) {
4122 LIST_ENTRY
* le
= RemoveHeadList(&VcbList
);
4123 Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
4125 TRACE("shutting down Vcb %p\n", Vcb
);
4130 Irp
->IoStatus
.Status
= Status
;
4131 Irp
->IoStatus
.Information
= 0;
4133 IoCompleteRequest( Irp
, IO_NO_INCREMENT
);
4137 IoSetTopLevelIrp(NULL
);
4139 FsRtlExitFileSystem();
4144 BOOL
is_file_name_valid(PUNICODE_STRING us
) {
4147 if (us
->Length
< sizeof(WCHAR
))
4150 if (us
->Length
> 255 * sizeof(WCHAR
))
4153 for (i
= 0; i
< us
->Length
/ sizeof(WCHAR
); i
++) {
4154 if (us
->Buffer
[i
] == '/' || us
->Buffer
[i
] == '<' || us
->Buffer
[i
] == '>' || us
->Buffer
[i
] == ':' || us
->Buffer
[i
] == '"' ||
4155 us
->Buffer
[i
] == '|' || us
->Buffer
[i
] == '?' || us
->Buffer
[i
] == '*' || (us
->Buffer
[i
] >= 1 && us
->Buffer
[i
] <= 31))
4159 if (us
->Buffer
[0] == '.' && (us
->Length
== sizeof(WCHAR
) || (us
->Length
== 2 * sizeof(WCHAR
) && us
->Buffer
[1] == '.')))
4166 static void STDCALL
init_serial() {
4169 Status
= IoGetDeviceObjectPointer(&log_device
, FILE_WRITE_DATA
, &comfo
, &comdo
);
4170 if (!NT_SUCCESS(Status
)) {
4171 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
4177 static void STDCALL
check_cpu() {
4178 unsigned int cpuInfo
[4];
4180 __get_cpuid(1, &cpuInfo
[0], &cpuInfo
[1], &cpuInfo
[2], &cpuInfo
[3]);
4181 have_sse42
= cpuInfo
[2] & bit_SSE4_2
;
4183 __cpuid(cpuInfo
, 1);
4184 have_sse42
= cpuInfo
[2] & (1 << 20);
4188 TRACE("SSE4.2 is supported\n");
4190 TRACE("SSE4.2 not supported\n");
4195 static void init_logging() {
4196 if (log_device
.Length
> 0)
4198 else if (log_file
.Length
> 0) {
4200 OBJECT_ATTRIBUTES oa
;
4201 IO_STATUS_BLOCK iosb
;
4206 InitializeObjectAttributes(&oa
, &log_file
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
4208 Status
= ZwCreateFile(&log_handle
, FILE_WRITE_DATA
, &oa
, &iosb
, NULL
, FILE_ATTRIBUTE_NORMAL
, FILE_SHARE_READ
,
4209 FILE_OPEN_IF
, FILE_NON_DIRECTORY_FILE
| FILE_WRITE_THROUGH
| FILE_SYNCHRONOUS_IO_ALERT
, NULL
, 0);
4211 if (!NT_SUCCESS(Status
)) {
4212 ERR("ZwCreateFile returned %08x\n", Status
);
4216 if (iosb
.Information
== FILE_OPENED
) { // already exists
4217 FILE_STANDARD_INFORMATION fsi
;
4218 FILE_POSITION_INFORMATION fpi
;
4220 static char delim
[] = "\n---\n";
4222 // move to end of file
4224 Status
= ZwQueryInformationFile(log_handle
, &iosb
, &fsi
, sizeof(FILE_STANDARD_INFORMATION
), FileStandardInformation
);
4226 if (!NT_SUCCESS(Status
)) {
4227 ERR("ZwQueryInformationFile returned %08x\n", Status
);
4231 fpi
.CurrentByteOffset
= fsi
.EndOfFile
;
4233 Status
= ZwSetInformationFile(log_handle
, &iosb
, &fpi
, sizeof(FILE_POSITION_INFORMATION
), FilePositionInformation
);
4235 if (!NT_SUCCESS(Status
)) {
4236 ERR("ZwSetInformationFile returned %08x\n", Status
);
4240 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, delim
, strlen(delim
), NULL
, NULL
);
4242 if (!NT_SUCCESS(Status
)) {
4243 ERR("ZwWriteFile returned %08x\n", Status
);
4248 dateline
= ExAllocatePoolWithTag(PagedPool
, 256, ALLOC_TAG
);
4251 ERR("out of memory\n");
4255 KeQuerySystemTime(&time
);
4257 RtlTimeToTimeFields(&time
, &tf
);
4259 sprintf(dateline
, "Starting logging at %04u-%02u-%02u %02u:%02u:%02u\n", tf
.Year
, tf
.Month
, tf
.Day
, tf
.Hour
, tf
.Minute
, tf
.Second
);
4261 Status
= ZwWriteFile(log_handle
, NULL
, NULL
, NULL
, &iosb
, dateline
, strlen(dateline
), NULL
, NULL
);
4263 if (!NT_SUCCESS(Status
)) {
4264 ERR("ZwWriteFile returned %08x\n", Status
);
4268 ExFreePool(dateline
);
4273 NTSTATUS STDCALL
DriverEntry(PDRIVER_OBJECT DriverObject
, PUNICODE_STRING RegistryPath
) {
4275 PDEVICE_OBJECT DeviceObject
;
4276 UNICODE_STRING device_nameW
;
4277 UNICODE_STRING dosdevice_nameW
;
4279 InitializeListHead(&uid_map_list
);
4281 log_device
.Buffer
= NULL
;
4282 log_device
.Length
= log_device
.MaximumLength
= 0;
4283 log_file
.Buffer
= NULL
;
4284 log_file
.Length
= log_file
.MaximumLength
= 0;
4286 read_registry(RegistryPath
);
4289 if (debug_log_level
> 0)
4295 TRACE("DriverEntry\n");
4297 registry_path
.Length
= registry_path
.MaximumLength
= RegistryPath
->Length
;
4298 registry_path
.Buffer
= ExAllocatePoolWithTag(PagedPool
, registry_path
.Length
, ALLOC_TAG
);
4300 if (!registry_path
.Buffer
) {
4301 ERR("out of memory\n");
4302 return STATUS_INSUFFICIENT_RESOURCES
;
4305 RtlCopyMemory(registry_path
.Buffer
, RegistryPath
->Buffer
, registry_path
.Length
);
4311 // TRACE("check CRC32C: %08x\n", calc_crc32c((UINT8*)"123456789", 9)); // should be e3069283
4313 drvobj
= DriverObject
;
4315 DriverObject
->DriverUnload
= DriverUnload
;
4317 DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = (PDRIVER_DISPATCH
)drv_create
;
4318 DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = (PDRIVER_DISPATCH
)drv_close
;
4319 DriverObject
->MajorFunction
[IRP_MJ_READ
] = (PDRIVER_DISPATCH
)drv_read
;
4320 DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = (PDRIVER_DISPATCH
)drv_write
;
4321 DriverObject
->MajorFunction
[IRP_MJ_QUERY_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_information
;
4322 DriverObject
->MajorFunction
[IRP_MJ_SET_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_information
;
4323 DriverObject
->MajorFunction
[IRP_MJ_QUERY_EA
] = (PDRIVER_DISPATCH
)drv_query_ea
;
4324 DriverObject
->MajorFunction
[IRP_MJ_SET_EA
] = (PDRIVER_DISPATCH
)drv_set_ea
;
4325 DriverObject
->MajorFunction
[IRP_MJ_FLUSH_BUFFERS
] = (PDRIVER_DISPATCH
)drv_flush_buffers
;
4326 DriverObject
->MajorFunction
[IRP_MJ_QUERY_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_query_volume_information
;
4327 DriverObject
->MajorFunction
[IRP_MJ_SET_VOLUME_INFORMATION
] = (PDRIVER_DISPATCH
)drv_set_volume_information
;
4328 DriverObject
->MajorFunction
[IRP_MJ_CLEANUP
] = (PDRIVER_DISPATCH
)drv_cleanup
;
4329 DriverObject
->MajorFunction
[IRP_MJ_DIRECTORY_CONTROL
] = (PDRIVER_DISPATCH
)drv_directory_control
;
4330 DriverObject
->MajorFunction
[IRP_MJ_FILE_SYSTEM_CONTROL
] = (PDRIVER_DISPATCH
)drv_file_system_control
;
4331 DriverObject
->MajorFunction
[IRP_MJ_LOCK_CONTROL
] = (PDRIVER_DISPATCH
)drv_lock_control
;
4332 DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = (PDRIVER_DISPATCH
)drv_device_control
;
4333 DriverObject
->MajorFunction
[IRP_MJ_SHUTDOWN
] = (PDRIVER_DISPATCH
)drv_shutdown
;
4334 DriverObject
->MajorFunction
[IRP_MJ_PNP
] = (PDRIVER_DISPATCH
)drv_pnp
;
4335 DriverObject
->MajorFunction
[IRP_MJ_QUERY_SECURITY
] = (PDRIVER_DISPATCH
)drv_query_security
;
4336 DriverObject
->MajorFunction
[IRP_MJ_SET_SECURITY
] = (PDRIVER_DISPATCH
)drv_set_security
;
4338 init_fast_io_dispatch(&DriverObject
->FastIoDispatch
);
4340 device_nameW
.Buffer
= device_name
;
4341 device_nameW
.Length
= device_nameW
.MaximumLength
= (USHORT
)wcslen(device_name
) * sizeof(WCHAR
);
4342 dosdevice_nameW
.Buffer
= dosdevice_name
;
4343 dosdevice_nameW
.Length
= dosdevice_nameW
.MaximumLength
= (USHORT
)wcslen(dosdevice_name
) * sizeof(WCHAR
);
4345 Status
= IoCreateDevice(DriverObject
, 0, &device_nameW
, FILE_DEVICE_DISK_FILE_SYSTEM
, FILE_DEVICE_SECURE_OPEN
, FALSE
, &DeviceObject
);
4346 if (!NT_SUCCESS(Status
)) {
4347 ERR("IoCreateDevice returned %08x\n", Status
);
4351 devobj
= DeviceObject
;
4353 DeviceObject
->Flags
&= ~DO_DEVICE_INITIALIZING
;
4355 Status
= IoCreateSymbolicLink(&dosdevice_nameW
, &device_nameW
);
4356 if (!NT_SUCCESS(Status
)) {
4357 ERR("IoCreateSymbolicLink returned %08x\n", Status
);
4361 Status
= init_cache();
4362 if (!NT_SUCCESS(Status
)) {
4363 ERR("init_cache returned %08x\n", Status
);
4367 InitializeListHead(&volumes
);
4368 look_for_vols(DriverObject
, &volumes
);
4370 InitializeListHead(&VcbList
);
4371 ExInitializeResourceLite(&global_loading_lock
);
4373 IoRegisterFileSystem(DeviceObject
);
4375 return STATUS_SUCCESS
;