[FASTFAT]
[reactos.git] / reactos / drivers / filesystems / fastfat / rw.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/vfat/rw.c
5 * PURPOSE: VFAT Filesystem
6 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
7 *
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #define NDEBUG
13 #include "vfat.h"
14
15 /*
16 * Uncomment to enable strict verification of cluster/offset pair
17 * caching. If this option is enabled you lose all the benefits of
18 * the caching and the read/write operations will actually be
19 * slower. It's meant only for debugging!!!
20 * - Filip Navara, 26/07/2004
21 */
22 /* #define DEBUG_VERIFY_OFFSET_CACHING */
23
24 /* FUNCTIONS *****************************************************************/
25
26 NTSTATUS
27 NextCluster(PDEVICE_EXTENSION DeviceExt,
28 ULONG FirstCluster,
29 PULONG CurrentCluster,
30 BOOLEAN Extend)
31 /*
32 * Return the next cluster in a FAT chain, possibly extending the chain if
33 * necessary
34 */
35 {
36 if (FirstCluster == 1)
37 {
38 (*CurrentCluster) += DeviceExt->FatInfo.SectorsPerCluster;
39 return(STATUS_SUCCESS);
40 }
41 else
42 {
43 if (Extend)
44 return GetNextClusterExtend(DeviceExt, (*CurrentCluster), CurrentCluster);
45 else
46 return GetNextCluster(DeviceExt, (*CurrentCluster), CurrentCluster);
47 }
48 }
49
50 NTSTATUS
51 OffsetToCluster(PDEVICE_EXTENSION DeviceExt,
52 ULONG FirstCluster,
53 ULONG FileOffset,
54 PULONG Cluster,
55 BOOLEAN Extend)
56 /*
57 * Return the cluster corresponding to an offset within a file,
58 * possibly extending the file if necessary
59 */
60 {
61 ULONG CurrentCluster;
62 ULONG i;
63 NTSTATUS Status;
64 /*
65 DPRINT("OffsetToCluster(DeviceExt %x, Fcb %x, FirstCluster %x,"
66 " FileOffset %x, Cluster %x, Extend %d)\n", DeviceExt,
67 Fcb, FirstCluster, FileOffset, Cluster, Extend);
68 */
69 if (FirstCluster == 0)
70 {
71 DbgPrint("OffsetToCluster is called with FirstCluster = 0!\n");
72 ASSERT(FALSE);
73 }
74
75 if (FirstCluster == 1)
76 {
77 /* root of FAT16 or FAT12 */
78 *Cluster = DeviceExt->FatInfo.rootStart + FileOffset
79 / (DeviceExt->FatInfo.BytesPerCluster) * DeviceExt->FatInfo.SectorsPerCluster;
80 return(STATUS_SUCCESS);
81 }
82 else
83 {
84 CurrentCluster = FirstCluster;
85 if (Extend)
86 {
87 for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
88 {
89 Status = GetNextClusterExtend (DeviceExt, CurrentCluster, &CurrentCluster);
90 if (!NT_SUCCESS(Status))
91 return(Status);
92 }
93 *Cluster = CurrentCluster;
94 }
95 else
96 {
97 for (i = 0; i < FileOffset / DeviceExt->FatInfo.BytesPerCluster; i++)
98 {
99 Status = GetNextCluster (DeviceExt, CurrentCluster, &CurrentCluster);
100 if (!NT_SUCCESS(Status))
101 return(Status);
102 }
103 *Cluster = CurrentCluster;
104 }
105 return(STATUS_SUCCESS);
106 }
107 }
108
109 static NTSTATUS
110 VfatReadFileData (PVFAT_IRP_CONTEXT IrpContext,
111 ULONG Length,
112 LARGE_INTEGER ReadOffset,
113 PULONG LengthRead)
114 /*
115 * FUNCTION: Reads data from a file
116 */
117 {
118 ULONG CurrentCluster;
119 ULONG FirstCluster;
120 ULONG StartCluster;
121 ULONG ClusterCount;
122 LARGE_INTEGER StartOffset;
123 PDEVICE_EXTENSION DeviceExt;
124 BOOLEAN First = TRUE;
125 PVFATFCB Fcb;
126 NTSTATUS Status;
127 ULONG BytesDone;
128 ULONG BytesPerSector;
129 ULONG BytesPerCluster;
130 ULONG LastCluster;
131 ULONG LastOffset;
132
133 /* PRECONDITION */
134 ASSERT(IrpContext);
135 DeviceExt = IrpContext->DeviceExt;
136 ASSERT(DeviceExt);
137 ASSERT(DeviceExt->FatInfo.BytesPerCluster);
138 ASSERT(IrpContext->FileObject);
139 ASSERT(IrpContext->FileObject->FsContext2 != NULL);
140
141 DPRINT("VfatReadFileData(DeviceExt %p, FileObject %p, "
142 "Length %d, ReadOffset 0x%I64x)\n", DeviceExt,
143 IrpContext->FileObject, Length, ReadOffset.QuadPart);
144
145 *LengthRead = 0;
146
147 Fcb = IrpContext->FileObject->FsContext;
148 BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
149 BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
150
151 ASSERT(ReadOffset.QuadPart + Length <= ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector));
152 ASSERT(ReadOffset.u.LowPart % BytesPerSector == 0);
153 ASSERT(Length % BytesPerSector == 0);
154
155 /* Is this a read of the FAT? */
156 if (Fcb->Flags & FCB_IS_FAT)
157 {
158 ReadOffset.QuadPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
159 Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
160
161 if (NT_SUCCESS(Status))
162 {
163 *LengthRead = Length;
164 }
165 else
166 {
167 DPRINT1("FAT reading failed, Status %x\n", Status);
168 }
169 return Status;
170 }
171 /* Is this a read of the Volume ? */
172 if (Fcb->Flags & FCB_IS_VOLUME)
173 {
174 Status = VfatReadDiskPartial(IrpContext, &ReadOffset, Length, 0, TRUE);
175 if (NT_SUCCESS(Status))
176 {
177 *LengthRead = Length;
178 }
179 else
180 {
181 DPRINT1("Volume reading failed, Status %x\n", Status);
182 }
183 return Status;
184 }
185
186 /*
187 * Find the first cluster
188 */
189 FirstCluster = CurrentCluster =
190 vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
191
192 if (FirstCluster == 1)
193 {
194 // Directory of FAT12/16 needs a special handling
195 if (ReadOffset.u.LowPart + Length > DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector)
196 {
197 Length = DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector - ReadOffset.u.LowPart;
198 }
199 ReadOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
200
201 // Fire up the read command
202
203 Status = VfatReadDiskPartial (IrpContext, &ReadOffset, Length, 0, TRUE);
204 if (NT_SUCCESS(Status))
205 {
206 *LengthRead = Length;
207 }
208 return Status;
209 }
210
211 ExAcquireFastMutex(&Fcb->LastMutex);
212 LastCluster = Fcb->LastCluster;
213 LastOffset = Fcb->LastOffset;
214 ExReleaseFastMutex(&Fcb->LastMutex);
215
216 /*
217 * Find the cluster to start the read from
218 */
219 if (LastCluster > 0 && ReadOffset.u.LowPart >= LastOffset)
220 {
221 Status = OffsetToCluster(DeviceExt, LastCluster,
222 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) -
223 LastOffset,
224 &CurrentCluster, FALSE);
225 #ifdef DEBUG_VERIFY_OFFSET_CACHING
226 /* DEBUG VERIFICATION */
227 {
228 ULONG CorrectCluster;
229 OffsetToCluster(DeviceExt, FirstCluster,
230 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
231 &CorrectCluster, FALSE);
232 if (CorrectCluster != CurrentCluster)
233 KeBugCheck(FAT_FILE_SYSTEM);
234 }
235 #endif
236 }
237 else
238 {
239 Status = OffsetToCluster(DeviceExt, FirstCluster,
240 ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster),
241 &CurrentCluster, FALSE);
242 }
243 if (!NT_SUCCESS(Status))
244 {
245 return(Status);
246 }
247
248 ExAcquireFastMutex(&Fcb->LastMutex);
249 Fcb->LastCluster = CurrentCluster;
250 Fcb->LastOffset = ROUND_DOWN (ReadOffset.u.LowPart, BytesPerCluster);
251 ExReleaseFastMutex(&Fcb->LastMutex);
252
253 KeInitializeEvent(&IrpContext->Event, NotificationEvent, FALSE);
254 IrpContext->RefCount = 1;
255
256 while (Length > 0 && CurrentCluster != 0xffffffff)
257 {
258 StartCluster = CurrentCluster;
259 StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
260 BytesDone = 0;
261 ClusterCount = 0;
262
263 do
264 {
265 ClusterCount++;
266 if (First)
267 {
268 BytesDone = min (Length, BytesPerCluster - (ReadOffset.u.LowPart % BytesPerCluster));
269 StartOffset.QuadPart += ReadOffset.u.LowPart % BytesPerCluster;
270 First = FALSE;
271 }
272 else
273 {
274 if (Length - BytesDone > BytesPerCluster)
275 {
276 BytesDone += BytesPerCluster;
277 }
278 else
279 {
280 BytesDone = Length;
281 }
282 }
283 Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
284 }
285 while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
286 DPRINT("start %08x, next %08x, count %d\n",
287 StartCluster, CurrentCluster, ClusterCount);
288
289 ExAcquireFastMutex(&Fcb->LastMutex);
290 Fcb->LastCluster = StartCluster + (ClusterCount - 1);
291 Fcb->LastOffset = ROUND_DOWN(ReadOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
292 ExReleaseFastMutex(&Fcb->LastMutex);
293
294 // Fire up the read command
295 Status = VfatReadDiskPartial (IrpContext, &StartOffset, BytesDone, *LengthRead, FALSE);
296 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
297 {
298 break;
299 }
300 *LengthRead += BytesDone;
301 Length -= BytesDone;
302 ReadOffset.u.LowPart += BytesDone;
303 }
304 if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
305 {
306 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
307 }
308 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
309 {
310 if (Length > 0)
311 {
312 Status = STATUS_UNSUCCESSFUL;
313 }
314 else
315 {
316 Status = IrpContext->Irp->IoStatus.Status;
317 }
318 }
319 return Status;
320 }
321
322 static NTSTATUS
323 VfatWriteFileData(PVFAT_IRP_CONTEXT IrpContext,
324 ULONG Length,
325 LARGE_INTEGER WriteOffset)
326 {
327 PDEVICE_EXTENSION DeviceExt;
328 PVFATFCB Fcb;
329 ULONG Count;
330 ULONG FirstCluster;
331 ULONG CurrentCluster;
332 ULONG BytesDone;
333 ULONG StartCluster;
334 ULONG ClusterCount;
335 NTSTATUS Status = STATUS_SUCCESS;
336 BOOLEAN First = TRUE;
337 ULONG BytesPerSector;
338 ULONG BytesPerCluster;
339 LARGE_INTEGER StartOffset;
340 ULONG BufferOffset;
341 ULONG LastCluster;
342 ULONG LastOffset;
343
344 /* PRECONDITION */
345 ASSERT(IrpContext);
346 DeviceExt = IrpContext->DeviceExt;
347 ASSERT(DeviceExt);
348 ASSERT(DeviceExt->FatInfo.BytesPerCluster);
349 ASSERT(IrpContext->FileObject);
350 ASSERT(IrpContext->FileObject->FsContext2 != NULL);
351
352 Fcb = IrpContext->FileObject->FsContext;
353 BytesPerCluster = DeviceExt->FatInfo.BytesPerCluster;
354 BytesPerSector = DeviceExt->FatInfo.BytesPerSector;
355
356 DPRINT("VfatWriteFileData(DeviceExt %p, FileObject %p, "
357 "Length %d, WriteOffset 0x%I64x), '%wZ'\n", DeviceExt,
358 IrpContext->FileObject, Length, WriteOffset,
359 &Fcb->PathNameU);
360
361 ASSERT(WriteOffset.QuadPart + Length <= Fcb->RFCB.AllocationSize.QuadPart);
362 ASSERT(WriteOffset.u.LowPart % BytesPerSector == 0);
363 ASSERT(Length % BytesPerSector == 0);
364
365 // Is this a write of the volume ?
366 if (Fcb->Flags & FCB_IS_VOLUME)
367 {
368 Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, TRUE);
369 if (!NT_SUCCESS(Status))
370 {
371 DPRINT1("Volume writing failed, Status %x\n", Status);
372 }
373 return Status;
374 }
375
376 // Is this a write to the FAT ?
377 if (Fcb->Flags & FCB_IS_FAT)
378 {
379 WriteOffset.u.LowPart += DeviceExt->FatInfo.FATStart * BytesPerSector;
380 IrpContext->RefCount = 1;
381 for (Count = 0; Count < DeviceExt->FatInfo.FATCount; Count++)
382 {
383 Status = VfatWriteDiskPartial(IrpContext, &WriteOffset, Length, 0, FALSE);
384 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
385 {
386 DPRINT1("FAT writing failed, Status %x\n", Status);
387 break;
388 }
389 WriteOffset.u.LowPart += Fcb->RFCB.FileSize.u.LowPart;
390 }
391 if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
392 {
393 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
394 }
395 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
396 {
397 Status = IrpContext->Irp->IoStatus.Status;
398 }
399 return Status;
400 }
401
402 /*
403 * Find the first cluster
404 */
405 FirstCluster = CurrentCluster =
406 vfatDirEntryGetFirstCluster (DeviceExt, &Fcb->entry);
407
408 if (FirstCluster == 1)
409 {
410 ASSERT(WriteOffset.u.LowPart + Length <= DeviceExt->FatInfo.rootDirectorySectors * BytesPerSector);
411 // Directory of FAT12/16 needs a special handling
412 WriteOffset.u.LowPart += DeviceExt->FatInfo.rootStart * BytesPerSector;
413 // Fire up the write command
414 Status = VfatWriteDiskPartial (IrpContext, &WriteOffset, Length, 0, TRUE);
415 return Status;
416 }
417
418 ExAcquireFastMutex(&Fcb->LastMutex);
419 LastCluster = Fcb->LastCluster;
420 LastOffset = Fcb->LastOffset;
421 ExReleaseFastMutex(&Fcb->LastMutex);
422
423 /*
424 * Find the cluster to start the write from
425 */
426 if (LastCluster > 0 && WriteOffset.u.LowPart >= LastOffset)
427 {
428 Status = OffsetToCluster(DeviceExt, LastCluster,
429 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) -
430 LastOffset,
431 &CurrentCluster, FALSE);
432 #ifdef DEBUG_VERIFY_OFFSET_CACHING
433 /* DEBUG VERIFICATION */
434 {
435 ULONG CorrectCluster;
436 OffsetToCluster(DeviceExt, FirstCluster,
437 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
438 &CorrectCluster, FALSE);
439 if (CorrectCluster != CurrentCluster)
440 KeBugCheck(FAT_FILE_SYSTEM);
441 }
442 #endif
443 }
444 else
445 {
446 Status = OffsetToCluster(DeviceExt, FirstCluster,
447 ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster),
448 &CurrentCluster, FALSE);
449 }
450
451 if (!NT_SUCCESS(Status))
452 {
453 return(Status);
454 }
455
456 ExAcquireFastMutex(&Fcb->LastMutex);
457 Fcb->LastCluster = CurrentCluster;
458 Fcb->LastOffset = ROUND_DOWN (WriteOffset.u.LowPart, BytesPerCluster);
459 ExReleaseFastMutex(&Fcb->LastMutex);
460
461 IrpContext->RefCount = 1;
462 BufferOffset = 0;
463
464 while (Length > 0 && CurrentCluster != 0xffffffff)
465 {
466 StartCluster = CurrentCluster;
467 StartOffset.QuadPart = ClusterToSector(DeviceExt, StartCluster) * BytesPerSector;
468 BytesDone = 0;
469 ClusterCount = 0;
470
471 do
472 {
473 ClusterCount++;
474 if (First)
475 {
476 BytesDone = min (Length, BytesPerCluster - (WriteOffset.u.LowPart % BytesPerCluster));
477 StartOffset.QuadPart += WriteOffset.u.LowPart % BytesPerCluster;
478 First = FALSE;
479 }
480 else
481 {
482 if (Length - BytesDone > BytesPerCluster)
483 {
484 BytesDone += BytesPerCluster;
485 }
486 else
487 {
488 BytesDone = Length;
489 }
490 }
491 Status = NextCluster(DeviceExt, FirstCluster, &CurrentCluster, FALSE);
492 }
493 while (StartCluster + ClusterCount == CurrentCluster && NT_SUCCESS(Status) && Length > BytesDone);
494 DPRINT("start %08x, next %08x, count %d\n",
495 StartCluster, CurrentCluster, ClusterCount);
496
497 ExAcquireFastMutex(&Fcb->LastMutex);
498 Fcb->LastCluster = StartCluster + (ClusterCount - 1);
499 Fcb->LastOffset = ROUND_DOWN(WriteOffset.u.LowPart, BytesPerCluster) + (ClusterCount - 1) * BytesPerCluster;
500 ExReleaseFastMutex(&Fcb->LastMutex);
501
502 // Fire up the write command
503 Status = VfatWriteDiskPartial (IrpContext, &StartOffset, BytesDone, BufferOffset, FALSE);
504 if (!NT_SUCCESS(Status) && Status != STATUS_PENDING)
505 {
506 break;
507 }
508 BufferOffset += BytesDone;
509 Length -= BytesDone;
510 WriteOffset.u.LowPart += BytesDone;
511 }
512 if (0 != InterlockedDecrement((PLONG)&IrpContext->RefCount))
513 {
514 KeWaitForSingleObject(&IrpContext->Event, Executive, KernelMode, FALSE, NULL);
515 }
516 if (NT_SUCCESS(Status) || Status == STATUS_PENDING)
517 {
518 if (Length > 0)
519 {
520 Status = STATUS_UNSUCCESSFUL;
521 }
522 else
523 {
524 Status = IrpContext->Irp->IoStatus.Status;
525 }
526 }
527 return Status;
528 }
529
530 NTSTATUS
531 VfatRead(PVFAT_IRP_CONTEXT IrpContext)
532 {
533 NTSTATUS Status;
534 PVFATFCB Fcb;
535 ULONG Length = 0;
536 ULONG ReturnedLength = 0;
537 PERESOURCE Resource = NULL;
538 LARGE_INTEGER ByteOffset;
539 PVOID Buffer;
540 PDEVICE_OBJECT DeviceToVerify;
541 ULONG BytesPerSector;
542
543 ASSERT(IrpContext);
544
545 DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
546
547 ASSERT(IrpContext->DeviceObject);
548
549 // This request is not allowed on the main device object
550 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
551 {
552 DPRINT("VfatRead is called with the main device object.\n");
553 Status = STATUS_INVALID_DEVICE_REQUEST;
554 goto ByeBye;
555 }
556
557 ASSERT(IrpContext->DeviceExt);
558 ASSERT(IrpContext->FileObject);
559 Fcb = IrpContext->FileObject->FsContext;
560 ASSERT(Fcb);
561
562 DPRINT("<%wZ>\n", &Fcb->PathNameU);
563
564 if (Fcb->Flags & FCB_IS_PAGE_FILE)
565 {
566 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
567 IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
568 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
569 DPRINT("Read from page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Read.ByteOffset.QuadPart);
570 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
571 VfatFreeIrpContext(IrpContext);
572 return Status;
573 }
574
575 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
576 Length = IrpContext->Stack->Parameters.Read.Length;
577 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
578
579 /* fail if file is a directory and no paged read */
580 if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
581 {
582 Status = STATUS_INVALID_PARAMETER;
583 goto ByeBye;
584 }
585
586
587 DPRINT("'%wZ', Offset: %d, Length %d\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
588
589 if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
590 {
591 Status = STATUS_INVALID_PARAMETER;
592 goto ByeBye;
593 }
594 if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
595 {
596 IrpContext->Irp->IoStatus.Information = 0;
597 Status = STATUS_END_OF_FILE;
598 goto ByeBye;
599 }
600 if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
601 {
602 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
603 {
604 DPRINT("%d %d\n", ByteOffset.u.LowPart, Length);
605 // non cached read must be sector aligned
606 Status = STATUS_INVALID_PARAMETER;
607 goto ByeBye;
608 }
609 }
610 if (Length == 0)
611 {
612 IrpContext->Irp->IoStatus.Information = 0;
613 Status = STATUS_SUCCESS;
614 goto ByeBye;
615 }
616
617 if (Fcb->Flags & FCB_IS_VOLUME)
618 {
619 Resource = &IrpContext->DeviceExt->DirResource;
620 }
621 else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
622 {
623 Resource = &Fcb->PagingIoResource;
624 }
625 else
626 {
627 Resource = &Fcb->MainResource;
628 }
629 if (!ExAcquireResourceSharedLite(Resource,
630 IrpContext->Flags & IRPCONTEXT_CANWAIT ? TRUE : FALSE))
631 {
632 Resource = NULL;
633 Status = STATUS_PENDING;
634 goto ByeBye;
635 }
636
637 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
638 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
639 {
640 if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
641 {
642 Status = STATUS_FILE_LOCK_CONFLICT;
643 goto ByeBye;
644 }
645 }
646
647 Buffer = VfatGetUserBuffer(IrpContext->Irp);
648 if (!Buffer)
649 {
650 Status = STATUS_INVALID_USER_BUFFER;
651 goto ByeBye;
652 }
653
654 if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
655 !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
656 {
657 // cached read
658 Status = STATUS_SUCCESS;
659 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
660 {
661 Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
662 Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
663 }
664
665 if (IrpContext->FileObject->PrivateCacheMap == NULL)
666 {
667 CcInitializeCacheMap(IrpContext->FileObject,
668 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
669 FALSE,
670 &(VfatGlobalData->CacheMgrCallbacks),
671 Fcb);
672 }
673 if (!CcCopyRead(IrpContext->FileObject, &ByteOffset, Length,
674 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT), Buffer,
675 &IrpContext->Irp->IoStatus))
676 {
677 Status = STATUS_PENDING;
678 goto ByeBye;
679 }
680 if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
681 {
682 Status = IrpContext->Irp->IoStatus.Status;
683 }
684 }
685 else
686 {
687 // non cached read
688 if (ByteOffset.QuadPart + Length > ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
689 {
690 Length = (ULONG)(ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
691 }
692
693 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
694 if (!NT_SUCCESS(Status))
695 {
696 goto ByeBye;
697 }
698
699 Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
700 if (Status == STATUS_VERIFY_REQUIRED)
701 {
702 DPRINT("VfatReadFileData returned STATUS_VERIFY_REQUIRED\n");
703 DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
704 IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
705 Status = IoVerifyVolume (DeviceToVerify, FALSE);
706
707 if (NT_SUCCESS(Status))
708 {
709 Status = VfatReadFileData(IrpContext, Length,
710 ByteOffset, &ReturnedLength);
711 }
712 }
713
714 if (NT_SUCCESS(Status))
715 {
716 IrpContext->Irp->IoStatus.Information = ReturnedLength;
717 }
718 }
719
720 ByeBye:
721 if (Resource)
722 {
723 ExReleaseResourceLite(Resource);
724 }
725
726 if (Status == STATUS_PENDING)
727 {
728 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
729 if (NT_SUCCESS(Status))
730 {
731 Status = VfatQueueRequest(IrpContext);
732 }
733 else
734 {
735 IrpContext->Irp->IoStatus.Status = Status;
736 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
737 VfatFreeIrpContext(IrpContext);
738 }
739 }
740 else
741 {
742 IrpContext->Irp->IoStatus.Status = Status;
743 if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
744 !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
745 (NT_SUCCESS(Status) || Status==STATUS_END_OF_FILE))
746 {
747 IrpContext->FileObject->CurrentByteOffset.QuadPart =
748 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
749 }
750
751 IoCompleteRequest(IrpContext->Irp,
752 (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
753 VfatFreeIrpContext(IrpContext);
754 }
755 DPRINT("%x\n", Status);
756 return Status;
757 }
758
759 NTSTATUS VfatWrite (PVFAT_IRP_CONTEXT IrpContext)
760 {
761 PVFATFCB Fcb;
762 PERESOURCE Resource = NULL;
763 LARGE_INTEGER ByteOffset;
764 LARGE_INTEGER OldFileSize;
765 NTSTATUS Status = STATUS_SUCCESS;
766 ULONG Length = 0;
767 ULONG OldAllocationSize;
768 PVOID Buffer;
769 ULONG BytesPerSector;
770 PDEVICE_OBJECT DeviceToVerify;
771
772 ASSERT(IrpContext);
773
774 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
775
776 ASSERT(IrpContext->DeviceObject);
777
778 // This request is not allowed on the main device object
779 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
780 {
781 DPRINT("VfatWrite is called with the main device object.\n");
782 Status = STATUS_INVALID_DEVICE_REQUEST;
783 goto ByeBye;
784 }
785
786 ASSERT(IrpContext->DeviceExt);
787 ASSERT(IrpContext->FileObject);
788 Fcb = IrpContext->FileObject->FsContext;
789 ASSERT(Fcb);
790
791 DPRINT("<%wZ>\n", &Fcb->PathNameU);
792
793 if (Fcb->Flags & FCB_IS_PAGE_FILE)
794 {
795 PFATINFO FatInfo = &IrpContext->DeviceExt->FatInfo;
796 IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart += FatInfo->dataStart * FatInfo->BytesPerSector;
797 IoSkipCurrentIrpStackLocation(IrpContext->Irp);
798 DPRINT("Write to page file, disk offset %I64x\n", IrpContext->Stack->Parameters.Write.ByteOffset.QuadPart);
799 Status = IoCallDriver(IrpContext->DeviceExt->StorageDevice, IrpContext->Irp);
800 VfatFreeIrpContext(IrpContext);
801 return Status;
802 }
803
804 /* fail if file is a directory and no paged read */
805 if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
806 {
807 Status = STATUS_INVALID_PARAMETER;
808 goto ByeBye;
809 }
810
811 ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
812 if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
813 ByteOffset.u.HighPart == -1)
814 {
815 ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
816 }
817 Length = IrpContext->Stack->Parameters.Write.Length;
818 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
819
820 if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
821 {
822 Status = STATUS_INVALID_PARAMETER;
823 goto ByeBye;
824 }
825
826 if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
827 1 == vfatDirEntryGetFirstCluster (IrpContext->DeviceExt, &Fcb->entry))
828 {
829 if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
830 {
831 // we can't extend the FAT, the volume or the root on FAT12/FAT16
832 Status = STATUS_END_OF_FILE;
833 goto ByeBye;
834 }
835 }
836
837 if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
838 {
839 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
840 {
841 // non cached write must be sector aligned
842 Status = STATUS_INVALID_PARAMETER;
843 goto ByeBye;
844 }
845 }
846
847 if (Length == 0)
848 {
849 /* FIXME:
850 * Update last write time
851 */
852 IrpContext->Irp->IoStatus.Information = 0;
853 Status = STATUS_SUCCESS;
854 goto ByeBye;
855 }
856
857 if (IrpContext->Irp->Flags & IRP_PAGING_IO)
858 {
859 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
860 {
861 Status = STATUS_INVALID_PARAMETER;
862 goto ByeBye;
863 }
864 if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
865 {
866 Length = ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
867 }
868 }
869
870 if (Fcb->Flags & FCB_IS_VOLUME)
871 {
872 Resource = &IrpContext->DeviceExt->DirResource;
873 }
874 else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
875 {
876 Resource = &Fcb->PagingIoResource;
877 }
878 else
879 {
880 Resource = &Fcb->MainResource;
881 }
882
883 if (Fcb->Flags & FCB_IS_PAGE_FILE)
884 {
885 if (!ExAcquireResourceSharedLite(Resource,
886 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
887 {
888 Resource = NULL;
889 Status = STATUS_PENDING;
890 goto ByeBye;
891 }
892 }
893 else
894 {
895 if (!ExAcquireResourceExclusiveLite(Resource,
896 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
897 {
898 Resource = NULL;
899 Status = STATUS_PENDING;
900 goto ByeBye;
901 }
902 }
903
904 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
905 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
906 {
907 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
908 {
909 Status = STATUS_FILE_LOCK_CONFLICT;
910 goto ByeBye;
911 }
912 }
913
914 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
915 {
916 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
917 {
918 Status = STATUS_PENDING;
919 goto ByeBye;
920 }
921 }
922
923 OldFileSize = Fcb->RFCB.FileSize;
924 OldAllocationSize = Fcb->RFCB.AllocationSize.u.LowPart;
925
926 Buffer = VfatGetUserBuffer(IrpContext->Irp);
927 if (!Buffer)
928 {
929 Status = STATUS_INVALID_USER_BUFFER;
930 goto ByeBye;
931 }
932
933
934 if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) &&
935 !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
936 ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
937 {
938 LARGE_INTEGER AllocationSize;
939 AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
940 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
941 IrpContext->DeviceExt, &AllocationSize);
942 if (!NT_SUCCESS (Status))
943 {
944 goto ByeBye;
945 }
946 }
947
948 if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
949 !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
950 {
951 // cached write
952
953 if (IrpContext->FileObject->PrivateCacheMap == NULL)
954 {
955 CcInitializeCacheMap(IrpContext->FileObject,
956 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
957 FALSE,
958 &VfatGlobalData->CacheMgrCallbacks,
959 Fcb);
960 }
961 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
962 {
963 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
964 }
965 if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
966 1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
967 {
968 IrpContext->Irp->IoStatus.Information = Length;
969 Status = STATUS_SUCCESS;
970 }
971 else
972 {
973 Status = STATUS_UNSUCCESSFUL;
974 }
975 }
976 else
977 {
978 // non cached write
979
980 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
981 {
982 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
983 }
984
985 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
986 if (!NT_SUCCESS(Status))
987 {
988 goto ByeBye;
989 }
990
991 Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
992 if (Status == STATUS_VERIFY_REQUIRED)
993 {
994 DPRINT("VfatWriteFileData returned STATUS_VERIFY_REQUIRED\n");
995 DeviceToVerify = IoGetDeviceToVerify(PsGetCurrentThread());
996 IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
997 Status = IoVerifyVolume (DeviceToVerify, FALSE);
998
999 if (NT_SUCCESS(Status))
1000 {
1001 Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
1002 }
1003 }
1004
1005 if (NT_SUCCESS(Status))
1006 {
1007 IrpContext->Irp->IoStatus.Information = Length;
1008 }
1009 }
1010
1011 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
1012 !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
1013 {
1014 if(!(*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
1015 {
1016 LARGE_INTEGER SystemTime;
1017 // set dates and times
1018 KeQuerySystemTime (&SystemTime);
1019 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1020 {
1021 FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
1022 &SystemTime, &Fcb->entry.FatX.UpdateDate,
1023 &Fcb->entry.FatX.UpdateTime);
1024 Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
1025 Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
1026 }
1027 else
1028 {
1029 FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
1030 &SystemTime, &Fcb->entry.Fat.UpdateDate,
1031 &Fcb->entry.Fat.UpdateTime);
1032 Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
1033 }
1034 /* set date and times to dirty */
1035 Fcb->Flags |= FCB_IS_DIRTY;
1036 }
1037 }
1038
1039 ByeBye:
1040 if (Resource)
1041 {
1042 ExReleaseResourceLite(Resource);
1043 }
1044
1045 if (Status == STATUS_PENDING)
1046 {
1047 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
1048 if (NT_SUCCESS(Status))
1049 {
1050 Status = VfatQueueRequest(IrpContext);
1051 }
1052 else
1053 {
1054 IrpContext->Irp->IoStatus.Status = Status;
1055 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
1056 VfatFreeIrpContext(IrpContext);
1057 }
1058 }
1059 else
1060 {
1061 IrpContext->Irp->IoStatus.Status = Status;
1062 if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
1063 !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
1064 {
1065 IrpContext->FileObject->CurrentByteOffset.QuadPart =
1066 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1067 }
1068
1069 IoCompleteRequest(IrpContext->Irp,
1070 (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
1071 VfatFreeIrpContext(IrpContext);
1072 }
1073 DPRINT("%x\n", Status);
1074 return Status;
1075 }
1076
1077