665 lines
16 KiB
C
665 lines
16 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
sh_get.c
|
||
|
||
Abstract:
|
||
|
||
This source file contains those functions of the Stream Head Driver that
|
||
deal with receiving messages from a stream.
|
||
|
||
Author:
|
||
|
||
Eric Chin (ericc) August 16, 1991
|
||
|
||
Revision History:
|
||
|
||
Notes:
|
||
|
||
The read error state of a stream is represented by ms->e_rerror. Once
|
||
set, this is never reset. This corresponds to the STREAMS semantics as
|
||
defined by AT&T. Once a user is notified of a read error on a stream,
|
||
about the only recourse is to close the stream.
|
||
|
||
--*/
|
||
#include "sh_inc.h"
|
||
|
||
|
||
//
|
||
// Local (Private) Functions
|
||
//
|
||
STATIC
|
||
VOID
|
||
cancel_get(
|
||
IN PDEVICE_OBJECT device,
|
||
IN PIRP irp
|
||
);
|
||
|
||
NTSTATUS
|
||
do_getmsg(
|
||
IN PIRP irp,
|
||
IN PFILE_OBJECT pfileobj,
|
||
IN int flags
|
||
);
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SHDispGetMsg(
|
||
IN PIRP irp,
|
||
IN PIO_STACK_LOCATION irpsp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the getmsg(2) API.
|
||
|
||
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;
|
||
NTSTATUS status;
|
||
PSTREAM_ENDPOINT ms;
|
||
int MyErrno, pri;
|
||
PGETMSG_ARGS_INOUT inbuf;
|
||
int ret;
|
||
mblk_t *mp;
|
||
int more = 0;
|
||
struct strbuf *strbufp;
|
||
int ctlsize, datasize, flags, *pretval, remains;
|
||
|
||
ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
|
||
METHOD_BUFFERED);
|
||
|
||
ms = (PSTREAM_ENDPOINT) irpsp->FileObject->FsContext;
|
||
|
||
if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(GETMSG_ARGS_INOUT) - 1) {
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg(%lx) insufficient nbytes = %lx\n",
|
||
irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
|
||
}
|
||
shortreply(irp, STATUS_INVALID_PARAMETER, 0);
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
// Need to ensure that the output buffer is big enough.
|
||
{
|
||
int cbOut = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
|
||
NTSTATUS LengthStatus = STATUS_INVALID_PARAMETER;
|
||
|
||
if (cbOut >= (sizeof(GETMSG_ARGS_INOUT) - 1))
|
||
{
|
||
pretval = (int *) irp->AssociatedIrp.SystemBuffer;
|
||
flags = * (pretval + 1);
|
||
strbufp = (struct strbuf *) (pretval + 2);
|
||
ctlsize = strbufp->maxlen;
|
||
datasize = (++strbufp)->maxlen;
|
||
|
||
cbOut -= (sizeof(GETMSG_ARGS_INOUT) - 1);
|
||
|
||
if (cbOut >= ctlsize)
|
||
{
|
||
cbOut -= ctlsize;
|
||
|
||
if (cbOut >= datasize)
|
||
{
|
||
// cbOut -= datasize;
|
||
LengthStatus = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (LengthStatus != STATUS_SUCCESS)
|
||
{
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg(%lx) outbuf insufficient nbytes = %lx\n",
|
||
irp, irpsp->Parameters.DeviceIoControl.OutputBufferLength));
|
||
}
|
||
shortreply(irp, LengthStatus, 0);
|
||
return (LengthStatus);
|
||
}
|
||
}
|
||
|
||
//
|
||
// the caller marshalled the input arguments contiguously thus:
|
||
//
|
||
// typedef struct _GETMSG_ARGS_INOUT_ { // getmsg()
|
||
// int a_retval; // ignore on input
|
||
// long a_flags; // 0 or RS_HIPRI
|
||
// struct strbuf a_ctlbuf; // (required)
|
||
// struct strbuf a_databuf; // (required)
|
||
// char a_stuff[1]; // a_ctlbuf.buf (optional)
|
||
// // a_databuf.buf (optional)
|
||
// } GETMSG_ARGS_INOUT, *PGETMSG_ARGS_INOUT;
|
||
//
|
||
inbuf = (PGETMSG_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
|
||
|
||
IF_STRMDBG(VERBOSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg(irp = %lx)\n", irp));
|
||
}
|
||
|
||
IoAcquireCancelSpinLock(&irp->CancelIrql);
|
||
|
||
spl_level = lock_strm(ms->e_strm);
|
||
|
||
if (ms->e_rerror) {
|
||
MyErrno = ms->e_rerror;
|
||
}
|
||
else if (ms->e_linked) {
|
||
MyErrno = EINVAL;
|
||
}
|
||
else {
|
||
MyErrno = 0;
|
||
}
|
||
|
||
if (MyErrno) {
|
||
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg() error = %d\n", MyErrno));
|
||
}
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
SHpGenReply(irp, -1, MyErrno);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
pri = (inbuf->a_flags == RS_HIPRI) ? QPCTL : 0;
|
||
|
||
if (shready(ms->e_strm, pri)) {
|
||
|
||
IF_STRMDBG(VERBOSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg() stream's shready()\n"));
|
||
}
|
||
// The two lines below are replaced by the lines between the vvv/^^^'s
|
||
// temp = msgreply(ms, irp);
|
||
// ASSERT(temp == 0);
|
||
|
||
// vvvvvvvv
|
||
// vvvvvvvv
|
||
/*
|
||
* the arguments are marshalled in one contiguous chunk, laid out as:
|
||
*
|
||
* an unused int (required)
|
||
* flags (required)
|
||
* struct strbuf ctrlbuf (required)
|
||
* struct strbuf databuf (required)
|
||
*/
|
||
pretval = (int *) irp->AssociatedIrp.SystemBuffer;
|
||
flags = * (pretval + 1);
|
||
strbufp = (struct strbuf *) (pretval + 2);
|
||
ctlsize = strbufp->maxlen;
|
||
datasize = (++strbufp)->maxlen;
|
||
|
||
/*
|
||
* st_getmsg() may set MORECTL and/or MOREDATA in *pretval; we must
|
||
* return it to the user-level runtime !!
|
||
*/
|
||
ret = st_getmsg(ms->e_strm, ctlsize, datasize, &flags, pretval,
|
||
&mp, &remains);
|
||
|
||
ASSERT(!ret);
|
||
ASSERT(mp);
|
||
// ^^^^^^^^
|
||
// ^^^^^^^^
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
|
||
// vvvvvvvv
|
||
// vvvvvvvv
|
||
mptoirp(mp, irp);
|
||
freemsg(mp);
|
||
// ^^^^^^^^
|
||
// ^^^^^^^^
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
if (ms->e_hup) {
|
||
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg() stream's was hung up\n"));
|
||
}
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
SHpGenReply(irp, -1, EINTR);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// enqueue this request in the waiting list of readers.
|
||
//
|
||
IoMarkIrpPending(irp);
|
||
|
||
if (irp->Cancel) {
|
||
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
|
||
IoSetCancelRoutine(irp, NULL);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
shortreply(irp, STATUS_CANCELLED, 0);
|
||
return(STATUS_CANCELLED);
|
||
}
|
||
|
||
status = SHAddPendingIrp(&(ms->e_readers), FALSE, irp, NULL);
|
||
|
||
ASSERT(!shready(ms->e_strm, 0));
|
||
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg() failed to SHAddPendingIrp\n"));
|
||
}
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
shortreply(irp, status, 0);
|
||
return(status);
|
||
}
|
||
|
||
IoSetCancelRoutine(irp, cancel_get);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
|
||
IF_STRMDBG(VERBOSE) {
|
||
STRMTRACE(("SHEAD: SHDispGetMsg(irp = %lx) q_count = %ld\n",
|
||
irp, RD(ms->e_strm->str_sq)->q_count ));
|
||
}
|
||
return(STATUS_PENDING);
|
||
|
||
} // SHDispGetMsg
|
||
|
||
|
||
|
||
int
|
||
SHpStreamError(
|
||
IN PLIST_ENTRY listhead,
|
||
IN int error
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine completes the IRPs waiting on a stream when an M_ERROR or
|
||
M_HANGUP arrives from downstream.
|
||
|
||
Arguments:
|
||
|
||
listhead - either e_readers, e_writers or e_ioctlers
|
||
error - the POSIX error code to return
|
||
|
||
Return Value:
|
||
|
||
The number of pending IRPs that were completed.
|
||
|
||
--*/
|
||
|
||
{
|
||
int count = 0;
|
||
PLIST_ENTRY tmp;
|
||
PWAITING_IRP item;
|
||
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: ShpStreamError() for M_HANGUP/M_ERROR\n"));
|
||
}
|
||
|
||
while (!IsListEmpty(listhead)) {
|
||
|
||
tmp = RemoveHeadList(listhead);
|
||
item = CONTAINING_RECORD(tmp,
|
||
WAITING_IRP,
|
||
w_list);
|
||
|
||
SHpGenReply(item->w_irp, -1, error);
|
||
ExFreePool(item);
|
||
count++;
|
||
}
|
||
return(count);
|
||
|
||
} // SHpStreamError
|
||
|
||
|
||
|
||
|
||
STATIC
|
||
VOID
|
||
cancel_get(
|
||
IN PDEVICE_OBJECT device,
|
||
IN PIRP irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when a getmsg() 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_GETMSG);
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_get(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_readers.Flink; tmp != &ms->e_readers; 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_get(irp = %lx) cancelled ok\n", irp));
|
||
}
|
||
return;
|
||
}
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_get(irp = %lx) not found\n", irp));
|
||
}
|
||
|
||
} // cancel_get
|
||
|
||
|
||
|
||
void
|
||
msgrdy(
|
||
IN struct msg_strm *ms,
|
||
IN int mtype
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by the Stream Head Driver when a message arrives
|
||
at the read queue of the specified stream.
|
||
|
||
Call this function with the stream endpoint locked !!
|
||
|
||
Arguments:
|
||
|
||
ms - pointer to the stream endpoint
|
||
mtype - type of the STREAMS message
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
int check, pri;
|
||
PLIST_ENTRY tmp;
|
||
PWAITING_IRP item;
|
||
PGETMSG_ARGS_INOUT inbuf;
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: msgrdy(ms = %lx, mtype = %x)\n", ms, mtype));
|
||
}
|
||
|
||
switch (mtype) {
|
||
case M_DATA:
|
||
case M_PROTO:
|
||
if (ms->e_strm_flags & POLLIN) {
|
||
ms->e_strm_flags &= ~POLLIN;
|
||
|
||
KeReleaseSemaphore(
|
||
&Poll_fired, // semaphore
|
||
SEMAPHORE_INCREMENT, // priority increment
|
||
1, // adjustment
|
||
FALSE // wait
|
||
);
|
||
}
|
||
break;
|
||
|
||
case M_PCPROTO:
|
||
if (ms->e_strm_flags & POLLPRI) {
|
||
ms->e_strm_flags &= ~POLLPRI;
|
||
|
||
KeReleaseSemaphore(
|
||
&Poll_fired, // semaphore
|
||
SEMAPHORE_INCREMENT, // priority increment
|
||
1, // adjustment
|
||
FALSE // wait
|
||
);
|
||
}
|
||
break;
|
||
|
||
case M_SIG:
|
||
if (ms->e_strm_flags & POLLMSG) {
|
||
ms->e_strm_flags &= ~POLLMSG;
|
||
|
||
KeReleaseSemaphore(
|
||
&Poll_fired, // semaphore
|
||
SEMAPHORE_INCREMENT, // priority increment
|
||
1, // adjustment
|
||
FALSE // wait
|
||
);
|
||
}
|
||
break;
|
||
|
||
default:
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: msgrdy(), msg type = %x unexpected\n", mtype));
|
||
}
|
||
ASSERT(0);
|
||
}
|
||
|
||
while (!IsListEmpty( &(ms->e_readers) )) {
|
||
|
||
tmp = RemoveHeadList( &(ms->e_readers) );
|
||
item = CONTAINING_RECORD(tmp,
|
||
WAITING_IRP,
|
||
w_list);
|
||
irp = item->w_irp;
|
||
|
||
//
|
||
// get the RS_HIPRI flag, if any, from the irp.
|
||
//
|
||
inbuf = (PGETMSG_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
|
||
pri = (inbuf->a_flags == RS_HIPRI) ? QPCTL : 0;
|
||
|
||
if (!shready(ms->e_strm, pri)) {
|
||
InsertHeadList( &(ms->e_readers), &(item->w_list) );
|
||
return;
|
||
}
|
||
check = msgreply(ms, irp);
|
||
ASSERT(check == 0);
|
||
|
||
ExFreePool(item);
|
||
}
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: msgrdy() completed ok\n"));
|
||
}
|
||
return;
|
||
|
||
} // msgrdy
|
||
|
||
|
||
void
|
||
strmevent(
|
||
IN STREAM_ENDPOINT *ms,
|
||
IN int rerror,
|
||
IN int werror,
|
||
IN int t
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function handles special messages that arrive at the stream head.
|
||
It is based on the SpiderStreams function of the same name.
|
||
|
||
Only M_ERROR and M_HANGUP messages are handled at present. M_ERROR is
|
||
straightforward to deal with. The error status in the stream structure
|
||
is set, and any pending requests failed.
|
||
|
||
M_HANGUP is very similar, except that read requests are allowed to
|
||
complete, and subsequent reads are treated as end of file, rather than
|
||
an error condition.
|
||
|
||
Arguments:
|
||
|
||
ms - pointer to stream endpoint
|
||
rerror - read queue error
|
||
werror - write queue error
|
||
t - type of STREAMS message
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
int succeeded;
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: strmevent(ms = %lx) entered\n", ms));
|
||
}
|
||
|
||
if (ms->TdiStreamPtr) {
|
||
TdiStreamEvent(ms, rerror, werror, t);
|
||
return;
|
||
}
|
||
|
||
if (rerror == NOERROR) {
|
||
rerror = ms->e_rerror; // get current value
|
||
}
|
||
else {
|
||
ms->e_rerror = rerror; // set read error status
|
||
}
|
||
|
||
if (werror == NOERROR) {
|
||
werror = ms->e_werror; // get current value
|
||
}
|
||
else {
|
||
ms->e_werror = werror; // set write error status
|
||
}
|
||
|
||
if ((rerror == 0) && (werror == 0)) { // errors zeroed out
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: strmevent(ms = %lx) [rw]error = 0\n", ms));
|
||
}
|
||
return;
|
||
}
|
||
|
||
switch (t) {
|
||
case M_HANGUP:
|
||
ms->e_hup = 1;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(0);
|
||
/* fall through */
|
||
|
||
case M_ERROR:
|
||
break;
|
||
}
|
||
|
||
if (rerror) {
|
||
SHpStreamError(&(ms->e_readers), rerror);
|
||
}
|
||
|
||
//
|
||
// in response to an M_ERROR or M_HANGUP for the write-side, fail
|
||
// any pending ioctl(), putmsg() or write() requests.
|
||
//
|
||
// If a pending ioctl() is being failed, don't forget to abort its
|
||
// timeout !!
|
||
//
|
||
if (werror) {
|
||
irp = ms->e_active_ioctl;
|
||
|
||
if (irp) {
|
||
ms->e_active_ioctl = NULL;
|
||
succeeded = 1;
|
||
|
||
if (irp->IoStatus.Information) {
|
||
succeeded = untimeout((int)irp->IoStatus.Information);
|
||
|
||
ASSERT((succeeded == 0) || (succeeded == 1));
|
||
}
|
||
|
||
if (succeeded) {
|
||
SHpGenReply(irp, -1, werror);
|
||
}
|
||
}
|
||
|
||
SHpStreamError(&(ms->e_ioctlers), werror);
|
||
SHpStreamError(&(ms->e_writers), werror);
|
||
}
|
||
|
||
//
|
||
// let poll()'ers know of the error/hangup
|
||
//
|
||
KeReleaseSemaphore(
|
||
&Poll_fired, // semaphore
|
||
SEMAPHORE_INCREMENT, // priority increment
|
||
1, // adjustment
|
||
FALSE // wait
|
||
);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: strmevent(ms = %lx) completed\n", ms));
|
||
}
|
||
return;
|
||
|
||
} // strmevent
|