817 lines
20 KiB
C
817 lines
20 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
sh_ioctl.c
|
||
|
||
Abstract:
|
||
|
||
This source deals with those streamio(2) functions that are synchronous,
|
||
in that a message is sent downstream, and a reply is waited for.
|
||
|
||
It is based on the SpiderSTREAMS source, stremul\msgsrvr.c.
|
||
|
||
Author:
|
||
|
||
Eric Chin (ericc) January 6, 1992
|
||
|
||
Revision History:
|
||
|
||
Notes:
|
||
|
||
1. The O_NONBLOCK state of a stream does not affect the behaviour of an
|
||
ioctl(I_STR).
|
||
|
||
2. 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 "shead.h"
|
||
#include "sh_proto.h"
|
||
|
||
|
||
|
||
//
|
||
// Private Functions
|
||
//
|
||
STATIC VOID
|
||
cancel_ioctl(
|
||
IN PDEVICE_OBJECT device,
|
||
IN PIRP irp
|
||
);
|
||
|
||
STATIC NTSTATUS
|
||
do_sioctl(
|
||
IN PIRP irp,
|
||
IN BOOLEAN from_queue,
|
||
IN int *spl_levelp,
|
||
OUT BOOLEAN *ignored OPTIONAL
|
||
);
|
||
|
||
STATIC VOID
|
||
handle_ioctlers (
|
||
IN STREAM_ENDPOINT *ms,
|
||
IN int *spl_levelp
|
||
);
|
||
|
||
|
||
STATIC int
|
||
ioc_timeout(
|
||
IN char *arg
|
||
);
|
||
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SHDispIStr(
|
||
IN PIRP irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to process an ioctl(I_STR). It is based on the
|
||
SpiderSTREAMS emulator's routine, msgserver().
|
||
|
||
This routine merely peels open the IRP, checks the arguments for
|
||
consistency, locks the appropriate stream, and then calls do_sioctl(),
|
||
which does the bulk of the work.
|
||
|
||
Arguments:
|
||
|
||
irp - pointer to the IRP representing this request
|
||
|
||
Return Value:
|
||
|
||
an NT status code.
|
||
|
||
--*/
|
||
|
||
{
|
||
int timout;
|
||
int spl_level;
|
||
int spl_level2;
|
||
NTSTATUS status;
|
||
PSTREAM_ENDPOINT ms;
|
||
PISTR_ARGS_INOUT inbuf;
|
||
struct strioctl *striop;
|
||
PIO_STACK_LOCATION irpsp;
|
||
|
||
irpsp = IoGetCurrentIrpStackLocation(irp);
|
||
|
||
ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
|
||
METHOD_BUFFERED);
|
||
|
||
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
||
|
||
if (irpsp->Parameters.DeviceIoControl.InputBufferLength <
|
||
sizeof(ISTR_ARGS_INOUT) - 1) {
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: SHDispIStr(%lx) insufficient nbytes = %lx\n",
|
||
irp, irpsp->Parameters.DeviceIoControl.InputBufferLength));
|
||
}
|
||
return(STATUS_INVALID_PARAMETER);
|
||
}
|
||
|
||
//
|
||
// the caller marshalled the input arguments contiguously thus:
|
||
//
|
||
// typedef struct _ISTR_ARGS_INOUT { // ioctl(,I_STR,)
|
||
// int a_iocode; // I_STR
|
||
// struct strioctl a_strio; // (required)
|
||
// int a_unused[2]; // (required)
|
||
// char a_stuff[1]; // ic_dp buffer (optional)
|
||
//
|
||
// } ISTR_ARGS_INOUT, PISTR_ARGS_INOUT;
|
||
//
|
||
//
|
||
//
|
||
inbuf = (PISTR_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
|
||
striop = &(inbuf->a_strio);
|
||
|
||
IF_STRMDBG(VERBOSE) {
|
||
STRMTRACE(("SHEAD: SHDispIStr(ic_cmd, timout, len = %lx, %lx, %lx)\n",
|
||
striop->ic_cmd, striop->ic_timout, striop->ic_len));
|
||
}
|
||
|
||
//
|
||
// don't let user-defined ioctl codes coincide with the standard STREAMS
|
||
// ioctl codes. Otherwise, confusion will reign.
|
||
//
|
||
switch (striop->ic_cmd) {
|
||
case I_LINK:
|
||
case I_UNLINK:
|
||
case I_PLINK:
|
||
case I_PUNLINK:
|
||
SHpGenReply(irp, -1, EINVAL);
|
||
return(STATUS_SUCCESS);
|
||
break;
|
||
}
|
||
|
||
if ((striop->ic_timout < -1) || (striop->ic_len < 0)) {
|
||
SHpGenReply(irp, -1, EINVAL);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
IoAcquireCancelSpinLock(&irp->CancelIrql);
|
||
|
||
if (irp->Cancel) {
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
shortreply(irp, STATUS_CANCELLED, 0);
|
||
return(STATUS_CANCELLED);
|
||
}
|
||
|
||
spl_level = lock_strm(ms->e_strm);
|
||
|
||
if (shrange(ms->e_strm, sizeof(struct iocblk), striop->ic_len) < 0) {
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
SHpGenReply(irp, -1, ERANGE);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
IoMarkIrpPending(irp);
|
||
|
||
//
|
||
// if the ioctl is to time out after a specific time, start a timer
|
||
// running. iocrdy() will clear the timer.
|
||
//
|
||
// Based on a tip from larryo, irp->IoStatus.Information is used only
|
||
// when an IRP is completed successfully. Hence, we keep the timer
|
||
// id there.
|
||
//
|
||
irp->IoStatus.Information = 0;
|
||
irp->IoStatus.Status = STATUS_PENDING;
|
||
|
||
if (striop->ic_timout != -1) {
|
||
timout = striop->ic_timout ? striop->ic_timout : STRTIMOUT;
|
||
|
||
irp->IoStatus.Information = timeout(ioc_timeout,
|
||
(char *) irp, timout * HZ);
|
||
}
|
||
|
||
//
|
||
// At any given time, there must only be one outstanding ioctl(I_STR) on
|
||
// a stream. If there is another ioctl outstanding, chain this IRP to
|
||
// the tail of the pending ioctl'ers list.
|
||
//
|
||
if (ms->e_active_ioctl) {
|
||
status = SHAddPendingIrp(&(ms->e_ioctlers), FALSE, irp, do_sioctl);
|
||
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
shortreply(irp, status, 0);
|
||
return(status);
|
||
}
|
||
|
||
IoSetCancelRoutine(irp, cancel_ioctl);
|
||
IoReleaseCancelSpinLock(irp->CancelIrql);
|
||
|
||
return(STATUS_PENDING);
|
||
}
|
||
|
||
ms->e_active_ioctl = irp;
|
||
|
||
//
|
||
// do_sioctl() calls unlock_strm(ms->e_strm).
|
||
//
|
||
|
||
IoReleaseCancelSpinLock((KIRQL) spl_level);
|
||
|
||
spl_level2 = (int) irp->CancelIrql;
|
||
|
||
if (do_sioctl(irp, FALSE, &spl_level2, NULL) != STATUS_PENDING) {
|
||
spl_level = lock_strm(ms->e_strm);
|
||
handle_ioctlers(ms, &spl_level);
|
||
}
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: SHDispIStr(ms = %lx) returns, irp pending\n", ms));
|
||
}
|
||
return(STATUS_PENDING);
|
||
|
||
} // SHDispIStr
|
||
|
||
|
||
|
||
STATIC VOID
|
||
cancel_ioctl(
|
||
IN PDEVICE_OBJECT device,
|
||
IN PIRP irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when an ioctl(...,I_STR,...) 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);
|
||
int spl_level2;
|
||
|
||
ASSERT(device == (PDEVICE_OBJECT) StreamDevice);
|
||
ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
|
||
ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
|
||
IOCTL_STREAMS_IOCTL);
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) entered\n", irp));
|
||
}
|
||
IoSetCancelRoutine(irp, NULL); /* unnecessary, but cheap */
|
||
|
||
ms = (PSTREAM_ENDPOINT) irpsp->FileObject->FsContext;
|
||
spl_level = lock_strm(ms->e_strm);
|
||
|
||
//
|
||
// I'm releasing the cancel spinlock and stream lock in the reverse
|
||
// order of acquisition. Thus, I need to swap the irql's.
|
||
//
|
||
|
||
spl_level2 = (int) irp->CancelIrql;
|
||
|
||
IoReleaseCancelSpinLock((KIRQL) spl_level);
|
||
|
||
spl_level = spl_level2;
|
||
|
||
if (irp->IoStatus.Information) {
|
||
if (untimeout((int)irp->IoStatus.Information) == 0) {
|
||
//
|
||
// the timeout routine is already running. Just return and let it
|
||
// handle the irp.
|
||
//
|
||
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
return;
|
||
}
|
||
irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
if (irp == ms->e_active_ioctl) {
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) cancelled\n", irp));
|
||
}
|
||
ms->e_active_ioctl = NULL;
|
||
|
||
handle_ioctlers(ms, &spl_level);
|
||
|
||
shortreply(irp, STATUS_CANCELLED, 0);
|
||
return;
|
||
}
|
||
|
||
for (tmp = ms->e_ioctlers.Flink;
|
||
tmp != &ms->e_ioctlers;
|
||
tmp = tmp->Flink) {
|
||
|
||
item = CONTAINING_RECORD(tmp,
|
||
WAITING_IRP,
|
||
w_list);
|
||
|
||
if (irp != item->w_irp) {
|
||
continue;
|
||
}
|
||
RemoveEntryList(&(item->w_list));
|
||
ExFreePool(item);
|
||
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
shortreply(irp, STATUS_CANCELLED, 0);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) cancelled\n", irp));
|
||
}
|
||
return;
|
||
}
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: cancel_ioctl(irp = %lx) not found\n", irp));
|
||
}
|
||
|
||
} // cancel_ioctl
|
||
|
||
|
||
|
||
|
||
STATIC NTSTATUS
|
||
do_sioctl(
|
||
IN PIRP irp,
|
||
IN BOOLEAN ignored,
|
||
IN int *spl_levelp,
|
||
OUT BOOLEAN *must_be_null
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to put an M_IOCTL message down a stream. It
|
||
either sends the message or chains it to ms->e_writers. This function
|
||
is similar to do_putmsg(), both of which are based on the SpiderStreams
|
||
emulator's function, do_req().
|
||
|
||
Call this function with the stream locked !!!
|
||
|
||
Arguments:
|
||
|
||
irp - pointer to the IRP representing this request
|
||
ignored - this parameter is ignored
|
||
spl_levelp - pointer to the interrupt priority level at which the
|
||
stream was locked
|
||
must_be_null - since this function doesn't set this optional return
|
||
parameter, this must be NULL
|
||
|
||
Return Value:
|
||
|
||
an NT status code. Unless this is STATUS_PENDING, this function has
|
||
completed the IRP.
|
||
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
int MyErrno;
|
||
mblk_t *mp;
|
||
struct iocblk *iocp;
|
||
PSTREAM_ENDPOINT ms;
|
||
PISTR_ARGS_INOUT inbuf;
|
||
struct strioctl *striop;
|
||
PIO_STACK_LOCATION irpsp;
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: do_sioctl(irp = %lx) entered\n", irp));
|
||
}
|
||
ASSERT(must_be_null == NULL);
|
||
|
||
irpsp = IoGetCurrentIrpStackLocation(irp);
|
||
|
||
//
|
||
// these were already verified by SHDispIStr().
|
||
//
|
||
ASSERT(irpsp->Parameters.DeviceIoControl.InputBufferLength >=
|
||
sizeof(ISTR_ARGS_INOUT) - 1);
|
||
ASSERT((irpsp->Parameters.DeviceIoControl.IoControlCode & 0x3) ==
|
||
METHOD_BUFFERED);
|
||
|
||
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
||
|
||
ASSERT(irp == ms->e_active_ioctl);
|
||
|
||
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_sioctl(%lx) error = %d\n", ms, MyErrno));
|
||
}
|
||
if (irp->IoStatus.Information) {
|
||
if (untimeout((int)irp->IoStatus.Information) == 0) {
|
||
//
|
||
// The timeout routine will handle this
|
||
//
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
return(STATUS_PENDING);
|
||
}
|
||
irp->IoStatus.Information = 0;
|
||
}
|
||
ms->e_active_ioctl = NULL;
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
SHpGenReply(irp, -1, MyErrno);
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// the caller marshalled the input arguments contiguously thus:
|
||
//
|
||
// typedef struct _ISTR_ARGS_INOUT { // ioctl(,I_STR,)
|
||
// int a_iocode; // I_STR
|
||
// struct strioctl a_strio; // (required)
|
||
// int a_unused[2]; // (required)
|
||
// char a_stuff[1]; // ic_dp buffer (optional)
|
||
//
|
||
// } ISTR_ARGS_INOUT, PISTR_ARGS_INOUT;
|
||
//
|
||
//
|
||
//
|
||
inbuf = (PISTR_ARGS_INOUT) irp->AssociatedIrp.SystemBuffer;
|
||
striop = &(inbuf->a_strio);
|
||
|
||
IF_STRMDBG(VERBOSE) {
|
||
STRMTRACE(("SHEAD: do_sioctl(ic_cmd, timout, len = %lx, %lx, %lx)\n",
|
||
striop->ic_cmd, striop->ic_timout, striop->ic_len));
|
||
}
|
||
|
||
mp = irptomp(irp, BPRI_LO, sizeof(struct iocblk), striop->ic_len,
|
||
(char *) &(inbuf->a_strio));
|
||
|
||
if (!mp) {
|
||
if (irp->IoStatus.Information) {
|
||
if (untimeout((int)irp->IoStatus.Information) == 0) {
|
||
//
|
||
// The timeout routine will handle this
|
||
//
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
return(STATUS_PENDING);
|
||
}
|
||
irp->IoStatus.Information = 0;
|
||
}
|
||
ms->e_active_ioctl = NULL;
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
shortreply(irp, STATUS_NO_MEMORY, 0);
|
||
return(STATUS_NO_MEMORY);
|
||
}
|
||
|
||
ASSERT(mp->b_datap->db_type == M_PROTO);
|
||
mp->b_datap->db_type = M_IOCTL;
|
||
|
||
iocp = (struct iocblk *) mp->b_rptr;
|
||
ASSERT(iocp);
|
||
|
||
iocp->ioc_cmd = striop->ic_cmd;
|
||
iocp->ioc_uid = 0;
|
||
iocp->ioc_gid = 0;
|
||
iocp->ioc_id = ++(ms->e_strm->str_iocid);
|
||
|
||
if (iocp->ioc_id == 0) {
|
||
iocp->ioc_id = ms->e_strm->str_iocid = 1;
|
||
}
|
||
iocp->ioc_count = striop->ic_len;
|
||
iocp->ioc_error = 0;
|
||
iocp->ioc_rval = 0;
|
||
|
||
//
|
||
// shput() calls unlock_strm(ms->e_strm).
|
||
//
|
||
shput(ms->e_strm, mp, 0, spl_levelp);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: do_sioctl(ms = %lx) returns, irp pending\n", ms));
|
||
}
|
||
return(STATUS_PENDING);
|
||
|
||
} // do_sioctl
|
||
|
||
|
||
|
||
STATIC VOID
|
||
handle_ioctlers (
|
||
IN STREAM_ENDPOINT *ms,
|
||
IN int *spl_levelp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine starts the next ioctl() that is pending on a stream. It
|
||
is based on the SpiderStreams emulator function of the same name.
|
||
|
||
This routine is called with the stream locked, and unlocks the stream
|
||
before returning.
|
||
|
||
Arguments:
|
||
|
||
ms - pointer to the stream endpoint
|
||
spl_level - priority level to resume after releasing lock.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
PLIST_ENTRY tmp;
|
||
PWAITING_IRP item;
|
||
|
||
ASSERT(ms);
|
||
if (ms->e_active_ioctl) {
|
||
//
|
||
// Already processing an ioctl
|
||
//
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
return;
|
||
}
|
||
ASSERT(!ms->e_active_ioctl);
|
||
|
||
while (!IsListEmpty(&(ms->e_ioctlers))) {
|
||
|
||
tmp = RemoveHeadList( &(ms->e_ioctlers) );
|
||
item = CONTAINING_RECORD(tmp,
|
||
WAITING_IRP,
|
||
w_list);
|
||
|
||
ASSERT(item->w_function == do_sioctl);
|
||
|
||
irp = item->w_irp;
|
||
ms->e_active_ioctl = irp;
|
||
|
||
ExFreePool(item);
|
||
|
||
if (do_sioctl(irp, FALSE, spl_levelp, NULL) == STATUS_PENDING) {
|
||
return;
|
||
}
|
||
*spl_levelp = lock_strm(ms->e_strm);
|
||
}
|
||
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
return;
|
||
|
||
} // handle_ioctlers
|
||
|
||
|
||
|
||
STATIC int
|
||
ioc_timeout(
|
||
IN char *arg
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called when an ioctl() times out. It is based on the
|
||
SpiderStreams emulator function of the same name.
|
||
|
||
Arguments:
|
||
|
||
arg - pointer to irp representing the ioctl() which timed out.
|
||
|
||
Return Value:
|
||
|
||
0
|
||
|
||
Discussion:
|
||
|
||
Suppose iocrdy() is called because our M_IOCACK arrives. iocrdy() calls
|
||
lock_strm(), and prepares to complete the IRP. Just then, our timeout
|
||
fires, and this function is called. We call lock_strm() and spin.
|
||
|
||
Then, iocrdy() completes the irp and calls unlock_strm(), unblocking us.
|
||
We unchain an irp from ms->e_ioctlers and completes it with an ETIME
|
||
error. However, this is the wrong IRP to complete !!
|
||
|
||
One possible solution is for arg to be a pointer to a structure containing
|
||
both ms and the IRP. Then, we can verify that the IRP at the head of the
|
||
list is ours. Still, what if the IRP is reused ?
|
||
|
||
The bug above may arise after any of the following situations:
|
||
|
||
1. a stream is dup()'ed,
|
||
2. we are called from a multi-threaded application,
|
||
3. when a stream is NtOpen()'ed without the SYNCHRONOUS flag,
|
||
4. an IRP is cancelled.
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
int spl_level;
|
||
PLIST_ENTRY tmp;
|
||
PWAITING_IRP item;
|
||
STREAM_ENDPOINT *ms;
|
||
PIO_STACK_LOCATION irpsp;
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: ioc_timeout(irp = %lx) entered\n", arg));
|
||
}
|
||
|
||
irp = (PIRP) arg;
|
||
irpsp = IoGetCurrentIrpStackLocation(irp);
|
||
ms = (STREAM_ENDPOINT *) irpsp->FileObject->FsContext;
|
||
|
||
ASSERT(irpsp->MajorFunction == IRP_MJ_DEVICE_CONTROL);
|
||
ASSERT(irpsp->Parameters.DeviceIoControl.IoControlCode ==
|
||
IOCTL_STREAMS_IOCTL);
|
||
|
||
spl_level = lock_strm(ms->e_strm);
|
||
|
||
if (irp == ms->e_active_ioctl) {
|
||
ms->e_active_ioctl = NULL;
|
||
|
||
handle_ioctlers(ms, &spl_level);
|
||
}
|
||
else {
|
||
|
||
for (tmp = ms->e_ioctlers.Flink;
|
||
tmp != &(ms->e_ioctlers);
|
||
tmp = tmp->Flink) {
|
||
|
||
item = CONTAINING_RECORD(tmp,
|
||
WAITING_IRP,
|
||
w_list);
|
||
|
||
if (irp != item->w_irp) {
|
||
continue;
|
||
}
|
||
RemoveEntryList(&(item->w_list));
|
||
ExFreePool(item);
|
||
break;
|
||
}
|
||
unlock_strm(ms->e_strm, spl_level);
|
||
}
|
||
|
||
irp->IoStatus.Information = 0;
|
||
|
||
SHpGenReply(irp, -1, ETIME);
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: ioc_timeout(irp = %lx) completed\n", irp));
|
||
}
|
||
return(0);
|
||
|
||
} // ioc_timeout
|
||
|
||
|
||
|
||
void
|
||
iocrdy(
|
||
IN STREAM_ENDPOINT *ms,
|
||
IN mblk_t *mp,
|
||
IN int *spl_levelp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called by the Stream Head Driver when either an M_IOCACK
|
||
or an M_IOCNAK message arrives at its read queue of the specified stream.
|
||
It is based on the SpiderStreams emulator function of the same name.
|
||
|
||
This function is called with the stream locked, and unlocks the stream
|
||
before returning.
|
||
|
||
Arguments:
|
||
|
||
ms - pointer to the stream endpoint
|
||
mp - pointer to the STREAMS message that arrived
|
||
spl_levelp - ptr to priority level to resume after releasing lock.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP irp;
|
||
struct iocblk *iocp = (struct iocblk *) mp->b_rptr;
|
||
|
||
IF_STRMDBG(CALL) {
|
||
STRMTRACE(("SHEAD: iocrdy(ms = %lx)\n", ms));
|
||
}
|
||
if (!ms) {
|
||
IF_STRMDBG(TERSE) {
|
||
STRMTRACE(("SHEAD: iocrdy(ms = NULL) !!\n"));
|
||
}
|
||
freemsg(mp);
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
return;
|
||
}
|
||
|
||
irp = ms->e_active_ioctl;
|
||
ms->e_active_ioctl = NULL;
|
||
|
||
//
|
||
// ensure that someone is still waiting for the reply.
|
||
//
|
||
if (!irp) {
|
||
freemsg(mp);
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
qenable((ms->e_strm)->str_sq);
|
||
// handle_ioctlers(ms, spl_levelp);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// if there's an ioctl timer running, clear it now.
|
||
//
|
||
if (irp->IoStatus.Information) {
|
||
if (untimeout((int)irp->IoStatus.Information) == 0) {
|
||
//
|
||
// The timeout routine will handle this
|
||
//
|
||
ms->e_active_ioctl = irp;
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
freemsg(mp);
|
||
return;
|
||
}
|
||
irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
switch (iocp->ioc_cmd) {
|
||
case I_LINK:
|
||
case I_UNLINK:
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
if (mp->b_datap->db_type == M_IOCACK) {
|
||
SHpGenReply(irp,
|
||
((struct linkblk *) (mp->b_cont->b_rptr))->l_index,
|
||
0);
|
||
}
|
||
else {
|
||
SHpGenReply(irp, -1, iocp->ioc_error);
|
||
}
|
||
// *spl_levelp = lock_strm(ms->e_strm);
|
||
freemsg(mp);
|
||
break;
|
||
|
||
case I_PLINK:
|
||
case I_PUNLINK:
|
||
ASSERT(0);
|
||
break;
|
||
|
||
//
|
||
// if we get here, it's an ioctl(I_STR). iocp->ioc_cmd is a some
|
||
// user-defined command code.
|
||
//
|
||
default:
|
||
unlock_strm(ms->e_strm, *spl_levelp);
|
||
(void) iocreply(mp, irp);
|
||
// *spl_levelp = lock_strm(ms->e_strm);
|
||
break;
|
||
}
|
||
|
||
qenable((ms->e_strm)->str_sq);
|
||
// handle_ioctlers(ms, spl_levelp);
|
||
return;
|
||
|
||
} // iocrdy
|