STREAMS Programming Guide

M_COPYOUT Example


Note –

Please see the copyout section in Writing Device Drivers for information on the 64–bit data structure macros.


The following code excerpts return option values for the STREAMS device by placing them in the user's options structure. This is done by a transparent ioctl(2) call of the form


struct options optadd;

ioctl(fd, GET_OPTIONS,(caddr_t) &optadd) 

or by a nontransparent I_STR call

	struct strioctl opts_strioctl;
	structure options optadd;

	opts_strioctl.ic_cmd = GET_OPTIONS;
	opts_strioctl.ic_timeout = -1
	opts_strioctl.ic_len = sizeof (struct options);
	opts_strioctl.ic_dp = (char *)&optadd;
	ioctl(fd, I_STR, (caddr_t) &opts_strioctl) 

In the nontransparent I_STR case, opts_strioctl.ic_dp points to the options structure optadd.

Example 8–7 illustrates support of both the I_STR and transparent forms of ioctl(2). The transparent form requires a single M_COPYOUT message following receipt of the M_IOCTL to copy out the contents of the structure. xxwput() is the write-side put procedure of module or driver xx.


Example 8–10 M_COPYOUT

	struct options {			/* same members as in user space */
		int			op_one;
		int			op_two;
		short			op_three;
		long			op_four;
	};

	static int
	xxwput (queue_t *q, mblk_t *mp)
	{
		struct iocblk *iocbp;
		struct copyreq *cqp;
		struct copyresp *csp;
		int transparent = 0;

		switch (mp->b_datap->db_type) {
			.
			.
			.
			case M_IOCTL:
				iocbp = (struct iocblk *)mp->b_rptr;
				switch (iocbp->ioc_cmd) {
					case GET_OPTIONS:
						if (iocbp->ioc_count == TRANSPARENT) {
						  transparent = 1;
						  cqp = (struct copyreq *)mp->b_rptr;
						  cqp->cq_size = sizeof(struct options);
						  /* Get struct address from
								linked M_DATA block */
						  cqp->cq_addr = (caddr_t) 
								*(caddr_t *)mp->b_cont->b_rptr;
						  cqp->cq_flag = 0;
						  /* No state necessary - we will only ever 
							* get one M_IOCDATA from the Stream head 
							* indicating success or failure for 
							* the copyout */
						}
						if (mp->b_cont)
							freemsg(mp->b_cont);
						if ((mp->b_cont = 
									allocb(sizeof(struct options), 
												BPRI_MED)) == NULL) {
						  mp->b_datap->db_type = M_IOCNAK;
						  iocbp->ioc_error = EAGAIN;
						  qreply(q, mp);
						  break;
						}
						/* hypothetical routine */
						xx_get_options(mp->b_cont);
						if (transparent) {
						  mp->b_datap->db_type = M_COPYOUT;
						  mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
						} else {
						  mp->b_datap->db_type = M_IOCACK;
						  iocbp->ioc_count = sizeof(struct options);
						}
						qreply(q, mp);
						break;

					default: /* M_IOCTL not for us */
						/*if module, pass on;if driver, nak ioctl*/
						break;
				} /* switch (iocbp->ioc_cmd) */
				break;

			case M_IOCDATA:
				csp = (struct copyresp *)mp->b_rptr;
				/* M_IOCDATA not for us */
				if (csp->cmd != GET_OPTIONS) {
					/*if module/pass on, if driver/free message*/
					break;
				}
				if ( csp->cp_rval ) {
					freemsg(mp);	/* failure */
					return (0);
				}
				/* Data successfully copied out, ack */

				/* reuse M_IOCDATA for ack */
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wptr = mp->b_rptr + sizeof(struct iocblk);
				/* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				qreply(q, mp);
				break;
				.
				.
				.
			} /* switch (mp->b_datap->db_type) */
			return (0);

xxwput() first checks whether the ioctl(2) command is transparent. If it is, the message is reused as an M_COPYOUT copy request message. The pointer to the receiving buffer is in the linked message and is copied into cq_addr. Because only a single copy out is being done, no state information needs to be stored in cq_private. The original linked message is freed, in case it isn't big enough to hold the request.

if (iocbp->ioc_count == TRANSPARENT) {
		transparent = 1;
		cqp = (struct copyreq *)mp->b_rptr;
		cqp->cq_size = sizeof(struct options);
		/* Get struct address from linked M_DATA block */
		cqp->cq_addr = (caddr_t) 
									*(caddr_t *)mp->b_cont->b_rptr;
		cqp->cq_flag = 0;
		/* No state necessary - we will only ever get one 
		 * M_IOCDATA from the Stream head indicating 
		 * success or failure for the copyout */
		}
		if (mp->b_cont)
			freemsg(mp->b_cont);

As an optimization, the following code checks the size of the message for reuse:

mp->b_cont->b_datap->db_lim 
- mp->b_cont->b_datap->db_base >= sizeof (struct options)


Note –

Hardening Information. After message reuse, make sure to retain the relation:


db_base <= b_rptr <= b_wptr <= db_lim

A new linked message is allocated to hold the option request. When using the transparent ioctl(2) M_COPYOUT command, data contained in the linked message is passed to the stream head. The stream head will copy the data to the user's address space and issue an M_IOCDATA in response to the M_COPYOUT message, which the module must acknowledge in an M_IOCACK message.

            /* hypothetical routine */
				xx_get_options(mp->b_cont);
				if (transparent) {
					mp->b_datap->db_type = M_COPYOUT;
					mp->b_wptr = mp->b_rptr + sizeof(struct copyreq);
				} else {
					mp->b_datap->db_type = M_IOCACK;
					iocbp->ioc_count = sizeof(struct options);
				}

If the message is not transparent (is issued through an I_STR ioctl(2)), the data is sent with the M_IOCACK acknowledgement message and copied into the buffer specified by the strioctl data structure. ioc_error, ioc_count, and ioc_rval are cleared to prevent any stale data from being passed back to the stream head.

           /* reuse M_IOCDATA for ack */
				mp->b_datap->db_type = M_IOCACK;
				mp->b_wptr = mp->b_rptr + sizeof(struct iocblk);
				/* can have been overwritten */
				iocbp->ioc_error = 0;
				iocbp->ioc_count = 0;
				iocbp->ioc_rval = 0;
				qreply(q, mp);
				break;