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