[FASTFAT] Adjust the 'UnCleanCount', followng commit 9c3c0d12.
[reactos.git] / drivers / filesystems / fastfat / misc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/misc.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER:
7 *
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "vfat.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ******************************************************************/
18
19 const char* MajorFunctionNames[] =
20 {
21 "IRP_MJ_CREATE",
22 "IRP_MJ_CREATE_NAMED_PIPE",
23 "IRP_MJ_CLOSE",
24 "IRP_MJ_READ",
25 "IRP_MJ_WRITE",
26 "IRP_MJ_QUERY_INFORMATION",
27 "IRP_MJ_SET_INFORMATION",
28 "IRP_MJ_QUERY_EA",
29 "IRP_MJ_SET_EA",
30 "IRP_MJ_FLUSH_BUFFERS",
31 "IRP_MJ_QUERY_VOLUME_INFORMATION",
32 "IRP_MJ_SET_VOLUME_INFORMATION",
33 "IRP_MJ_DIRECTORY_CONTROL",
34 "IRP_MJ_FILE_SYSTEM_CONTROL",
35 "IRP_MJ_DEVICE_CONTROL",
36 "IRP_MJ_INTERNAL_DEVICE_CONTROL",
37 "IRP_MJ_SHUTDOWN",
38 "IRP_MJ_LOCK_CONTROL",
39 "IRP_MJ_CLEANUP",
40 "IRP_MJ_CREATE_MAILSLOT",
41 "IRP_MJ_QUERY_SECURITY",
42 "IRP_MJ_SET_SECURITY",
43 "IRP_MJ_POWER",
44 "IRP_MJ_SYSTEM_CONTROL",
45 "IRP_MJ_DEVICE_CHANGE",
46 "IRP_MJ_QUERY_QUOTA",
47 "IRP_MJ_SET_QUOTA",
48 "IRP_MJ_PNP",
49 "IRP_MJ_MAXIMUM_FUNCTION"
50 };
51
52 static LONG QueueCount = 0;
53
54 static VOID VfatFreeIrpContext(PVFAT_IRP_CONTEXT);
55 static PVFAT_IRP_CONTEXT VfatAllocateIrpContext(PDEVICE_OBJECT, PIRP);
56 static NTSTATUS VfatQueueRequest(PVFAT_IRP_CONTEXT);
57
58 /* FUNCTIONS ****************************************************************/
59
60 static
61 NTSTATUS
62 VfatLockControl(
63 IN PVFAT_IRP_CONTEXT IrpContext)
64 {
65 PVFATFCB Fcb;
66 NTSTATUS Status;
67
68 DPRINT("VfatLockControl(IrpContext %p)\n", IrpContext);
69
70 ASSERT(IrpContext);
71
72 Fcb = (PVFATFCB)IrpContext->FileObject->FsContext;
73
74 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
75 {
76 return STATUS_INVALID_DEVICE_REQUEST;
77 }
78
79 if (vfatFCBIsDirectory(Fcb))
80 {
81 return STATUS_INVALID_PARAMETER;
82 }
83
84 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
85 Status = FsRtlProcessFileLock(&Fcb->FileLock,
86 IrpContext->Irp,
87 NULL);
88 return Status;
89 }
90
91 static
92 NTSTATUS
93 VfatDeviceControl(
94 IN PVFAT_IRP_CONTEXT IrpContext)
95 {
96 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
97
98 IrpContext->Flags &= ~IRPCONTEXT_COMPLETE;
99
100 return IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
101 }
102
103 static
104 NTSTATUS
105 VfatDispatchRequest(
106 IN PVFAT_IRP_CONTEXT IrpContext)
107 {
108 NTSTATUS Status;
109 BOOLEAN QueueIrp, CompleteIrp;
110
111 DPRINT("VfatDispatchRequest (IrpContext %p), is called for %s\n", IrpContext,
112 IrpContext->MajorFunction >= IRP_MJ_MAXIMUM_FUNCTION ? "????" : MajorFunctionNames[IrpContext->MajorFunction]);
113
114 ASSERT(IrpContext);
115
116 FsRtlEnterFileSystem();
117
118 switch (IrpContext->MajorFunction)
119 {
120 case IRP_MJ_CLOSE:
121 Status = VfatClose(IrpContext);
122 break;
123
124 case IRP_MJ_CREATE:
125 Status = VfatCreate(IrpContext);
126 break;
127
128 case IRP_MJ_READ:
129 Status = VfatRead(IrpContext);
130 break;
131
132 case IRP_MJ_WRITE:
133 Status = VfatWrite(IrpContext);
134 break;
135
136 case IRP_MJ_FILE_SYSTEM_CONTROL:
137 Status = VfatFileSystemControl(IrpContext);
138 break;
139
140 case IRP_MJ_QUERY_INFORMATION:
141 Status = VfatQueryInformation(IrpContext);
142 break;
143
144 case IRP_MJ_SET_INFORMATION:
145 Status = VfatSetInformation(IrpContext);
146 break;
147
148 case IRP_MJ_DIRECTORY_CONTROL:
149 Status = VfatDirectoryControl(IrpContext);
150 break;
151
152 case IRP_MJ_QUERY_VOLUME_INFORMATION:
153 Status = VfatQueryVolumeInformation(IrpContext);
154 break;
155
156 case IRP_MJ_SET_VOLUME_INFORMATION:
157 Status = VfatSetVolumeInformation(IrpContext);
158 break;
159
160 case IRP_MJ_LOCK_CONTROL:
161 Status = VfatLockControl(IrpContext);
162 break;
163
164 case IRP_MJ_DEVICE_CONTROL:
165 Status = VfatDeviceControl(IrpContext);
166 break;
167
168 case IRP_MJ_CLEANUP:
169 Status = VfatCleanup(IrpContext);
170 break;
171
172 case IRP_MJ_FLUSH_BUFFERS:
173 Status = VfatFlush(IrpContext);
174 break;
175
176 case IRP_MJ_PNP:
177 Status = VfatPnp(IrpContext);
178 break;
179
180 default:
181 DPRINT1("Unexpected major function %x\n", IrpContext->MajorFunction);
182 Status = STATUS_DRIVER_INTERNAL_ERROR;
183 }
184
185 QueueIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_QUEUE);
186 CompleteIrp = BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_COMPLETE);
187
188 ASSERT((!CompleteIrp && !QueueIrp) ||
189 (CompleteIrp && !QueueIrp) ||
190 (!CompleteIrp && QueueIrp));
191
192 if (CompleteIrp)
193 {
194 IrpContext->Irp->IoStatus.Status = Status;
195 IoCompleteRequest(IrpContext->Irp, IrpContext->PriorityBoost);
196 }
197
198 if (QueueIrp)
199 {
200 /* Reset our status flags before queueing the IRP */
201 IrpContext->Flags |= IRPCONTEXT_COMPLETE;
202 IrpContext->Flags &= ~IRPCONTEXT_QUEUE;
203 Status = VfatQueueRequest(IrpContext);
204 }
205 else
206 {
207 /* Unless the IRP was queued, always free the IRP context */
208 VfatFreeIrpContext(IrpContext);
209 }
210
211 FsRtlExitFileSystem();
212
213 return Status;
214 }
215
216 VOID
217 NTAPI
218 VfatHandleDeferredWrite(
219 IN PVOID IrpContext,
220 IN PVOID Unused)
221 {
222 VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
223 }
224
225 NTSTATUS
226 NTAPI
227 VfatBuildRequest(
228 IN PDEVICE_OBJECT DeviceObject,
229 IN PIRP Irp)
230 {
231 NTSTATUS Status;
232 PVFAT_IRP_CONTEXT IrpContext;
233
234 DPRINT("VfatBuildRequest (DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
235
236 ASSERT(DeviceObject);
237 ASSERT(Irp);
238
239 IrpContext = VfatAllocateIrpContext(DeviceObject, Irp);
240 if (IrpContext == NULL)
241 {
242 Status = STATUS_INSUFFICIENT_RESOURCES;
243 Irp->IoStatus.Status = Status;
244 IoCompleteRequest(Irp, IO_NO_INCREMENT);
245 }
246 else
247 {
248 Status = VfatDispatchRequest(IrpContext);
249 }
250 return Status;
251 }
252
253 static
254 VOID
255 VfatFreeIrpContext(
256 PVFAT_IRP_CONTEXT IrpContext)
257 {
258 ASSERT(IrpContext);
259 ExFreeToNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList, IrpContext);
260 }
261
262 static
263 PVFAT_IRP_CONTEXT
264 VfatAllocateIrpContext(
265 PDEVICE_OBJECT DeviceObject,
266 PIRP Irp)
267 {
268 PVFAT_IRP_CONTEXT IrpContext;
269 /*PIO_STACK_LOCATION Stack;*/
270 UCHAR MajorFunction;
271
272 DPRINT("VfatAllocateIrpContext(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
273
274 ASSERT(DeviceObject);
275 ASSERT(Irp);
276
277 IrpContext = ExAllocateFromNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList);
278 if (IrpContext)
279 {
280 RtlZeroMemory(IrpContext, sizeof(VFAT_IRP_CONTEXT));
281 IrpContext->Irp = Irp;
282 IrpContext->DeviceObject = DeviceObject;
283 IrpContext->DeviceExt = DeviceObject->DeviceExtension;
284 IrpContext->Stack = IoGetCurrentIrpStackLocation(Irp);
285 ASSERT(IrpContext->Stack);
286 MajorFunction = IrpContext->MajorFunction = IrpContext->Stack->MajorFunction;
287 IrpContext->MinorFunction = IrpContext->Stack->MinorFunction;
288 IrpContext->FileObject = IrpContext->Stack->FileObject;
289 IrpContext->Flags = IRPCONTEXT_COMPLETE;
290
291 /* Easy cases that can wait */
292 if (MajorFunction == IRP_MJ_CLEANUP ||
293 MajorFunction == IRP_MJ_CREATE ||
294 MajorFunction == IRP_MJ_SHUTDOWN ||
295 MajorFunction == IRP_MJ_CLOSE /* likely to be fixed */)
296 {
297 SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
298 }
299 /* Cases that can wait if synchronous IRP */
300 else if ((MajorFunction == IRP_MJ_DEVICE_CONTROL ||
301 MajorFunction == IRP_MJ_QUERY_INFORMATION ||
302 MajorFunction == IRP_MJ_SET_INFORMATION ||
303 MajorFunction == IRP_MJ_FLUSH_BUFFERS ||
304 MajorFunction == IRP_MJ_LOCK_CONTROL ||
305 MajorFunction == IRP_MJ_QUERY_VOLUME_INFORMATION ||
306 MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION ||
307 MajorFunction == IRP_MJ_DIRECTORY_CONTROL ||
308 MajorFunction == IRP_MJ_WRITE ||
309 MajorFunction == IRP_MJ_READ) &&
310 IoIsOperationSynchronous(Irp))
311 {
312 SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
313 }
314 /* Cases that can wait if synchronous or if no FO */
315 else if ((MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
316 MajorFunction == IRP_MJ_PNP) &&
317 (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL ||
318 IoIsOperationSynchronous(Irp)))
319 {
320 SetFlag(IrpContext->Flags, IRPCONTEXT_CANWAIT);
321 }
322
323 KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
324 IrpContext->RefCount = 0;
325 IrpContext->PriorityBoost = IO_NO_INCREMENT;
326 }
327 return IrpContext;
328 }
329
330 static WORKER_THREAD_ROUTINE VfatDoRequest;
331
332 static
333 VOID
334 NTAPI
335 VfatDoRequest(
336 PVOID IrpContext)
337 {
338 InterlockedDecrement(&QueueCount);
339 DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n",
340 IrpContext, ((PVFAT_IRP_CONTEXT)IrpContext)->MajorFunction, QueueCount);
341 VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
342 }
343
344 static
345 NTSTATUS
346 VfatQueueRequest(
347 PVFAT_IRP_CONTEXT IrpContext)
348 {
349 InterlockedIncrement(&QueueCount);
350 DPRINT("VfatQueueRequest(IrpContext %p), %d\n", IrpContext, QueueCount);
351
352 ASSERT(IrpContext != NULL);
353 ASSERT(IrpContext->Irp != NULL);
354 ASSERT(!(IrpContext->Flags & IRPCONTEXT_QUEUE) &&
355 (IrpContext->Flags & IRPCONTEXT_COMPLETE));
356
357 IrpContext->Flags |= IRPCONTEXT_CANWAIT;
358 IoMarkIrpPending(IrpContext->Irp);
359 ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext);
360 ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue);
361 return STATUS_PENDING;
362 }
363
364 PVOID
365 VfatGetUserBuffer(
366 IN PIRP Irp,
367 IN BOOLEAN Paging)
368 {
369 ASSERT(Irp);
370
371 if (Irp->MdlAddress)
372 {
373 return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, (Paging ? HighPagePriority : NormalPagePriority));
374 }
375 else
376 {
377 return Irp->UserBuffer;
378 }
379 }
380
381 NTSTATUS
382 VfatLockUserBuffer(
383 IN PIRP Irp,
384 IN ULONG Length,
385 IN LOCK_OPERATION Operation)
386 {
387 ASSERT(Irp);
388
389 if (Irp->MdlAddress)
390 {
391 return STATUS_SUCCESS;
392 }
393
394 IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp);
395
396 if (!Irp->MdlAddress)
397 {
398 return STATUS_INSUFFICIENT_RESOURCES;
399 }
400
401 _SEH2_TRY
402 {
403 MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, Operation);
404 }
405 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
406 {
407 IoFreeMdl(Irp->MdlAddress);
408 Irp->MdlAddress = NULL;
409 _SEH2_YIELD(return _SEH2_GetExceptionCode());
410 }
411 _SEH2_END;
412
413 return STATUS_SUCCESS;
414 }
415
416 BOOLEAN
417 VfatCheckForDismount(
418 IN PDEVICE_EXTENSION DeviceExt,
419 IN BOOLEAN Force)
420 {
421 KIRQL OldIrql;
422 ULONG UnCleanCount;
423 PVPB Vpb;
424 BOOLEAN Delete;
425
426 DPRINT1("VfatCheckForDismount(%p, %u)\n", DeviceExt, Force);
427
428 /* If the VCB is OK (not under uninitialization) and we don't force dismount, do nothing */
429 if (BooleanFlagOn(DeviceExt->Flags, VCB_GOOD) && !Force)
430 {
431 return FALSE;
432 }
433
434 /*
435 * NOTE: In their *CheckForDismount() function, our 3rd-party FS drivers
436 * as well as MS' fastfat, perform a comparison check of the current VCB's
437 * VPB ReferenceCount with some sort of "dangling"/"residual" open count,
438 * depending on whether or not we are in IRP_MJ_CREATE.
439 * It seems to be related to the fact that the volume root directory as
440 * well as auxiliary data stream(s) are still opened, and only these are
441 * allowed to be opened at that moment. After analysis it appears that for
442 * the ReactOS' fastfat, this number is equal to "2".
443 */
444 UnCleanCount = 2;
445
446 /* Lock VPB */
447 IoAcquireVpbSpinLock(&OldIrql);
448
449 /* Reference it and check if a create is being done */
450 Vpb = DeviceExt->IoVPB;
451 DPRINT("Vpb->ReferenceCount = %d\n", Vpb->ReferenceCount);
452 if (Vpb->ReferenceCount != UnCleanCount || DeviceExt->OpenHandleCount != 0)
453 {
454 /* If we force-unmount, copy the VPB to our local own to prepare later dismount */
455 if (Force && Vpb->RealDevice->Vpb == Vpb && DeviceExt->SpareVPB != NULL)
456 {
457 RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
458 DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
459 DeviceExt->SpareVPB->Size = sizeof(VPB);
460 DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
461 DeviceExt->SpareVPB->DeviceObject = NULL;
462 DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
463 DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
464 DeviceExt->SpareVPB = NULL;
465 DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
466
467 /* We are uninitializing, the VCB cannot be used anymore */
468 ClearFlag(DeviceExt->Flags, VCB_GOOD);
469 }
470
471 /* Don't do anything for now */
472 Delete = FALSE;
473 }
474 else
475 {
476 /* Otherwise, delete the volume */
477 Delete = TRUE;
478
479 /* Swap the VPB with our local own */
480 if (Vpb->RealDevice->Vpb == Vpb && DeviceExt->SpareVPB != NULL)
481 {
482 RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
483 DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
484 DeviceExt->SpareVPB->Size = sizeof(VPB);
485 DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
486 DeviceExt->SpareVPB->DeviceObject = NULL;
487 DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
488 DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
489 DeviceExt->SpareVPB = NULL;
490 DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
491
492 /* We are uninitializing, the VCB cannot be used anymore */
493 ClearFlag(DeviceExt->Flags, VCB_GOOD);
494 }
495
496 /*
497 * We defer setting the VPB's DeviceObject to NULL for later because
498 * we want to handle the closing of the internal opened meta-files.
499 */
500
501 /* Clear the mounted and locked flags in the VPB */
502 ClearFlag(Vpb->Flags, VPB_MOUNTED | VPB_LOCKED);
503 }
504
505 /* Release lock and return status */
506 IoReleaseVpbSpinLock(OldIrql);
507
508 /* If we were to delete, delete volume */
509 if (Delete)
510 {
511 LARGE_INTEGER Zero = {{0,0}};
512 PVFATFCB Fcb;
513
514 /* We are uninitializing, the VCB cannot be used anymore */
515 ClearFlag(DeviceExt->Flags, VCB_GOOD);
516
517 /* Invalidate and close the internal opened meta-files */
518 if (DeviceExt->RootFcb)
519 {
520 Fcb = DeviceExt->RootFcb;
521 CcUninitializeCacheMap(Fcb->FileObject,
522 &Zero,
523 NULL);
524 ObDereferenceObject(Fcb->FileObject);
525 DeviceExt->RootFcb = NULL;
526 vfatDestroyFCB(Fcb);
527 }
528 if (DeviceExt->VolumeFcb)
529 {
530 Fcb = DeviceExt->VolumeFcb;
531 #ifndef VOLUME_IS_NOT_CACHED_WORK_AROUND_IT
532 CcUninitializeCacheMap(Fcb->FileObject,
533 &Zero,
534 NULL);
535 ObDereferenceObject(Fcb->FileObject);
536 #endif
537 DeviceExt->VolumeFcb = NULL;
538 vfatDestroyFCB(Fcb);
539 }
540 if (DeviceExt->FATFileObject)
541 {
542 Fcb = DeviceExt->FATFileObject->FsContext;
543 CcUninitializeCacheMap(DeviceExt->FATFileObject,
544 &Zero,
545 NULL);
546 DeviceExt->FATFileObject->FsContext = NULL;
547 ObDereferenceObject(DeviceExt->FATFileObject);
548 DeviceExt->FATFileObject = NULL;
549 vfatDestroyFCB(Fcb);
550 }
551
552 /*
553 * Now that the closing of the internal opened meta-files has been
554 * handled, we can now set the VPB's DeviceObject to NULL.
555 */
556 Vpb->DeviceObject = NULL;
557
558 /* If we have a local VPB, we'll have to delete it
559 * but we won't dismount us - something went bad before
560 */
561 if (DeviceExt->SpareVPB)
562 {
563 ExFreePool(DeviceExt->SpareVPB);
564 }
565 /* Otherwise, delete any of the available VPB if its reference count is zero */
566 else if (DeviceExt->IoVPB->ReferenceCount == 0)
567 {
568 ExFreePool(DeviceExt->IoVPB);
569 }
570
571 /* Remove the volume from the list */
572 ExAcquireResourceExclusiveLite(&VfatGlobalData->VolumeListLock, TRUE);
573 RemoveEntryList(&DeviceExt->VolumeListEntry);
574 ExReleaseResourceLite(&VfatGlobalData->VolumeListLock);
575
576 /* Uninitialize the notify synchronization object */
577 FsRtlNotifyUninitializeSync(&DeviceExt->NotifySync);
578
579 /* Release resources */
580 ExFreePoolWithTag(DeviceExt->Statistics, TAG_STATS);
581 ExDeleteResourceLite(&DeviceExt->DirResource);
582 ExDeleteResourceLite(&DeviceExt->FatResource);
583
584 /* Dismount our device if possible */
585 ObfDereferenceObject(DeviceExt->StorageDevice);
586 IoDeleteDevice(DeviceExt->VolumeDevice);
587 }
588
589 return Delete;
590 }