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

700 lines
16 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
sh_irp.c
Abstract:
This source file contains the functions that convert between NT IRPs
and STREAMS messages.
Most functions in this module are based on identically named routines
in the SpiderStreams emulator source, stremul/msgrtns.c.
Author:
Eric Chin (ericc) August 16, 1991
Revision History:
--*/
#include "sh_inc.h"
/*
* Local (Private) Functions
*/
STATIC void
mp_buf_free(
IN char *p
);
STATIC int
mptoirp(
IN mblk_t *mp,
IN PIRP irp
);
VOID
SHpGenReply(
IN PIRP irp,
IN int retval,
IN int MyErrno
)
/*++
Routine Description:
This function is called to complete IRPs that convey generic STREAMS
API's: ioctl(I_FDINSERT), ioctl(I_STR), putmsg(), ....
By generic, we mean it returns a return value and possible an errno.
Arguments:
irp - irp to complete
retval - return value of the ioctl(,I_FDINSERT,) or putmsg()
errno - POSIX error value, if any
Return Value:
none.
--*/
{
PSTRM_ARGS_OUT outptr;
PIO_STACK_LOCATION pIrpSp;
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: SHpGenReply(irp = %lx, %lx, %lx) entered\n",
irp, retval, MyErrno));
}
pIrpSp = IoGetCurrentIrpStackLocation(irp);
// Check size of output buffer.
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(STRM_ARGS_OUT))
{
shortreply(irp, STATUS_BUFFER_TOO_SMALL, 0);
return;
}
//
// have IopCompleteRequest() copy the following back to the user's
// output buffer, laid out as:
//
// typedef struct _STRM_ARGS_OUT_ { // generic return parameters
// int a_retval; // return value
// int a_errno; // errno if retval == -1
//
// } STRM_ARGS_OUT, *PSTRM_ARGS_OUT;
//
outptr = (PSTRM_ARGS_OUT) irp->AssociatedIrp.SystemBuffer;
outptr->a_retval = retval;
outptr->a_errno = MyErrno;
shortreply(irp, STATUS_SUCCESS, sizeof(STRM_ARGS_OUT));
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: SHpGenReply(irp = %lx) done\n", irp));
}
return;
} // SHpGenReply
int
iocreply(
IN mblk_t *mp,
IN PIRP irp
)
/*++
Routine Description:
This function sends the reply to an M_IOCTL message back to the user,
reverting a STREAMS message into an NT irp. It is loosely based on the
SpiderStreams functions, iocreply() and mptomsg(), in stremul/msgrtns.c.
It should be called after the appropriate routine has taken the message
off the queue. If it fails to send a message, it returns a negative
value.
Acquire the lock of ms->e_strm before calling, and release it afterwards !!
Arguments:
mp - pointer to the message to reply to
irp - pointer to the IRP
Return Value:
-1 - no message is ready to be sent to the user
-2 - failed to send a message to the user
--*/
{
mblk_t *tmp;
int length;
char *outbuf;
int *pretval;
int nbytes = 0;
struct iocblk *iocp;
struct strioctl *striop;
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: iocreply(mp = %lx, irp = %lx) entered\n", mp, irp));
}
if (!mp) {
return(-1);
}
outbuf = irp->AssociatedIrp.SystemBuffer;
pretval = (int *) outbuf;
*pretval = 0;
switch (mp->b_datap->db_type) {
/*
* for ioctl(I_STR), arrange the return parameters contiguously in outbuf
* in the format:
*
* int return value (required)
* union {
* int errno; (required)
* struct strioctl; (ic_cmd is not valid !!)
* }
* int (required)
* int (required)
* ic_dp buffer (optional)
*/
case M_IOCACK:
iocp = (struct iocblk *) mp->b_rptr;
*pretval = iocp->ioc_rval;
striop = (struct strioctl *) ((int *) outbuf + 1);
striop->ic_len = 0;
outbuf = (char *) (striop + 1) + 2 * sizeof(int);
for (tmp = mp->b_cont; tmp; tmp = tmp->b_cont) {
ASSERT(tmp->b_datap->db_type == M_DATA);
length = (int)(tmp->b_wptr - tmp->b_rptr);
ASSERT(length >= 0);
striop->ic_len += length;
RtlCopyMemory(outbuf, tmp->b_rptr, length);
outbuf += length;
}
nbytes = (int) ( outbuf - (char *) irp->AssociatedIrp.SystemBuffer );
break;
case M_IOCNAK:
iocp = (struct iocblk *) mp->b_rptr;
*pretval = -1;
*(pretval + 1) = iocp->ioc_error;
nbytes = 2 * sizeof(int);
break;
default:
ASSERT(0); /* shouldn't come here */
}
shortreply(irp, STATUS_SUCCESS, nbytes);
freemsg(mp);
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: iocreply(irp = %lx) nbytes = %lx, completed ok\n",
irp, nbytes));
}
return(0);
} // iocreply
mblk_t *
irptomp(
IN PIRP irp,
IN int pri,
IN int ctlsize,
IN int datasize,
IN char *mbuf
)
/*++
Routine Description:
This function converts the buffers associated with an NT irp into a
STREAMS message. It is based on the SpiderSTREAMS routine, msgtomp().
The first block of the message created is either an M_PROTO or M_DATA
block. To make it M_PCPROTO, M_IOCTL, ..., set it yourself after
this function returns !!
Arguments:
irp - pointer to the IRP.
pri - buffer allocation priority. BPRI_LO, BPRI_MED or BPRI_HI.
ctlsize - length of control part message
datasize - length of data part of message
mbuf - pointer to a contiguous chunk containing first the control
part of the message, if any, and then the data part.
Return Value:
pointer to the resulting STREAMS message, or NULL if unsuccessful.
--*/
{
unsigned char *extra;
mblk_t *mp = (mblk_t *) NULL;
mblk_t *cmp = (mblk_t *) NULL;
frtn_t fr_rtn;
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: irptomp(clen, dlen, mbuf = %lx, %lx, %lx) entered\n",
ctlsize, datasize, mbuf));
}
/*
* special case for constructing a zero length message.
*/
if ((max(ctlsize, 0) + max(datasize, 0)) == 0) {
mp = allocb(0, pri);
if (!mp) {
IF_STRMDBG(TERSE) {
STRMTRACE(("SHEAD: irptomp(), allocb of 0 failed\n"));
}
return((mblk_t *) NULL);
}
ASSERT(mp->b_datap->db_type == M_DATA);
if (ctlsize != -1) {
mp->b_datap->db_type = M_PROTO;
}
ASSERT(mp->b_wptr == mp->b_rptr);
return(mp);
}
fr_rtn.free_func = mp_buf_free;
if (ctlsize >= 0) {
if (ctlsize) {
extra = ExAllocatePool(NonPagedPool, ctlsize);
if (!extra) {
return((mblk_t *) NULL);
}
RtlCopyMemory(extra, mbuf, ctlsize);
fr_rtn.free_arg = (char *) extra;
cmp = esballoc(extra, ctlsize, pri, &fr_rtn);
}
else {
cmp = allocb(0, pri);
}
if (!cmp) {
IF_STRMDBG(TERSE) {
STRMTRACE(("SHEAD: irptomp(), esballoc %x failed\n", ctlsize));
}
return((mblk_t *) NULL);
}
ASSERT(cmp->b_datap->db_type == M_DATA);
cmp->b_datap->db_type = M_PROTO;
ASSERT(cmp->b_wptr == cmp->b_rptr);
cmp->b_wptr += ctlsize;
}
if (datasize >= 0) {
if (datasize) {
extra = ExAllocatePool(NonPagedPool, datasize);
if (!extra) {
if (cmp) {
freemsg(cmp);
}
return((mblk_t *) NULL);
}
RtlCopyMemory(extra,
(ctlsize <= 0) ? mbuf : mbuf + ctlsize, datasize);
fr_rtn.free_arg = (char *) extra;
mp = esballoc(extra, datasize, pri, &fr_rtn);
}
else {
mp = allocb(0, pri);
}
if (!mp) {
IF_STRMDBG(TERSE) {
STRMTRACE(("SHEAD: irptomp(), esballoc %x failed\n", ctlsize));
}
if (cmp) {
freemsg(cmp);
}
return((mblk_t *) NULL);
}
ASSERT(mp->b_datap->db_type == M_DATA);
ASSERT(mp->b_wptr == mp->b_rptr);
mp->b_wptr += datasize;
}
if (cmp) {
cmp->b_cont = mp;
mp = cmp;
}
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: irptomp(mbuf = %lx) returns, mp = %lx\n", mbuf, mp));
}
return(mp);
} // irptomp
STATIC void
mp_buf_free(
IN char *p
)
{
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: mp_buf_free(%lx) entered\n", p));
}
if (p) {
ExFreePool(p);
}
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: mp_buf_free(%lx) completed\n", p));
}
return;
} // mp_buf_free
STATIC int
mptoirp(
IN mblk_t *mp,
IN PIRP irp
)
/*++
Routine Description:
This function converts a STREAMS message into an NT irp. It is based
on the SpiderStreams routine, mptomsg(), in stremul/msgrtns.c.
It should be called after the appropriate routine has taken the message
off the queue. If it fails to send a message, it returns a negative
value.
The caller must free the STREAMS message, mp. This function doesn't !!
Arguments:
mp
irp
Return Value:
number of bytes copied back to user space, or a negative value if
unsuccessful.
--*/
{
char *outbuf;
int *pretval;
int length, nbytes;
struct strbuf *strbufp;
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: mptoirp(mp = %lx, irp = %lx) entered\n", mp, irp));
}
/*
* for getmsg(), arrange the return parameters contiguously in outbuf
* in the format:
*
* int return value (required)
* flags / errno (required)
* struct strbuf ctrlbuf (required)
* struct strbuf databuf (required)
* ctrl buffer (optional)
* data buffer (optional)
*/
outbuf = irp->AssociatedIrp.SystemBuffer;
pretval = (int *) outbuf;
strbufp = (struct strbuf *) (pretval + 2); /* struct strbuf ctrlbuf */
outbuf = (char *) (strbufp + 2); /* ctrl buffer */
/*
* ensure that the return value is copied back to the user-level runtime.
* It was zeroed in ShDispGetmsg(), and the MORECTL, MOREDATA bits may
* have set by st_getmsg().
*/
nbytes = 2 * sizeof(int);
switch (mp->b_datap->db_type) {
case M_PCPROTO:
*(pretval + 1) = RS_HIPRI; /* flags */
goto doproto;
case M_PROTO:
*(pretval + 1) = 0; /* flags */
doproto:
length = (int)(mp->b_wptr - mp->b_rptr);
if (strbufp->maxlen < length) {
length = strbufp->maxlen;
*pretval |= MORECTL;
}
strbufp->len = length;
RtlCopyMemory(outbuf, mp->b_rptr, strbufp->len);
mp = mp->b_cont;
goto dodata;
case M_DATA:
*(pretval + 1) = 0; /* flags */
strbufp->len = 0; /* ctrlbuf->len */
dodata:
outbuf += strbufp->len;
(++strbufp)->len = 0;
for (; mp; mp = mp->b_cont) {
ASSERT(mp->b_datap->db_type == M_DATA);
length = (int)(mp->b_wptr - mp->b_rptr);
ASSERT(length >= 0);
if (strbufp->maxlen < length) {
length = strbufp->maxlen;
*pretval |= MOREDATA;
}
RtlCopyMemory(outbuf, mp->b_rptr, length);
outbuf += length;
strbufp->len += length;
if ((strbufp->maxlen -= length) == 0) {
break;
}
}
nbytes = (int)( outbuf - (char *) irp->AssociatedIrp.SystemBuffer );
break;
default:
IF_STRMDBG(TERSE) {
STRMTRACE(("SHEAD: mptoirp(), unexpected db_type = %x\n",
mp->b_datap->db_type));
}
ASSERT(0); /* shouldn't come here */
KeBugCheck(STREAMS_INTERNAL_ERROR);
break;
}
/*
* no matter what, we always pass back the return value and the flags
* to the user-level runtime.
*/
ASSERT(nbytes >= 2 * sizeof(int));
shortreply(irp, STATUS_SUCCESS, nbytes);
IF_STRMDBG(CALL) {
STRMTRACE(("SHEAD: mptoirp(irp = %lx), %lx, completed\n", irp, nbytes));
}
return(nbytes);
} // mptoirp
int
msgreply(
IN STREAM_ENDPOINT *ms,
IN PIRP irp
)
/*++
Routine Description:
This function gets a STREAMS message to complete an IRP representing
a getmsg(). It is based on the SpiderStreams emulator function of the
same name.
Lock the stream, ms->e_strm, before calling this function, and unlock
it after this function returns !!
Arguments:
ms - stream endpoint from whose read queue to get the message from
irp - IRP to complete
Return Value:
0 - successful completion
-1 - no message is ready to be sent to the user
-2 - failed to send a message to the user
--*/
{
int ret;
mblk_t *mp;
int more = 0;
struct strbuf *strbufp;
int ctlsize, datasize, flags, *pretval, remains;
/*
* 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);
if (ret) {
ASSERT(0);
shortreply(irp, STATUS_SUCCESS, 0);
return(0);
}
if (!mp) {
return(-1);
}
//
// Unlike SpiderSTREAMS, our mptoirp() function never fails !! Hence
// the assertion.
//
if (mptoirp(mp, irp) < 0) {
ASSERT(0);
st_putback(ms->e_strm, mp, remains);
return(-2);
}
/*
* Spider frees mp by chasing mp->b_next. Why ?
*/
ASSERT(!(mp->b_next));
freemsg(mp);
return(0);
} // msgreply
int
shortreply(
IN PIRP irp,
IN int status,
IN int nbytes
)
/*++
Routine Description:
This function completes an IRP, and arranges for return parameters,
if any, to be copied.
Although somewhat a misnomer, this function is named after a similar
function in the SpiderSTREAMS emulator.
Arguments:
irp - pointer to the IRP to complete
status - completion status of the IRP
nbytes - number of bytes to return
Return Value:
number of bytes copied back to the user.
--*/
{
CCHAR priboost;
IF_STRMDBG(CALL) {
STRMTRACE((
"SHEAD: shortreply(irp, status, nbytes = %lx, %lx, %lx) entered\n",
irp, status, nbytes));
}
//
// set the irp's cancel routine to NULL, or the system may bugcheck
// with the bugcode, CANCEL_STATE_IN_COMPLETED_IRP !!
//
// ref: IoCancelIrp(), ...\ntos\io\iosubs.c.
//
//
IoAcquireCancelSpinLock(&irp->CancelIrql);
IoSetCancelRoutine(irp, NULL);
IoReleaseCancelSpinLock(irp->CancelIrql);
//
// irp->IoStatus.Information is meaningful only for STATUS_SUCCESS
//
ASSERT(!nbytes || (status == STATUS_SUCCESS));
irp->IoStatus.Information = nbytes;
irp->IoStatus.Status = status;
priboost = (CCHAR) ((status == STATUS_SUCCESS) ?
IO_NETWORK_INCREMENT : IO_NO_INCREMENT);
IoCompleteRequest(irp, priboost);
return(nbytes);
} // shortreply