/*++ 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