xref: /reactos/drivers/filesystems/btrfs/devctrl.c (revision bd712186)
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 
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 
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 
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 %08x\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 %08x\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 
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", 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 %08x\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 %08x\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 %08x\n", Status);
201     }
202 
203     ObDereferenceObject(FileObject);
204 
205     volume_removal(drvobj, &pnp_name);
206 
207     if (RtlCompareMemory(guid, &GUID_DEVINTERFACE_DISK, sizeof(GUID)) == sizeof(GUID))
208         disk_arrival(drvobj, &pnp_name);
209     else
210         volume_arrival(drvobj, &pnp_name);
211 
212     return STATUS_SUCCESS;
213 }
214 
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 
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 %x\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)
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     if (!IrpSp->FileObject || IrpSp->FileObject->FsContext != Vcb->volume_fcb) {
283         Status = STATUS_INVALID_PARAMETER;
284         goto end;
285     }
286 
287     switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
288         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
289             Status = mountdev_query_stable_guid(Vcb, Irp);
290             goto end;
291 
292         case IOCTL_DISK_IS_WRITABLE:
293             Status = is_writable(Vcb);
294             goto end;
295 
296         default:
297             TRACE("unhandled control code %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
298             break;
299     }
300 
301     IoSkipCurrentIrpStackLocation(Irp);
302 
303     Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
304 
305     goto end2;
306 
307 end:
308     Irp->IoStatus.Status = Status;
309 
310     if (Status != STATUS_PENDING)
311         IoCompleteRequest(Irp, IO_NO_INCREMENT);
312 
313 end2:
314     TRACE("returning %08x\n", Status);
315 
316     if (top_level)
317         IoSetTopLevelIrp(NULL);
318 
319     FsRtlExitFileSystem();
320 
321     return Status;
322 }
323