xref: /reactos/drivers/filesystems/btrfs/devctrl.c (revision 50cf16b3)
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*)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)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*)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 control_ioctl(PIRP Irp) {
216     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
217     NTSTATUS Status;
218 
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);
222             break;
223 
224         case IOCTL_BTRFS_PROBE_VOLUME:
225             Status = probe_volume(Irp->AssociatedIrp.SystemBuffer, IrpSp->Parameters.FileSystemControl.InputBufferLength, Irp->RequestorMode);
226             break;
227 
228         default:
229             TRACE("unhandled ioctl %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
230             Status = STATUS_NOT_IMPLEMENTED;
231             break;
232     }
233 
234     return Status;
235 }
236 
237 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL)
238 _Function_class_(DRIVER_DISPATCH)
239 NTSTATUS NTAPI drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
240     NTSTATUS Status;
241     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
242     device_extension* Vcb = DeviceObject->DeviceExtension;
243     BOOL top_level;
244 
245     FsRtlEnterFileSystem();
246 
247     top_level = is_top_level(Irp);
248 
249     Irp->IoStatus.Information = 0;
250 
251     if (Vcb) {
252         if (Vcb->type == VCB_TYPE_CONTROL) {
253             Status = control_ioctl(Irp);
254             goto end;
255         } else if (Vcb->type == VCB_TYPE_VOLUME) {
256             Status = vol_device_control(DeviceObject, Irp);
257             goto end;
258         } else if (Vcb->type != VCB_TYPE_FS) {
259             Status = STATUS_INVALID_PARAMETER;
260             goto end;
261         }
262     } else {
263         Status = STATUS_INVALID_PARAMETER;
264         goto end;
265     }
266 
267     if (!IrpSp->FileObject || IrpSp->FileObject->FsContext != Vcb->volume_fcb) {
268         Status = STATUS_INVALID_PARAMETER;
269         goto end;
270     }
271 
272     switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
273         case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
274             Status = mountdev_query_stable_guid(Vcb, Irp);
275             goto end;
276 
277         case IOCTL_DISK_IS_WRITABLE:
278             Status = is_writable(Vcb);
279             goto end;
280 
281         default:
282             TRACE("unhandled control code %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
283             break;
284     }
285 
286     IoSkipCurrentIrpStackLocation(Irp);
287 
288     Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
289 
290     goto end2;
291 
292 end:
293     Irp->IoStatus.Status = Status;
294 
295     if (Status != STATUS_PENDING)
296         IoCompleteRequest(Irp, IO_NO_INCREMENT);
297 
298 end2:
299     TRACE("returning %08x\n", Status);
300 
301     if (top_level)
302         IoSetTopLevelIrp(NULL);
303 
304     FsRtlExitFileSystem();
305 
306     return Status;
307 }
308