796 lines
21 KiB
C
796 lines
21 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
sh_put.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This source file contains those functions of the Stream Head Driver that
|
|||
|
deal with sending messages down a stream.
|
|||
|
|
|||
|
It is based on the SpiderSTREAMS source, stremul\msgsrvr.c.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Eric Chin (ericc) August 16, 1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
The write error state of a stream is represented by ms->e_werror. Once
|
|||
|
set, this is never reset. This corresponds to the STREAMS semantics as
|
|||
|
defined by AT&T. Once a user is notified of a write error on a stream,
|
|||
|
about the only recourse is to close the stream.
|
|||
|
|
|||
|
--*/
|
|||
|
#include "sh_inc.h"
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
* Private Functions
|
|||
|
*/
|
|||
|
STATIC VOID
|
|||
|
cancel_put(
|
|||
|
IN PDEVICE_OBJECT device,
|
|||
|
IN PIRP irp
|
|||
|
);
|
|||
|
|
|||
|
STATIC NTSTATUS
|
|||
|
do_putmsg(
|
|||
|
IN PIRP irp,
|
|||
|
IN BOOLEAN from_queue,
|
|||
|
IN int *spl_levelp,
|
|||
|
OUT BOOLEAN *pmore
|
|||
|
);
|
|||
|
|
|||
|
STATIC queue_t *
|
|||
|
handle_to_queue (
|
|||
|
IN HANDLE handle
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SHDispFdInsert(
|
|||
|
IN PIRP irp,
|
|||
|
IN PIO_STACK_LOCATION irpsp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to put a message down a stream. It is based on
|
|||
|
the SpiderStreams emulator's msgserver() routine.
|
|||
|
|
|||
|
This routine merely peels open the IRP, checks the putmsg() arguments
|
|||
|
for consistency, locks the appropriate stream, and then calls
|
|||
|
do_putmsg(), which does the bulk of the work.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
irp - pointer to the IRP representing this request
|
|||
|
irpsp - pointer to the IRP stack location for this request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
An NT status code. Whatever the return value, this function will arrange
|
|||
|
for the IRP to be completed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
int spl_level;
|
|||
|
queue_t *iq = NULL;
|
|||
|
PSTREAM_ENDPOINT ms;
|
|||
|
PPUTMSG_ARGS_IN inbuf;
|
|||
|
struct strbuf *ctrlptr, *dataptr;
|
|||
|
|
|||
|
ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
|
|||
|
METHOD_BUFFERED);
|
|||
|
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
|||
|
|
|||
|
if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(PUTMSG_ARGS_IN) - 1) {
|
|||
|
IF_STRMDBG(TERSE) {
|
|||
|
STRMTRACE(("SHEAD: SHDispFdInsert(%lx) insufficient nbytes = %lx\n",
|
|||
|
irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
|
|||
|
}
|
|||
|
shortreply(irp, STATUS_INVALID_PARAMETER, 0);
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// the caller marshalled the input arguments contiguously thus:
|
|||
|
//
|
|||
|
// typedef struct _PUTMSG_ARGS_IN_ {
|
|||
|
// int a_iocode; // I_FDINSERT
|
|||
|
// long a_flags; // 0 | RS_HIPRI
|
|||
|
// struct strbuf a_ctlbuf; // (required)
|
|||
|
// struct strbuf a_databuf; // (required)
|
|||
|
// HANDLE a_insert.i_fildes; // (required)
|
|||
|
// int a_offset; // (required)
|
|||
|
// char a_stuff[1]; // s_ctlbuf.buf (required)
|
|||
|
// // s_databuf.buf (optional)
|
|||
|
// } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
|
|||
|
//
|
|||
|
// When the message has no data part, the caller must have set
|
|||
|
// a_databuf.len = -1 !!
|
|||
|
//
|
|||
|
//
|
|||
|
inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
|
|||
|
ctrlptr = &(inbuf->a_ctlbuf);
|
|||
|
dataptr = &(inbuf->a_databuf);
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: SHDispFdInsert(flags, clen, dlen = %d, %lx, %lx)\n",
|
|||
|
inbuf->a_flags, ctrlptr->len, dataptr->len));
|
|||
|
}
|
|||
|
|
|||
|
if (((inbuf->a_flags != 0) && (inbuf->a_flags != RS_HIPRI)) ||
|
|||
|
(ctrlptr->len <= 0)) {
|
|||
|
SHpGenReply(irp, -1, EINVAL);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
if ((inbuf->a_offset < 0) ||
|
|||
|
(inbuf->a_offset % sizeof(char *)) ||
|
|||
|
(ctrlptr->len < (signed) (inbuf->a_offset + sizeof(char *)))) {
|
|||
|
SHpGenReply(irp, -1, EINVAL);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
if (inbuf->a_insert.i_fildes &&
|
|||
|
(inbuf->a_insert.i_fildes != INVALID_HANDLE_VALUE)) {
|
|||
|
iq = handle_to_queue(inbuf->a_insert.i_fildes);
|
|||
|
}
|
|||
|
if (!iq) {
|
|||
|
SHpGenReply(irp, -1, EINVAL);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
inbuf->a_insert.i_targetq = iq; // this is a union !!
|
|||
|
|
|||
|
//
|
|||
|
// if no data part is to be sent, specify it unambiguously for
|
|||
|
// irptomp()'s sake,
|
|||
|
//
|
|||
|
if (dataptr->len == 0) {
|
|||
|
dataptr->len = -1;
|
|||
|
}
|
|||
|
|
|||
|
spl_level = lock_strm(ms->e_strm);
|
|||
|
|
|||
|
if (shrange(ms->e_strm, ctrlptr->len, dataptr->len) < 0) {
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
SHpGenReply(irp, -1, ERANGE);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* do_putmsg() has, or will arrange to, complete the IRP.
|
|||
|
*/
|
|||
|
do_putmsg(irp, FALSE, &spl_level, NULL);
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: SHDispFdInsert(ms = %lx) completed ok\n", ms));
|
|||
|
}
|
|||
|
return(STATUS_PENDING);
|
|||
|
|
|||
|
} // SHDispFdInsert
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
SHDispPutMsg(
|
|||
|
IN PIRP irp,
|
|||
|
IN PIO_STACK_LOCATION irpsp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to put a message down a stream. It is based on
|
|||
|
the SpiderStreams emulator's msgserver() routine.
|
|||
|
|
|||
|
This routine merely peels open the IRP, checks the putmsg() arguments
|
|||
|
for consistency, locks the appropriate stream, and then calls
|
|||
|
do_putmsg(), which does the bulk of the work.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
irp - pointer to the IRP representing this request
|
|||
|
irpsp - pointer to the IRP stack location for this request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
An NT status code. Whatever the return value, this function will arrange
|
|||
|
for the IRP to be completed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
int spl_level;
|
|||
|
PSTREAM_ENDPOINT ms;
|
|||
|
PPUTMSG_ARGS_IN inbuf;
|
|||
|
struct strbuf *ctrlptr, *dataptr;
|
|||
|
|
|||
|
ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
|
|||
|
METHOD_BUFFERED);
|
|||
|
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
|||
|
|
|||
|
if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(PUTMSG_ARGS_IN) - 1) {
|
|||
|
IF_STRMDBG(TERSE) {
|
|||
|
STRMTRACE(("SHEAD: SHDispPutMsg(%lx) insufficient nbytes = %lx\n",
|
|||
|
irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
|
|||
|
}
|
|||
|
shortreply(irp, STATUS_INVALID_PARAMETER, 0);
|
|||
|
return(STATUS_INVALID_PARAMETER);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// the caller marshalled the input arguments contiguously thus:
|
|||
|
//
|
|||
|
// typedef struct _PUTMSG_ARGS_IN_ {
|
|||
|
// int a_iocode; // 0
|
|||
|
// long a_flags; // 0 | RS_HIPRI
|
|||
|
// struct strbuf a_ctlbuf; // (required)
|
|||
|
// struct strbuf a_databuf; // (required)
|
|||
|
// HANDLE a_fildes; // -1
|
|||
|
// int a_offset; // 0
|
|||
|
// char a_stuff[1]; // s_ctlbuf.buf (optional)
|
|||
|
// // s_databuf.buf (optional)
|
|||
|
// } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
|
|||
|
//
|
|||
|
// When the message has no control part, the caller must have set
|
|||
|
// a_ctlbuf.len = -1 !! Ditto for a_databuf.len.
|
|||
|
//
|
|||
|
//
|
|||
|
inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
|
|||
|
ctrlptr = &(inbuf->a_ctlbuf);
|
|||
|
dataptr = &(inbuf->a_databuf);
|
|||
|
|
|||
|
ASSERT(inbuf->a_insert.i_fildes == INVALID_HANDLE_VALUE);
|
|||
|
inbuf->a_insert.i_targetq = NULL;
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: SHDispPutMsg(flags, clen, dlen = %d, %lx, %lx)\n",
|
|||
|
inbuf->a_flags, ctrlptr->len, dataptr->len));
|
|||
|
}
|
|||
|
switch (inbuf->a_flags) {
|
|||
|
case 0:
|
|||
|
if ((ctrlptr->len < 0) && (dataptr->len < 0)) {
|
|||
|
SHpGenReply(irp, 0, 0);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case RS_HIPRI:
|
|||
|
if (ctrlptr->len >= 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
/* fall through */
|
|||
|
|
|||
|
default:
|
|||
|
SHpGenReply(irp, -1, EINVAL);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
spl_level = lock_strm(ms->e_strm);
|
|||
|
|
|||
|
/*
|
|||
|
* ms->e_wropt may be changed by ioctl(I_SWROPT). However, the current
|
|||
|
* state of ms->e_wropt applies to this put operation.
|
|||
|
*/
|
|||
|
if ((ctrlptr->len <= 0) &&
|
|||
|
(dataptr->len <= 0) &&
|
|||
|
!(ms->e_wropt & SNDZERO)) {
|
|||
|
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
SHpGenReply(irp, 0, 0);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
if (shrange(ms->e_strm, ctrlptr->len, dataptr->len) < 0) {
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
SHpGenReply(irp, -1, ERANGE);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* do_putmsg() has, or will arrange to, complete the IRP.
|
|||
|
*/
|
|||
|
do_putmsg(irp, FALSE, &spl_level, NULL);
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: SHDispPutMsg(ms = %lx) completed ok\n", ms));
|
|||
|
}
|
|||
|
return(STATUS_PENDING);
|
|||
|
|
|||
|
} // SHDispPutMsg
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STATIC VOID
|
|||
|
cancel_put(
|
|||
|
IN PDEVICE_OBJECT device,
|
|||
|
IN PIRP irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when an put operation on a stream is cancelled.
|
|||
|
|
|||
|
It must release the cancel spinlock before returning !! The caller
|
|||
|
has already acquired the cancel spinlock. ref: IoCancelIrp().
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
device - pointer to the device object
|
|||
|
irp - pointer to the irp of this request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
int spl_level;
|
|||
|
PLIST_ENTRY tmp;
|
|||
|
PWAITING_IRP item;
|
|||
|
PSTREAM_ENDPOINT ms;
|
|||
|
PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(irp);
|
|||
|
|
|||
|
ASSERT(device == (PDEVICE_OBJECT) StreamDevice);
|
|||
|
ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
|
|||
|
ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_STREAMS_PUTMSG);
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: cancel_put(irp = %lx) entered\n", irp));
|
|||
|
}
|
|||
|
IoSetCancelRoutine(irp, NULL); /* unnecessary, but cheap */
|
|||
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|||
|
|
|||
|
ms = (PSTREAM_ENDPOINT) irpsp->FileObject->FsContext;
|
|||
|
spl_level = lock_strm(ms->e_strm);
|
|||
|
|
|||
|
for (tmp = ms->e_writers.Flink; tmp != &ms->e_writers; tmp = tmp->Flink) {
|
|||
|
|
|||
|
item = CONTAINING_RECORD(tmp,
|
|||
|
WAITING_IRP,
|
|||
|
w_list);
|
|||
|
|
|||
|
if (irp != item->w_irp) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
RemoveEntryList(&(item->w_list));
|
|||
|
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
ExFreePool(item);
|
|||
|
|
|||
|
shortreply(irp, STATUS_CANCELLED, 0);
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: cancel_put(irp = %lx) cancelled ok\n", irp));
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: cancel_put(irp = %lx) not found\n", irp));
|
|||
|
}
|
|||
|
|
|||
|
} // cancel_put
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STATIC NTSTATUS
|
|||
|
do_putmsg(
|
|||
|
IN PIRP irp,
|
|||
|
IN BOOLEAN from_queue,
|
|||
|
IN int *spl_levelp,
|
|||
|
OUT BOOLEAN *pmore OPTIONAL
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called to put a message down a stream. It either
|
|||
|
completes the irp or chains it to ms->e_writers. In any case,
|
|||
|
once this function is called, it will arrange for the IRP to be
|
|||
|
completed.
|
|||
|
|
|||
|
Call this function with the stream locked !!!
|
|||
|
|
|||
|
This function is based on the SpiderStreams emulator's function, do_req().
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
irp - pointer to the IRP representing this request
|
|||
|
from_queue - TRUE if this is an IRP just unchained from ms->e_writers
|
|||
|
spl_levelp - pointer to the interrupt priority level at which the
|
|||
|
stream was locked
|
|||
|
pmore - if not the stream is not flow-controlled when this
|
|||
|
function returns, this will be TRUE. Otherwise, it
|
|||
|
will be set to false.
|
|||
|
|
|||
|
*pmore is basically set to the value of !canput():
|
|||
|
|
|||
|
This is to accommodate the logic in shwsrv(), the primary caller of this
|
|||
|
function. shwsrv() is most interested in the state of stream's write
|
|||
|
queue: should it call this function again ?
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
an NT status code.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
mblk_t *mp;
|
|||
|
queue_t *iq;
|
|||
|
NTSTATUS status;
|
|||
|
int MyErrno, flags;
|
|||
|
STREAM_ENDPOINT *ms;
|
|||
|
PPUTMSG_ARGS_IN inbuf;
|
|||
|
PIO_STACK_LOCATION irpsp;
|
|||
|
struct strbuf *ctrlptr, *dataptr;
|
|||
|
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: do_putmsg(irp = %lx) entered\n", irp));
|
|||
|
}
|
|||
|
irpsp = IoGetCurrentIrpStackLocation(irp);
|
|||
|
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
|||
|
|
|||
|
//
|
|||
|
// this was already verified by SHDispFdInsert() or SHDispPutMsg().
|
|||
|
//
|
|||
|
ASSERT(irpsp->Parameters.DeviceIoControl.InputBufferLength >=
|
|||
|
sizeof(PUTMSG_ARGS_IN) - 1);
|
|||
|
|
|||
|
//
|
|||
|
// the caller marshalled the input arguments contiguously thus:
|
|||
|
//
|
|||
|
// typedef struct _PUTMSG_ARGS_IN_ {
|
|||
|
// int a_iocode; // I_FDINSERT or 0
|
|||
|
// long a_flags; // 0 or RS_HIPRI
|
|||
|
// struct strbuf a_ctlbuf; // (required)
|
|||
|
// struct strbuf a_databuf; // (required)
|
|||
|
// struct queue *a_insert.i_targetq; // (optional)
|
|||
|
// int a_offset; // (optional)
|
|||
|
// char a_stuff[1]; // s_ctlbuf.buf (optional)
|
|||
|
// // s_databuf.buf (optional)
|
|||
|
// } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
|
|||
|
//
|
|||
|
// When the message has no control part, the caller must have set
|
|||
|
// ctrlbuf->len = -1 !! Ditto for databuf->len.
|
|||
|
//
|
|||
|
inbuf = (PPUTMSG_ARGS_IN) irp->AssociatedIrp.SystemBuffer;
|
|||
|
flags = inbuf->a_flags;
|
|||
|
ctrlptr = &(inbuf->a_ctlbuf);
|
|||
|
dataptr = &(inbuf->a_databuf);
|
|||
|
|
|||
|
IF_STRMDBG(VERBOSE) {
|
|||
|
STRMTRACE(("SHEAD: do_putmsg(flags, clen, dlen = %d, %lx, %lx)\n",
|
|||
|
flags, ctrlptr->len, dataptr->len));
|
|||
|
}
|
|||
|
|
|||
|
if (pmore) {
|
|||
|
*pmore = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if (ms->e_werror) {
|
|||
|
MyErrno = ms->e_werror;
|
|||
|
}
|
|||
|
else if (ms->e_linked) {
|
|||
|
MyErrno = EINVAL;
|
|||
|
}
|
|||
|
else {
|
|||
|
MyErrno = 0;
|
|||
|
}
|
|||
|
|
|||
|
if (MyErrno) {
|
|||
|
IF_STRMDBG(TERSE) {
|
|||
|
STRMTRACE(("SHEAD: do_putmsg(%lx) error = %d\n", ms, MyErrno));
|
|||
|
}
|
|||
|
unlock_strm(ms->e_strm, *spl_levelp);
|
|||
|
|
|||
|
SHpGenReply(irp, -1, MyErrno);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// if downstream flow control is being exerted, enqueue this request in
|
|||
|
// the waiting list of writers.
|
|||
|
//
|
|||
|
// High-priority messages are not subject to flow control.
|
|||
|
//
|
|||
|
// check whether this stream is nonblocking !!!
|
|||
|
//
|
|||
|
//
|
|||
|
if ((flags != RS_HIPRI) &&
|
|||
|
(!IsListEmpty( &ms->e_writers ) && !from_queue) ||
|
|||
|
shblocked(ms->e_strm)) {
|
|||
|
|
|||
|
IF_STRMDBG(VERBOSE) {
|
|||
|
STRMTRACE((
|
|||
|
"SHEAD: do_putmsg(irp = %lx) flow-ctrl, (!%x && !%x) || %x\n", irp,
|
|||
|
IsListEmpty( &ms->e_writers ), from_queue, shblocked(ms->e_strm)));
|
|||
|
}
|
|||
|
if (pmore) {
|
|||
|
*pmore = FALSE;
|
|||
|
}
|
|||
|
IoMarkIrpPending(irp);
|
|||
|
|
|||
|
unlock_strm(ms->e_strm, *spl_levelp);
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&irp->CancelIrql);
|
|||
|
|
|||
|
if (irp->Cancel) {
|
|||
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|||
|
shortreply(irp, STATUS_CANCELLED, 0);
|
|||
|
return(STATUS_CANCELLED);
|
|||
|
}
|
|||
|
|
|||
|
*spl_levelp = lock_strm(ms->e_strm);
|
|||
|
|
|||
|
status = SHAddPendingIrp(
|
|||
|
&(ms->e_writers),
|
|||
|
from_queue,
|
|||
|
irp,
|
|||
|
do_putmsg);
|
|||
|
|
|||
|
unlock_strm(ms->e_strm, *spl_levelp);
|
|||
|
|
|||
|
if (status != STATUS_SUCCESS) {
|
|||
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|||
|
shortreply(irp, status, 0);
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
IoSetCancelRoutine(irp, cancel_put);
|
|||
|
IoReleaseCancelSpinLock(irp->CancelIrql);
|
|||
|
|
|||
|
return(STATUS_PENDING);
|
|||
|
}
|
|||
|
|
|||
|
mp = irptomp(irp, BPRI_LO, ctrlptr->len, dataptr->len, inbuf->a_stuff);
|
|||
|
|
|||
|
if (!mp) {
|
|||
|
unlock_strm(ms->e_strm, *spl_levelp);
|
|||
|
shortreply(irp, STATUS_NO_MEMORY, 0);
|
|||
|
return(STATUS_NO_MEMORY);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Both SHDispFdInsert() and SHDispPutMsg() verified that if RS_HIPRI
|
|||
|
// was set, a control part exists.
|
|||
|
//
|
|||
|
if (flags == RS_HIPRI) {
|
|||
|
ASSERT(mp->b_datap->db_type == M_PROTO);
|
|||
|
mp->b_datap->db_type = M_PCPROTO;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The stream of a_insert.i_targetq should be locked in
|
|||
|
// SHDispFdInsert(), and unlocked after the shput() ?
|
|||
|
//
|
|||
|
if (inbuf->a_insert.i_targetq) {
|
|||
|
|
|||
|
iq = inbuf->a_insert.i_targetq;
|
|||
|
|
|||
|
//
|
|||
|
// Spider's do_req() has the line below before the while loop:
|
|||
|
//
|
|||
|
// iq = WR(iq);
|
|||
|
//
|
|||
|
// This is incorrect because:
|
|||
|
//
|
|||
|
// a) iq already points to the write queue,
|
|||
|
// b) it causes iq to be traversed upstream, instead of downstream.
|
|||
|
//
|
|||
|
while (iq->q_next) {
|
|||
|
iq = iq->q_next;
|
|||
|
}
|
|||
|
* ((queue_t **) (mp->b_rptr + inbuf->a_offset)) = RD(iq);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// since irptomp() made a copy, we can complete the putmsg() now.
|
|||
|
//
|
|||
|
SHpGenReply(irp, 0, 0);
|
|||
|
|
|||
|
//
|
|||
|
// shput() does the unlock_strm(ms->e_strm) for us.
|
|||
|
//
|
|||
|
shput(ms->e_strm, mp, 0, spl_levelp);
|
|||
|
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: do_putmsg(irp = %lx) completed ok\n", irp));
|
|||
|
}
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
|
|||
|
} // do_putmsg
|
|||
|
|
|||
|
|
|||
|
|
|||
|
STATIC queue_t *
|
|||
|
handle_to_queue (
|
|||
|
IN HANDLE handle
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns a pointer to a queue structure, given the NT handle
|
|||
|
of its stream. It is based on the SpiderStreams Emulator function,
|
|||
|
fd_to_queue().
|
|||
|
|
|||
|
Do not call this function at DISPATCH_LEVEL !! ObReferenceObjectByHandle()
|
|||
|
must be called from either LOW_LEVEL or APC_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
handle - handle relevant only in the current process' context
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
pointer to the queue structure associated with that handle
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
STREAM_ENDPOINT *ms;
|
|||
|
PFILE_OBJECT pfileobj;
|
|||
|
|
|||
|
IF_STRMDBG(CALL) {
|
|||
|
STRMTRACE(("SHEAD: handle_to_queue(%lx) \n", handle));
|
|||
|
}
|
|||
|
status = ObReferenceObjectByHandle(
|
|||
|
handle, // Handle
|
|||
|
FILE_READ_DATA, // DesiredAccess
|
|||
|
*IoFileObjectType, // ObjectType
|
|||
|
KernelMode, // AccessMode
|
|||
|
(PVOID *) &pfileobj, // *object
|
|||
|
NULL // HandleInformation
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status) ||
|
|||
|
!((STREAM_ENDPOINT *) pfileobj->FsContext) ||
|
|||
|
!((STREAM_ENDPOINT *) pfileobj->FsContext)->e_strm) {
|
|||
|
return((queue_t *) NULL);
|
|||
|
}
|
|||
|
ms = (STREAM_ENDPOINT *) pfileobj->FsContext;
|
|||
|
ObDereferenceObject(pfileobj);
|
|||
|
|
|||
|
return(ms->e_strm->str_sq->q_next);
|
|||
|
|
|||
|
} // handle_to_queue
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void
|
|||
|
shwsrv(
|
|||
|
IN struct msg_strm *ms
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called from two places: from the Stream Head's write
|
|||
|
service procedure, headwsrv(), and when the Stream Head driver's
|
|||
|
bufcall() and esbbcall() are triggered.
|
|||
|
|
|||
|
It is based on the SpiderStreams function of the same name.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ms - pointer to the stream endpoint
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
none.
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp;
|
|||
|
int spl_level;
|
|||
|
BOOLEAN carryon;
|
|||
|
PLIST_ENTRY tmp;
|
|||
|
PWAITING_IRP item;
|
|||
|
START_FUNCTION function;
|
|||
|
PTPI_OBJECT ObjectPtr;
|
|||
|
PTPI_CONNECTION_OBJECT ConnectionPtr;
|
|||
|
|
|||
|
//
|
|||
|
// ensure that the stream has not gone away.
|
|||
|
//
|
|||
|
if ( !(ms->e_strm) ) {
|
|||
|
IF_STRMDBG(TERSE) {
|
|||
|
STRMTRACE(("SHEAD: shwsrv(%lx) called on null stream\n", ms));
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
spl_level = lock_strm(ms->e_strm);
|
|||
|
|
|||
|
//
|
|||
|
// handle anyone waiting to put a message down this stream.
|
|||
|
//
|
|||
|
while (!IsListEmpty( &(ms->e_writers) )) {
|
|||
|
|
|||
|
tmp = RemoveHeadList( &(ms->e_writers) );
|
|||
|
item = CONTAINING_RECORD(tmp,
|
|||
|
WAITING_IRP,
|
|||
|
w_list);
|
|||
|
|
|||
|
irp = item->w_irp;
|
|||
|
function = item->w_function;
|
|||
|
ExFreePool(item);
|
|||
|
|
|||
|
carryon = TRUE;
|
|||
|
|
|||
|
(void) (*function)(irp, TRUE, &spl_level, &carryon);
|
|||
|
|
|||
|
if (!carryon) {
|
|||
|
return;
|
|||
|
}
|
|||
|
spl_level = lock_strm(ms->e_strm);
|
|||
|
}
|
|||
|
|
|||
|
if (ms->e_strm_flags & POLLOUT) {
|
|||
|
ms->e_strm_flags &= ~POLLOUT;
|
|||
|
|
|||
|
KeReleaseSemaphore(
|
|||
|
&Poll_fired, // semaphore
|
|||
|
SEMAPHORE_INCREMENT, // priority increment
|
|||
|
1, // adjustment
|
|||
|
FALSE // wait
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this is a TdiStream and it has become unblocked, I need to do a
|
|||
|
// SEND_POSSIBLE indication to the user.
|
|||
|
//
|
|||
|
|
|||
|
if ((ObjectPtr = ms->TdiStreamPtr) &&
|
|||
|
(ObjectPtr->Tag == TPI_CONNECTION_OBJECT_TYPE)) {
|
|||
|
|
|||
|
ConnectionPtr = &ObjectPtr->Object.TpiConnection;
|
|||
|
|
|||
|
//
|
|||
|
// The Blocked flag is always accessed under the StreamLock
|
|||
|
//
|
|||
|
|
|||
|
if (ConnectionPtr->Blocked) {
|
|||
|
ConnectionPtr->Blocked = FALSE;
|
|||
|
SHTdiEventSendPossible(ObjectPtr);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
unlock_strm(ms->e_strm, spl_level);
|
|||
|
|
|||
|
} // shwsrv
|