[BTRFS] Upgrade to 1.4
[reactos.git] / drivers / filesystems / btrfs / devctrl.c
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 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 __stdcall 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 }