1 /* Copyright (c) Mark Harmstone 2016
3 * This file is part of WinBtrfs.
5 * WinBtrfs is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public Licence as published by
7 * the Free Software Foundation, either version 3 of the Licence, or
8 * (at your option) any later version.
10 * WinBtrfs is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public Licence for more details.
15 * You should have received a copy of the GNU Lesser General Public Licence
16 * along with WinBtrfs. If not, see <http://www.gnu.org/licenses/>. */
18 #include "btrfs_drv.h"
26 extern LIST_ENTRY VcbList
;
27 extern ERESOURCE global_loading_lock
;
29 static NTSTATUS
part0_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
31 part0_device_extension
* p0de
= DeviceObject
->DeviceExtension
;
32 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
34 TRACE("control code = %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
36 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
37 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID
:
39 MOUNTDEV_UNIQUE_ID
* mduid
;
41 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
)) {
42 Status
= STATUS_BUFFER_TOO_SMALL
;
43 Irp
->IoStatus
.Status
= Status
;
44 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
45 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
49 mduid
= Irp
->AssociatedIrp
.SystemBuffer
;
50 mduid
->UniqueIdLength
= sizeof(BTRFS_UUID
);
52 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
) {
53 Status
= STATUS_BUFFER_OVERFLOW
;
54 Irp
->IoStatus
.Status
= Status
;
55 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
);
56 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
60 RtlCopyMemory(mduid
->UniqueId
, &p0de
->uuid
, sizeof(BTRFS_UUID
));
62 Status
= STATUS_SUCCESS
;
63 Irp
->IoStatus
.Status
= Status
;
64 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_UNIQUE_ID
) - 1 + mduid
->UniqueIdLength
;
65 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
70 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
:
74 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_NAME
)) {
75 Status
= STATUS_BUFFER_TOO_SMALL
;
76 Irp
->IoStatus
.Status
= Status
;
77 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
78 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
82 name
= Irp
->AssociatedIrp
.SystemBuffer
;
83 name
->NameLength
= p0de
->name
.Length
;
85 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< offsetof(MOUNTDEV_NAME
, Name
[0]) + name
->NameLength
) {
86 Status
= STATUS_BUFFER_OVERFLOW
;
87 Irp
->IoStatus
.Status
= Status
;
88 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_NAME
);
89 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
93 RtlCopyMemory(name
->Name
, p0de
->name
.Buffer
, p0de
->name
.Length
);
95 Status
= STATUS_SUCCESS
;
96 Irp
->IoStatus
.Status
= Status
;
97 Irp
->IoStatus
.Information
= offsetof(MOUNTDEV_NAME
, Name
[0]) + name
->NameLength
;
98 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
104 IoSkipCurrentIrpStackLocation(Irp
);
106 Status
= IoCallDriver(p0de
->devobj
, Irp
);
108 TRACE("returning %08x\n", Status
);
113 static NTSTATUS
mountdev_query_stable_guid(device_extension
* Vcb
, PIRP Irp
) {
114 MOUNTDEV_STABLE_GUID
* msg
= Irp
->UserBuffer
;
115 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
117 TRACE("IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
119 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_STABLE_GUID
))
120 return STATUS_INVALID_PARAMETER
;
122 RtlCopyMemory(&msg
->StableGuid
, &Vcb
->superblock
.uuid
, sizeof(GUID
));
124 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_STABLE_GUID
);
126 return STATUS_SUCCESS
;
129 static NTSTATUS
get_partition_info_ex(device_extension
* Vcb
, PIRP Irp
) {
131 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
132 PARTITION_INFORMATION_EX
* piex
;
134 TRACE("IOCTL_DISK_GET_PARTITION_INFO_EX\n");
136 Status
= dev_ioctl(Vcb
->Vpb
->RealDevice
, IOCTL_DISK_GET_PARTITION_INFO_EX
, NULL
, 0,
137 Irp
->UserBuffer
, IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
, TRUE
, &Irp
->IoStatus
);
138 if (!NT_SUCCESS(Status
))
141 piex
= (PARTITION_INFORMATION_EX
*)Irp
->UserBuffer
;
143 if (piex
->PartitionStyle
== PARTITION_STYLE_MBR
) {
144 piex
->Mbr
.PartitionType
= PARTITION_IFS
;
145 piex
->Mbr
.RecognizedPartition
= TRUE
;
146 } else if (piex
->PartitionStyle
== PARTITION_STYLE_GPT
) {
147 piex
->Gpt
.PartitionType
= PARTITION_BASIC_DATA_GUID
;
150 return STATUS_SUCCESS
;
153 static NTSTATUS
is_writable(device_extension
* Vcb
, PIRP Irp
) {
154 TRACE("IOCTL_DISK_IS_WRITABLE\n");
156 return Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_SUCCESS
;
159 static NTSTATUS
query_filesystems(void* data
, ULONG length
) {
161 LIST_ENTRY
*le
, *le2
;
162 btrfs_filesystem
* bfs
= NULL
;
165 ExAcquireResourceSharedLite(&global_loading_lock
, TRUE
);
167 if (IsListEmpty(&VcbList
)) {
168 if (length
< sizeof(btrfs_filesystem
)) {
169 Status
= STATUS_BUFFER_OVERFLOW
;
172 RtlZeroMemory(data
, sizeof(btrfs_filesystem
));
173 Status
= STATUS_SUCCESS
;
180 while (le
!= &VcbList
) {
181 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
182 btrfs_filesystem_device
* bfd
;
185 bfs
->next_entry
= itemsize
;
186 bfs
= (btrfs_filesystem
*)((UINT8
*)bfs
+ itemsize
);
190 if (length
< offsetof(btrfs_filesystem
, device
)) {
191 Status
= STATUS_BUFFER_OVERFLOW
;
195 itemsize
= offsetof(btrfs_filesystem
, device
);
196 length
-= offsetof(btrfs_filesystem
, device
);
199 RtlCopyMemory(&bfs
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
201 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, TRUE
);
203 bfs
->num_devices
= Vcb
->superblock
.num_devices
;
207 le2
= Vcb
->devices
.Flink
;
208 while (le2
!= &Vcb
->devices
) {
209 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
213 bfd
= (btrfs_filesystem_device
*)((UINT8
*)bfd
+ offsetof(btrfs_filesystem_device
, name
[0]) + bfd
->name_length
);
217 if (length
< offsetof(btrfs_filesystem_device
, name
[0])) {
218 ExReleaseResourceLite(&Vcb
->tree_lock
);
219 Status
= STATUS_BUFFER_OVERFLOW
;
223 itemsize
+= offsetof(btrfs_filesystem_device
, name
[0]);
224 length
-= offsetof(btrfs_filesystem_device
, name
[0]);
226 RtlCopyMemory(&bfd
->uuid
, &dev
->devitem
.device_uuid
, sizeof(BTRFS_UUID
));
228 Status
= dev_ioctl(dev
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), TRUE
, NULL
);
229 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
230 ExReleaseResourceLite(&Vcb
->tree_lock
);
231 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
235 if (mdn
.NameLength
> length
) {
236 ExReleaseResourceLite(&Vcb
->tree_lock
);
237 Status
= STATUS_BUFFER_OVERFLOW
;
241 Status
= dev_ioctl(dev
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &bfd
->name_length
, offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
, TRUE
, NULL
);
242 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
243 ExReleaseResourceLite(&Vcb
->tree_lock
);
244 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
248 itemsize
+= bfd
->name_length
;
249 length
-= bfd
->name_length
;
254 ExReleaseResourceLite(&Vcb
->tree_lock
);
259 Status
= STATUS_SUCCESS
;
262 ExReleaseResourceLite(&global_loading_lock
);
267 static NTSTATUS
control_ioctl(PIRP Irp
) {
268 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
271 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
272 case IOCTL_BTRFS_QUERY_FILESYSTEMS
:
273 Status
= query_filesystems(map_user_buffer(Irp
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
277 TRACE("unhandled ioctl %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
278 Status
= STATUS_NOT_IMPLEMENTED
;
285 NTSTATUS STDCALL
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
287 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
288 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
291 FsRtlEnterFileSystem();
293 top_level
= is_top_level(Irp
);
295 Irp
->IoStatus
.Information
= 0;
298 if (Vcb
->type
== VCB_TYPE_PARTITION0
) {
299 Status
= part0_device_control(DeviceObject
, Irp
);
301 } else if (Vcb
->type
== VCB_TYPE_CONTROL
) {
302 Status
= control_ioctl(Irp
);
306 Status
= STATUS_INVALID_PARAMETER
;
310 if (!IrpSp
->FileObject
|| IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
) {
311 Status
= STATUS_INVALID_PARAMETER
;
315 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
316 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID
:
317 Status
= mountdev_query_stable_guid(Vcb
, Irp
);
320 case IOCTL_DISK_GET_PARTITION_INFO_EX
:
321 Status
= get_partition_info_ex(Vcb
, Irp
);
324 case IOCTL_DISK_IS_WRITABLE
:
325 Status
= is_writable(Vcb
, Irp
);
329 TRACE("unhandled control code %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
333 IoSkipCurrentIrpStackLocation(Irp
);
335 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
340 Irp
->IoStatus
.Status
= Status
;
342 if (Status
!= STATUS_PENDING
)
343 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
347 IoSetTopLevelIrp(NULL
);
349 FsRtlExitFileSystem();