xref: /reactos/drivers/filesystems/cdfs/read.c (revision 40462c92)
1 /*++
2 
3 Copyright (c) 1989-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Read.c
8 
9 Abstract:
10 
11     This module implements the File Read routine for Read called by the
12     Fsd/Fsp dispatch drivers.
13 
14 
15 --*/
16 
17 #include "cdprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (CDFS_BUG_CHECK_READ)
24 
25 //
26 //  VOID
27 //  SafeZeroMemory (
28 //      _Out_ PUCHAR At,
29 //      _In_ ULONG ByteCount
30 //      );
31 //
32 
33 //
34 //  This macro just puts a nice little try-except around RtlZeroMemory
35 //
36 
37 #ifndef __REACTOS__
38 #define SafeZeroMemory(IC,AT,BYTE_COUNT) {                  \
39     _SEH2_TRY {                                             \
40         RtlZeroMemory( (AT), (BYTE_COUNT) );                \
41 __pragma(warning(suppress: 6320))                           \
42     } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) {           \
43          CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER );   \
44     } _SEH2_END;                                            \
45 }
46 #else
47 #define SafeZeroMemory(IC,AT,BYTE_COUNT) {                  \
48     _SEH2_TRY {                                             \
49         RtlZeroMemory( (AT), (BYTE_COUNT) );                \
50     } _SEH2_EXCEPT( EXCEPTION_EXECUTE_HANDLER ) {           \
51          CdRaiseStatus( IC, STATUS_INVALID_USER_BUFFER );   \
52     } _SEH2_END;                                            \
53 }
54 #endif
55 
56 //
57 // Read ahead amount used for normal data files
58 //
59 
60 #define READ_AHEAD_GRANULARITY           (0x10000)
61 
62 #ifdef ALLOC_PRAGMA
63 #pragma alloc_text(PAGE, CdCommonRead)
64 #endif
65 
66 
67 
68 _Requires_lock_held_(_Global_critical_region_)
69 NTSTATUS
70 CdCommonRead (
71     _Inout_ PIRP_CONTEXT IrpContext,
72     _Inout_ PIRP Irp
73     )
74 
75 /*++
76 
77 Routine Description:
78 
79     This is the common entry point for NtReadFile calls.  For synchronous requests,
80     CommonRead will complete the request in the current thread.  If not
81     synchronous the request will be passed to the Fsp if there is a need to
82     block.
83 
84 Arguments:
85 
86     Irp - Supplies the Irp to process
87 
88 Return Value:
89 
90     NTSTATUS - The result of this operation.
91 
92 --*/
93 
94 {
95     NTSTATUS Status = STATUS_SUCCESS;
96     PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation( Irp );
97 
98     TYPE_OF_OPEN TypeOfOpen;
99     PFCB Fcb;
100     PCCB Ccb;
101 
102     BOOLEAN Wait;
103     ULONG PagingIo;
104     ULONG SynchronousIo;
105     ULONG NonCachedIo;
106     PVOID UserBuffer;
107 
108     LONGLONG StartingOffset;
109     LONGLONG ByteRange;
110     ULONG ByteCount;
111     ULONG ReadByteCount;
112     ULONG OriginalByteCount;
113 
114     PVOID SystemBuffer;
115 
116     BOOLEAN ReleaseFile = TRUE;
117 
118     CD_IO_CONTEXT LocalIoContext;
119 
120     PAGED_CODE();
121 
122     //
123     //  If this is a zero length read then return SUCCESS immediately.
124     //
125 
126     if (IrpSp->Parameters.Read.Length == 0) {
127 
128         CdCompleteRequest( IrpContext, Irp, STATUS_SUCCESS );
129         return STATUS_SUCCESS;
130     }
131 
132     //
133     //  Decode the file object and verify we support read on this.  It
134     //  must be a user file, stream file or volume file (for a data disk).
135     //
136 
137     TypeOfOpen = CdDecodeFileObject( IrpContext, IrpSp->FileObject, &Fcb, &Ccb );
138 
139     // Internal lock object is acquired if return status is STATUS_PENDING
140     _Analysis_suppress_lock_checking_(Fcb->Resource);
141 
142     if ((TypeOfOpen == UnopenedFileObject) ||
143         (TypeOfOpen == UserDirectoryOpen)) {
144 
145         CdCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
146         return STATUS_INVALID_DEVICE_REQUEST;
147     }
148 
149     //
150     //  Examine our input parameters to determine if this is noncached and/or
151     //  a paging io operation.
152     //
153 
154     Wait = BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
155     PagingIo = FlagOn( Irp->Flags, IRP_PAGING_IO );
156     NonCachedIo = FlagOn( Irp->Flags, IRP_NOCACHE );
157     SynchronousIo = FlagOn( IrpSp->FileObject->Flags, FO_SYNCHRONOUS_IO );
158 
159 
160     //
161     //  Extract the range of the Io.
162     //
163 
164     StartingOffset = IrpSp->Parameters.Read.ByteOffset.QuadPart;
165     OriginalByteCount = ByteCount = IrpSp->Parameters.Read.Length;
166 
167     ByteRange = StartingOffset + ByteCount;
168 
169     //
170     //  Make sure that Dasd access is always non-cached.
171     //
172 
173     if (TypeOfOpen == UserVolumeOpen) {
174 
175         NonCachedIo = TRUE;
176     }
177 
178     //
179     //  Acquire the file shared to perform the read.  If we are doing paging IO,
180     //  it may be the case that we would have a deadlock imminent because we may
181     //  block on shared access, so starve out any exclusive waiters.  This requires
182     //  a degree of caution - we believe that any paging IO bursts will recede and
183     //  allow the exclusive waiter in.
184     //
185 
186     if (PagingIo) {
187 
188         CdAcquireFileSharedStarveExclusive( IrpContext, Fcb );
189 
190     } else {
191 
192         CdAcquireFileShared( IrpContext, Fcb );
193     }
194 
195     //
196     //  Use a try-finally to facilitate cleanup.
197     //
198 
199     _SEH2_TRY {
200 
201         //
202         //  Verify the Fcb.  Allow reads if this is a DASD handle that is
203         //  dismounting the volume.
204         //
205 
206         if ((TypeOfOpen != UserVolumeOpen) || (NULL == Ccb) ||
207             !FlagOn( Ccb->Flags, CCB_FLAG_DISMOUNT_ON_CLOSE))  {
208 
209             CdVerifyFcbOperation( IrpContext, Fcb );
210         }
211 
212         //
213         //  If this is a non-cached then check whether we need to post this
214         //  request if this thread can't block.
215         //
216 
217         if (!Wait && NonCachedIo) {
218 
219             //
220             //  XA requests must always be waitable.
221             //
222 
223             if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) {
224 
225                 SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST );
226                 try_return( Status = STATUS_CANT_WAIT );
227             }
228         }
229 
230         //
231         //  If this is a user request then verify the oplock and filelock state.
232         //
233 
234         if (TypeOfOpen == UserFileOpen) {
235 
236             //
237             //  We check whether we can proceed
238             //  based on the state of the file oplocks.
239             //
240 
241             Status = FsRtlCheckOplock( CdGetFcbOplock(Fcb),
242                                        Irp,
243                                        IrpContext,
244                                        (PVOID)CdOplockComplete,/* ReactOS Change: GCC "assignment from incompatible pointer type" */
245                                        (PVOID)CdPrePostIrp );/* ReactOS Change: GCC "assignment from incompatible pointer type" */
246 
247             //
248             //  If the result is not STATUS_SUCCESS then the Irp was completed
249             //  elsewhere.
250             //
251 
252             if (Status != STATUS_SUCCESS) {
253 
254                 Irp = NULL;
255                 IrpContext = NULL;
256 
257                 try_return( NOTHING );
258             }
259 
260             if (!PagingIo &&
261                 (Fcb->FileLock != NULL) &&
262                 !FsRtlCheckLockForReadAccess( Fcb->FileLock, Irp )) {
263 
264                 try_return( Status = STATUS_FILE_LOCK_CONFLICT );
265             }
266         }
267 
268         //
269         //  Check request beyond end of file if this is not a read on a volume
270         //  handle marked for extended DASD IO.
271         //
272 
273         if ((TypeOfOpen != UserVolumeOpen) ||
274             (!FlagOn( Ccb->Flags, CCB_FLAG_ALLOW_EXTENDED_DASD_IO ))) {
275 
276             //
277             //  Complete the request if it begins beyond the end of file.
278             //
279 
280             if (StartingOffset >= Fcb->FileSize.QuadPart) {
281 
282                 try_return( Status = STATUS_END_OF_FILE );
283             }
284 
285             //
286             //  Truncate the read if it extends beyond the end of the file.
287             //
288 
289             if (ByteRange > Fcb->FileSize.QuadPart) {
290 
291                 ByteCount = (ULONG) (Fcb->FileSize.QuadPart - StartingOffset);
292                 ByteRange = Fcb->FileSize.QuadPart;
293             }
294         }
295 
296         //
297         //  Handle the non-cached read first.
298         //
299 
300         if (NonCachedIo) {
301 
302             //
303             //  If we have an unaligned transfer then post this request if
304             //  we can't wait.  Unaligned means that the starting offset
305             //  is not on a sector boundary or the read is not integral
306             //  sectors.
307             //
308 
309             ReadByteCount = BlockAlign( Fcb->Vcb, ByteCount );
310 
311             if (SectorOffset( StartingOffset ) ||
312                 SectorOffset( ReadByteCount ) ||
313                 (ReadByteCount > OriginalByteCount)) {
314 
315                 if (!Wait) {
316 
317                     CdRaiseStatus( IrpContext, STATUS_CANT_WAIT );
318                 }
319 
320                 //
321                 //  Make sure we don't overwrite the buffer.
322                 //
323 
324                 ReadByteCount = ByteCount;
325             }
326 
327             //
328             //  Initialize the IoContext for the read.
329             //  If there is a context pointer, we need to make sure it was
330             //  allocated and not a stale stack pointer.
331             //
332 
333             if (IrpContext->IoContext == NULL ||
334                 !FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO )) {
335 
336                 //
337                 //  If we can wait, use the context on the stack.  Otherwise
338                 //  we need to allocate one.
339                 //
340 
341                 if (Wait) {
342 
343                     IrpContext->IoContext = &LocalIoContext;
344                     ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );
345 
346                 } else {
347 
348                     IrpContext->IoContext = CdAllocateIoContext();
349                     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );
350                 }
351             }
352 
353             RtlZeroMemory( IrpContext->IoContext, sizeof( CD_IO_CONTEXT ));
354 
355             //
356             //  Store whether we allocated this context structure in the structure
357             //  itself.
358             //
359 
360             IrpContext->IoContext->AllocatedContext =
361                 BooleanFlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_ALLOC_IO );
362 
363             if (Wait) {
364 
365                 KeInitializeEvent( &IrpContext->IoContext->SyncEvent,
366                                    NotificationEvent,
367                                    FALSE );
368 
369             } else {
370 
371                 IrpContext->IoContext->ResourceThreadId = ExGetCurrentResourceThread();
372                 IrpContext->IoContext->Resource = Fcb->Resource;
373                 IrpContext->IoContext->RequestedByteCount = ByteCount;
374             }
375 
376             Irp->IoStatus.Information = ReadByteCount;
377 
378             //
379             //  Call one of the NonCacheIo routines to perform the actual
380             //  read.
381             //
382 
383             if (FlagOn( Fcb->FcbState, FCB_STATE_RAWSECTOR_MASK )) {
384 
385                 Status = CdNonCachedXARead( IrpContext, Fcb, StartingOffset, ReadByteCount );
386 
387             } else {
388 
389                 Status = CdNonCachedRead( IrpContext, Fcb, StartingOffset, ReadByteCount );
390             }
391 
392             //
393             //  Don't complete this request now if STATUS_PENDING was returned.
394             //
395 
396             if (Status == STATUS_PENDING) {
397 
398                 Irp = NULL;
399                 ReleaseFile = FALSE;
400 
401             //
402             //  Test is we should zero part of the buffer or update the
403             //  synchronous file position.
404             //
405 
406             } else {
407 
408                 //
409                 //  Convert any unknown error code to IO_ERROR.
410                 //
411 
412                 if (!NT_SUCCESS( Status )) {
413 
414                     //
415                     //  Set the information field to zero.
416                     //
417 
418                     Irp->IoStatus.Information = 0;
419 
420                     //
421                     //  Raise if this is a user induced error.
422                     //
423 
424                     if (IoIsErrorUserInduced( Status )) {
425 
426                         CdRaiseStatus( IrpContext, Status );
427                     }
428 
429                     Status = FsRtlNormalizeNtstatus( Status, STATUS_UNEXPECTED_IO_ERROR );
430 
431                 //
432                 //  Check if there is any portion of the user's buffer to zero.
433                 //
434 
435                 } else if (ReadByteCount != ByteCount) {
436 
437                     CdMapUserBuffer( IrpContext, &UserBuffer);
438 
439                     SafeZeroMemory( IrpContext,
440                                     Add2Ptr( UserBuffer,
441                                              ByteCount,
442                                              PVOID ),
443                                     ReadByteCount - ByteCount );
444 
445                     Irp->IoStatus.Information = ByteCount;
446                 }
447 
448                 //
449                 //  Update the file position if this is a synchronous request.
450                 //
451 
452                 if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) {
453 
454                     IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange;
455                 }
456             }
457 
458             try_return( NOTHING );
459         }
460 
461         //
462         //  Handle the cached case.  Start by initializing the private
463         //  cache map.
464         //
465 
466         if (IrpSp->FileObject->PrivateCacheMap == NULL) {
467 
468             //
469             //  Now initialize the cache map.
470             //
471 
472             CcInitializeCacheMap( IrpSp->FileObject,
473                                   (PCC_FILE_SIZES) &Fcb->AllocationSize,
474                                   FALSE,
475                                   &CdData.CacheManagerCallbacks,
476                                   Fcb );
477 
478             CcSetReadAheadGranularity( IrpSp->FileObject, READ_AHEAD_GRANULARITY );
479         }
480 
481         //
482         //  Read from the cache if this is not an Mdl read.
483         //
484 
485         if (!FlagOn( IrpContext->MinorFunction, IRP_MN_MDL )) {
486 
487             //
488             // If we are in the Fsp now because we had to wait earlier,
489             // we must map the user buffer, otherwise we can use the
490             // user's buffer directly.
491             //
492 
493             CdMapUserBuffer( IrpContext, &SystemBuffer );
494 
495             //
496             // Now try to do the copy.
497             //
498 
499             if (!CcCopyRead( IrpSp->FileObject,
500                              (PLARGE_INTEGER) &StartingOffset,
501                              ByteCount,
502                              Wait,
503                              SystemBuffer,
504                              &Irp->IoStatus )) {
505 
506                 try_return( Status = STATUS_CANT_WAIT );
507             }
508 
509             //
510             //  If the call didn't succeed, raise the error status
511             //
512 
513             if (!NT_SUCCESS( Irp->IoStatus.Status )) {
514 
515                 CdNormalizeAndRaiseStatus( IrpContext, Irp->IoStatus.Status );
516             }
517 
518         //
519         //  Otherwise perform the MdlRead operation.
520         //
521 
522         } else {
523 
524             CcMdlRead( IrpSp->FileObject,
525                        (PLARGE_INTEGER) &StartingOffset,
526                        ByteCount,
527                        &Irp->MdlAddress,
528                        &Irp->IoStatus );
529 
530             Status = Irp->IoStatus.Status;
531         }
532 
533         //
534         //  Update the current file position in the user file object.
535         //
536 
537         if (SynchronousIo && !PagingIo && NT_SUCCESS( Status )) {
538 
539             IrpSp->FileObject->CurrentByteOffset.QuadPart = ByteRange;
540         }
541 
542     try_exit:  NOTHING;
543     } _SEH2_FINALLY {
544 
545         //
546         //  Release the Fcb.
547         //
548 
549         if (ReleaseFile) {
550 
551             CdReleaseFile( IrpContext, Fcb );
552         }
553     } _SEH2_END;
554 
555     //
556     //  Post the request if we got CANT_WAIT.
557     //
558 
559     if (Status == STATUS_CANT_WAIT) {
560 
561         Status = CdFsdPostRequest( IrpContext, Irp );
562 
563     //
564     //  Otherwise complete the request.
565     //
566 
567     } else {
568 
569         CdCompleteRequest( IrpContext, Irp, Status );
570     }
571 
572     return Status;
573 }
574 
575 
576 
577