[FASTFAT]
[reactos.git] / reactos / 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 (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY)
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
110 DPRINT("VfatDispatchRequest (IrpContext %p), is called for %s\n", IrpContext,
111 IrpContext->MajorFunction >= IRP_MJ_MAXIMUM_FUNCTION ? "????" : MajorFunctionNames[IrpContext->MajorFunction]);
112
113 ASSERT(IrpContext);
114
115 FsRtlEnterFileSystem();
116
117 switch (IrpContext->MajorFunction)
118 {
119 case IRP_MJ_CLOSE:
120 Status = VfatClose(IrpContext);
121 break;
122
123 case IRP_MJ_CREATE:
124 Status = VfatCreate(IrpContext);
125 break;
126
127 case IRP_MJ_READ:
128 Status = VfatRead(IrpContext);
129 break;
130
131 case IRP_MJ_WRITE:
132 Status = VfatWrite (IrpContext);
133 break;
134
135 case IRP_MJ_FILE_SYSTEM_CONTROL:
136 Status = VfatFileSystemControl(IrpContext);
137 break;
138
139 case IRP_MJ_QUERY_INFORMATION:
140 Status = VfatQueryInformation (IrpContext);
141 break;
142
143 case IRP_MJ_SET_INFORMATION:
144 Status = VfatSetInformation (IrpContext);
145 break;
146
147 case IRP_MJ_DIRECTORY_CONTROL:
148 Status = VfatDirectoryControl(IrpContext);
149 break;
150
151 case IRP_MJ_QUERY_VOLUME_INFORMATION:
152 Status = VfatQueryVolumeInformation(IrpContext);
153 break;
154
155 case IRP_MJ_SET_VOLUME_INFORMATION:
156 Status = VfatSetVolumeInformation(IrpContext);
157 break;
158
159 case IRP_MJ_LOCK_CONTROL:
160 Status = VfatLockControl(IrpContext);
161 break;
162
163 case IRP_MJ_DEVICE_CONTROL:
164 Status = VfatDeviceControl(IrpContext);
165 break;
166
167 case IRP_MJ_CLEANUP:
168 Status = VfatCleanup(IrpContext);
169 break;
170
171 case IRP_MJ_FLUSH_BUFFERS:
172 Status = VfatFlush(IrpContext);
173 break;
174
175 case IRP_MJ_PNP:
176 Status = VfatPnp(IrpContext);
177 break;
178
179 default:
180 DPRINT1("Unexpected major function %x\n", IrpContext->MajorFunction);
181 Status = STATUS_DRIVER_INTERNAL_ERROR;
182 }
183
184 ASSERT((!(IrpContext->Flags & IRPCONTEXT_COMPLETE) && !(IrpContext->Flags & IRPCONTEXT_QUEUE)) ||
185 ((IrpContext->Flags & IRPCONTEXT_COMPLETE) && !(IrpContext->Flags & IRPCONTEXT_QUEUE)) ||
186 (!(IrpContext->Flags & IRPCONTEXT_COMPLETE) && (IrpContext->Flags & IRPCONTEXT_QUEUE)));
187
188 if (IrpContext->Flags & IRPCONTEXT_COMPLETE)
189 {
190 IrpContext->Irp->IoStatus.Status = Status;
191 IoCompleteRequest(IrpContext->Irp, IrpContext->PriorityBoost);
192 }
193
194 if (IrpContext->Flags & IRPCONTEXT_QUEUE)
195 {
196 /* Reset our status flags before queueing the IRP */
197 IrpContext->Flags |= IRPCONTEXT_COMPLETE;
198 IrpContext->Flags &= ~IRPCONTEXT_QUEUE;
199 Status = VfatQueueRequest(IrpContext);
200 }
201 else
202 {
203 /* Unless the IRP was queued, always free the IRP context */
204 VfatFreeIrpContext(IrpContext);
205 }
206
207 FsRtlExitFileSystem();
208
209 return Status;
210 }
211
212 NTSTATUS
213 NTAPI
214 VfatBuildRequest(
215 IN PDEVICE_OBJECT DeviceObject,
216 IN PIRP Irp)
217 {
218 NTSTATUS Status;
219 PVFAT_IRP_CONTEXT IrpContext;
220
221 DPRINT("VfatBuildRequest (DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
222
223 ASSERT(DeviceObject);
224 ASSERT(Irp);
225
226 IrpContext = VfatAllocateIrpContext(DeviceObject, Irp);
227 if (IrpContext == NULL)
228 {
229 Status = STATUS_INSUFFICIENT_RESOURCES;
230 Irp->IoStatus.Status = Status;
231 IoCompleteRequest(Irp, IO_NO_INCREMENT);
232 }
233 else
234 {
235 Status = VfatDispatchRequest(IrpContext);
236 }
237 return Status;
238 }
239
240 static
241 VOID
242 VfatFreeIrpContext(
243 PVFAT_IRP_CONTEXT IrpContext)
244 {
245 ASSERT(IrpContext);
246 ExFreeToNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList, IrpContext);
247 }
248
249 static
250 PVFAT_IRP_CONTEXT
251 VfatAllocateIrpContext(
252 PDEVICE_OBJECT DeviceObject,
253 PIRP Irp)
254 {
255 PVFAT_IRP_CONTEXT IrpContext;
256 /*PIO_STACK_LOCATION Stack;*/
257 UCHAR MajorFunction;
258
259 DPRINT("VfatAllocateIrpContext(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
260
261 ASSERT(DeviceObject);
262 ASSERT(Irp);
263
264 IrpContext = ExAllocateFromNPagedLookasideList(&VfatGlobalData->IrpContextLookasideList);
265 if (IrpContext)
266 {
267 RtlZeroMemory(IrpContext, sizeof(VFAT_IRP_CONTEXT));
268 IrpContext->Irp = Irp;
269 IrpContext->DeviceObject = DeviceObject;
270 IrpContext->DeviceExt = DeviceObject->DeviceExtension;
271 IrpContext->Stack = IoGetCurrentIrpStackLocation(Irp);
272 ASSERT(IrpContext->Stack);
273 MajorFunction = IrpContext->MajorFunction = IrpContext->Stack->MajorFunction;
274 IrpContext->MinorFunction = IrpContext->Stack->MinorFunction;
275 IrpContext->FileObject = IrpContext->Stack->FileObject;
276 IrpContext->Flags = IRPCONTEXT_COMPLETE;
277 if (MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL ||
278 MajorFunction == IRP_MJ_DEVICE_CONTROL ||
279 MajorFunction == IRP_MJ_SHUTDOWN)
280 {
281 IrpContext->Flags |= IRPCONTEXT_CANWAIT;
282 }
283 else if (MajorFunction != IRP_MJ_CLEANUP &&
284 MajorFunction != IRP_MJ_CLOSE &&
285 IoIsOperationSynchronous(Irp))
286 {
287 IrpContext->Flags |= IRPCONTEXT_CANWAIT;
288 }
289 KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
290 IrpContext->RefCount = 0;
291 IrpContext->PriorityBoost = IO_NO_INCREMENT;
292 }
293 return IrpContext;
294 }
295
296 static WORKER_THREAD_ROUTINE VfatDoRequest;
297
298 static
299 VOID
300 NTAPI
301 VfatDoRequest(
302 PVOID IrpContext)
303 {
304 InterlockedDecrement(&QueueCount);
305 DPRINT("VfatDoRequest(IrpContext %p), MajorFunction %x, %d\n",
306 IrpContext, ((PVFAT_IRP_CONTEXT)IrpContext)->MajorFunction, QueueCount);
307 VfatDispatchRequest((PVFAT_IRP_CONTEXT)IrpContext);
308 }
309
310 static
311 NTSTATUS
312 VfatQueueRequest(
313 PVFAT_IRP_CONTEXT IrpContext)
314 {
315 InterlockedIncrement(&QueueCount);
316 DPRINT("VfatQueueRequest(IrpContext %p), %d\n", IrpContext, QueueCount);
317
318 ASSERT(IrpContext != NULL);
319 ASSERT(IrpContext->Irp != NULL);
320 ASSERT(!(IrpContext->Flags & IRPCONTEXT_QUEUE) &&
321 (IrpContext->Flags & IRPCONTEXT_COMPLETE));
322
323 IrpContext->Flags |= IRPCONTEXT_CANWAIT;
324 IoMarkIrpPending(IrpContext->Irp);
325 ExInitializeWorkItem(&IrpContext->WorkQueueItem, VfatDoRequest, IrpContext);
326 ExQueueWorkItem(&IrpContext->WorkQueueItem, CriticalWorkQueue);
327 return STATUS_PENDING;
328 }
329
330 PVOID
331 VfatGetUserBuffer(
332 IN PIRP Irp,
333 IN BOOLEAN Paging)
334 {
335 ASSERT(Irp);
336
337 if (Irp->MdlAddress)
338 {
339 return MmGetSystemAddressForMdlSafe(Irp->MdlAddress, (Paging ? HighPagePriority : NormalPagePriority));
340 }
341 else
342 {
343 return Irp->UserBuffer;
344 }
345 }
346
347 NTSTATUS
348 VfatLockUserBuffer(
349 IN PIRP Irp,
350 IN ULONG Length,
351 IN LOCK_OPERATION Operation)
352 {
353 ASSERT(Irp);
354
355 if (Irp->MdlAddress)
356 {
357 return STATUS_SUCCESS;
358 }
359
360 IoAllocateMdl(Irp->UserBuffer, Length, FALSE, FALSE, Irp);
361
362 if (!Irp->MdlAddress)
363 {
364 return STATUS_INSUFFICIENT_RESOURCES;
365 }
366
367 MmProbeAndLockPages(Irp->MdlAddress, Irp->RequestorMode, Operation);
368
369 return STATUS_SUCCESS;
370 }
371
372 BOOLEAN
373 VfatCheckForDismount(
374 IN PDEVICE_EXTENSION DeviceExt,
375 IN BOOLEAN Create)
376 {
377 KIRQL OldIrql;
378 PVPB Vpb;
379 BOOLEAN Delete;
380
381 DPRINT1("VfatCheckForDismount(%p, %u)\n", DeviceExt, Create);
382
383 /* Lock VPB */
384 IoAcquireVpbSpinLock(&OldIrql);
385
386 /* Reference it and check if a create is being done */
387 Vpb = DeviceExt->IoVPB;
388 if (Vpb->ReferenceCount != Create)
389 {
390 /* Copy the VPB to our local own to prepare later dismount */
391 if (DeviceExt->SpareVPB != NULL)
392 {
393 RtlZeroMemory(DeviceExt->SpareVPB, sizeof(VPB));
394 DeviceExt->SpareVPB->Type = IO_TYPE_VPB;
395 DeviceExt->SpareVPB->Size = sizeof(VPB);
396 DeviceExt->SpareVPB->RealDevice = DeviceExt->IoVPB->RealDevice;
397 DeviceExt->SpareVPB->DeviceObject = NULL;
398 DeviceExt->SpareVPB->Flags = DeviceExt->IoVPB->Flags & VPB_REMOVE_PENDING;
399 DeviceExt->IoVPB->RealDevice->Vpb = DeviceExt->SpareVPB;
400 DeviceExt->SpareVPB = NULL;
401 DeviceExt->IoVPB->Flags |= VPB_PERSISTENT;
402 }
403
404 /* Don't do anything */
405 Delete = FALSE;
406 }
407 else
408 {
409 /* Otherwise, delete the volume */
410 Delete = TRUE;
411
412 /* Check if it has a VPB and unmount it */
413 if (Vpb->RealDevice->Vpb == Vpb)
414 {
415 Vpb->DeviceObject = NULL;
416 Vpb->Flags &= ~VPB_MOUNTED;
417 }
418 }
419
420 /* Release lock and return status */
421 IoReleaseVpbSpinLock(OldIrql);
422
423 /* If we were to delete, delete volume */
424 if (Delete)
425 {
426 PVPB DelVpb;
427
428 /* If we have a local VPB, we'll have to delete it
429 * but we won't dismount us - something went bad before
430 */
431 if (DeviceExt->SpareVPB)
432 {
433 DelVpb = DeviceExt->SpareVPB;
434 }
435 /* Otherwise, dismount our device if possible */
436 else
437 {
438 if (DeviceExt->IoVPB->ReferenceCount)
439 {
440 ObfDereferenceObject(DeviceExt->StorageDevice);
441 IoDeleteDevice(DeviceExt->VolumeDevice);
442 return Delete;
443 }
444
445 DelVpb = DeviceExt->IoVPB;
446 }
447
448 /* Delete any of the available VPB and dismount */
449 ExFreePool(DelVpb);
450 ObfDereferenceObject(DeviceExt->StorageDevice);
451 IoDeleteDevice(DeviceExt->VolumeDevice);
452
453 return Delete;
454 }
455
456 return Delete;
457 }