[BTRFS] Upgrade to 1.5
[reactos.git] / drivers / filesystems / btrfs / fastio.c
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, %x, %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, %x, %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, %x, %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 }