- Workaround for supporting a reads from pipe after the other end has been closed...
[reactos.git] / reactos / drivers / fs / np / rw.c
1 /* $Id: rw.c,v 1.15 2004/05/10 19:58:10 navaraf Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: services/fs/np/rw.c
6 * PURPOSE: Named pipe filesystem
7 * PROGRAMMER: David Welch <welch@cwcom.net>
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ddk/ntddk.h>
13 #include <rosrtl/minmax.h>
14 #include "npfs.h"
15
16 #define NDEBUG
17 #include <debug.h>
18
19 /* FUNCTIONS *****************************************************************/
20
21 #ifndef NDEBUG
22 VOID HexDump(PUCHAR Buffer, ULONG Length)
23 {
24 CHAR Line[65];
25 UCHAR ch;
26 const char Hex[] = "0123456789ABCDEF";
27 int i, j;
28
29 DbgPrint("---------------\n");
30
31 for (i = 0; i < ROUND_UP(Length, 16); i+= 16)
32 {
33 memset(Line, ' ', 64);
34 Line[64] = 0;
35
36 for (j = 0; j < 16 && j + i < Length; j++)
37 {
38 ch = Buffer[i + j];
39 Line[3*j + 0] = Hex[ch >> 4];
40 Line[3*j + 1] = Hex[ch & 0x0f];
41 Line[48 + j] = isprint(ch) ? ch : '.';
42 }
43 DbgPrint("%s\n", Line);
44 }
45 DbgPrint("---------------\n");
46 }
47 #endif
48
49 NTSTATUS STDCALL
50 NpfsRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
51 {
52 PIO_STACK_LOCATION IoStack;
53 PFILE_OBJECT FileObject;
54 NTSTATUS Status;
55 PNPFS_DEVICE_EXTENSION DeviceExt;
56 KIRQL OldIrql;
57 ULONG Information;
58 PNPFS_FCB Fcb;
59 PNPFS_FCB ReadFcb;
60 PNPFS_PIPE Pipe;
61 ULONG Length;
62 PVOID Buffer;
63 ULONG CopyLength;
64 ULONG TempLength;
65
66 DPRINT("NpfsRead(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
67
68 DeviceExt = (PNPFS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
69 IoStack = IoGetCurrentIrpStackLocation(Irp);
70 FileObject = IoStack->FileObject;
71 Fcb = FileObject->FsContext;
72 Pipe = Fcb->Pipe;
73 ReadFcb = Fcb->OtherSide;
74
75 if (ReadFcb == NULL)
76 {
77 DPRINT("Pipe is NOT connected!\n");
78 if (Fcb->PipeState == FILE_PIPE_LISTENING_STATE)
79 Status = STATUS_PIPE_LISTENING;
80 else if (Fcb->PipeState == FILE_PIPE_DISCONNECTED_STATE)
81 Status = STATUS_PIPE_DISCONNECTED;
82 else
83 Status = STATUS_PIPE_BROKEN;
84 Information = 0;
85 DPRINT("%x\n", Status);
86 goto done;
87 }
88
89 if (Irp->MdlAddress == NULL)
90 {
91 DPRINT("Irp->MdlAddress == NULL\n");
92 Status = STATUS_UNSUCCESSFUL;
93 Information = 0;
94 goto done;
95 }
96
97 if (ReadFcb->Data == NULL)
98 {
99 DPRINT("Pipe is NOT readable!\n");
100 Status = STATUS_UNSUCCESSFUL;
101 Information = 0;
102 goto done;
103 }
104
105 #ifdef FIN_WORKAROUND_READCLOSE
106 if (ReadFcb->ReadDataAvailable == 0 &&
107 ReadFcb->PipeState == FILE_PIPE_CLOSING_STATE)
108 {
109 DPRINT("Other end of pipe is closed!\n");
110 Status = STATUS_PIPE_BROKEN;
111 Information = 0;
112 goto done;
113 }
114 #endif
115
116 Status = STATUS_SUCCESS;
117 Length = IoStack->Parameters.Read.Length;
118 Information = 0;
119
120 Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
121 KeAcquireSpinLock(&ReadFcb->DataListLock, &OldIrql);
122 while (1)
123 {
124 /* FIXME: check if in blocking mode */
125 if (ReadFcb->ReadDataAvailable == 0)
126 {
127 KeResetEvent(&Fcb->Event);
128 KeSetEvent(&ReadFcb->Event, IO_NO_INCREMENT, FALSE);
129 KeReleaseSpinLock(&ReadFcb->DataListLock, OldIrql);
130 if (Information > 0)
131 {
132 Status = STATUS_SUCCESS;
133 goto done;
134 }
135 if (Fcb->PipeState != FILE_PIPE_CONNECTED_STATE)
136 {
137 DPRINT("PipeState: %x\n", Fcb->PipeState);
138 Status = STATUS_PIPE_BROKEN;
139 goto done;
140 }
141 /* Wait for ReadEvent to become signaled */
142 DPRINT("Waiting for readable data (%S)\n", Pipe->PipeName.Buffer);
143 Status = KeWaitForSingleObject(&Fcb->Event,
144 UserRequest,
145 KernelMode,
146 FALSE,
147 NULL);
148 DPRINT("Finished waiting (%S)! Status: %x\n", Pipe->PipeName.Buffer, Status);
149 #ifndef FIN_WORKAROUND_READCLOSE
150 /*
151 * It's possible that the event was signaled because the
152 * other side of pipe was closed.
153 */
154 if (Fcb->PipeState != FILE_PIPE_CONNECTED_STATE)
155 {
156 DPRINT("PipeState: %x\n", Fcb->PipeState);
157 Status = STATUS_PIPE_BROKEN;
158 goto done;
159 }
160 #endif
161 KeAcquireSpinLock(&ReadFcb->DataListLock, &OldIrql);
162 }
163
164 if (Pipe->PipeReadMode == FILE_PIPE_BYTE_STREAM_MODE)
165 {
166 DPRINT("Byte stream mode\n");
167 /* Byte stream mode */
168 while (Length > 0 && ReadFcb->ReadDataAvailable > 0)
169 {
170 CopyLength = RtlRosMin(ReadFcb->ReadDataAvailable, Length);
171 if (ReadFcb->ReadPtr + CopyLength <= ReadFcb->Data + ReadFcb->MaxDataLength)
172 {
173 memcpy(Buffer, ReadFcb->ReadPtr, CopyLength);
174 ReadFcb->ReadPtr += CopyLength;
175 if (ReadFcb->ReadPtr == ReadFcb->Data + ReadFcb->MaxDataLength)
176 {
177 ReadFcb->ReadPtr = ReadFcb->Data;
178 }
179 }
180 else
181 {
182 TempLength = ReadFcb->Data + ReadFcb->MaxDataLength - ReadFcb->ReadPtr;
183 memcpy(Buffer, ReadFcb->ReadPtr, TempLength);
184 memcpy(Buffer + TempLength, ReadFcb->Data, CopyLength - TempLength);
185 ReadFcb->ReadPtr = ReadFcb->Data + CopyLength - TempLength;
186 }
187
188 Buffer += CopyLength;
189 Length -= CopyLength;
190 Information += CopyLength;
191
192 ReadFcb->ReadDataAvailable -= CopyLength;
193 ReadFcb->WriteQuotaAvailable += CopyLength;
194 }
195
196 if (Length == 0)
197 {
198 KeSetEvent(&ReadFcb->Event, IO_NO_INCREMENT, FALSE);
199 break;
200 }
201 }
202 else
203 {
204 DPRINT("Message mode\n");
205
206 /* Message mode */
207 if (ReadFcb->ReadDataAvailable)
208 {
209 /* Truncate the message if the receive buffer is too small */
210 CopyLength = RtlRosMin(ReadFcb->ReadDataAvailable, Length);
211 memcpy(Buffer, ReadFcb->Data, CopyLength);
212
213 #ifndef NDEBUG
214 DPRINT("Length %d Buffer %x\n",CopyLength,Buffer);
215 HexDump((PUCHAR)Buffer, CopyLength);
216 #endif
217
218 Information = CopyLength;
219 ReadFcb->ReadDataAvailable = 0;
220 ReadFcb->WriteQuotaAvailable = ReadFcb->MaxDataLength;
221 }
222 if (Information > 0)
223 {
224 KeSetEvent(&ReadFcb->Event, IO_NO_INCREMENT, FALSE);
225 break;
226 }
227 }
228
229 #ifdef FIN_WORKAROUND_READCLOSE
230 if (ReadFcb->ReadDataAvailable == 0 &&
231 ReadFcb->PipeState == FILE_PIPE_CLOSING_STATE)
232 {
233 DPRINT("Other end of pipe is closed!\n");
234 break;
235 }
236 #endif
237 }
238 KeReleaseSpinLock(&ReadFcb->DataListLock, OldIrql);
239
240 done:
241 Irp->IoStatus.Status = Status;
242 Irp->IoStatus.Information = Information;
243
244 IoCompleteRequest(Irp, IO_NO_INCREMENT);
245
246 return(Status);
247 }
248
249
250 NTSTATUS STDCALL
251 NpfsWrite(PDEVICE_OBJECT DeviceObject,
252 PIRP Irp)
253 {
254 PIO_STACK_LOCATION IoStack;
255 PFILE_OBJECT FileObject;
256 PNPFS_FCB Fcb = NULL;
257 PNPFS_PIPE Pipe = NULL;
258 PUCHAR Buffer;
259 NTSTATUS Status = STATUS_SUCCESS;
260 ULONG Length;
261 ULONG Offset;
262 KIRQL OldIrql;
263 ULONG Information;
264 ULONG CopyLength;
265 ULONG TempLength;
266
267 DPRINT("NpfsWrite()\n");
268
269 IoStack = IoGetCurrentIrpStackLocation(Irp);
270 FileObject = IoStack->FileObject;
271 DPRINT("FileObject %p\n", FileObject);
272 DPRINT("Pipe name %wZ\n", &FileObject->FileName);
273
274 Fcb = FileObject->FsContext;
275 Pipe = Fcb->Pipe;
276
277 Length = IoStack->Parameters.Write.Length;
278 Offset = IoStack->Parameters.Write.ByteOffset.u.LowPart;
279 Information = 0;
280
281 if (Irp->MdlAddress == NULL)
282 {
283 DPRINT("Irp->MdlAddress == NULL\n");
284 Status = STATUS_UNSUCCESSFUL;
285 Length = 0;
286 goto done;
287 }
288
289 if (Fcb->OtherSide == NULL)
290 {
291 DPRINT("Pipe is NOT connected!\n");
292 if (Fcb->PipeState == FILE_PIPE_LISTENING_STATE)
293 Status = STATUS_PIPE_LISTENING;
294 else if (Fcb->PipeState == FILE_PIPE_DISCONNECTED_STATE)
295 Status = STATUS_PIPE_DISCONNECTED;
296 else
297 Status = STATUS_UNSUCCESSFUL;
298 Length = 0;
299 goto done;
300 }
301
302 if (Fcb->Data == NULL)
303 {
304 DPRINT("Pipe is NOT writable!\n");
305 Status = STATUS_UNSUCCESSFUL;
306 Length = 0;
307 goto done;
308 }
309
310 Status = STATUS_SUCCESS;
311 Buffer = MmGetSystemAddressForMdl (Irp->MdlAddress);
312
313 KeAcquireSpinLock(&Fcb->DataListLock, &OldIrql);
314 #ifndef NDEBUG
315 DPRINT("Length %d Buffer %x Offset %x\n",Length,Buffer,Offset);
316 HexDump(Buffer, Length);
317 #endif
318
319 while(1)
320 {
321 if (Fcb->WriteQuotaAvailable == 0)
322 {
323 KeResetEvent(&Fcb->Event);
324 KeSetEvent(&Fcb->OtherSide->Event, IO_NO_INCREMENT, FALSE);
325 KeReleaseSpinLock(&Fcb->DataListLock, OldIrql);
326 if (Fcb->PipeState != FILE_PIPE_CONNECTED_STATE)
327 {
328 Status = STATUS_PIPE_BROKEN;
329 goto done;
330 }
331 DPRINT("Waiting for buffer space (%S)\n", Pipe->PipeName.Buffer);
332 Status = KeWaitForSingleObject(&Fcb->Event,
333 UserRequest,
334 KernelMode,
335 FALSE,
336 NULL);
337 DPRINT("Finished waiting (%S)! Status: %x\n", Pipe->PipeName.Buffer, Status);
338 #ifndef FIN_WORKAROUND_READCLOSE
339 /*
340 * It's possible that the event was signaled because the
341 * other side of pipe was closed.
342 */
343 if (Fcb->PipeState != FILE_PIPE_CONNECTED_STATE)
344 {
345 DPRINT("PipeState: %x\n", Fcb->PipeState);
346 Status = STATUS_PIPE_BROKEN;
347 goto done;
348 }
349 #endif
350 KeAcquireSpinLock(&Fcb->DataListLock, &OldIrql);
351 }
352 if (Pipe->PipeWriteMode == FILE_PIPE_BYTE_STREAM_MODE)
353 {
354 DPRINT("Byte stream mode\n");
355 while (Length > 0 && Fcb->WriteQuotaAvailable > 0)
356 {
357 CopyLength = RtlRosMin(Length, Fcb->WriteQuotaAvailable);
358 if (Fcb->WritePtr + CopyLength <= Fcb->Data + Fcb->MaxDataLength)
359 {
360 memcpy(Fcb->WritePtr, Buffer, CopyLength);
361 Fcb->WritePtr += CopyLength;
362 if (Fcb->WritePtr == Fcb->Data + Fcb->MaxDataLength)
363 {
364 Fcb->WritePtr = Fcb->Data;
365 }
366 }
367 else
368 {
369 TempLength = Fcb->Data + Fcb->MaxDataLength - Fcb->WritePtr;
370 memcpy(Fcb->WritePtr, Buffer, TempLength);
371 memcpy(Fcb->Data, Buffer + TempLength, CopyLength - TempLength);
372 Fcb->WritePtr = Fcb->Data + CopyLength - TempLength;
373 }
374
375 Buffer += CopyLength;
376 Length -= CopyLength;
377 Information += CopyLength;
378
379 Fcb->ReadDataAvailable += CopyLength;
380 Fcb->WriteQuotaAvailable -= CopyLength;
381 }
382
383 if (Length == 0)
384 {
385 KeSetEvent(&Fcb->OtherSide->Event, IO_NO_INCREMENT, FALSE);
386 break;
387 }
388 }
389 else
390 {
391 if (Length > 0)
392 {
393 CopyLength = RtlRosMin(Length, Fcb->WriteQuotaAvailable);
394 memcpy(Fcb->Data, Buffer, CopyLength);
395
396 Information = CopyLength;
397 Fcb->ReadDataAvailable = CopyLength;
398 Fcb->WriteQuotaAvailable = 0;
399 }
400 if (Information > 0)
401 {
402 KeSetEvent(&Fcb->OtherSide->Event, IO_NO_INCREMENT, FALSE);
403 break;
404 }
405 }
406 }
407 KeReleaseSpinLock(&Fcb->DataListLock, OldIrql);
408
409 done:
410 Irp->IoStatus.Status = Status;
411 Irp->IoStatus.Information = Information;
412
413 IoCompleteRequest(Irp, IO_NO_INCREMENT);
414
415 return(Status);
416 }
417
418 /* EOF */