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 %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 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 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 %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) 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