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