[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / devctrl.c
1 /* Copyright (c) Mark Harmstone 2016
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 #ifndef __REACTOS__
20 #include <winioctl.h>
21 #endif
22 #include <mountdev.h>
23 #include <initguid.h>
24 #include <diskguid.h>
25
26 extern LIST_ENTRY VcbList;
27 extern ERESOURCE global_loading_lock;
28
29 static NTSTATUS part0_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
30 NTSTATUS Status;
31 part0_device_extension* p0de = DeviceObject->DeviceExtension;
32 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
33
34 TRACE("control code = %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
35
36 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
37 case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID:
38 {
39 MOUNTDEV_UNIQUE_ID* mduid;
40
41 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID)) {
42 Status = STATUS_BUFFER_TOO_SMALL;
43 Irp->IoStatus.Status = Status;
44 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
45 IoCompleteRequest(Irp, IO_NO_INCREMENT);
46 return Status;
47 }
48
49 mduid = Irp->AssociatedIrp.SystemBuffer;
50 mduid->UniqueIdLength = sizeof(BTRFS_UUID);
51
52 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength) {
53 Status = STATUS_BUFFER_OVERFLOW;
54 Irp->IoStatus.Status = Status;
55 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID);
56 IoCompleteRequest(Irp, IO_NO_INCREMENT);
57 return Status;
58 }
59
60 RtlCopyMemory(mduid->UniqueId, &p0de->uuid, sizeof(BTRFS_UUID));
61
62 Status = STATUS_SUCCESS;
63 Irp->IoStatus.Status = Status;
64 Irp->IoStatus.Information = sizeof(MOUNTDEV_UNIQUE_ID) - 1 + mduid->UniqueIdLength;
65 IoCompleteRequest(Irp, IO_NO_INCREMENT);
66
67 return Status;
68 }
69
70 case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME:
71 {
72 PMOUNTDEV_NAME name;
73
74 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_NAME)) {
75 Status = STATUS_BUFFER_TOO_SMALL;
76 Irp->IoStatus.Status = Status;
77 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
78 IoCompleteRequest(Irp, IO_NO_INCREMENT);
79 return Status;
80 }
81
82 name = Irp->AssociatedIrp.SystemBuffer;
83 name->NameLength = p0de->name.Length;
84
85 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength) {
86 Status = STATUS_BUFFER_OVERFLOW;
87 Irp->IoStatus.Status = Status;
88 Irp->IoStatus.Information = sizeof(MOUNTDEV_NAME);
89 IoCompleteRequest(Irp, IO_NO_INCREMENT);
90 return Status;
91 }
92
93 RtlCopyMemory(name->Name, p0de->name.Buffer, p0de->name.Length);
94
95 Status = STATUS_SUCCESS;
96 Irp->IoStatus.Status = Status;
97 Irp->IoStatus.Information = offsetof(MOUNTDEV_NAME, Name[0]) + name->NameLength;
98 IoCompleteRequest(Irp, IO_NO_INCREMENT);
99
100 return Status;
101 }
102 }
103
104 IoSkipCurrentIrpStackLocation(Irp);
105
106 Status = IoCallDriver(p0de->devobj, Irp);
107
108 TRACE("returning %08x\n", Status);
109
110 return Status;
111 }
112
113 static NTSTATUS mountdev_query_stable_guid(device_extension* Vcb, PIRP Irp) {
114 MOUNTDEV_STABLE_GUID* msg = Irp->UserBuffer;
115 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
116
117 TRACE("IOCTL_MOUNTDEV_QUERY_STABLE_GUID\n");
118
119 if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MOUNTDEV_STABLE_GUID))
120 return STATUS_INVALID_PARAMETER;
121
122 RtlCopyMemory(&msg->StableGuid, &Vcb->superblock.uuid, sizeof(GUID));
123
124 Irp->IoStatus.Information = sizeof(MOUNTDEV_STABLE_GUID);
125
126 return STATUS_SUCCESS;
127 }
128
129 static NTSTATUS get_partition_info_ex(device_extension* Vcb, PIRP Irp) {
130 NTSTATUS Status;
131 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
132 PARTITION_INFORMATION_EX* piex;
133
134 TRACE("IOCTL_DISK_GET_PARTITION_INFO_EX\n");
135
136 Status = dev_ioctl(Vcb->Vpb->RealDevice, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
137 Irp->UserBuffer, IrpSp->Parameters.DeviceIoControl.OutputBufferLength, TRUE, &Irp->IoStatus);
138 if (!NT_SUCCESS(Status))
139 return Status;
140
141 piex = (PARTITION_INFORMATION_EX*)Irp->UserBuffer;
142
143 if (piex->PartitionStyle == PARTITION_STYLE_MBR) {
144 piex->Mbr.PartitionType = PARTITION_IFS;
145 piex->Mbr.RecognizedPartition = TRUE;
146 } else if (piex->PartitionStyle == PARTITION_STYLE_GPT) {
147 piex->Gpt.PartitionType = PARTITION_BASIC_DATA_GUID;
148 }
149
150 return STATUS_SUCCESS;
151 }
152
153 static NTSTATUS is_writable(device_extension* Vcb, PIRP Irp) {
154 TRACE("IOCTL_DISK_IS_WRITABLE\n");
155
156 return Vcb->readonly ? STATUS_MEDIA_WRITE_PROTECTED : STATUS_SUCCESS;
157 }
158
159 static NTSTATUS query_filesystems(void* data, ULONG length) {
160 NTSTATUS Status;
161 LIST_ENTRY *le, *le2;
162 btrfs_filesystem* bfs = NULL;
163 ULONG itemsize;
164
165 ExAcquireResourceSharedLite(&global_loading_lock, TRUE);
166
167 if (IsListEmpty(&VcbList)) {
168 if (length < sizeof(btrfs_filesystem)) {
169 Status = STATUS_BUFFER_OVERFLOW;
170 goto end;
171 } else {
172 RtlZeroMemory(data, sizeof(btrfs_filesystem));
173 Status = STATUS_SUCCESS;
174 goto end;
175 }
176 }
177
178 le = VcbList.Flink;
179
180 while (le != &VcbList) {
181 device_extension* Vcb = CONTAINING_RECORD(le, device_extension, list_entry);
182 btrfs_filesystem_device* bfd;
183
184 if (bfs) {
185 bfs->next_entry = itemsize;
186 bfs = (btrfs_filesystem*)((UINT8*)bfs + itemsize);
187 } else
188 bfs = data;
189
190 if (length < offsetof(btrfs_filesystem, device)) {
191 Status = STATUS_BUFFER_OVERFLOW;
192 goto end;
193 }
194
195 itemsize = offsetof(btrfs_filesystem, device);
196 length -= offsetof(btrfs_filesystem, device);
197
198 bfs->next_entry = 0;
199 RtlCopyMemory(&bfs->uuid, &Vcb->superblock.uuid, sizeof(BTRFS_UUID));
200
201 ExAcquireResourceSharedLite(&Vcb->tree_lock, TRUE);
202
203 bfs->num_devices = Vcb->superblock.num_devices;
204
205 bfd = NULL;
206
207 le2 = Vcb->devices.Flink;
208 while (le2 != &Vcb->devices) {
209 device* dev = CONTAINING_RECORD(le2, device, list_entry);
210 MOUNTDEV_NAME mdn;
211
212 if (bfd)
213 bfd = (btrfs_filesystem_device*)((UINT8*)bfd + offsetof(btrfs_filesystem_device, name[0]) + bfd->name_length);
214 else
215 bfd = &bfs->device;
216
217 if (length < offsetof(btrfs_filesystem_device, name[0])) {
218 ExReleaseResourceLite(&Vcb->tree_lock);
219 Status = STATUS_BUFFER_OVERFLOW;
220 goto end;
221 }
222
223 itemsize += offsetof(btrfs_filesystem_device, name[0]);
224 length -= offsetof(btrfs_filesystem_device, name[0]);
225
226 RtlCopyMemory(&bfd->uuid, &dev->devitem.device_uuid, sizeof(BTRFS_UUID));
227
228 Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &mdn, sizeof(MOUNTDEV_NAME), TRUE, NULL);
229 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
230 ExReleaseResourceLite(&Vcb->tree_lock);
231 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
232 goto end;
233 }
234
235 if (mdn.NameLength > length) {
236 ExReleaseResourceLite(&Vcb->tree_lock);
237 Status = STATUS_BUFFER_OVERFLOW;
238 goto end;
239 }
240
241 Status = dev_ioctl(dev->devobj, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, &bfd->name_length, offsetof(MOUNTDEV_NAME, Name[0]) + mdn.NameLength, TRUE, NULL);
242 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) {
243 ExReleaseResourceLite(&Vcb->tree_lock);
244 ERR("IOCTL_MOUNTDEV_QUERY_DEVICE_NAME returned %08x\n", Status);
245 goto end;
246 }
247
248 itemsize += bfd->name_length;
249 length -= bfd->name_length;
250
251 le2 = le2->Flink;
252 }
253
254 ExReleaseResourceLite(&Vcb->tree_lock);
255
256 le = le->Flink;
257 }
258
259 Status = STATUS_SUCCESS;
260
261 end:
262 ExReleaseResourceLite(&global_loading_lock);
263
264 return Status;
265 }
266
267 static NTSTATUS control_ioctl(PIRP Irp) {
268 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
269 NTSTATUS Status;
270
271 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
272 case IOCTL_BTRFS_QUERY_FILESYSTEMS:
273 Status = query_filesystems(map_user_buffer(Irp), IrpSp->Parameters.FileSystemControl.OutputBufferLength);
274 break;
275
276 default:
277 TRACE("unhandled ioctl %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
278 Status = STATUS_NOT_IMPLEMENTED;
279 break;
280 }
281
282 return Status;
283 }
284
285 NTSTATUS STDCALL drv_device_control(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) {
286 NTSTATUS Status;
287 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
288 device_extension* Vcb = DeviceObject->DeviceExtension;
289 BOOL top_level;
290
291 FsRtlEnterFileSystem();
292
293 top_level = is_top_level(Irp);
294
295 Irp->IoStatus.Information = 0;
296
297 if (Vcb) {
298 if (Vcb->type == VCB_TYPE_PARTITION0) {
299 Status = part0_device_control(DeviceObject, Irp);
300 goto end2;
301 } else if (Vcb->type == VCB_TYPE_CONTROL) {
302 Status = control_ioctl(Irp);
303 goto end;
304 }
305 } else {
306 Status = STATUS_INVALID_PARAMETER;
307 goto end;
308 }
309
310 if (!IrpSp->FileObject || IrpSp->FileObject->FsContext != Vcb->volume_fcb) {
311 Status = STATUS_INVALID_PARAMETER;
312 goto end;
313 }
314
315 switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
316 case IOCTL_MOUNTDEV_QUERY_STABLE_GUID:
317 Status = mountdev_query_stable_guid(Vcb, Irp);
318 goto end;
319
320 case IOCTL_DISK_GET_PARTITION_INFO_EX:
321 Status = get_partition_info_ex(Vcb, Irp);
322 goto end;
323
324 case IOCTL_DISK_IS_WRITABLE:
325 Status = is_writable(Vcb, Irp);
326 goto end;
327
328 default:
329 TRACE("unhandled control code %x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
330 break;
331 }
332
333 IoSkipCurrentIrpStackLocation(Irp);
334
335 Status = IoCallDriver(Vcb->Vpb->RealDevice, Irp);
336
337 goto end2;
338
339 end:
340 Irp->IoStatus.Status = Status;
341
342 if (Status != STATUS_PENDING)
343 IoCompleteRequest(Irp, IO_NO_INCREMENT);
344
345 end2:
346 if (top_level)
347 IoSetTopLevelIrp(NULL);
348
349 FsRtlExitFileSystem();
350
351 return Status;
352 }