[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 ULONG BytesPerSector;
541
542 ASSERT(IrpContext);
543
544 DPRINT("VfatRead(IrpContext %p)\n", IrpContext);
545
546 ASSERT(IrpContext->DeviceObject);
547
548 // This request is not allowed on the main device object
549 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
550 {
551 DPRINT("VfatRead is called with the main device object.\n");
552 Status = STATUS_INVALID_DEVICE_REQUEST;
553 goto ByeBye;
554 }
555
556 ASSERT(IrpContext->DeviceExt);
557 ASSERT(IrpContext->FileObject);
558 Fcb = IrpContext->FileObject->FsContext;
559 ASSERT(Fcb);
560
561 DPRINT("<%wZ>\n", &Fcb->PathNameU);
562
563 ByteOffset = IrpContext->Stack->Parameters.Read.ByteOffset;
564 Length = IrpContext->Stack->Parameters.Read.Length;
565 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
566
567 /* fail if file is a directory and no paged read */
568 if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
569 {
570 Status = STATUS_INVALID_PARAMETER;
571 goto ByeBye;
572 }
573
574
575 DPRINT("'%wZ', Offset: %d, Length %d\n", &Fcb->PathNameU, ByteOffset.u.LowPart, Length);
576
577 if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
578 {
579 Status = STATUS_INVALID_PARAMETER;
580 goto ByeBye;
581 }
582 if (ByteOffset.QuadPart >= Fcb->RFCB.FileSize.QuadPart)
583 {
584 IrpContext->Irp->IoStatus.Information = 0;
585 Status = STATUS_END_OF_FILE;
586 goto ByeBye;
587 }
588 if (IrpContext->Irp->Flags & (IRP_PAGING_IO | IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
589 {
590 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
591 {
592 DPRINT("%d %d\n", ByteOffset.u.LowPart, Length);
593 // non cached read must be sector aligned
594 Status = STATUS_INVALID_PARAMETER;
595 goto ByeBye;
596 }
597 }
598 if (Length == 0)
599 {
600 IrpContext->Irp->IoStatus.Information = 0;
601 Status = STATUS_SUCCESS;
602 goto ByeBye;
603 }
604
605 if (Fcb->Flags & FCB_IS_VOLUME)
606 {
607 Resource = &IrpContext->DeviceExt->DirResource;
608 }
609 else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
610 {
611 Resource = &Fcb->PagingIoResource;
612 }
613 else
614 {
615 Resource = &Fcb->MainResource;
616 }
617 if (!ExAcquireResourceSharedLite(Resource,
618 IrpContext->Flags & IRPCONTEXT_CANWAIT ? TRUE : FALSE))
619 {
620 Resource = NULL;
621 Status = STATUS_PENDING;
622 goto ByeBye;
623 }
624
625 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
626 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
627 {
628 if (!FsRtlCheckLockForReadAccess(&Fcb->FileLock, IrpContext->Irp))
629 {
630 Status = STATUS_FILE_LOCK_CONFLICT;
631 goto ByeBye;
632 }
633 }
634
635 Buffer = VfatGetUserBuffer(IrpContext->Irp);
636 if (!Buffer)
637 {
638 Status = STATUS_INVALID_USER_BUFFER;
639 goto ByeBye;
640 }
641
642 if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
643 !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
644 {
645 // cached read
646 Status = STATUS_SUCCESS;
647 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
648 {
649 Length = Fcb->RFCB.FileSize.u.LowPart - ByteOffset.u.LowPart;
650 Status = /*STATUS_END_OF_FILE*/STATUS_SUCCESS;
651 }
652
653 if (IrpContext->FileObject->PrivateCacheMap == NULL)
654 {
655 CcInitializeCacheMap(IrpContext->FileObject,
656 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
657 FALSE,
658 &(VfatGlobalData->CacheMgrCallbacks),
659 Fcb);
660 }
661 if (!CcCopyRead(IrpContext->FileObject, &ByteOffset, Length,
662 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT), Buffer,
663 &IrpContext->Irp->IoStatus))
664 {
665 Status = STATUS_PENDING;
666 goto ByeBye;
667 }
668 if (!NT_SUCCESS(IrpContext->Irp->IoStatus.Status))
669 {
670 Status = IrpContext->Irp->IoStatus.Status;
671 }
672 }
673 else
674 {
675 // non cached read
676 if (ByteOffset.QuadPart + Length > ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector))
677 {
678 Length = (ULONG)(ROUND_UP(Fcb->RFCB.FileSize.QuadPart, BytesPerSector) - ByteOffset.QuadPart);
679 }
680
681 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
682 if (!NT_SUCCESS(Status))
683 {
684 goto ByeBye;
685 }
686
687 Status = VfatReadFileData(IrpContext, Length, ByteOffset, &ReturnedLength);
688 if (NT_SUCCESS(Status))
689 {
690 IrpContext->Irp->IoStatus.Information = ReturnedLength;
691 }
692 }
693
694 ByeBye:
695 if (Resource)
696 {
697 ExReleaseResourceLite(Resource);
698 }
699
700 if (Status == STATUS_PENDING)
701 {
702 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoWriteAccess);
703 if (NT_SUCCESS(Status))
704 {
705 Status = VfatQueueRequest(IrpContext);
706 }
707 else
708 {
709 IrpContext->Irp->IoStatus.Status = Status;
710 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
711 VfatFreeIrpContext(IrpContext);
712 }
713 }
714 else
715 {
716 IrpContext->Irp->IoStatus.Status = Status;
717 if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
718 !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
719 (NT_SUCCESS(Status) || Status==STATUS_END_OF_FILE))
720 {
721 IrpContext->FileObject->CurrentByteOffset.QuadPart =
722 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
723 }
724
725 IoCompleteRequest(IrpContext->Irp,
726 (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
727 VfatFreeIrpContext(IrpContext);
728 }
729 DPRINT("%x\n", Status);
730 return Status;
731 }
732
733 NTSTATUS VfatWrite (PVFAT_IRP_CONTEXT IrpContext)
734 {
735 PVFATFCB Fcb;
736 PERESOURCE Resource = NULL;
737 LARGE_INTEGER ByteOffset;
738 LARGE_INTEGER OldFileSize;
739 NTSTATUS Status = STATUS_SUCCESS;
740 ULONG Length = 0;
741 PVOID Buffer;
742 ULONG BytesPerSector;
743
744 ASSERT(IrpContext);
745
746 DPRINT("VfatWrite(IrpContext %p)\n", IrpContext);
747
748 ASSERT(IrpContext->DeviceObject);
749
750 // This request is not allowed on the main device object
751 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
752 {
753 DPRINT("VfatWrite is called with the main device object.\n");
754 Status = STATUS_INVALID_DEVICE_REQUEST;
755 goto ByeBye;
756 }
757
758 ASSERT(IrpContext->DeviceExt);
759 ASSERT(IrpContext->FileObject);
760 Fcb = IrpContext->FileObject->FsContext;
761 ASSERT(Fcb);
762
763 DPRINT("<%wZ>\n", &Fcb->PathNameU);
764
765 /* fail if file is a directory and no paged read */
766 if (*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY && !(IrpContext->Irp->Flags & IRP_PAGING_IO))
767 {
768 Status = STATUS_INVALID_PARAMETER;
769 goto ByeBye;
770 }
771
772 ByteOffset = IrpContext->Stack->Parameters.Write.ByteOffset;
773 if (ByteOffset.u.LowPart == FILE_WRITE_TO_END_OF_FILE &&
774 ByteOffset.u.HighPart == -1)
775 {
776 ByteOffset.QuadPart = Fcb->RFCB.FileSize.QuadPart;
777 }
778 Length = IrpContext->Stack->Parameters.Write.Length;
779 BytesPerSector = IrpContext->DeviceExt->FatInfo.BytesPerSector;
780
781 if (ByteOffset.u.HighPart && !(Fcb->Flags & FCB_IS_VOLUME))
782 {
783 Status = STATUS_INVALID_PARAMETER;
784 goto ByeBye;
785 }
786
787 if (Fcb->Flags & (FCB_IS_FAT | FCB_IS_VOLUME) ||
788 1 == vfatDirEntryGetFirstCluster (IrpContext->DeviceExt, &Fcb->entry))
789 {
790 if (ByteOffset.QuadPart + Length > Fcb->RFCB.FileSize.QuadPart)
791 {
792 // we can't extend the FAT, the volume or the root on FAT12/FAT16
793 Status = STATUS_END_OF_FILE;
794 goto ByeBye;
795 }
796 }
797
798 if (IrpContext->Irp->Flags & (IRP_PAGING_IO|IRP_NOCACHE) || (Fcb->Flags & FCB_IS_VOLUME))
799 {
800 if (ByteOffset.u.LowPart % BytesPerSector != 0 || Length % BytesPerSector != 0)
801 {
802 // non cached write must be sector aligned
803 Status = STATUS_INVALID_PARAMETER;
804 goto ByeBye;
805 }
806 }
807
808 if (Length == 0)
809 {
810 /* FIXME:
811 * Update last write time
812 */
813 IrpContext->Irp->IoStatus.Information = 0;
814 Status = STATUS_SUCCESS;
815 goto ByeBye;
816 }
817
818 if (IrpContext->Irp->Flags & IRP_PAGING_IO)
819 {
820 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
821 {
822 Status = STATUS_INVALID_PARAMETER;
823 goto ByeBye;
824 }
825 if (ByteOffset.u.LowPart + Length > ROUND_UP(Fcb->RFCB.AllocationSize.u.LowPart, BytesPerSector))
826 {
827 Length = ROUND_UP(Fcb->RFCB.FileSize.u.LowPart, BytesPerSector) - ByteOffset.u.LowPart;
828 }
829 }
830
831 if (Fcb->Flags & FCB_IS_VOLUME)
832 {
833 Resource = &IrpContext->DeviceExt->DirResource;
834 }
835 else if (IrpContext->Irp->Flags & IRP_PAGING_IO)
836 {
837 Resource = &Fcb->PagingIoResource;
838 }
839 else
840 {
841 Resource = &Fcb->MainResource;
842 }
843
844 if (Fcb->Flags & FCB_IS_PAGE_FILE)
845 {
846 if (!ExAcquireResourceSharedLite(Resource,
847 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
848 {
849 Resource = NULL;
850 Status = STATUS_PENDING;
851 goto ByeBye;
852 }
853 }
854 else
855 {
856 if (!ExAcquireResourceExclusiveLite(Resource,
857 (BOOLEAN)(IrpContext->Flags & IRPCONTEXT_CANWAIT)))
858 {
859 Resource = NULL;
860 Status = STATUS_PENDING;
861 goto ByeBye;
862 }
863 }
864
865 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
866 FsRtlAreThereCurrentFileLocks(&Fcb->FileLock))
867 {
868 if (!FsRtlCheckLockForWriteAccess(&Fcb->FileLock, IrpContext->Irp))
869 {
870 Status = STATUS_FILE_LOCK_CONFLICT;
871 goto ByeBye;
872 }
873 }
874
875 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT) && !(Fcb->Flags & FCB_IS_VOLUME))
876 {
877 if (ByteOffset.u.LowPart + Length > Fcb->RFCB.AllocationSize.u.LowPart)
878 {
879 Status = STATUS_PENDING;
880 goto ByeBye;
881 }
882 }
883
884 OldFileSize = Fcb->RFCB.FileSize;
885
886 Buffer = VfatGetUserBuffer(IrpContext->Irp);
887 if (!Buffer)
888 {
889 Status = STATUS_INVALID_USER_BUFFER;
890 goto ByeBye;
891 }
892
893
894 if (!(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)) &&
895 !(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
896 ByteOffset.u.LowPart + Length > Fcb->RFCB.FileSize.u.LowPart)
897 {
898 LARGE_INTEGER AllocationSize;
899 AllocationSize.QuadPart = ByteOffset.u.LowPart + Length;
900 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject, Fcb,
901 IrpContext->DeviceExt, &AllocationSize);
902 if (!NT_SUCCESS (Status))
903 {
904 goto ByeBye;
905 }
906 }
907
908 if (!(IrpContext->Irp->Flags & (IRP_NOCACHE|IRP_PAGING_IO)) &&
909 !(Fcb->Flags & (FCB_IS_PAGE_FILE|FCB_IS_VOLUME)))
910 {
911 // cached write
912
913 if (IrpContext->FileObject->PrivateCacheMap == NULL)
914 {
915 CcInitializeCacheMap(IrpContext->FileObject,
916 (PCC_FILE_SIZES)(&Fcb->RFCB.AllocationSize),
917 FALSE,
918 &VfatGlobalData->CacheMgrCallbacks,
919 Fcb);
920 }
921 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
922 {
923 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
924 }
925 if (CcCopyWrite(IrpContext->FileObject, &ByteOffset, Length,
926 1 /*IrpContext->Flags & IRPCONTEXT_CANWAIT*/, Buffer))
927 {
928 IrpContext->Irp->IoStatus.Information = Length;
929 Status = STATUS_SUCCESS;
930 }
931 else
932 {
933 Status = STATUS_UNSUCCESSFUL;
934 }
935 }
936 else
937 {
938 // non cached write
939
940 if (ByteOffset.QuadPart > OldFileSize.QuadPart)
941 {
942 CcZeroData(IrpContext->FileObject, &OldFileSize, &ByteOffset, TRUE);
943 }
944
945 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
946 if (!NT_SUCCESS(Status))
947 {
948 goto ByeBye;
949 }
950
951 Status = VfatWriteFileData(IrpContext, Length, ByteOffset);
952 if (NT_SUCCESS(Status))
953 {
954 IrpContext->Irp->IoStatus.Information = Length;
955 }
956 }
957
958 if (!(IrpContext->Irp->Flags & IRP_PAGING_IO) &&
959 !(Fcb->Flags & (FCB_IS_FAT|FCB_IS_VOLUME)))
960 {
961 if(!(*Fcb->Attributes & FILE_ATTRIBUTE_DIRECTORY))
962 {
963 LARGE_INTEGER SystemTime;
964 // set dates and times
965 KeQuerySystemTime (&SystemTime);
966 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
967 {
968 FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
969 &SystemTime, &Fcb->entry.FatX.UpdateDate,
970 &Fcb->entry.FatX.UpdateTime);
971 Fcb->entry.FatX.AccessDate = Fcb->entry.FatX.UpdateDate;
972 Fcb->entry.FatX.AccessTime = Fcb->entry.FatX.UpdateTime;
973 }
974 else
975 {
976 FsdSystemTimeToDosDateTime (IrpContext->DeviceExt,
977 &SystemTime, &Fcb->entry.Fat.UpdateDate,
978 &Fcb->entry.Fat.UpdateTime);
979 Fcb->entry.Fat.AccessDate = Fcb->entry.Fat.UpdateDate;
980 }
981 /* set date and times to dirty */
982 Fcb->Flags |= FCB_IS_DIRTY;
983 }
984 }
985
986 ByeBye:
987 if (Resource)
988 {
989 ExReleaseResourceLite(Resource);
990 }
991
992 if (Status == STATUS_PENDING)
993 {
994 Status = VfatLockUserBuffer(IrpContext->Irp, Length, IoReadAccess);
995 if (NT_SUCCESS(Status))
996 {
997 Status = VfatQueueRequest(IrpContext);
998 }
999 else
1000 {
1001 IrpContext->Irp->IoStatus.Status = Status;
1002 IoCompleteRequest(IrpContext->Irp, IO_NO_INCREMENT);
1003 VfatFreeIrpContext(IrpContext);
1004 }
1005 }
1006 else
1007 {
1008 IrpContext->Irp->IoStatus.Status = Status;
1009 if (IrpContext->FileObject->Flags & FO_SYNCHRONOUS_IO &&
1010 !(IrpContext->Irp->Flags & IRP_PAGING_IO) && NT_SUCCESS(Status))
1011 {
1012 IrpContext->FileObject->CurrentByteOffset.QuadPart =
1013 ByteOffset.QuadPart + IrpContext->Irp->IoStatus.Information;
1014 }
1015
1016 IoCompleteRequest(IrpContext->Irp,
1017 (CCHAR)(NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT));
1018 VfatFreeIrpContext(IrpContext);
1019 }
1020 DPRINT("%x\n", Status);
1021 return Status;
1022 }
1023
1024