|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
s_ioctl.c
Abstract:
This module implements the s_ioctl() operation used by the socket library.
Author:
Eric Chin (ericc) July 26, 1991
Revision History:
Sam Patton (sampa) August 13, 1991 changed errno to {get|set}lasterror --*/ #include "common.h"
//
// BUGBUG: Remove this structure when eric implements
// neither I/O. Right now, it is needed because sockets allocates
// the space for this structure in an ioctl call.
//
/*
* IOCTL structure - this structure is the format of the M_IOCTL message type. */ struct iocblk { int ioc_cmd; /* ioctl command type */ unsigned short ioc_uid; /* effective uid of user */ unsigned short ioc_gid; /* effective gid of user */ unsigned int ioc_id; /* ioctl id */ unsigned int ioc_count; /* count of bytes in data field */ int ioc_error; /* error code */ int ioc_rval; /* return value */ };
//
// BUGBUG:
// The max amount of data that any module in the stream can return in an
// M_IOCACK message should probably be queried from the Stream Head driver.
//
#define MAX_DATA_AMOUNT 0x1000
//
// Declaration of Local Functions
//
static int s_debug( IN HANDLE fd, IN OUT struct strdebug *dbgbufp );
static int s_fdinsert( IN HANDLE fd, IN struct strfdinsert *iblk );
static int s_link( IN HANDLE fd, IN HANDLE fd2 );
static int s_push( IN HANDLE fd, IN char *name );
static int s_sioctl( IN HANDLE fd, IN OUT struct strioctl *iocp );
static int s_unlink( IN HANDLE fd, IN int muxid );
int s_ioctl( IN HANDLE fd, IN int cmd, IN OUT void *arg OPTIONAL )
/*++
Routine Description:
This procedure is called to perform a STREAMS ioctl() on a stream as defined in streamio(7) of the Unix Programmer's Guide: STREAMS.
Arguments:
fd - NT file handle command - ioctl command code arg - command-dependent arg, usually a pointer to some structure
Return Value:
0 if successful, -1 otherwise.
--*/ { switch (cmd) { case I_STR: return(s_sioctl(fd, (struct strioctl *) arg));
case I_DEBUG: return(s_debug(fd, (struct strdebug *) arg));
case I_FDINSERT: return(s_fdinsert(fd, (struct strfdinsert *) arg));
case I_PUSH: return(s_push(fd, (char *) arg));
case I_LINK: return(s_link(fd, (HANDLE) arg));
case I_UNLINK: return(s_unlink(fd, (int) ((ULONG_PTR)arg)));
default: SetLastError(EINVAL); return(-1); } }
static int s_debug( IN HANDLE fd, IN OUT struct strdebug *dbgbufp )
/*++
Routine Description:
This procedure performs an I_DEBUG ioctl command on a stream.
Arguments:
fd - NT file handle dbgbufp - pointer to a strdebug structure
Return Value:
0 if successful, -1 otherwise.
--*/
{ char *tmp; char *chunk; NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb;
if (dbgbufp == NULL) { SetLastError(EINVAL); return(-1); } chunksz = sizeof(int) + sizeof(struct strdebug);
if (!(chunk = (char *) LocalAlloc(LMEM_FIXED, chunksz))) { SetLastError(ENOSPC); return(-1); }
//
// marshall the arguments into one contiguous chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_DEBUG
// struct strdebug dbgbuf;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
* ((int *) chunk) = I_DEBUG; tmp = chunk + sizeof(int);
memcpy(tmp, dbgbufp, sizeof(struct strdebug));
status = NtDeviceIoControlFile( fd, NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, TRUE, NULL); }
if (!NT_SUCCESS(status)) { LocalFree((HANDLE) chunk); SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// the Stream Head driver returned values in one chunk, laid out as:
//
// int return value (required)
// int errno; (required)
//
retval = * (int *) chunk;
if (retval == -1) { SetLastError(* (int *) (chunk + sizeof(int))); } LocalFree((HANDLE) chunk); return(retval); }
int s_fdinsert( IN HANDLE fd, IN struct strfdinsert *iblk )
/*++
Routine Description:
This function performs an ioctl(I_FDINSERT) on a stream, which is a special form of putmsg().
This function is synchronous, in the NT sense: it blocks until the API completes.
Arguments:
fd - NT file handle iblk - pointer to a strfdinsert structure
Return Value:
0 if successful, -1 otherwise.
--*/
{ char *tmp; NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb; PSTRM_ARGS_OUT oparm; PPUTMSG_ARGS_IN chunk;
if (!iblk) { SetLastError(EINVAL); return(-1); } if (iblk->ctlbuf.len <= 0) { SetLastError(ERANGE); return(-1); }
//
// iblk->databuf.len may be -1, to indicate no data buffer.
//
chunksz = sizeof(PUTMSG_ARGS_IN) - 1 + iblk->ctlbuf.len + max(iblk->databuf.len, 0);
if (!(chunk = (PPUTMSG_ARGS_IN) LocalAlloc(LMEM_FIXED, chunksz))) { SetLastError(ENOSPC); return(-1); }
//
// marshall the arguments into one contiguous chunk. However, for
// commonality with putmsg(), we rearrange the strfdinsert structure
// as below:
//
// typedef struct _PUTMSG_ARGS_IN_ {
// int a_iocode; // I_FDINSERT
// long a_flags; // 0 | RS_HIPRI
// struct strbuf a_ctlbuf; // (required)
// struct strbuf a_databuf; // (required)
// HANDLE a_insert.i_fildes; // (required)
// int a_offset; // (optional)
// char a_stuff[1]; // s_ctlbuf.buf (required)
// // s_databuf.buf (optional)
// } PUTMSG_ARGS_IN, *PPUTMSG_ARGS_IN;
//
//
chunk->a_iocode = I_FDINSERT; chunk->a_flags = iblk->flags; chunk->a_ctlbuf = iblk->ctlbuf; // structure copy
chunk->a_databuf = iblk->databuf; // structure copy
chunk->a_insert.i_fildes = iblk->fildes; chunk->a_offset = iblk->offset;
tmp = (char *) chunk->a_stuff;
assert(iblk->ctlbuf.len > 0); memcpy(tmp, iblk->ctlbuf.buf, iblk->ctlbuf.len); tmp += iblk->ctlbuf.len;
if (iblk->databuf.len > 0) { memcpy(tmp, iblk->databuf.buf, iblk->databuf.len); }
ASSERT(chunksz >= sizeof(STRM_ARGS_OUT));
status = NtDeviceIoControlFile( fd, // Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, // Handle
TRUE, // Alertable
NULL); // Timeout
}
if (!NT_SUCCESS(status)) { LocalFree(chunk); SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// the return parameters from the Stream Head Driver are 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;
//
//
oparm = (PSTRM_ARGS_OUT) chunk; retval = oparm->a_retval;
if (retval == -1) { SetLastError(oparm->a_errno); } LocalFree(chunk); return(retval);
} // s_fdinsert
static int s_link( IN HANDLE fd, IN HANDLE fd2 )
/*++
Routine Description:
This procedure performs an I_LINK ioctl command on a stream.
Arguments:
fd - NT file handle to upstream driver fd2 - NT file handle to downstream driver
Return Value:
multiplexor id number, or -1 if unsuccessful
--*/ { char *chunk; NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb;
chunksz = sizeof(int) + sizeof(HANDLE);
if (!(chunk = (char *) LocalAlloc(LMEM_FIXED, chunksz))) { SetLastError(ENOSPC); return(-1); }
//
// marshall the arguments into one contiguous chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_LINK
//
// union {
// HANDLE l_fd2;
// } s_link;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
* ((int *) chunk) = I_LINK; * (PHANDLE) ((int *) chunk + 1) = fd2;
status = NtDeviceIoControlFile( fd, NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, TRUE, NULL); }
if (!NT_SUCCESS(status)) { LocalFree((HANDLE) chunk); SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// the Stream Head driver returned values in one chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_LINK
//
// union {
// HANDLE l_fd2;
// } s_link;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
if ((retval = * (int *) chunk) == -1) { SetLastError(* (int *) (chunk + sizeof(int))); } LocalFree((HANDLE) chunk); return(retval); }
static int s_push( IN HANDLE fd, IN char *name )
/*++
Routine Description:
This procedure performs an I_LINK ioctl command on a stream.
Arguments:
fd - NT file handle to stream name - name of STREAMS module to be pushed
Return Value:
0 if successful, -1 otherwise
--*/ { char *chunk; NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb;
chunksz = (int)(max(2 * sizeof(int), sizeof(int) + strlen(name) + 1));
if (!(chunk = (char *) LocalAlloc(LMEM_FIXED, chunksz))) { SetLastError(ENOSPC); return(-1); }
//
// marshall the arguments into one contiguous chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_PUSH
//
// union {
// char p_name[1];
// } s_push;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
* ((int *) chunk) = I_PUSH; strcpy(chunk + sizeof(int), name);
status = NtDeviceIoControlFile( fd, NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, TRUE, NULL); }
if (!NT_SUCCESS(status)) { LocalFree((HANDLE) chunk); SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// the Stream Head driver returned values in one chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_LINK
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
if ((retval = * (int *) chunk) == -1) { SetLastError(* (int *) (chunk + sizeof(int))); } LocalFree((HANDLE) chunk); return(retval); }
static int s_sioctl( IN HANDLE fd, IN OUT struct strioctl *iocp )
/*++
Routine Description:
This procedure performs an ioctl(I_STR) on a stream.
Arguments:
fd - NT file handle iocp - pointer to a strioctl structure
Return Value:
0 if successful, -1 otherwise.
--*/
{ NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb; PISTR_ARGS_INOUT chunk;
union outparms { ISTR_ARGS_INOUT o_ok; STRM_ARGS_OUT o_bad; } *oparm;
if (!iocp || (iocp->ic_len < 0)) { SetLastError(EINVAL); return(-1); } chunksz = sizeof(ISTR_ARGS_INOUT) + max(iocp->ic_len, MAX_DATA_AMOUNT); chunk = (PISTR_ARGS_INOUT) LocalAlloc(LMEM_FIXED, chunksz);
if (!chunk) { SetLastError(ENOSPC); return(-1); }
//
// marshall the arguments into one contiguous chunk, laid out as:
//
// typedef struct _ISTR_ARGS_INOUT { // ioctl(I_STR)
// int a_iocode; // I_STR
// struct strioctl a_strio; // (required)
// int a_unused[2]; // (required) BUGBUG
// char a_stuff[1]; // (optional)
//
// } ISTR_ARGS_INOUT, *PISTR_ARGS_INOUT;
//
//
// An optimizing compiler will warn that the assertion below contains
// unreachable code. Ignore the warning.
//
assert((char *) chunk->a_stuff - (char *) &(chunk->a_strio) >= sizeof(struct iocblk));
chunk->a_iocode = I_STR; memcpy(&(chunk->a_strio), iocp, sizeof(struct strioctl));
if (iocp->ic_len >= 0) { memcpy(&(chunk->a_stuff), iocp->ic_dp, iocp->ic_len); }
status = NtDeviceIoControlFile( fd, // Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, // Handle
TRUE, // Alertable
NULL); // Timeout
}
if (!NT_SUCCESS(status)) { LocalFree(chunk); SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// if there was an error, the return parameters from the Stream Head
// Driver are 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;
//
//
oparm = (union outparms *) chunk; retval = oparm->o_bad.a_retval;
if (retval == -1) { SetLastError(oparm->o_bad.a_errno); LocalFree(chunk); return(retval); }
//
// if there wasn't an error, the return parameters from the Stream Head
// Driver are laid out as:
//
// typedef struct _ISTR_ARGS_INOUT { // ioctl(I_STR)
// int a_iocode; // return value
// struct strioctl a_strio; // (required)
// int a_unused[2];
// char a_stuff[1]; // (optional)
//
// } ISTR_ARGS_INOUT, *PISTR_ARGS_INOUT;
//
// However, a_iocode now holds the return value.
//
//
if (iocp && iocp->ic_dp) { iocp->ic_len = oparm->o_ok.a_strio.ic_len;
if (iocp->ic_len >= 0) { memcpy(iocp->ic_dp, oparm->o_ok.a_stuff, iocp->ic_len); } } LocalFree(chunk); return(retval);
} // s_sioctl
static int s_unlink( IN HANDLE fd, IN int muxid )
/*++
Routine Description:
This procedure performs an I_UNLINK ioctl command on a stream.
Arguments:
fd - NT file handle to upstream driver muxid - STREAMS multiplexor id of the lower stream
Return Value:
0 on success, or -1 on failure
--*/ { int chunk[2]; NTSTATUS status; int chunksz, retval; IO_STATUS_BLOCK iosb;
//
// marshall the arguments into one contiguous chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_UNLINK
//
// union {
// int l_muxid;
// } s_unlink;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
chunk[0] = I_UNLINK; chunk[1] = muxid; chunksz = sizeof(chunk);
status = NtDeviceIoControlFile( fd, // Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
IOCTL_STREAMS_IOCTL, // IoControlCode
(PVOID) chunk, // InputBuffer
chunksz, // InputBufferSize
(PVOID) chunk, // OutputBuffer
chunksz); // OutputBufferSize
if (status == STATUS_PENDING) { status = NtWaitForSingleObject( fd, TRUE, NULL); }
if (!NT_SUCCESS(status)) { SetLastError(MapNtToPosixStatus(status)); return(-1); }
//
// the Stream Head driver returned values in one chunk, laid out as:
//
// union {
// struct {
// int s_code; // I_UNLINK
//
// union {
// HANDLE l_fd2;
// } s_link;
// } in;
//
// struct {
// int s_retval;
// int s_errno;
// } out;
// };
//
if ((retval = chunk[0]) == -1) { SetLastError(chunk[1]); } return(retval);
} // s_unlink
|