summaryrefslogtreecommitdiffstats
path: root/private/ntos/mailslot/readsup.c
blob: f89f16f2edebbf082cbbc577dfaca21649a7d875 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    readsup.c

Abstract:

    This module implements the read support routine.  This is a common
    read function that is called to do read and peek.

Author:

    Manny Weiser (mannyw)    15-Jan-1991

Revision History:

--*/

#include "mailslot.h"

//
// The debug trace level
//

#define Dbg                              (DEBUG_TRACE_READSUP)

#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, MsReadDataQueue )
#pragma alloc_text( PAGE, MsTimeoutRead )
#endif

IO_STATUS_BLOCK
MsReadDataQueue (
    IN PDATA_QUEUE ReadQueue,
    IN ENTRY_TYPE Operation,
    IN PUCHAR ReadBuffer,
    IN ULONG ReadLength,
    OUT PULONG MessageLength
    )

/*++

Routine Description:

    This function reads data from the read queue and fills up the
    read buffer.  It will also dequeue the data entry if this is not
    a peek operation.

    It will only be called if there is at least one message to read.

    *** This function must be called from within a try statement.

Arguments:

    ReadQueue - Provides the read queue to examine.  Its state must
        already be set to WriteEntries.

    Operation - Indicates the type of operation to perform.  If the
        operation is Peek, the write data entry is not dequeued.

    ReadBuffer - Supplies a buffer to receive the data

    ReadLength - Supplies the length, in bytes, of ReadBuffer.

    MessageLength - Returns the full size of the message, even if the
        read buffer is not large enough to contain the entire message.

Return Value:

    IO_STATUS_BLOCK - Indicates the result of the operation.

--*/

{
    IO_STATUS_BLOCK iosb;

    PLIST_ENTRY listEntry;
    PDATA_ENTRY dataEntry;
    PFCB fcb;

    PUCHAR writeBuffer;
    ULONG writeLength;

    ULONG amountRead;

    PAGED_CODE();
    DebugTrace(+1, Dbg, "MsReadDataQueue\n", 0);
    DebugTrace( 0, Dbg, "ReadQueue     = %08lx\n", (ULONG)ReadQueue);
    DebugTrace( 0, Dbg, "Operation     = %08lx\n", Operation);
    DebugTrace( 0, Dbg, "ReadBuffer    = %08lx\n", (ULONG)ReadBuffer);
    DebugTrace( 0, Dbg, "ReadLength    = %08lx\n", ReadLength);

    //
    // Read the first message out of the data queue.
    //

    iosb.Status = STATUS_SUCCESS;

    listEntry = MsGetNextDataQueueEntry( ReadQueue );
    ASSERT( listEntry != &ReadQueue->DataEntryList );

    dataEntry = CONTAINING_RECORD( listEntry, DATA_ENTRY, ListEntry );

    //
    // Calculate how much data is in this entry.
    //

    writeBuffer = dataEntry->DataPointer;
    writeLength = dataEntry->DataSize;

    DebugTrace(0, Dbg, "WriteBuffer    = %08lx\n", (ULONG)writeBuffer);
    DebugTrace(0, Dbg, "WriteLength    = %08lx\n", writeLength);

    //
    // Fail this operation, if it is a read and the buffer is not large
    // enough.
    //

    if ( (Operation != Peek) && (ReadLength < writeLength) ) {

        iosb.Information = 0;
        iosb.Status = STATUS_BUFFER_TOO_SMALL;

        return iosb;

    }

    //
    // Copy data from the write buffer at write offset to the
    // read buffer by the mininum of write remaining or read length
    //

    RtlMoveMemory( ReadBuffer,
                   writeBuffer,
                   writeLength );

    *MessageLength = dataEntry->DataSize;


    //
    // If write length is larger than read length, this must be an
    // overflowed peek.
    //

    if ( writeLength > ReadLength) {

        DebugTrace(0, Dbg, "Overflowed peek buffer\n", 0);

        iosb.Status = STATUS_BUFFER_OVERFLOW;

    } else {

        //
        // The write entry is done so remove it from the read
        // queue, if this is not a peek operation.  This might
        // also have an IRP that needs to be completed.
        //

        if (Operation != Peek) {

            PIRP writeIrp;

            if ((writeIrp = MsRemoveDataQueueEntry( ReadQueue,
                                                    dataEntry )) != NULL) {

                //
                // Update the last modification time.
                //

                fcb = CONTAINING_RECORD( ReadQueue, FCB, DataQueue );
                KeQuerySystemTime( &fcb->Specific.Fcb.LastModificationTime );

                MsCompleteRequest( writeIrp, STATUS_SUCCESS );
            }
        }

        DebugTrace(0, Dbg, "Successful mailslot read\n", 0);

        //
        // Indicate success.
        //

        iosb.Status = STATUS_SUCCESS;
    }

    amountRead = writeLength < ReadLength ? writeLength : ReadLength;

    DebugTrace(0, Dbg, "Amount read = %08lx\n", amountRead);

    iosb.Information = amountRead;
    DebugTrace(-1, Dbg, "MsReadDataQueue -> iosb.Status = %08lx\n", iosb.Status);
    return iosb;
}


VOID
MsTimeoutRead (
    IN PVOID Context
    )

/*++

Routine Description:

    This routine times out a read operation.  It gains exclusive
    access to the FCB, and searches the data queue of read operations.

    If the timed out read operation is not found, it is assumed that
    a write IRP completed the read after the time out DPC ran, but
    before this function could complete the read IRP.

Arguments:

    Context - a pointer to our WorkContext

Return Value:

    None.

--*/

{
    PDATA_QUEUE dataQueue;
    PLIST_ENTRY listEntry;
    PIRP queuedIrp;
    PDATA_ENTRY dataEntry;
    PWORK_CONTEXT workContext;
    PIRP irp;
    PFCB fcb;

    PAGED_CODE();

    //
    // Reference our local variables.
    //

    workContext = (PWORK_CONTEXT)Context;

    irp = workContext->Irp;
    fcb = workContext->Fcb;

    //
    // Acquire exclusive access to the FCB.  This must succeed.
    //

    MsAcquireExclusiveFcb( fcb );

    //
    // Walk the FCB's data queue, looking for this read IRP.
    //

    dataQueue = &fcb->DataQueue;

    for (listEntry = MsGetNextDataQueueEntry( dataQueue );
         listEntry != &dataQueue->DataEntryList;
         listEntry = listEntry->Flink ) {

         //
         // This is an outstanding read request from this FCB.
         // If it is the read request that has timed out, then
         // complete it with a timed out status.
         //

         dataEntry = CONTAINING_RECORD( listEntry, DATA_ENTRY, ListEntry );
         queuedIrp = dataEntry->Irp;

         if ( queuedIrp == irp ) {

             //
             // The timed out IRP is still queued.  Dequeue it and complete
             // it now.
             //

             MsRemoveDataQueueEntry( dataQueue, dataEntry );
             DebugTrace(0, Dbg, "Completing IRP %08lx\n", (ULONG)queuedIrp );

             MsCompleteRequest( queuedIrp, STATUS_IO_TIMEOUT );

             break;
         }

    }

    //
    // Free the work context structure.  Note that we have to free this
    // whenever this routine runs, even if the I/O request was cancelled,
    // because the canceller didn't free it.
    //

    ExFreePool( workContext );

    //
    // Release the FCB, and derefernce it.
    //

    MsReleaseFcb( fcb );
    MsDereferenceFcb( fcb );

}