windows-nt/Source/XPSP1/NT/net/streams/sys/sh_get.c

665 lines
16 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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