1 /* Copyright (c) Mark Harmstone 2016-17
2 *
3 * This file is part of WinBtrfs.
4 *
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.
9 *
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.
14 *
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/>. */
17
18 #include "btrfs_drv.h"
19 #include <ntdddisk.h>
20 #include <mountdev.h>
21 #include <diskguid.h>
22
23 extern PDRIVER_OBJECT drvobj;
24 extern LIST_ENTRY VcbList;
25 extern ERESOURCE global_loading_lock;
26
mountdev_query_stable_guid(device_extension * Vcb,PIRP Irp)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);
30
31 TRACE("IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
32
33 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID))
34 return STATUS_INVALID_PARAMETER;
35
36 RtlCopyMemory(&msg->StableGuid, &Vcb->superblock.uuid, sizeof(GUID));
37
38 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
39
40 return STATUS_SUCCESS;
41 }
42
is_writable(device_extension * Vcb)43 static NTSTATUS is_writable(device_extension* Vcb) {
44 TRACE("IOCTL_DISK_IS_WRITABLE\n");
45
46 return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS;
47 }
48
query_filesystems(void * data,ULONG length)49 static NTSTATUS query_filesystems(void* data, ULONG length) {
50 NTSTATUS Status;
51 LIST_ENTRY *le, *le2;
52 btrfs_filesystem* bfs = NULL;
53 ULONG itemsize;
54
55 ExAcquireResourceSharedLite(&global_loading_lock, true);
56
57 if (IsListEmpty(&VcbList)) {
58 if (length < sizeof(btrfs_filesystem)) {
59 Status = STATUS_BUFFER_OVERFLOW;
60 goto end;
61 } else {
62 RtlZeroMemory(data, sizeof(btrfs_filesystem));
63 Status = STATUS_SUCCESS;
64 goto end;
65 }
66 }
67
68 le = VcbList.Flink;
69
70 while (le != &VcbList) {
71 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
72 btrfs_filesystem_device* bfd;
73
74 if (bfs) {
75 bfs->next_entry = itemsize;
76 bfs = (btrfs_filesystem*)((uint8_t*)bfs + itemsize);
77 } else
78 bfs = data;
79
80 if (length < offsetof(btrfs_filesystem, device)) {
81 Status = STATUS_BUFFER_OVERFLOW;
82 goto end;
83 }
84
85 itemsize = offsetof(btrfs_filesystem, device);
86 length -= offsetof(btrfs_filesystem, device);
87
88 bfs->next_entry = 0;
89 RtlCopyMemory(&bfs->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
90
91 ExAcquireResourceSharedLite(&Vcb->tree_lock, true);
92
93 bfs->num_devices = (uint32_t)Vcb->superblock.num_devices;
94
95 bfd = NULL;
96
97 le2 = Vcb->devices.Flink;
98 while (le2 != &Vcb->devices) {
99 device* dev = CONTAINING_RECORD(le2, device, list_entry);
100 MOUNTDEV_NAME mdn;
101
102 if (bfd)
103 bfd = (btrfs_filesystem_device*)((uint8_t*)bfd + offsetof(btrfs_filesystem_device, name[0]) + bfd->name_length);
104 else
105 bfd = &bfs->device;
106
107 if (length < offsetof(btrfs_filesystem_device, name[0])) {
108 ExReleaseResourceLite(&Vcb->tree_lock);
109 Status = STATUS_BUFFER_OVERFLOW;
110 goto end;
111 }
112
113 itemsize += (ULONG)offsetof(btrfs_filesystem_device, name[0]);
114 length -= (ULONG)offsetof(btrfs_filesystem_device, name[0]);
115
116 RtlCopyMemory(&bfd->uuid, &dev->devitem.device_uuid, sizeof(BTRFS_UUID));
117
118 if (dev->devobj) {
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 %08lx\n", Status);
123 goto end;
124 }
125
126 if (mdn.NameLength > length) {
127 ExReleaseResourceLite(&Vcb->tree_lock);
128 Status = STATUS_BUFFER_OVERFLOW;
129 goto end;
130 }
131
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 %08lx\n", Status);
136 goto end;
137 }
138
139 itemsize += bfd->name_length;
140 length -= bfd->name_length;
141 } else {
142 bfd->missing = true;
143 bfd->name_length = 0;
144 }
145
146 le2 = le2->Flink;
147 }
148
149 ExReleaseResourceLite(&Vcb->tree_lock);
150
151 le = le->Flink;
152 }
153
154 Status = STATUS_SUCCESS;
155
156 end:
157 ExReleaseResourceLite(&global_loading_lock);
158
159 return Status;
160 }
161
probe_volume(void * data,ULONG length,KPROCESSOR_MODE processor_mode)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;
165 NTSTATUS Status;
166 PDEVICE_OBJECT DeviceObject;
167 PFILE_OBJECT FileObject;
168 const GUID* guid;
169
170 if (length < sizeof(MOUNTDEV_NAME))
171 return STATUS_INVALID_PARAMETER;
172
173 if (length < offsetof(MOUNTDEV_NAME, Name[0]) + mdn->NameLength)
174 return STATUS_INVALID_PARAMETER;
175
176 TRACE("%.*S\n", (int)(mdn->NameLength / sizeof(WCHAR)), mdn->Name);
177
178 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_MANAGE_VOLUME_PRIVILEGE), processor_mode))
179 return STATUS_PRIVILEGE_NOT_HELD;
180
181 path.Buffer = mdn->Name;
182 path.Length = path.MaximumLength = mdn->NameLength;
183
184 Status = IoGetDeviceObjectPointer(&path, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject);
185 if (!NT_SUCCESS(Status)) {
186 ERR("IoGetDeviceObjectPointer returned %08lx\n", Status);
187 return Status;
188 }
189
190 Status = get_device_pnp_name(DeviceObject, &pnp_name, &guid);
191 if (!NT_SUCCESS(Status)) {
192 ERR("get_device_pnp_name returned %08lx\n", Status);
193 ObDereferenceObject(FileObject);
194 return Status;
195 }
196
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 %08lx\n", Status);
201 }
202
203 ObDereferenceObject(FileObject);
204
205 volume_removal(&pnp_name);
206
207 if (RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID))
208 disk_arrival(&pnp_name);
209 else
210 volume_arrival(&pnp_name, false);
211
212 return STATUS_SUCCESS;
213 }
214
ioctl_unload(PIRP Irp)215 static NTSTATUS ioctl_unload(PIRP Irp) {
216 if (!SeSinglePrivilegeCheck(RtlConvertLongToLuid(SE_LOAD_DRIVER_PRIVILEGE), Irp->RequestorMode)) {
217 ERR("insufficient privileges\n");
218 return STATUS_PRIVILEGE_NOT_HELD;
219 }
220
221 do_shutdown(Irp);
222
223 return STATUS_SUCCESS;
224 }
225
control_ioctl(PIRP Irp)226 static NTSTATUS control_ioctl(PIRP Irp) {
227 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
228 NTSTATUS Status;
229
230 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
231 case IOCTL_BTRFS_QUERY_FILESYSTEMS:
232 Status = query_filesystems(map_user_buffer(Irp, NormalPagePriority), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
233 break;
234
235 case IOCTL_BTRFS_PROBE_VOLUME:
236 Status = probe_volume(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
237 break;
238
239 case IOCTL_BTRFS_UNLOAD:
240 Status = ioctl_unload(Irp);
241 break;
242
243 default:
244 TRACE("unhandled ioctl %lx\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
245 Status = STATUS_NOT_IMPLEMENTED;
246 break;
247 }
248
249 return Status;
250 }
251
252 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
_Function_class_(DRIVER_DISPATCH)253 _Function_class_(DRIVER_DISPATCH)
254 NTSTATUS __stdcall drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
255 NTSTATUS Status;
256 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
257 device_extension* Vcb = DeviceObject->DeviceExtension;
258 bool top_level;
259
260 FsRtlEnterFileSystem();
261
262 top_level = is_top_level(Irp);
263
264 Irp->IoStatus.Information = 0;
265
266 if (Vcb) {
267 if (Vcb->type == VCB_TYPE_CONTROL) {
268 Status = control_ioctl(Irp);
269 goto end;
270 } else if (Vcb->type == VCB_TYPE_VOLUME) {
271 Status = vol_device_control(DeviceObject, Irp);
272 goto end;
273 } else if (Vcb->type != VCB_TYPE_FS) {
274 Status = STATUS_INVALID_PARAMETER;
275 goto end;
276 }
277 } else {
278 Status = STATUS_INVALID_PARAMETER;
279 goto end;
280 }
281
282 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
283 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
284 Status = mountdev_query_stable_guid(Vcb, Irp);
285 goto end;
286
287 case IOCTL_DISK_IS_WRITABLE:
288 Status = is_writable(Vcb);
289 goto end;
290
291 default:
292 TRACE("unhandled control code %lx\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
293 break;
294 }
295
296 IoSkipCurrentIrpStackLocation(Irp);
297
298 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
299
300 goto end2;
301
302 end:
303 Irp->IoStatus.Status = Status;
304
305 if (Status != STATUS_PENDING)
306 IoCompleteRequest(Irp, IO_NO_INCREMENT);
307
308 end2:
309 TRACE("returning %08lx\n", Status);
310
311 if (top_level)
312 IoSetTopLevelIrp(NULL);
313
314 FsRtlExitFileSystem();
315
316 return Status;
317 }
318