1 /* Copyright (c) Mark Harmstone 2016-17
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"
23 extern PDRIVER_OBJECT drvobj
;
24 extern LIST_ENTRY VcbList
;
25 extern ERESOURCE global_loading_lock
;
27 static NTSTATUS
mountdev_query_stable_guid(device_extension
* Vcb
, PIRP Irp
) {
28 MOUNTDEV_STABLE_GUID
* msg
= Irp
->UserBuffer
;
29 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
31 TRACE("IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
33 if (IrpSp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(MOUNTDEV_STABLE_GUID
))
34 return STATUS_INVALID_PARAMETER
;
36 RtlCopyMemory(&msg
->StableGuid
, &Vcb
->superblock
.uuid
, sizeof(GUID
));
38 Irp
->IoStatus
.Information
= sizeof(MOUNTDEV_STABLE_GUID
);
40 return STATUS_SUCCESS
;
43 static NTSTATUS
is_writable(device_extension
* Vcb
) {
44 TRACE("IOCTL_DISK_IS_WRITABLE\n");
46 return Vcb
->readonly
? STATUS_MEDIA_WRITE_PROTECTED
: STATUS_SUCCESS
;
49 static NTSTATUS
query_filesystems(void* data
, ULONG length
) {
52 btrfs_filesystem
* bfs
= NULL
;
55 ExAcquireResourceSharedLite(&global_loading_lock
, true);
57 if (IsListEmpty(&VcbList
)) {
58 if (length
< sizeof(btrfs_filesystem
)) {
59 Status
= STATUS_BUFFER_OVERFLOW
;
62 RtlZeroMemory(data
, sizeof(btrfs_filesystem
));
63 Status
= STATUS_SUCCESS
;
70 while (le
!= &VcbList
) {
71 device_extension
* Vcb
= CONTAINING_RECORD(le
, device_extension
, list_entry
);
72 btrfs_filesystem_device
* bfd
;
75 bfs
->next_entry
= itemsize
;
76 bfs
= (btrfs_filesystem
*)((uint8_t*)bfs
+ itemsize
);
80 if (length
< offsetof(btrfs_filesystem
, device
)) {
81 Status
= STATUS_BUFFER_OVERFLOW
;
85 itemsize
= offsetof(btrfs_filesystem
, device
);
86 length
-= offsetof(btrfs_filesystem
, device
);
89 RtlCopyMemory(&bfs
->uuid
, &Vcb
->superblock
.uuid
, sizeof(BTRFS_UUID
));
91 ExAcquireResourceSharedLite(&Vcb
->tree_lock
, true);
93 bfs
->num_devices
= (uint32_t)Vcb
->superblock
.num_devices
;
97 le2
= Vcb
->devices
.Flink
;
98 while (le2
!= &Vcb
->devices
) {
99 device
* dev
= CONTAINING_RECORD(le2
, device
, list_entry
);
103 bfd
= (btrfs_filesystem_device
*)((uint8_t*)bfd
+ offsetof(btrfs_filesystem_device
, name
[0]) + bfd
->name_length
);
107 if (length
< offsetof(btrfs_filesystem_device
, name
[0])) {
108 ExReleaseResourceLite(&Vcb
->tree_lock
);
109 Status
= STATUS_BUFFER_OVERFLOW
;
113 itemsize
+= (ULONG
)offsetof(btrfs_filesystem_device
, name
[0]);
114 length
-= (ULONG
)offsetof(btrfs_filesystem_device
, name
[0]);
116 RtlCopyMemory(&bfd
->uuid
, &dev
->devitem
.device_uuid
, sizeof(BTRFS_UUID
));
119 Status
= dev_ioctl(dev
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &mdn
, sizeof(MOUNTDEV_NAME
), true, NULL
);
120 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
121 ExReleaseResourceLite(&Vcb
->tree_lock
);
122 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
126 if (mdn
.NameLength
> length
) {
127 ExReleaseResourceLite(&Vcb
->tree_lock
);
128 Status
= STATUS_BUFFER_OVERFLOW
;
132 Status
= dev_ioctl(dev
->devobj
, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME
, NULL
, 0, &bfd
->name_length
, (ULONG
)offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
.NameLength
, true, NULL
);
133 if (!NT_SUCCESS(Status
) && Status
!= STATUS_BUFFER_OVERFLOW
) {
134 ExReleaseResourceLite(&Vcb
->tree_lock
);
135 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status
);
139 itemsize
+= bfd
->name_length
;
140 length
-= bfd
->name_length
;
143 bfd
->name_length
= 0;
149 ExReleaseResourceLite(&Vcb
->tree_lock
);
154 Status
= STATUS_SUCCESS
;
157 ExReleaseResourceLite(&global_loading_lock
);
162 static NTSTATUS
probe_volume(void* data
, ULONG length
, KPROCESSOR_MODE processor_mode
) {
163 MOUNTDEV_NAME
* mdn
= (MOUNTDEV_NAME
*)data
;
164 UNICODE_STRING path
, pnp_name
;
166 PDEVICE_OBJECT DeviceObject
;
167 PFILE_OBJECT FileObject
;
170 if (length
< sizeof(MOUNTDEV_NAME
))
171 return STATUS_INVALID_PARAMETER
;
173 if (length
< offsetof(MOUNTDEV_NAME
, Name
[0]) + mdn
->NameLength
)
174 return STATUS_INVALID_PARAMETER
;
176 TRACE("%.*S\n", mdn
->NameLength
/ sizeof(WCHAR
), mdn
->Name
);
178 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE
), processor_mode
))
179 return STATUS_PRIVILEGE_NOT_HELD
;
181 path
.Buffer
= mdn
->Name
;
182 path
.Length
= path
.MaximumLength
= mdn
->NameLength
;
184 Status
= IoGetDeviceObjectPointer(&path
, FILE_READ_ATTRIBUTES
, &FileObject
, &DeviceObject
);
185 if (!NT_SUCCESS(Status
)) {
186 ERR("IoGetDeviceObjectPointer returned %08x\n", Status
);
190 Status
= get_device_pnp_name(DeviceObject
, &pnp_name
, &guid
);
191 if (!NT_SUCCESS(Status
)) {
192 ERR("get_device_pnp_name returned %08x\n", Status
);
193 ObDereferenceObject(FileObject
);
197 if (RtlCompareMemory(guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
)) {
198 Status
= dev_ioctl(DeviceObject
, IOCTL_DISK_UPDATE_PROPERTIES
, NULL
, 0, NULL
, 0, true, NULL
);
199 if (!NT_SUCCESS(Status
))
200 WARN("IOCTL_DISK_UPDATE_PROPERTIES returned %08x\n", Status
);
203 ObDereferenceObject(FileObject
);
205 volume_removal(drvobj
, &pnp_name
);
207 if (RtlCompareMemory(guid
, &GUID_DEVINTERFACE_DISK
, sizeof(GUID
)) == sizeof(GUID
))
208 disk_arrival(drvobj
, &pnp_name
);
210 volume_arrival(drvobj
, &pnp_name
);
212 return STATUS_SUCCESS
;
215 static NTSTATUS
control_ioctl(PIRP Irp
) {
216 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
219 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
220 case IOCTL_BTRFS_QUERY_FILESYSTEMS
:
221 Status
= query_filesystems(map_user_buffer(Irp
, NormalPagePriority
), IrpSp
->Parameters
.FileSystemControl
.OutputBufferLength
);
224 case IOCTL_BTRFS_PROBE_VOLUME
:
225 Status
= probe_volume(Irp
->AssociatedIrp
.SystemBuffer
, IrpSp
->Parameters
.FileSystemControl
.InputBufferLength
, Irp
->RequestorMode
);
229 TRACE("unhandled ioctl %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
230 Status
= STATUS_NOT_IMPLEMENTED
;
237 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL
)
238 _Function_class_(DRIVER_DISPATCH
)
239 NTSTATUS __stdcall
drv_device_control(IN PDEVICE_OBJECT DeviceObject
, IN PIRP Irp
) {
241 PIO_STACK_LOCATION IrpSp
= IoGetCurrentIrpStackLocation(Irp
);
242 device_extension
* Vcb
= DeviceObject
->DeviceExtension
;
245 FsRtlEnterFileSystem();
247 top_level
= is_top_level(Irp
);
249 Irp
->IoStatus
.Information
= 0;
252 if (Vcb
->type
== VCB_TYPE_CONTROL
) {
253 Status
= control_ioctl(Irp
);
255 } else if (Vcb
->type
== VCB_TYPE_VOLUME
) {
256 Status
= vol_device_control(DeviceObject
, Irp
);
258 } else if (Vcb
->type
!= VCB_TYPE_FS
) {
259 Status
= STATUS_INVALID_PARAMETER
;
263 Status
= STATUS_INVALID_PARAMETER
;
267 if (!IrpSp
->FileObject
|| IrpSp
->FileObject
->FsContext
!= Vcb
->volume_fcb
) {
268 Status
= STATUS_INVALID_PARAMETER
;
272 switch (IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
) {
273 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID
:
274 Status
= mountdev_query_stable_guid(Vcb
, Irp
);
277 case IOCTL_DISK_IS_WRITABLE
:
278 Status
= is_writable(Vcb
);
282 TRACE("unhandled control code %x\n", IrpSp
->Parameters
.DeviceIoControl
.IoControlCode
);
286 IoSkipCurrentIrpStackLocation(Irp
);
288 Status
= IoCallDriver(Vcb
->Vpb
->RealDevice
, Irp
);
293 Irp
->IoStatus
.Status
= Status
;
295 if (Status
!= STATUS_PENDING
)
296 IoCompleteRequest(Irp
, IO_NO_INCREMENT
);
299 TRACE("returning %08x\n", Status
);
302 IoSetTopLevelIrp(NULL
);
304 FsRtlExitFileSystem();