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