xref: /reactos/drivers/filesystems/btrfs/fastio.c (revision 7115d7ba)
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