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