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 20 FAST_IO_DISPATCH FastIoDispatch; 21 22 _Function_class_(FAST_IO_QUERY_BASIC_INFO) 23 static BOOLEAN __stdcall fast_query_basic_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_BASIC_INFORMATION fbi, 24 PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 25 fcb* fcb; 26 ccb* ccb; 27 28 FsRtlEnterFileSystem(); 29 30 TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fbi, IoStatus, DeviceObject); 31 32 if (!FileObject) { 33 FsRtlExitFileSystem(); 34 return false; 35 } 36 37 fcb = FileObject->FsContext; 38 39 if (!fcb) { 40 FsRtlExitFileSystem(); 41 return false; 42 } 43 44 ccb = FileObject->FsContext2; 45 46 if (!ccb) { 47 FsRtlExitFileSystem(); 48 return false; 49 } 50 51 if (!(ccb->access & (FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES))) { 52 FsRtlExitFileSystem(); 53 return false; 54 } 55 56 if (fcb->ads) { 57 if (!ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb) { 58 FsRtlExitFileSystem(); 59 return false; 60 } 61 62 fcb = ccb->fileref->parent->fcb; 63 } 64 65 if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { 66 FsRtlExitFileSystem(); 67 return false; 68 } 69 70 if (fcb == fcb->Vcb->dummy_fcb) { 71 LARGE_INTEGER time; 72 73 KeQuerySystemTime(&time); 74 fbi->CreationTime = fbi->LastAccessTime = fbi->LastWriteTime = fbi->ChangeTime = time; 75 } else { 76 fbi->CreationTime.QuadPart = unix_time_to_win(&fcb->inode_item.otime); 77 fbi->LastAccessTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_atime); 78 fbi->LastWriteTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_mtime); 79 fbi->ChangeTime.QuadPart = unix_time_to_win(&fcb->inode_item.st_ctime); 80 } 81 82 fbi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 83 84 IoStatus->Status = STATUS_SUCCESS; 85 IoStatus->Information = sizeof(FILE_BASIC_INFORMATION); 86 87 ExReleaseResourceLite(fcb->Header.Resource); 88 89 FsRtlExitFileSystem(); 90 91 return true; 92 } 93 94 _Function_class_(FAST_IO_QUERY_STANDARD_INFO) 95 static BOOLEAN __stdcall fast_query_standard_info(PFILE_OBJECT FileObject, BOOLEAN wait, PFILE_STANDARD_INFORMATION fsi, 96 PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 97 fcb* fcb; 98 ccb* ccb; 99 bool ads; 100 ULONG adssize; 101 102 FsRtlEnterFileSystem(); 103 104 TRACE("(%p, %u, %p, %p, %p)\n", FileObject, wait, fsi, IoStatus, DeviceObject); 105 106 if (!FileObject) { 107 FsRtlExitFileSystem(); 108 return false; 109 } 110 111 fcb = FileObject->FsContext; 112 ccb = FileObject->FsContext2; 113 114 if (!fcb) { 115 FsRtlExitFileSystem(); 116 return false; 117 } 118 119 if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { 120 FsRtlExitFileSystem(); 121 return false; 122 } 123 124 ads = fcb->ads; 125 126 if (ads) { 127 struct _fcb* fcb2; 128 129 if (!ccb || !ccb->fileref || !ccb->fileref->parent || !ccb->fileref->parent->fcb) { 130 ExReleaseResourceLite(fcb->Header.Resource); 131 FsRtlExitFileSystem(); 132 return false; 133 } 134 135 adssize = fcb->adsdata.Length; 136 137 fcb2 = ccb->fileref->parent->fcb; 138 139 ExReleaseResourceLite(fcb->Header.Resource); 140 141 fcb = fcb2; 142 143 if (!ExAcquireResourceSharedLite(fcb->Header.Resource, wait)) { 144 FsRtlExitFileSystem(); 145 return false; 146 } 147 148 fsi->AllocationSize.QuadPart = fsi->EndOfFile.QuadPart = adssize; 149 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 150 fsi->Directory = false; 151 } else { 152 fsi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 153 fsi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 154 fsi->NumberOfLinks = fcb->inode_item.st_nlink; 155 fsi->Directory = S_ISDIR(fcb->inode_item.st_mode); 156 } 157 158 fsi->DeletePending = ccb->fileref ? ccb->fileref->delete_on_close : false; 159 160 IoStatus->Status = STATUS_SUCCESS; 161 IoStatus->Information = sizeof(FILE_STANDARD_INFORMATION); 162 163 ExReleaseResourceLite(fcb->Header.Resource); 164 165 FsRtlExitFileSystem(); 166 167 return true; 168 } 169 170 _Function_class_(FAST_IO_CHECK_IF_POSSIBLE) 171 static BOOLEAN __stdcall fast_io_check_if_possible(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, 172 ULONG LockKey, BOOLEAN CheckForReadOperation, PIO_STATUS_BLOCK IoStatus, 173 PDEVICE_OBJECT DeviceObject) { 174 fcb* fcb = FileObject->FsContext; 175 LARGE_INTEGER len2; 176 177 UNUSED(Wait); 178 UNUSED(IoStatus); 179 UNUSED(DeviceObject); 180 181 len2.QuadPart = Length; 182 183 if (CheckForReadOperation) { 184 if (FsRtlFastCheckLockForRead(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess())) 185 return true; 186 } else { 187 if (!fcb->Vcb->readonly && !is_subvol_readonly(fcb->subvol, NULL) && FsRtlFastCheckLockForWrite(&fcb->lock, FileOffset, &len2, LockKey, FileObject, PsGetCurrentProcess())) 188 return true; 189 } 190 191 return false; 192 } 193 194 _Function_class_(FAST_IO_QUERY_NETWORK_OPEN_INFO) 195 static BOOLEAN __stdcall fast_io_query_network_open_info(PFILE_OBJECT FileObject, BOOLEAN Wait, FILE_NETWORK_OPEN_INFORMATION* fnoi, 196 PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 197 fcb* fcb; 198 ccb* ccb; 199 file_ref* fileref; 200 201 FsRtlEnterFileSystem(); 202 203 TRACE("(%p, %u, %p, %p, %p)\n", FileObject, Wait, fnoi, IoStatus, DeviceObject); 204 205 RtlZeroMemory(fnoi, sizeof(FILE_NETWORK_OPEN_INFORMATION)); 206 207 fcb = FileObject->FsContext; 208 209 if (!fcb || fcb == fcb->Vcb->volume_fcb) { 210 FsRtlExitFileSystem(); 211 return false; 212 } 213 214 ccb = FileObject->FsContext2; 215 216 if (!ccb) { 217 FsRtlExitFileSystem(); 218 return false; 219 } 220 221 fileref = ccb->fileref; 222 223 if (fcb == fcb->Vcb->dummy_fcb) { 224 LARGE_INTEGER time; 225 226 KeQuerySystemTime(&time); 227 fnoi->CreationTime = fnoi->LastAccessTime = fnoi->LastWriteTime = fnoi->ChangeTime = time; 228 } else { 229 INODE_ITEM* ii; 230 231 if (fcb->ads) { 232 if (!fileref || !fileref->parent) { 233 ERR("no fileref for stream\n"); 234 FsRtlExitFileSystem(); 235 return false; 236 } 237 238 ii = &fileref->parent->fcb->inode_item; 239 } else 240 ii = &fcb->inode_item; 241 242 fnoi->CreationTime.QuadPart = unix_time_to_win(&ii->otime); 243 fnoi->LastAccessTime.QuadPart = unix_time_to_win(&ii->st_atime); 244 fnoi->LastWriteTime.QuadPart = unix_time_to_win(&ii->st_mtime); 245 fnoi->ChangeTime.QuadPart = unix_time_to_win(&ii->st_ctime); 246 } 247 248 if (fcb->ads) { 249 fnoi->AllocationSize.QuadPart = fnoi->EndOfFile.QuadPart = fcb->adsdata.Length; 250 fnoi->FileAttributes = fileref->parent->fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fileref->parent->fcb->atts; 251 } else { 252 fnoi->AllocationSize.QuadPart = fcb_alloc_size(fcb); 253 fnoi->EndOfFile.QuadPart = S_ISDIR(fcb->inode_item.st_mode) ? 0 : fcb->inode_item.st_size; 254 fnoi->FileAttributes = fcb->atts == 0 ? FILE_ATTRIBUTE_NORMAL : fcb->atts; 255 } 256 257 FsRtlExitFileSystem(); 258 259 return true; 260 } 261 262 _Function_class_(FAST_IO_ACQUIRE_FOR_MOD_WRITE) 263 static NTSTATUS __stdcall fast_io_acquire_for_mod_write(PFILE_OBJECT FileObject, PLARGE_INTEGER EndingOffset, 264 struct _ERESOURCE **ResourceToRelease, PDEVICE_OBJECT DeviceObject) { 265 fcb* fcb; 266 267 TRACE("(%p, %I64x, %p, %p)\n", FileObject, EndingOffset ? EndingOffset->QuadPart : 0, ResourceToRelease, DeviceObject); 268 269 UNUSED(EndingOffset); 270 UNUSED(DeviceObject); 271 272 fcb = FileObject->FsContext; 273 274 if (!fcb) 275 return STATUS_INVALID_PARAMETER; 276 277 // Make sure we don't get interrupted by the flush thread, which can cause a deadlock 278 279 if (!ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, false)) 280 return STATUS_CANT_WAIT; 281 282 if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, false)) { 283 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 284 TRACE("returning STATUS_CANT_WAIT\n"); 285 return STATUS_CANT_WAIT; 286 } 287 288 // Ideally this would be PagingIoResource, but that doesn't play well with copy-on-write, 289 // as we can't guarantee that we won't need to do any reallocations. 290 291 *ResourceToRelease = fcb->Header.Resource; 292 293 TRACE("returning STATUS_SUCCESS\n"); 294 295 return STATUS_SUCCESS; 296 } 297 298 _Function_class_(FAST_IO_RELEASE_FOR_MOD_WRITE) 299 static NTSTATUS __stdcall fast_io_release_for_mod_write(PFILE_OBJECT FileObject, struct _ERESOURCE *ResourceToRelease, 300 PDEVICE_OBJECT DeviceObject) { 301 fcb* fcb; 302 303 TRACE("(%p, %p, %p)\n", FileObject, ResourceToRelease, DeviceObject); 304 305 UNUSED(DeviceObject); 306 307 fcb = FileObject->FsContext; 308 309 ExReleaseResourceLite(ResourceToRelease); 310 311 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 312 313 return STATUS_SUCCESS; 314 } 315 316 _Function_class_(FAST_IO_ACQUIRE_FOR_CCFLUSH) 317 static NTSTATUS __stdcall fast_io_acquire_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject) { 318 UNUSED(FileObject); 319 UNUSED(DeviceObject); 320 321 IoSetTopLevelIrp((PIRP)FSRTL_CACHE_TOP_LEVEL_IRP); 322 323 return STATUS_SUCCESS; 324 } 325 326 _Function_class_(FAST_IO_RELEASE_FOR_CCFLUSH) 327 static NTSTATUS __stdcall fast_io_release_for_ccflush(PFILE_OBJECT FileObject, PDEVICE_OBJECT DeviceObject) { 328 UNUSED(FileObject); 329 UNUSED(DeviceObject); 330 331 if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP) 332 IoSetTopLevelIrp(NULL); 333 334 return STATUS_SUCCESS; 335 } 336 337 _Function_class_(FAST_IO_WRITE) 338 static BOOLEAN __stdcall fast_io_write(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, ULONG Length, BOOLEAN Wait, ULONG LockKey, PVOID Buffer, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 339 fcb* fcb = FileObject->FsContext; 340 bool ret; 341 342 FsRtlEnterFileSystem(); 343 344 if (!ExAcquireResourceSharedLite(&fcb->Vcb->tree_lock, Wait)) { 345 FsRtlExitFileSystem(); 346 return false; 347 } 348 349 if (!ExAcquireResourceExclusiveLite(fcb->Header.Resource, Wait)) { 350 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 351 FsRtlExitFileSystem(); 352 return false; 353 } 354 355 ret = FsRtlCopyWrite(FileObject, FileOffset, Length, Wait, LockKey, Buffer, IoStatus, DeviceObject); 356 357 if (ret) 358 fcb->inode_item.st_size = fcb->Header.FileSize.QuadPart; 359 360 ExReleaseResourceLite(fcb->Header.Resource); 361 ExReleaseResourceLite(&fcb->Vcb->tree_lock); 362 363 FsRtlExitFileSystem(); 364 365 return ret; 366 } 367 368 _Function_class_(FAST_IO_LOCK) 369 static BOOLEAN __stdcall fast_io_lock(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, 370 ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock, PIO_STATUS_BLOCK IoStatus, 371 PDEVICE_OBJECT DeviceObject) { 372 BOOLEAN ret; 373 fcb* fcb = FileObject->FsContext; 374 375 TRACE("(%p, %I64x, %I64x, %p, %lx, %u, %u, %p, %p)\n", FileObject, FileOffset ? FileOffset->QuadPart : 0, Length ? Length->QuadPart : 0, 376 ProcessId, Key, FailImmediately, ExclusiveLock, IoStatus, DeviceObject); 377 378 if (fcb->type != BTRFS_TYPE_FILE) { 379 WARN("can only lock files\n"); 380 IoStatus->Status = STATUS_INVALID_PARAMETER; 381 IoStatus->Information = 0; 382 return true; 383 } 384 385 FsRtlEnterFileSystem(); 386 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 387 388 ret = FsRtlFastLock(&fcb->lock, FileObject, FileOffset, Length, ProcessId, Key, FailImmediately, 389 ExclusiveLock, IoStatus, NULL, false); 390 391 if (ret) 392 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 393 394 ExReleaseResourceLite(fcb->Header.Resource); 395 FsRtlExitFileSystem(); 396 397 return ret; 398 } 399 400 _Function_class_(FAST_IO_UNLOCK_SINGLE) 401 static BOOLEAN __stdcall fast_io_unlock_single(PFILE_OBJECT FileObject, PLARGE_INTEGER FileOffset, PLARGE_INTEGER Length, PEPROCESS ProcessId, 402 ULONG Key, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 403 fcb* fcb = FileObject->FsContext; 404 405 TRACE("(%p, %I64x, %I64x, %p, %lx, %p, %p)\n", FileObject, FileOffset ? FileOffset->QuadPart : 0, Length ? Length->QuadPart : 0, 406 ProcessId, Key, IoStatus, DeviceObject); 407 408 IoStatus->Information = 0; 409 410 if (fcb->type != BTRFS_TYPE_FILE) { 411 WARN("can only lock files\n"); 412 IoStatus->Status = STATUS_INVALID_PARAMETER; 413 return true; 414 } 415 416 FsRtlEnterFileSystem(); 417 418 IoStatus->Status = FsRtlFastUnlockSingle(&fcb->lock, FileObject, FileOffset, Length, ProcessId, Key, NULL, false); 419 420 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 421 422 FsRtlExitFileSystem(); 423 424 return true; 425 } 426 427 _Function_class_(FAST_IO_UNLOCK_ALL) 428 static BOOLEAN __stdcall fast_io_unlock_all(PFILE_OBJECT FileObject, PEPROCESS ProcessId, PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 429 fcb* fcb = FileObject->FsContext; 430 431 TRACE("(%p, %p, %p, %p)\n", FileObject, ProcessId, IoStatus, DeviceObject); 432 433 IoStatus->Information = 0; 434 435 if (fcb->type != BTRFS_TYPE_FILE) { 436 WARN("can only lock files\n"); 437 IoStatus->Status = STATUS_INVALID_PARAMETER; 438 return true; 439 } 440 441 FsRtlEnterFileSystem(); 442 443 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 444 445 IoStatus->Status = FsRtlFastUnlockAll(&fcb->lock, FileObject, ProcessId, NULL); 446 447 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 448 449 ExReleaseResourceLite(fcb->Header.Resource); 450 451 FsRtlExitFileSystem(); 452 453 return true; 454 } 455 456 _Function_class_(FAST_IO_UNLOCK_ALL_BY_KEY) 457 static BOOLEAN __stdcall fast_io_unlock_all_by_key(PFILE_OBJECT FileObject, PVOID ProcessId, ULONG Key, 458 PIO_STATUS_BLOCK IoStatus, PDEVICE_OBJECT DeviceObject) { 459 fcb* fcb = FileObject->FsContext; 460 461 TRACE("(%p, %p, %lx, %p, %p)\n", FileObject, ProcessId, Key, IoStatus, DeviceObject); 462 463 IoStatus->Information = 0; 464 465 if (fcb->type != BTRFS_TYPE_FILE) { 466 WARN("can only lock files\n"); 467 IoStatus->Status = STATUS_INVALID_PARAMETER; 468 return true; 469 } 470 471 FsRtlEnterFileSystem(); 472 473 ExAcquireResourceSharedLite(fcb->Header.Resource, true); 474 475 IoStatus->Status = FsRtlFastUnlockAllByKey(&fcb->lock, FileObject, ProcessId, Key, NULL); 476 477 fcb->Header.IsFastIoPossible = fast_io_possible(fcb); 478 479 ExReleaseResourceLite(fcb->Header.Resource); 480 481 FsRtlExitFileSystem(); 482 483 return true; 484 } 485 486 void init_fast_io_dispatch(FAST_IO_DISPATCH** fiod) { 487 RtlZeroMemory(&FastIoDispatch, sizeof(FastIoDispatch)); 488 489 FastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH); 490 491 FastIoDispatch.FastIoCheckIfPossible = fast_io_check_if_possible; 492 FastIoDispatch.FastIoRead = FsRtlCopyRead; 493 FastIoDispatch.FastIoWrite = fast_io_write; 494 FastIoDispatch.FastIoQueryBasicInfo = fast_query_basic_info; 495 FastIoDispatch.FastIoQueryStandardInfo = fast_query_standard_info; 496 FastIoDispatch.FastIoLock = fast_io_lock; 497 FastIoDispatch.FastIoUnlockSingle = fast_io_unlock_single; 498 FastIoDispatch.FastIoUnlockAll = fast_io_unlock_all; 499 FastIoDispatch.FastIoUnlockAllByKey = fast_io_unlock_all_by_key; 500 FastIoDispatch.FastIoQueryNetworkOpenInfo = fast_io_query_network_open_info; 501 FastIoDispatch.AcquireForModWrite = fast_io_acquire_for_mod_write; 502 FastIoDispatch.MdlRead = FsRtlMdlReadDev; 503 FastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev; 504 FastIoDispatch.PrepareMdlWrite = FsRtlPrepareMdlWriteDev; 505 FastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev; 506 FastIoDispatch.ReleaseForModWrite = fast_io_release_for_mod_write; 507 FastIoDispatch.AcquireForCcFlush = fast_io_acquire_for_ccflush; 508 FastIoDispatch.ReleaseForCcFlush = fast_io_release_for_ccflush; 509 510 *fiod = &FastIoDispatch; 511 } 512