- Update to trunk
[reactos.git] / drivers / filesystems / npfs / fsctrl.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: drivers/fs/np/fsctrl.c
5 * PURPOSE: Named pipe filesystem
6 * PROGRAMMER: David Welch <welch@cwcom.net>
7 * Eric Kohl
8 * Michael Martin
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include "npfs.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18 //#define USING_PROPER_NPFS_WAIT_SEMANTICS
19
20 /* FUNCTIONS *****************************************************************/
21
22 static DRIVER_CANCEL NpfsListeningCancelRoutine;
23 static VOID NTAPI
24 NpfsListeningCancelRoutine(IN PDEVICE_OBJECT DeviceObject,
25 IN PIRP Irp)
26 {
27 PNPFS_WAITER_ENTRY Waiter;
28
29 Waiter = (PNPFS_WAITER_ENTRY)&Irp->Tail.Overlay.DriverContext;
30
31 DPRINT("NpfsListeningCancelRoutine() called for <%wZ>\n",
32 &Waiter->Ccb->Fcb->PipeName);
33
34 IoReleaseCancelSpinLock(Irp->CancelIrql);
35
36
37 KeLockMutex(&Waiter->Ccb->Fcb->CcbListLock);
38 RemoveEntryList(&Waiter->Entry);
39 KeUnlockMutex(&Waiter->Ccb->Fcb->CcbListLock);
40
41 Irp->IoStatus.Status = STATUS_CANCELLED;
42 Irp->IoStatus.Information = 0;
43 IoCompleteRequest(Irp, IO_NO_INCREMENT);
44 }
45
46
47 static NTSTATUS
48 NpfsAddListeningServerInstance(PIRP Irp,
49 PNPFS_CCB Ccb)
50 {
51 PNPFS_WAITER_ENTRY Entry;
52 KIRQL oldIrql;
53
54 Entry = (PNPFS_WAITER_ENTRY)&Irp->Tail.Overlay.DriverContext;
55
56 Entry->Ccb = Ccb;
57
58 KeLockMutex(&Ccb->Fcb->CcbListLock);
59
60 IoMarkIrpPending(Irp);
61 InsertTailList(&Ccb->Fcb->WaiterListHead, &Entry->Entry);
62
63 IoAcquireCancelSpinLock(&oldIrql);
64 if (!Irp->Cancel)
65 {
66 (void)IoSetCancelRoutine(Irp, NpfsListeningCancelRoutine);
67 IoReleaseCancelSpinLock(oldIrql);
68 KeUnlockMutex(&Ccb->Fcb->CcbListLock);
69 return STATUS_PENDING;
70 }
71 IoReleaseCancelSpinLock(oldIrql);
72
73 RemoveEntryList(&Entry->Entry);
74
75 Irp->IoStatus.Status = STATUS_CANCELLED;
76 Irp->IoStatus.Information = 0;
77 IoCompleteRequest(Irp, IO_NO_INCREMENT);
78 KeUnlockMutex(&Ccb->Fcb->CcbListLock);
79
80 return STATUS_CANCELLED;
81 }
82
83
84 static NTSTATUS
85 NpfsConnectPipe(PIRP Irp,
86 PNPFS_CCB Ccb)
87 {
88 PIO_STACK_LOCATION IoStack;
89 PFILE_OBJECT FileObject;
90 ULONG Flags;
91 PLIST_ENTRY current_entry;
92 PNPFS_FCB Fcb;
93 PNPFS_CCB ClientCcb;
94 NTSTATUS Status;
95
96 DPRINT("NpfsConnectPipe()\n");
97
98 if (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE)
99 {
100 KeResetEvent(&Ccb->ConnectEvent);
101 return STATUS_PIPE_CONNECTED;
102 }
103
104 if (Ccb->PipeState == FILE_PIPE_CLOSING_STATE)
105 return STATUS_PIPE_CLOSING;
106
107 DPRINT("Waiting for connection...\n");
108
109 Fcb = Ccb->Fcb;
110 IoStack = IoGetCurrentIrpStackLocation(Irp);
111 FileObject = IoStack->FileObject;
112 Flags = FileObject->Flags;
113
114 /* search for a listening client fcb */
115 KeLockMutex(&Fcb->CcbListLock);
116
117 current_entry = Fcb->ClientCcbListHead.Flink;
118 while (current_entry != &Fcb->ClientCcbListHead)
119 {
120 ClientCcb = CONTAINING_RECORD(current_entry,
121 NPFS_CCB,
122 CcbListEntry);
123
124 if (ClientCcb->PipeState == 0)
125 {
126 /* found a passive (waiting) client CCB */
127 DPRINT("Passive (waiting) client CCB found -- wake the client\n");
128 KeSetEvent(&ClientCcb->ConnectEvent, IO_NO_INCREMENT, FALSE);
129 break;
130 }
131
132 #if 0
133 if (ClientCcb->PipeState == FILE_PIPE_LISTENING_STATE)
134 {
135 /* found a listening client CCB */
136 DPRINT("Listening client CCB found -- connecting\n");
137
138 /* connect client and server CCBs */
139 Ccb->OtherSide = ClientCcb;
140 ClientCcb->OtherSide = Ccb;
141
142 /* set connected state */
143 Ccb->PipeState = FILE_PIPE_CONNECTED_STATE;
144 ClientCcb->PipeState = FILE_PIPE_CONNECTED_STATE;
145
146 KeUnlockMutex(&Fcb->CcbListLock);
147
148 /* FIXME: create and initialize data queues */
149
150 /* signal client's connect event */
151 DPRINT("Setting the ConnectEvent for %x\n", ClientCcb);
152 KeSetEvent(&ClientCcb->ConnectEvent, IO_NO_INCREMENT, FALSE);
153
154 return STATUS_PIPE_CONNECTED;
155 }
156 #endif
157
158 current_entry = current_entry->Flink;
159 }
160
161 /* no listening client fcb found */
162 DPRINT("No listening client fcb found -- waiting for client\n");
163
164 Ccb->PipeState = FILE_PIPE_LISTENING_STATE;
165
166 Status = NpfsAddListeningServerInstance(Irp, Ccb);
167
168 KeUnlockMutex(&Fcb->CcbListLock);
169
170 if (Flags & FO_SYNCHRONOUS_IO)
171 {
172 KeWaitForSingleObject(&Ccb->ConnectEvent,
173 UserRequest,
174 Irp->RequestorMode,
175 FALSE,
176 NULL);
177 }
178
179 DPRINT("NpfsConnectPipe() done (Status %lx)\n", Status);
180
181 return Status;
182 }
183
184
185 static NTSTATUS
186 NpfsDisconnectPipe(PNPFS_CCB Ccb)
187 {
188 NTSTATUS Status;
189 PNPFS_FCB Fcb;
190 PNPFS_CCB OtherSide;
191 BOOLEAN Server;
192
193 DPRINT("NpfsDisconnectPipe()\n");
194
195 Fcb = Ccb->Fcb;
196 KeLockMutex(&Fcb->CcbListLock);
197
198 if (Ccb->PipeState == FILE_PIPE_DISCONNECTED_STATE)
199 {
200 DPRINT("Pipe is already disconnected\n");
201 Status = STATUS_PIPE_DISCONNECTED;
202 }
203 else if ((!Ccb->OtherSide) && (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE))
204 {
205 ExAcquireFastMutex(&Ccb->DataListLock);
206 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
207 ExReleaseFastMutex(&Ccb->DataListLock);
208 Status = STATUS_SUCCESS;
209 }
210 else if (Ccb->PipeState == FILE_PIPE_CONNECTED_STATE)
211 {
212 Server = (Ccb->PipeEnd == FILE_PIPE_SERVER_END);
213 OtherSide = Ccb->OtherSide;
214 //Ccb->OtherSide = NULL;
215 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
216 /* Lock the server first */
217 if (Server)
218 {
219 ExAcquireFastMutex(&Ccb->DataListLock);
220 ExAcquireFastMutex(&OtherSide->DataListLock);
221 }
222 else
223 {
224 ExAcquireFastMutex(&OtherSide->DataListLock);
225 ExAcquireFastMutex(&Ccb->DataListLock);
226 }
227 OtherSide->PipeState = FILE_PIPE_DISCONNECTED_STATE;
228 //OtherSide->OtherSide = NULL;
229 /*
230 * Signaling the write event. If is possible that an other
231 * thread waits for an empty buffer.
232 */
233 KeSetEvent(&OtherSide->ReadEvent, IO_NO_INCREMENT, FALSE);
234 KeSetEvent(&OtherSide->WriteEvent, IO_NO_INCREMENT, FALSE);
235 if (Server)
236 {
237 ExReleaseFastMutex(&OtherSide->DataListLock);
238 ExReleaseFastMutex(&Ccb->DataListLock);
239 }
240 else
241 {
242 ExReleaseFastMutex(&Ccb->DataListLock);
243 ExReleaseFastMutex(&OtherSide->DataListLock);
244 }
245 Status = STATUS_SUCCESS;
246 }
247 else if (Ccb->PipeState == FILE_PIPE_LISTENING_STATE)
248 {
249 PLIST_ENTRY Entry;
250 PNPFS_WAITER_ENTRY WaitEntry = NULL;
251 BOOLEAN Complete = FALSE;
252 PIRP Irp = NULL;
253
254 Entry = Ccb->Fcb->WaiterListHead.Flink;
255 while (Entry != &Ccb->Fcb->WaiterListHead)
256 {
257 WaitEntry = CONTAINING_RECORD(Entry, NPFS_WAITER_ENTRY, Entry);
258 if (WaitEntry->Ccb == Ccb)
259 {
260 RemoveEntryList(Entry);
261 Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DriverContext);
262 Complete = (NULL == IoSetCancelRoutine(Irp, NULL));
263 break;
264 }
265 Entry = Entry->Flink;
266 }
267
268 if (Irp)
269 {
270 if (Complete)
271 {
272 Irp->IoStatus.Status = STATUS_PIPE_BROKEN;
273 Irp->IoStatus.Information = 0;
274 IoCompleteRequest(Irp, IO_NO_INCREMENT);
275 }
276 }
277 Ccb->PipeState = FILE_PIPE_DISCONNECTED_STATE;
278 Status = STATUS_SUCCESS;
279 }
280 else if (Ccb->PipeState == FILE_PIPE_CLOSING_STATE)
281 {
282 Status = STATUS_PIPE_CLOSING;
283 }
284 else
285 {
286 Status = STATUS_UNSUCCESSFUL;
287 }
288 KeUnlockMutex(&Fcb->CcbListLock);
289 return Status;
290 }
291
292
293 static NTSTATUS
294 NpfsWaitPipe(PIRP Irp,
295 PNPFS_CCB Ccb)
296 {
297 PLIST_ENTRY current_entry;
298 PNPFS_FCB Fcb;
299 PNPFS_CCB ServerCcb;
300 PFILE_PIPE_WAIT_FOR_BUFFER WaitPipe;
301 LARGE_INTEGER TimeOut;
302 NTSTATUS Status;
303 #ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
304 PNPFS_VCB Vcb;
305 UNICODE_STRING PipeName;
306 #endif
307
308 DPRINT("NpfsWaitPipe\n");
309
310 WaitPipe = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
311
312 #ifdef USING_PROPER_NPFS_WAIT_SEMANTICS
313 /* Fail, if the CCB does not represent the root directory */
314 if (Ccb->Type != CCB_DIRECTORY)
315 return STATUS_ILLEGAL_FUNCTION;
316
317 /* Calculate the pipe name length and allocate the buffer */
318 PipeName.Length = WaitPipe->NameLength + sizeof(WCHAR);
319 PipeName.MaximumLength = PipeName.Length + sizeof(WCHAR);
320 PipeName.Buffer = ExAllocatePool(NonPagedPool, PipeName.MaximumLength);
321 if (PipeName.Buffer == NULL)
322 {
323 DPRINT1("Could not allocate memory for the pipe name!\n");
324 return STATUS_NO_MEMORY;
325 }
326
327 /* Copy the pipe name into the buffer, prepend a backslash and append a 0 character */
328 PipeName.Buffer[0] = L'\\';
329 RtlCopyMemory(&PipeName.Buffer[1],
330 &WaitPipe->Name[0],
331 WaitPipe->NameLength);
332 PipeName.Buffer[PipeName.Length / sizeof(WCHAR)] = 0;
333
334 DPRINT("Waiting for Pipe %wZ\n", &PipeName);
335
336 /* Get the VCB */
337 Vcb = Ccb->Fcb->Vcb;
338
339 /* Lock the pipe list */
340 KeLockMutex(&Vcb->PipeListLock);
341
342 /* File a pipe with the given name */
343 Fcb = NpfsFindPipe(Vcb,
344 &PipeName);
345
346 /* Unlock the pipe list */
347 KeUnlockMutex(&Vcb->PipeListLock);
348
349 /* Release the pipe name buffer */
350 ExFreePool(PipeName.Buffer);
351
352 /* Fail if not pipe was found */
353 if (Fcb == NULL)
354 {
355 DPRINT("No pipe found!\n", Fcb);
356 return STATUS_OBJECT_NAME_NOT_FOUND;
357 }
358
359 DPRINT("Fcb %p\n", Fcb);
360 #else
361 Fcb = Ccb->Fcb;
362
363 if (Ccb->PipeState != 0)
364 {
365 DPRINT("Pipe is not in passive (waiting) state!\n");
366 return STATUS_UNSUCCESSFUL;
367 }
368 #endif
369
370 /* search for listening server */
371 current_entry = Fcb->ServerCcbListHead.Flink;
372 while (current_entry != &Fcb->ServerCcbListHead)
373 {
374 ServerCcb = CONTAINING_RECORD(current_entry,
375 NPFS_CCB,
376 CcbListEntry);
377
378 if (ServerCcb->PipeState == FILE_PIPE_LISTENING_STATE)
379 {
380 /* found a listening server CCB */
381 DPRINT("Listening server CCB found -- connecting\n");
382
383 return STATUS_SUCCESS;
384 }
385
386 current_entry = current_entry->Flink;
387 }
388
389 /* No listening server fcb found */
390
391 /* If no timeout specified, use the default one */
392 if (WaitPipe->TimeoutSpecified)
393 TimeOut = WaitPipe->Timeout;
394 else
395 TimeOut = Fcb->TimeOut;
396
397 /* Wait for one */
398 Status = KeWaitForSingleObject(&Ccb->ConnectEvent,
399 UserRequest,
400 KernelMode,
401 FALSE,
402 &TimeOut);
403
404 DPRINT("KeWaitForSingleObject() returned (Status %lx)\n", Status);
405
406 return Status;
407 }
408
409
410 /*
411 * FUNCTION: Return current state of a pipe
412 * ARGUMENTS:
413 * Irp = Pointer to I/O request packet
414 * IrpSp = Pointer to current stack location of Irp
415 * RETURNS:
416 * Status of operation
417 */
418
419 /*
420 * FUNCTION: Peek at a pipe (get information about messages)
421 * ARGUMENTS:
422 * Irp = Pointer to I/O request packet
423 * IoStack = Pointer to current stack location of Irp
424 * RETURNS:
425 * Status of operation
426 */
427 static NTSTATUS
428 NpfsPeekPipe(PIRP Irp,
429 PIO_STACK_LOCATION IoStack)
430 {
431 ULONG OutputBufferLength;
432 ULONG ReturnLength = 0;
433 PFILE_PIPE_PEEK_BUFFER Reply;
434 PNPFS_FCB Fcb;
435 PNPFS_CCB Ccb;
436 NTSTATUS Status;
437 ULONG MessageCount = 0;
438 ULONG MessageLength;
439 ULONG ReadDataAvailable;
440 PVOID BufferPtr;
441
442 DPRINT("NpfsPeekPipe\n");
443
444 OutputBufferLength = IoStack->Parameters.DeviceIoControl.OutputBufferLength;
445 DPRINT("OutputBufferLength: %lu\n", OutputBufferLength);
446
447 /* Validate parameters */
448 if (OutputBufferLength < sizeof(FILE_PIPE_PEEK_BUFFER))
449 {
450 DPRINT1("Buffer too small\n");
451 return STATUS_INVALID_PARAMETER;
452 }
453
454 Ccb = IoStack->FileObject->FsContext2;
455 Reply = (PFILE_PIPE_PEEK_BUFFER)Irp->AssociatedIrp.SystemBuffer;
456 Fcb = Ccb->Fcb;
457
458
459 Reply->NamedPipeState = Ccb->PipeState;
460
461 Reply->ReadDataAvailable = Ccb->ReadDataAvailable;
462 DPRINT("ReadDataAvailable: %lu\n", Ccb->ReadDataAvailable);
463
464 ExAcquireFastMutex(&Ccb->DataListLock);
465 BufferPtr = Ccb->ReadPtr;
466 DPRINT("BufferPtr = %x\n", BufferPtr);
467 if (Ccb->Fcb->PipeType == FILE_PIPE_BYTE_STREAM_TYPE)
468 {
469 DPRINT("Byte Stream Mode\n");
470 Reply->MessageLength = Ccb->ReadDataAvailable;
471 DPRINT("Reply->MessageLength %lu\n",Reply->MessageLength );
472 MessageCount = 1;
473
474 if (Reply->Data[0] && (OutputBufferLength >= Ccb->ReadDataAvailable + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0])))
475 {
476 ReturnLength = Ccb->ReadDataAvailable;
477 memcpy(&Reply->Data[0], (PVOID)BufferPtr, Ccb->ReadDataAvailable);
478 }
479 }
480 else
481 {
482 DPRINT("Message Mode\n");
483 ReadDataAvailable=Ccb->ReadDataAvailable;
484
485 if (ReadDataAvailable > 0)
486 {
487 memcpy(&Reply->MessageLength, BufferPtr, sizeof(ULONG));
488
489 while ((ReadDataAvailable > 0) && (BufferPtr < Ccb->WritePtr))
490 {
491 memcpy(&MessageLength, BufferPtr, sizeof(MessageLength));
492
493 ASSERT(MessageLength > 0);
494
495 DPRINT("MessageLength = %lu\n",MessageLength);
496 ReadDataAvailable -= MessageLength;
497 MessageCount++;
498
499 /* If its the first message, copy the Message if the size of buffer is large enough */
500 if (MessageCount==1)
501 {
502 if ((Reply->Data[0])
503 && (OutputBufferLength >= (MessageLength + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]))))
504 {
505 memcpy(&Reply->Data[0], (PVOID)((ULONG_PTR)BufferPtr + sizeof(MessageLength)), MessageLength);
506 ReturnLength = MessageLength;
507 }
508 }
509
510 BufferPtr =(PVOID)((ULONG_PTR)BufferPtr + MessageLength + sizeof(MessageLength));
511 DPRINT("BufferPtr = %x\n", BufferPtr);
512 DPRINT("ReadDataAvailable: %lu\n", ReadDataAvailable);
513 }
514
515 if (ReadDataAvailable != 0)
516 {
517 DPRINT1("Possible memory corruption.\n");
518 ASSERT(FALSE);
519 }
520 }
521 }
522 ExReleaseFastMutex(&Ccb->DataListLock);
523
524 Reply->NumberOfMessages = MessageCount;
525
526 Irp->IoStatus.Information = ReturnLength + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
527 Irp->IoStatus.Status = STATUS_SUCCESS;
528
529 Status = STATUS_SUCCESS;
530
531 DPRINT("NpfsPeekPipe done\n");
532
533 return Status;
534 }
535
536
537 NTSTATUS NTAPI
538 NpfsFileSystemControl(PDEVICE_OBJECT DeviceObject,
539 PIRP Irp)
540 {
541 PIO_STACK_LOCATION IoStack;
542 PFILE_OBJECT FileObject;
543 NTSTATUS Status;
544 PNPFS_VCB Vcb;
545 PNPFS_FCB Fcb;
546 PNPFS_CCB Ccb;
547
548 DPRINT("NpfsFileSystemContol(DeviceObject %p Irp %p)\n", DeviceObject, Irp);
549
550 Vcb = (PNPFS_VCB)DeviceObject->DeviceExtension;
551 IoStack = IoGetCurrentIrpStackLocation(Irp);
552 DPRINT("IoStack: %p\n", IoStack);
553 FileObject = IoStack->FileObject;
554 DPRINT("FileObject: %p\n", FileObject);
555 Ccb = FileObject->FsContext2;
556 DPRINT("CCB: %p\n", Ccb);
557 Fcb = Ccb->Fcb;
558 DPRINT("Pipe: %p\n", Fcb);
559 DPRINT("PipeName: %wZ\n", &Fcb->PipeName);
560
561 Irp->IoStatus.Information = 0;
562
563 switch (IoStack->Parameters.FileSystemControl.FsControlCode)
564 {
565 case FSCTL_PIPE_ASSIGN_EVENT:
566 DPRINT1("Assign event not implemented\n");
567 Status = STATUS_NOT_IMPLEMENTED;
568 break;
569
570 case FSCTL_PIPE_DISCONNECT:
571 DPRINT("Disconnecting pipe %wZ\n", &Fcb->PipeName);
572 Status = NpfsDisconnectPipe(Ccb);
573 break;
574
575 case FSCTL_PIPE_LISTEN:
576 DPRINT("Connecting pipe %wZ\n", &Fcb->PipeName);
577 Status = NpfsConnectPipe(Irp, Ccb);
578 break;
579
580 case FSCTL_PIPE_PEEK:
581 DPRINT("Peeking pipe %wZ\n", &Fcb->PipeName);
582 Status = NpfsPeekPipe(Irp, (PIO_STACK_LOCATION)IoStack);
583 break;
584
585 case FSCTL_PIPE_QUERY_EVENT:
586 DPRINT1("Query event not implemented\n");
587 Status = STATUS_NOT_IMPLEMENTED;
588 break;
589
590 case FSCTL_PIPE_TRANSCEIVE:
591 /* If you implement this, please remove the workaround in
592 lib/kernel32/file/npipe.c function TransactNamedPipe() */
593 DPRINT1("Transceive not implemented\n");
594 Status = STATUS_NOT_IMPLEMENTED;
595 break;
596
597 case FSCTL_PIPE_WAIT:
598 DPRINT("Waiting for pipe %wZ\n", &Fcb->PipeName);
599 Status = NpfsWaitPipe(Irp, Ccb);
600 break;
601
602 case FSCTL_PIPE_IMPERSONATE:
603 DPRINT1("Impersonate not implemented\n");
604 Status = STATUS_NOT_IMPLEMENTED;
605 break;
606
607 case FSCTL_PIPE_SET_CLIENT_PROCESS:
608 DPRINT1("Set client process not implemented\n");
609 Status = STATUS_NOT_IMPLEMENTED;
610 break;
611
612 case FSCTL_PIPE_QUERY_CLIENT_PROCESS:
613 DPRINT1("Query client process not implemented\n");
614 Status = STATUS_NOT_IMPLEMENTED;
615 break;
616
617 case FSCTL_PIPE_INTERNAL_READ:
618 DPRINT1("Internal read not implemented\n");
619 Status = STATUS_NOT_IMPLEMENTED;
620 break;
621
622 case FSCTL_PIPE_INTERNAL_WRITE:
623 DPRINT1("Internal write not implemented\n");
624 Status = STATUS_NOT_IMPLEMENTED;
625 break;
626
627 case FSCTL_PIPE_INTERNAL_TRANSCEIVE:
628 DPRINT1("Internal transceive not implemented\n");
629 Status = STATUS_NOT_IMPLEMENTED;
630 break;
631
632 case FSCTL_PIPE_INTERNAL_READ_OVFLOW:
633 DPRINT1("Internal read overflow not implemented\n");
634 Status = STATUS_NOT_IMPLEMENTED;
635 break;
636
637 default:
638 DPRINT1("Unrecognized IoControlCode: %x\n",
639 IoStack->Parameters.FileSystemControl.FsControlCode);
640 Status = STATUS_UNSUCCESSFUL;
641 }
642
643 if (Status != STATUS_PENDING)
644 {
645 Irp->IoStatus.Status = Status;
646
647 IoCompleteRequest(Irp, IO_NO_INCREMENT);
648 }
649
650 return Status;
651 }
652
653
654 NTSTATUS NTAPI
655 NpfsFlushBuffers(PDEVICE_OBJECT DeviceObject,
656 PIRP Irp)
657 {
658 /* FIXME: Implement */
659
660 Irp->IoStatus.Status = STATUS_SUCCESS;
661 Irp->IoStatus.Information = 0;
662
663 IoCompleteRequest(Irp, IO_NO_INCREMENT);
664
665 return STATUS_SUCCESS;
666 }
667
668 /* EOF */