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
|