14bff34e3Sthurlow /*
24bff34e3Sthurlow  * Copyright (c) 2000-2001 Boris Popov
34bff34e3Sthurlow  * All rights reserved.
44bff34e3Sthurlow  *
54bff34e3Sthurlow  * Redistribution and use in source and binary forms, with or without
64bff34e3Sthurlow  * modification, are permitted provided that the following conditions
74bff34e3Sthurlow  * are met:
84bff34e3Sthurlow  * 1. Redistributions of source code must retain the above copyright
94bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer.
104bff34e3Sthurlow  * 2. Redistributions in binary form must reproduce the above copyright
114bff34e3Sthurlow  *    notice, this list of conditions and the following disclaimer in the
124bff34e3Sthurlow  *    documentation and/or other materials provided with the distribution.
134bff34e3Sthurlow  * 3. All advertising materials mentioning features or use of this software
144bff34e3Sthurlow  *    must display the following acknowledgement:
154bff34e3Sthurlow  *    This product includes software developed by Boris Popov.
164bff34e3Sthurlow  * 4. Neither the name of the author nor the names of any co-contributors
174bff34e3Sthurlow  *    may be used to endorse or promote products derived from this software
184bff34e3Sthurlow  *    without specific prior written permission.
194bff34e3Sthurlow  *
204bff34e3Sthurlow  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
214bff34e3Sthurlow  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
224bff34e3Sthurlow  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
234bff34e3Sthurlow  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
244bff34e3Sthurlow  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
254bff34e3Sthurlow  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
264bff34e3Sthurlow  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
274bff34e3Sthurlow  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
284bff34e3Sthurlow  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
294bff34e3Sthurlow  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
304bff34e3Sthurlow  * SUCH DAMAGE.
314bff34e3Sthurlow  *
324bff34e3Sthurlow  * $Id: smb_usr.c,v 1.15 2004/12/13 00:25:18 lindak Exp $
334bff34e3Sthurlow  */
344bff34e3Sthurlow 
359c9af259SGordon Ross /*
36*613a2f6bSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
379c9af259SGordon Ross  * Use is subject to license terms.
389c9af259SGordon Ross  */
394bff34e3Sthurlow 
404bff34e3Sthurlow #include <sys/param.h>
414bff34e3Sthurlow #include <sys/kmem.h>
424bff34e3Sthurlow #include <sys/systm.h>
434bff34e3Sthurlow #include <sys/policy.h>
444bff34e3Sthurlow #include <sys/conf.h>
454bff34e3Sthurlow #include <sys/proc.h>
464bff34e3Sthurlow #include <sys/fcntl.h>
47*613a2f6bSGordon Ross #include <sys/file.h>
484bff34e3Sthurlow #include <sys/socket.h>
49*613a2f6bSGordon Ross #include <sys/sunddi.h>
504bff34e3Sthurlow #include <sys/cmn_err.h>
514bff34e3Sthurlow 
524bff34e3Sthurlow #include <netsmb/smb_osdep.h>
534bff34e3Sthurlow 
544bff34e3Sthurlow #include <netsmb/smb.h>
554bff34e3Sthurlow #include <netsmb/smb_conn.h>
564bff34e3Sthurlow #include <netsmb/smb_rq.h>
574bff34e3Sthurlow #include <netsmb/smb_subr.h>
584bff34e3Sthurlow #include <netsmb/smb_dev.h>
594bff34e3Sthurlow 
60*613a2f6bSGordon Ross static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg);
61*613a2f6bSGordon Ross 
624bff34e3Sthurlow /*
63*613a2f6bSGordon Ross  * Ioctl function for SMBIOC_FLAGS2
644bff34e3Sthurlow  */
65*613a2f6bSGordon Ross int
66*613a2f6bSGordon Ross smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags)
67*613a2f6bSGordon Ross {
68*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
69*613a2f6bSGordon Ross 
70*613a2f6bSGordon Ross 	/* This ioctl requires a session. */
71*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
72*613a2f6bSGordon Ross 		return (ENOTCONN);
73*613a2f6bSGordon Ross 
74*613a2f6bSGordon Ross 	/*
75*613a2f6bSGordon Ross 	 * Return the flags2 value.
76*613a2f6bSGordon Ross 	 */
77*613a2f6bSGordon Ross 	if (ddi_copyout(&vcp->vc_hflags2, (void *)arg,
78*613a2f6bSGordon Ross 	    sizeof (u_int16_t), flags))
79*613a2f6bSGordon Ross 		return (EFAULT);
80*613a2f6bSGordon Ross 
81*613a2f6bSGordon Ross 	return (0);
82*613a2f6bSGordon Ross }
834bff34e3Sthurlow 
844bff34e3Sthurlow /*
85*613a2f6bSGordon Ross  * Ioctl function for SMBIOC_GETSSNKEY
86*613a2f6bSGordon Ross  * Size copied out is SMBIOC_HASH_SZ.
87*613a2f6bSGordon Ross  *
88*613a2f6bSGordon Ross  * The RPC library needs this for encrypting things
89*613a2f6bSGordon Ross  * like "set password" requests.  This is called
90*613a2f6bSGordon Ross  * with an active RPC binding, so the connection
91*613a2f6bSGordon Ross  * will already be active (but this checks).
924bff34e3Sthurlow  */
93*613a2f6bSGordon Ross int
94*613a2f6bSGordon Ross smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags)
954bff34e3Sthurlow {
96*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
97*613a2f6bSGordon Ross 
98*613a2f6bSGordon Ross 	/* This ioctl requires an active session. */
99*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
100*613a2f6bSGordon Ross 		return (ENOTCONN);
101*613a2f6bSGordon Ross 	if (vcp->vc_state != SMBIOD_ST_VCACTIVE)
102*613a2f6bSGordon Ross 		return (ENOTCONN);
1034bff34e3Sthurlow 
1044bff34e3Sthurlow 	/*
105*613a2f6bSGordon Ross 	 * Return the session key.
1064bff34e3Sthurlow 	 */
107*613a2f6bSGordon Ross 	if (ddi_copyout(vcp->vc_ssn_key, (void *)arg,
108*613a2f6bSGordon Ross 	    SMBIOC_HASH_SZ, flags))
109*613a2f6bSGordon Ross 		return (EFAULT);
110*613a2f6bSGordon Ross 
111*613a2f6bSGordon Ross 	return (0);
112*613a2f6bSGordon Ross }
113*613a2f6bSGordon Ross 
114*613a2f6bSGordon Ross /*
115*613a2f6bSGordon Ross  * Ioctl function for SMBIOC_REQUEST
116*613a2f6bSGordon Ross  */
117*613a2f6bSGordon Ross int
118*613a2f6bSGordon Ross smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
119*613a2f6bSGordon Ross {
120*613a2f6bSGordon Ross 	struct smb_cred scred;
121*613a2f6bSGordon Ross 	struct smb_share *ssp;
122*613a2f6bSGordon Ross 	smbioc_rq_t *ioc = NULL;
123*613a2f6bSGordon Ross 	struct smb_rq *rqp = NULL;
124*613a2f6bSGordon Ross 	struct mbchain *mbp;
125*613a2f6bSGordon Ross 	struct mdchain *mdp;
126*613a2f6bSGordon Ross 	uint32_t rsz;
127*613a2f6bSGordon Ross 	int err, mbseg;
128*613a2f6bSGordon Ross 
129*613a2f6bSGordon Ross 	/* This ioctl requires a share. */
130*613a2f6bSGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
131*613a2f6bSGordon Ross 		return (ENOTCONN);
132*613a2f6bSGordon Ross 
133*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
134*613a2f6bSGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
135*613a2f6bSGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
136*613a2f6bSGordon Ross 		err = EFAULT;
137*613a2f6bSGordon Ross 		goto out;
1384bff34e3Sthurlow 	}
1394bff34e3Sthurlow 
140*613a2f6bSGordon Ross 	/* See ddi_copyin, ddi_copyout */
141*613a2f6bSGordon Ross 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
1424bff34e3Sthurlow 
1434bff34e3Sthurlow 	/*
144*613a2f6bSGordon Ross 	 * Lots of SMB commands could be safe, but
145*613a2f6bSGordon Ross 	 * these are the only ones used by libsmbfs.
1464bff34e3Sthurlow 	 */
147*613a2f6bSGordon Ross 	switch (ioc->ioc_cmd) {
148*613a2f6bSGordon Ross 		/* These are OK */
149*613a2f6bSGordon Ross 	case SMB_COM_CLOSE:
150*613a2f6bSGordon Ross 	case SMB_COM_FLUSH:
151*613a2f6bSGordon Ross 	case SMB_COM_NT_CREATE_ANDX:
152*613a2f6bSGordon Ross 	case SMB_COM_OPEN_PRINT_FILE:
153*613a2f6bSGordon Ross 	case SMB_COM_CLOSE_PRINT_FILE:
154*613a2f6bSGordon Ross 		break;
155*613a2f6bSGordon Ross 
156*613a2f6bSGordon Ross 	default:
157*613a2f6bSGordon Ross 		err = EPERM;
158*613a2f6bSGordon Ross 		goto out;
1594bff34e3Sthurlow 	}
1604bff34e3Sthurlow 
161*613a2f6bSGordon Ross 	err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp);
162*613a2f6bSGordon Ross 	if (err)
163*613a2f6bSGordon Ross 		goto out;
164*613a2f6bSGordon Ross 
165*613a2f6bSGordon Ross 	mbp = &rqp->sr_rq;
166*613a2f6bSGordon Ross 	err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg);
167*613a2f6bSGordon Ross 
168*613a2f6bSGordon Ross 	err = smb_rq_simple(rqp);
169*613a2f6bSGordon Ross 	if (err == 0) {
170*613a2f6bSGordon Ross 		/*
171*613a2f6bSGordon Ross 		 * This may have been an open, so save the
172*613a2f6bSGordon Ross 		 * generation ID of the share, which we
173*613a2f6bSGordon Ross 		 * check before trying read or write.
174*613a2f6bSGordon Ross 		 */
175*613a2f6bSGordon Ross 		sdp->sd_vcgenid = ssp->ss_vcgenid;
176*613a2f6bSGordon Ross 
177*613a2f6bSGordon Ross 		/*
178*613a2f6bSGordon Ross 		 * Have reply data. to copyout.
179*613a2f6bSGordon Ross 		 * SMB header already parsed.
180*613a2f6bSGordon Ross 		 */
181*613a2f6bSGordon Ross 		mdp = &rqp->sr_rp;
182*613a2f6bSGordon Ross 		rsz = msgdsize(mdp->md_top) - SMB_HDRLEN;
183*613a2f6bSGordon Ross 		if (ioc->ioc_rbufsz < rsz) {
184*613a2f6bSGordon Ross 			err = EOVERFLOW;
185*613a2f6bSGordon Ross 			goto out;
186*613a2f6bSGordon Ross 		}
187*613a2f6bSGordon Ross 		ioc->ioc_rbufsz = rsz;
188*613a2f6bSGordon Ross 		err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg);
189*613a2f6bSGordon Ross 		if (err)
190*613a2f6bSGordon Ross 			goto out;
191*613a2f6bSGordon Ross 
1924bff34e3Sthurlow 	}
1934bff34e3Sthurlow 
194*613a2f6bSGordon Ross 	ioc->ioc_errclass = rqp->sr_errclass;
195*613a2f6bSGordon Ross 	ioc->ioc_serror = rqp->sr_serror;
196*613a2f6bSGordon Ross 	ioc->ioc_error = rqp->sr_error;
197*613a2f6bSGordon Ross 	ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
1984bff34e3Sthurlow 
199*613a2f6bSGordon Ross out:
200*613a2f6bSGordon Ross 	if (rqp != NULL)
201*613a2f6bSGordon Ross 		smb_rq_done(rqp); /* free rqp */
202*613a2f6bSGordon Ross 	if (ioc != NULL)
203*613a2f6bSGordon Ross 		kmem_free(ioc, sizeof (*ioc));
204*613a2f6bSGordon Ross 	smb_credrele(&scred);
205*613a2f6bSGordon Ross 
206*613a2f6bSGordon Ross 	return (err);
2074bff34e3Sthurlow 
2084bff34e3Sthurlow }
2094bff34e3Sthurlow 
210*613a2f6bSGordon Ross /*
211*613a2f6bSGordon Ross  * Ioctl function for SMBIOC_T2RQ
212*613a2f6bSGordon Ross  */
213*613a2f6bSGordon Ross int
214*613a2f6bSGordon Ross smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
2154bff34e3Sthurlow {
216*613a2f6bSGordon Ross 	struct smb_cred scred;
217*613a2f6bSGordon Ross 	struct smb_share *ssp;
218*613a2f6bSGordon Ross 	smbioc_t2rq_t *ioc = NULL;
219*613a2f6bSGordon Ross 	struct smb_t2rq *t2p = NULL;
220*613a2f6bSGordon Ross 	struct mdchain *mdp;
221*613a2f6bSGordon Ross 	int err, len, mbseg;
222*613a2f6bSGordon Ross 
223*613a2f6bSGordon Ross 	/* This ioctl requires a share. */
224*613a2f6bSGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
225*613a2f6bSGordon Ross 		return (ENOTCONN);
2264bff34e3Sthurlow 
227*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
228*613a2f6bSGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
229*613a2f6bSGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
230*613a2f6bSGordon Ross 		err = EFAULT;
231*613a2f6bSGordon Ross 		goto out;
2324bff34e3Sthurlow 	}
233*613a2f6bSGordon Ross 
234*613a2f6bSGordon Ross 	/* See ddi_copyin, ddi_copyout */
235*613a2f6bSGordon Ross 	mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER;
236*613a2f6bSGordon Ross 
237*613a2f6bSGordon Ross 	if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) {
238*613a2f6bSGordon Ross 		err = EINVAL;
239*613a2f6bSGordon Ross 		goto out;
240*613a2f6bSGordon Ross 	}
241*613a2f6bSGordon Ross 
242*613a2f6bSGordon Ross 	t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP);
243*613a2f6bSGordon Ross 	err = smb_t2_init(t2p, SSTOCP(ssp),
244*613a2f6bSGordon Ross 	    ioc->ioc_setup, ioc->ioc_setupcnt, &scred);
245*613a2f6bSGordon Ross 	if (err)
246*613a2f6bSGordon Ross 		goto out;
247*613a2f6bSGordon Ross 	len = t2p->t2_setupcount = ioc->ioc_setupcnt;
248*613a2f6bSGordon Ross 	if (len > 1)
249*613a2f6bSGordon Ross 		t2p->t2_setupdata = ioc->ioc_setup;
250*613a2f6bSGordon Ross 
251*613a2f6bSGordon Ross 	/* This ioc member is a fixed-size array. */
252*613a2f6bSGordon Ross 	if (ioc->ioc_name[0]) {
253*613a2f6bSGordon Ross 		/* Get the name length - carefully! */
254*613a2f6bSGordon Ross 		ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0';
255*613a2f6bSGordon Ross 		t2p->t_name_len = strlen(ioc->ioc_name);
256*613a2f6bSGordon Ross 		t2p->t_name = ioc->ioc_name;
257*613a2f6bSGordon Ross 	}
258*613a2f6bSGordon Ross 	t2p->t2_maxscount = 0;
259*613a2f6bSGordon Ross 	t2p->t2_maxpcount = ioc->ioc_rparamcnt;
260*613a2f6bSGordon Ross 	t2p->t2_maxdcount = ioc->ioc_rdatacnt;
261*613a2f6bSGordon Ross 
262*613a2f6bSGordon Ross 	/* Transmit parameters */
263*613a2f6bSGordon Ross 	err = smb_cpdatain(&t2p->t2_tparam,
264*613a2f6bSGordon Ross 	    ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg);
265*613a2f6bSGordon Ross 	if (err)
266*613a2f6bSGordon Ross 		goto out;
267*613a2f6bSGordon Ross 
268*613a2f6bSGordon Ross 	/* Transmit data */
269*613a2f6bSGordon Ross 	err = smb_cpdatain(&t2p->t2_tdata,
270*613a2f6bSGordon Ross 	    ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg);
271*613a2f6bSGordon Ross 	if (err)
272*613a2f6bSGordon Ross 		goto out;
273*613a2f6bSGordon Ross 
274*613a2f6bSGordon Ross 	err = smb_t2_request(t2p);
275*613a2f6bSGordon Ross 
276*613a2f6bSGordon Ross 	/* Copyout returned parameters. */
277*613a2f6bSGordon Ross 	mdp = &t2p->t2_rparam;
278*613a2f6bSGordon Ross 	if (err == 0 && mdp->md_top != NULL) {
279*613a2f6bSGordon Ross 		/* User's buffer large enough? */
280*613a2f6bSGordon Ross 		len = m_fixhdr(mdp->md_top);
281*613a2f6bSGordon Ross 		if (len > ioc->ioc_rparamcnt) {
282*613a2f6bSGordon Ross 			err = EMSGSIZE;
283*613a2f6bSGordon Ross 			goto out;
284*613a2f6bSGordon Ross 		}
285*613a2f6bSGordon Ross 		ioc->ioc_rparamcnt = (ushort_t)len;
286*613a2f6bSGordon Ross 		err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg);
287*613a2f6bSGordon Ross 		if (err)
288*613a2f6bSGordon Ross 			goto out;
289*613a2f6bSGordon Ross 	} else
290*613a2f6bSGordon Ross 		ioc->ioc_rparamcnt = 0;
291*613a2f6bSGordon Ross 
292*613a2f6bSGordon Ross 	/* Copyout returned data. */
293*613a2f6bSGordon Ross 	mdp = &t2p->t2_rdata;
294*613a2f6bSGordon Ross 	if (err == 0 && mdp->md_top != NULL) {
295*613a2f6bSGordon Ross 		/* User's buffer large enough? */
296*613a2f6bSGordon Ross 		len = m_fixhdr(mdp->md_top);
297*613a2f6bSGordon Ross 		if (len > ioc->ioc_rdatacnt) {
298*613a2f6bSGordon Ross 			err = EMSGSIZE;
299*613a2f6bSGordon Ross 			goto out;
300*613a2f6bSGordon Ross 		}
301*613a2f6bSGordon Ross 		ioc->ioc_rdatacnt = (ushort_t)len;
302*613a2f6bSGordon Ross 		err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg);
303*613a2f6bSGordon Ross 		if (err)
304*613a2f6bSGordon Ross 			goto out;
305*613a2f6bSGordon Ross 	} else
306*613a2f6bSGordon Ross 		ioc->ioc_rdatacnt = 0;
307*613a2f6bSGordon Ross 
308*613a2f6bSGordon Ross 	ioc->ioc_errclass = t2p->t2_sr_errclass;
309*613a2f6bSGordon Ross 	ioc->ioc_serror = t2p->t2_sr_serror;
310*613a2f6bSGordon Ross 	ioc->ioc_error = t2p->t2_sr_error;
311*613a2f6bSGordon Ross 	ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2;
312*613a2f6bSGordon Ross 
313*613a2f6bSGordon Ross 	ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
314*613a2f6bSGordon Ross 
315*613a2f6bSGordon Ross 
316*613a2f6bSGordon Ross out:
317*613a2f6bSGordon Ross 	if (t2p != NULL) {
318*613a2f6bSGordon Ross 		/* Note: t2p->t_name no longer allocated */
319*613a2f6bSGordon Ross 		smb_t2_done(t2p);
320*613a2f6bSGordon Ross 		kmem_free(t2p, sizeof (*t2p));
321*613a2f6bSGordon Ross 	}
322*613a2f6bSGordon Ross 	if (ioc != NULL)
323*613a2f6bSGordon Ross 		kmem_free(ioc, sizeof (*ioc));
324*613a2f6bSGordon Ross 	smb_credrele(&scred);
325*613a2f6bSGordon Ross 
326*613a2f6bSGordon Ross 	return (err);
3274bff34e3Sthurlow }
3284bff34e3Sthurlow 
329*613a2f6bSGordon Ross /* helper for _t2request */
3304bff34e3Sthurlow static int
331*613a2f6bSGordon Ross smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg)
3324bff34e3Sthurlow {
333*613a2f6bSGordon Ross 	int error;
334*613a2f6bSGordon Ross 
335*613a2f6bSGordon Ross 	if (len == 0)
336*613a2f6bSGordon Ross 		return (0);
337*613a2f6bSGordon Ross 	error = mb_init(mbp);
338*613a2f6bSGordon Ross 	if (error)
339*613a2f6bSGordon Ross 		return (error);
340*613a2f6bSGordon Ross 	return (mb_put_mem(mbp, data, len, mbseg));
3414bff34e3Sthurlow }
3424bff34e3Sthurlow 
343*613a2f6bSGordon Ross /*
344*613a2f6bSGordon Ross  * Helper for nsmb_ioctl cases
345*613a2f6bSGordon Ross  * SMBIOC_READ, SMBIOC_WRITE
346*613a2f6bSGordon Ross  */
3471b34bc4aSbs int
348*613a2f6bSGordon Ross smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
3491b34bc4aSbs {
350*613a2f6bSGordon Ross 	struct smb_cred scred;
351*613a2f6bSGordon Ross 	struct smb_share *ssp;
352*613a2f6bSGordon Ross 	smbioc_rw_t *ioc = NULL;
353*613a2f6bSGordon Ross 	struct iovec aiov[1];
354*613a2f6bSGordon Ross 	struct uio  auio;
355*613a2f6bSGordon Ross 	u_int16_t fh;
356*613a2f6bSGordon Ross 	int err;
357*613a2f6bSGordon Ross 	uio_rw_t rw;
3581b34bc4aSbs 
359*613a2f6bSGordon Ross 	/* This ioctl requires a share. */
360*613a2f6bSGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
361*613a2f6bSGordon Ross 		return (ENOTCONN);
362*613a2f6bSGordon Ross 
363*613a2f6bSGordon Ross 	/* After reconnect, force close+reopen */
364*613a2f6bSGordon Ross 	if (sdp->sd_vcgenid != ssp->ss_vcgenid)
365*613a2f6bSGordon Ross 		return (ESTALE);
366*613a2f6bSGordon Ross 
367*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
368*613a2f6bSGordon Ross 	ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP);
369*613a2f6bSGordon Ross 	if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) {
370*613a2f6bSGordon Ross 		err = EFAULT;
3711b34bc4aSbs 		goto out;
372*613a2f6bSGordon Ross 	}
373*613a2f6bSGordon Ross 
374*613a2f6bSGordon Ross 	switch (cmd) {
375*613a2f6bSGordon Ross 	case SMBIOC_READ:
376*613a2f6bSGordon Ross 		rw = UIO_READ;
377*613a2f6bSGordon Ross 		break;
378*613a2f6bSGordon Ross 	case SMBIOC_WRITE:
379*613a2f6bSGordon Ross 		rw = UIO_WRITE;
380*613a2f6bSGordon Ross 		break;
381*613a2f6bSGordon Ross 	default:
382*613a2f6bSGordon Ross 		err = ENODEV;
383*613a2f6bSGordon Ross 		goto out;
384*613a2f6bSGordon Ross 	}
385*613a2f6bSGordon Ross 
386*613a2f6bSGordon Ross 	fh = ioc->ioc_fh;
387*613a2f6bSGordon Ross 
388*613a2f6bSGordon Ross 	aiov[0].iov_base = ioc->ioc_base;
389*613a2f6bSGordon Ross 	aiov[0].iov_len = (size_t)ioc->ioc_cnt;
390*613a2f6bSGordon Ross 
391*613a2f6bSGordon Ross 	auio.uio_iov = aiov;
392*613a2f6bSGordon Ross 	auio.uio_iovcnt = 1;
393*613a2f6bSGordon Ross 	auio.uio_loffset = ioc->ioc_offset;
394*613a2f6bSGordon Ross 	auio.uio_segflg = (flags & FKIOCTL) ?
395*613a2f6bSGordon Ross 	    UIO_SYSSPACE : UIO_USERSPACE;
396*613a2f6bSGordon Ross 	auio.uio_fmode = 0;
397*613a2f6bSGordon Ross 	auio.uio_resid = (size_t)ioc->ioc_cnt;
398*613a2f6bSGordon Ross 
399*613a2f6bSGordon Ross 	err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0);
400*613a2f6bSGordon Ross 
401*613a2f6bSGordon Ross 	/*
402*613a2f6bSGordon Ross 	 * On return ioc_cnt holds the
403*613a2f6bSGordon Ross 	 * number of bytes transferred.
404*613a2f6bSGordon Ross 	 */
405*613a2f6bSGordon Ross 	ioc->ioc_cnt -= auio.uio_resid;
406*613a2f6bSGordon Ross 
407*613a2f6bSGordon Ross 	ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags);
408*613a2f6bSGordon Ross 
4091b34bc4aSbs out:
410*613a2f6bSGordon Ross 	if (ioc != NULL)
411*613a2f6bSGordon Ross 		kmem_free(ioc, sizeof (*ioc));
412*613a2f6bSGordon Ross 	smb_credrele(&scred);
413*613a2f6bSGordon Ross 
414*613a2f6bSGordon Ross 	return (err);
4151b34bc4aSbs }
4161b34bc4aSbs 
417*613a2f6bSGordon Ross /*
418*613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE
419*613a2f6bSGordon Ross  * Find or create a session (a.k.a. "VC" in here)
420*613a2f6bSGordon Ross  */
4214bff34e3Sthurlow int
422*613a2f6bSGordon Ross smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
4234bff34e3Sthurlow {
424*613a2f6bSGordon Ross 	struct smb_cred scred;
425*613a2f6bSGordon Ross 	smbioc_ossn_t *ossn = NULL;
4264bff34e3Sthurlow 	struct smb_vc *vcp = NULL;
4274bff34e3Sthurlow 	int error = 0;
428*613a2f6bSGordon Ross 	uid_t realuid;
4294bff34e3Sthurlow 
430*613a2f6bSGordon Ross 	/* Should be no VC */
431*613a2f6bSGordon Ross 	if (sdp->sd_vc != NULL)
432*613a2f6bSGordon Ross 		return (EISCONN);
433*613a2f6bSGordon Ross 
434*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
435*613a2f6bSGordon Ross 	ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP);
436*613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) {
437*613a2f6bSGordon Ross 		error = EFAULT;
438*613a2f6bSGordon Ross 		goto out;
4394bff34e3Sthurlow 	}
440*613a2f6bSGordon Ross 
441*613a2f6bSGordon Ross 	/*
442*613a2f6bSGordon Ross 	 * Only superuser can specify a UID or GID.
443*613a2f6bSGordon Ross 	 */
444*613a2f6bSGordon Ross 	realuid = crgetruid(cr);
445*613a2f6bSGordon Ross 	if (ossn->ssn_owner == SMBM_ANY_OWNER)
446*613a2f6bSGordon Ross 		ossn->ssn_owner = realuid;
447*613a2f6bSGordon Ross 	else {
4484bff34e3Sthurlow 		/*
449*613a2f6bSGordon Ross 		 * Do we have the privilege to create with the
450*613a2f6bSGordon Ross 		 * specified uid?  (does uid == cr->cr_uid, etc.)
4514bff34e3Sthurlow 		 */
452*613a2f6bSGordon Ross 		if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) {
453*613a2f6bSGordon Ross 			error = EPERM;
454*613a2f6bSGordon Ross 			goto out;
4554bff34e3Sthurlow 		}
456*613a2f6bSGordon Ross 		/* ossn->ssn_owner is OK */
4574bff34e3Sthurlow 	}
4584bff34e3Sthurlow 
459*613a2f6bSGordon Ross 	/*
460*613a2f6bSGordon Ross 	 * Make sure the strings are null terminated.
461*613a2f6bSGordon Ross 	 */
462*613a2f6bSGordon Ross 	ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0';
463*613a2f6bSGordon Ross 	ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0';
464*613a2f6bSGordon Ross 	ossn->ssn_id.id_user[   SMBIOC_MAX_NAME-1] = '\0';
4654bff34e3Sthurlow 
466*613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_CREATE)
467*613a2f6bSGordon Ross 		ossn->ssn_vopt |= SMBVOPT_CREATE;
468*613a2f6bSGordon Ross 	else /* FIND */
469*613a2f6bSGordon Ross 		ossn->ssn_vopt &= ~SMBVOPT_CREATE;
4704bff34e3Sthurlow 
471*613a2f6bSGordon Ross 	error = smb_vc_findcreate(ossn, &scred, &vcp);
4724bff34e3Sthurlow 	if (error)
4734bff34e3Sthurlow 		goto out;
474*613a2f6bSGordon Ross 	ASSERT(vcp != NULL);
4754bff34e3Sthurlow 
4764bff34e3Sthurlow 	/*
477*613a2f6bSGordon Ross 	 * We have a VC, held, but not locked.
478*613a2f6bSGordon Ross 	 * If we're creating, mark this instance as
479*613a2f6bSGordon Ross 	 * an open from IOD so close can do cleanup.
480*613a2f6bSGordon Ross 	 *
481*613a2f6bSGordon Ross 	 * XXX: Would be nice to have a back pointer
482*613a2f6bSGordon Ross 	 * from the VC to this (IOD) sdp instance.
4834bff34e3Sthurlow 	 */
484*613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_CREATE) {
485*613a2f6bSGordon Ross 		if (vcp->iod_thr != NULL) {
486*613a2f6bSGordon Ross 			error = EEXIST;
487*613a2f6bSGordon Ross 			goto out;
488*613a2f6bSGordon Ross 		}
489*613a2f6bSGordon Ross 		sdp->sd_flags |= NSMBFL_IOD;
490*613a2f6bSGordon Ross 	} else {
491*613a2f6bSGordon Ross 		/*
492*613a2f6bSGordon Ross 		 * Wait for it to finish connecting
493*613a2f6bSGordon Ross 		 * (or reconnect) if necessary.
494*613a2f6bSGordon Ross 		 */
495*613a2f6bSGordon Ross 		if (vcp->vc_state != SMBIOD_ST_VCACTIVE) {
496*613a2f6bSGordon Ross 			error = smb_iod_reconnect(vcp);
497*613a2f6bSGordon Ross 			if (error != 0)
498*613a2f6bSGordon Ross 				goto out;
499*613a2f6bSGordon Ross 		}
500*613a2f6bSGordon Ross 	}
501*613a2f6bSGordon Ross 
502*613a2f6bSGordon Ross 	/*
503*613a2f6bSGordon Ross 	 * The VC has a hold from _findvc
504*613a2f6bSGordon Ross 	 * which we keep until _SSN_RELE
505*613a2f6bSGordon Ross 	 * or nsmb_close().
506*613a2f6bSGordon Ross 	 */
507*613a2f6bSGordon Ross 	sdp->sd_level = SMBL_VC;
508*613a2f6bSGordon Ross 	sdp->sd_vc = vcp;
509*613a2f6bSGordon Ross 	vcp = NULL;
510*613a2f6bSGordon Ross 	(void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags);
5114bff34e3Sthurlow 
5124bff34e3Sthurlow out:
513*613a2f6bSGordon Ross 	if (vcp) {
514*613a2f6bSGordon Ross 		/* Error path: rele hold from _findcreate */
515*613a2f6bSGordon Ross 		smb_vc_rele(vcp);
516*613a2f6bSGordon Ross 	}
517*613a2f6bSGordon Ross 	if (ossn != NULL)
518*613a2f6bSGordon Ross 		kmem_free(ossn, sizeof (*ossn));
519*613a2f6bSGordon Ross 	smb_credrele(&scred);
520*613a2f6bSGordon Ross 
5214bff34e3Sthurlow 	return (error);
5224bff34e3Sthurlow }
5234bff34e3Sthurlow 
524*613a2f6bSGordon Ross /*
525*613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL
526*613a2f6bSGordon Ross  * Release or kill the current session.
527*613a2f6bSGordon Ross  */
5284bff34e3Sthurlow int
529*613a2f6bSGordon Ross smb_usr_drop_ssn(smb_dev_t *sdp, int cmd)
5304bff34e3Sthurlow {
531*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
5324bff34e3Sthurlow 
533*613a2f6bSGordon Ross 	/* Must have a VC. */
534*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
535*613a2f6bSGordon Ross 		return (ENOTCONN);
5364bff34e3Sthurlow 
537*613a2f6bSGordon Ross 	/* If we have a share ref, drop it too. */
538*613a2f6bSGordon Ross 	if (sdp->sd_share) {
539*613a2f6bSGordon Ross 		smb_share_rele(sdp->sd_share);
540*613a2f6bSGordon Ross 		sdp->sd_share = NULL;
541*613a2f6bSGordon Ross 		sdp->sd_level = SMBL_VC;
5424bff34e3Sthurlow 	}
5434bff34e3Sthurlow 
544*613a2f6bSGordon Ross 	if (cmd == SMBIOC_SSN_KILL)
545*613a2f6bSGordon Ross 		smb_vc_kill(vcp);
5464bff34e3Sthurlow 
547*613a2f6bSGordon Ross 	/* Drop the VC ref. */
548*613a2f6bSGordon Ross 	smb_vc_rele(vcp);
549*613a2f6bSGordon Ross 	sdp->sd_vc = NULL;
550*613a2f6bSGordon Ross 	sdp->sd_level = 0;
551*613a2f6bSGordon Ross 
552*613a2f6bSGordon Ross 	return (0);
5534bff34e3Sthurlow }
5544bff34e3Sthurlow 
5554bff34e3Sthurlow /*
556*613a2f6bSGordon Ross  * Find or create a tree (connected share)
5574bff34e3Sthurlow  */
5584bff34e3Sthurlow int
559*613a2f6bSGordon Ross smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr)
5604bff34e3Sthurlow {
561*613a2f6bSGordon Ross 	struct smb_cred scred;
562*613a2f6bSGordon Ross 	smbioc_tcon_t *tcon = NULL;
563*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
564*613a2f6bSGordon Ross 	struct smb_share *ssp = NULL;
565*613a2f6bSGordon Ross 	int error = 0;
5664bff34e3Sthurlow 
567*613a2f6bSGordon Ross 	/* Must have a VC. */
568*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
569*613a2f6bSGordon Ross 		return (ENOTCONN);
570*613a2f6bSGordon Ross 	/* Should not have a share. */
571*613a2f6bSGordon Ross 	if (sdp->sd_share != NULL)
572*613a2f6bSGordon Ross 		return (EISCONN);
573*613a2f6bSGordon Ross 
574*613a2f6bSGordon Ross 	smb_credinit(&scred, cr);
575*613a2f6bSGordon Ross 	tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP);
576*613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) {
577*613a2f6bSGordon Ross 		error = EFAULT;
578*613a2f6bSGordon Ross 		goto out;
5794bff34e3Sthurlow 	}
580*613a2f6bSGordon Ross 
581*613a2f6bSGordon Ross 	/*
582*613a2f6bSGordon Ross 	 * Make sure the strings are null terminated.
583*613a2f6bSGordon Ross 	 */
584*613a2f6bSGordon Ross 	tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0';
585*613a2f6bSGordon Ross 	tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0';
586*613a2f6bSGordon Ross 	tcon->tc_sh.sh_type_req[SMBIOC_STYPE_LEN-1] = '\0';
587*613a2f6bSGordon Ross 	bzero(tcon->tc_sh.sh_type_ret, SMBIOC_STYPE_LEN);
588*613a2f6bSGordon Ross 
589*613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_CONNECT)
590*613a2f6bSGordon Ross 		tcon->tc_opt |= SMBSOPT_CREATE;
591*613a2f6bSGordon Ross 	else /* FIND */
592*613a2f6bSGordon Ross 		tcon->tc_opt &= ~SMBSOPT_CREATE;
593*613a2f6bSGordon Ross 
594*613a2f6bSGordon Ross 	error = smb_share_findcreate(tcon, vcp, &ssp, &scred);
5954bff34e3Sthurlow 	if (error)
596*613a2f6bSGordon Ross 		goto out;
597*613a2f6bSGordon Ross 	ASSERT(ssp != NULL);
598*613a2f6bSGordon Ross 
599*613a2f6bSGordon Ross 	/*
600*613a2f6bSGordon Ross 	 * We have a share, held, but not locked.
601*613a2f6bSGordon Ross 	 * If we're creating, do tree connect now,
602*613a2f6bSGordon Ross 	 * otherwise let that wait for a request.
603*613a2f6bSGordon Ross 	 */
604*613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_CONNECT) {
605*613a2f6bSGordon Ross 		error = smb_share_tcon(ssp, &scred);
606*613a2f6bSGordon Ross 		if (error)
607*613a2f6bSGordon Ross 			goto out;
6084bff34e3Sthurlow 	}
609*613a2f6bSGordon Ross 
610*613a2f6bSGordon Ross 	/*
611*613a2f6bSGordon Ross 	 * Give caller the real share type from
612*613a2f6bSGordon Ross 	 * the tree connect response, so they can
613*613a2f6bSGordon Ross 	 * see if they got the requested type.
614*613a2f6bSGordon Ross 	 */
615*613a2f6bSGordon Ross 	memcpy(tcon->tc_sh.sh_type_ret,
616*613a2f6bSGordon Ross 	    ssp->ss_type_ret, SMBIOC_STYPE_LEN);
617*613a2f6bSGordon Ross 
618*613a2f6bSGordon Ross 	/*
619*613a2f6bSGordon Ross 	 * The share has a hold from _tcon
620*613a2f6bSGordon Ross 	 * which we keep until nsmb_close()
621*613a2f6bSGordon Ross 	 * or the SMBIOC_TDIS below.
622*613a2f6bSGordon Ross 	 */
623*613a2f6bSGordon Ross 	sdp->sd_level = SMBL_SHARE;
624*613a2f6bSGordon Ross 	sdp->sd_share = ssp;
625*613a2f6bSGordon Ross 	ssp = NULL;
626*613a2f6bSGordon Ross 	(void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags);
627*613a2f6bSGordon Ross 
628*613a2f6bSGordon Ross out:
629*613a2f6bSGordon Ross 	if (ssp) {
630*613a2f6bSGordon Ross 		/* Error path: rele hold from _findcreate */
631*613a2f6bSGordon Ross 		smb_share_rele(ssp);
6324bff34e3Sthurlow 	}
633*613a2f6bSGordon Ross 	if (tcon) {
634*613a2f6bSGordon Ross 		/*
635*613a2f6bSGordon Ross 		 * This structure may contain a
636*613a2f6bSGordon Ross 		 * cleartext password, so zap it.
637*613a2f6bSGordon Ross 		 */
638*613a2f6bSGordon Ross 		bzero(tcon, sizeof (*tcon));
639*613a2f6bSGordon Ross 		kmem_free(tcon, sizeof (*tcon));
640*613a2f6bSGordon Ross 	}
641*613a2f6bSGordon Ross 	smb_credrele(&scred);
6424bff34e3Sthurlow 
643*613a2f6bSGordon Ross 	return (error);
6444bff34e3Sthurlow }
6454bff34e3Sthurlow 
646*613a2f6bSGordon Ross /*
647*613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL
648*613a2f6bSGordon Ross  * Release or kill the current tree
649*613a2f6bSGordon Ross  */
650*613a2f6bSGordon Ross int
651*613a2f6bSGordon Ross smb_usr_drop_tree(smb_dev_t *sdp, int cmd)
6524bff34e3Sthurlow {
653*613a2f6bSGordon Ross 	struct smb_share *ssp = NULL;
6544bff34e3Sthurlow 
655*613a2f6bSGordon Ross 	/* Must have a VC and a share. */
656*613a2f6bSGordon Ross 	if (sdp->sd_vc == NULL)
657*613a2f6bSGordon Ross 		return (ENOTCONN);
658*613a2f6bSGordon Ross 	if ((ssp = sdp->sd_share) == NULL)
659*613a2f6bSGordon Ross 		return (ENOTCONN);
660*613a2f6bSGordon Ross 
661*613a2f6bSGordon Ross 	if (cmd == SMBIOC_TREE_KILL)
662*613a2f6bSGordon Ross 		smb_share_kill(ssp);
663*613a2f6bSGordon Ross 
664*613a2f6bSGordon Ross 	/* Drop the share ref. */
665*613a2f6bSGordon Ross 	smb_share_rele(sdp->sd_share);
666*613a2f6bSGordon Ross 	sdp->sd_share = NULL;
667*613a2f6bSGordon Ross 	sdp->sd_level = SMBL_VC;
668*613a2f6bSGordon Ross 
669*613a2f6bSGordon Ross 	return (0);
6704bff34e3Sthurlow }
6714bff34e3Sthurlow 
672*613a2f6bSGordon Ross 
673*613a2f6bSGordon Ross /*
674*613a2f6bSGordon Ross  * Ioctl function: SMBIOC_IOD_WORK
675*613a2f6bSGordon Ross  *
676*613a2f6bSGordon Ross  * Become the reader (IOD) thread, until either the connection is
677*613a2f6bSGordon Ross  * reset by the server, or until the connection is idle longer than
678*613a2f6bSGordon Ross  * some max time. (max idle time not yet implemented)
679*613a2f6bSGordon Ross  */
6804bff34e3Sthurlow int
681*613a2f6bSGordon Ross smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr)
6824bff34e3Sthurlow {
683*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
684*613a2f6bSGordon Ross 	int err = 0;
6854bff34e3Sthurlow 
686*613a2f6bSGordon Ross 	/* Must have a valid session. */
687*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
6884bff34e3Sthurlow 		return (EINVAL);
689*613a2f6bSGordon Ross 	if (vcp->vc_flags & SMBV_GONE)
690*613a2f6bSGordon Ross 		return (EINVAL);
691*613a2f6bSGordon Ross 
692*613a2f6bSGordon Ross 	/*
693*613a2f6bSGordon Ross 	 * Is there already an IOD for this VC?
694*613a2f6bSGordon Ross 	 * (Should never happen.)
695*613a2f6bSGordon Ross 	 */
696*613a2f6bSGordon Ross 	SMB_VC_LOCK(vcp);
697*613a2f6bSGordon Ross 	if (vcp->iod_thr == NULL)
698*613a2f6bSGordon Ross 		vcp->iod_thr = curthread;
699*613a2f6bSGordon Ross 	else
700*613a2f6bSGordon Ross 		err = EEXIST;
701*613a2f6bSGordon Ross 	SMB_VC_UNLOCK(vcp);
702*613a2f6bSGordon Ross 	if (err)
703*613a2f6bSGordon Ross 		return (err);
704*613a2f6bSGordon Ross 
705*613a2f6bSGordon Ross 	/*
706*613a2f6bSGordon Ross 	 * Copy the "work" state, etc. into the VC
707*613a2f6bSGordon Ross 	 * The MAC key is copied separately.
708*613a2f6bSGordon Ross 	 */
709*613a2f6bSGordon Ross 	if (ddi_copyin((void *)arg, &vcp->vc_work,
710*613a2f6bSGordon Ross 	    sizeof (smbioc_ssn_work_t), flags)) {
711*613a2f6bSGordon Ross 		err = EFAULT;
712*613a2f6bSGordon Ross 		goto out;
7134bff34e3Sthurlow 	}
714*613a2f6bSGordon Ross 	if (vcp->vc_u_maclen) {
715*613a2f6bSGordon Ross 		vcp->vc_mackeylen = vcp->vc_u_maclen;
716*613a2f6bSGordon Ross 		vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP);
717*613a2f6bSGordon Ross 		if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey,
718*613a2f6bSGordon Ross 		    vcp->vc_mackeylen, flags)) {
719*613a2f6bSGordon Ross 			err = EFAULT;
720*613a2f6bSGordon Ross 			goto out;
7214bff34e3Sthurlow 		}
7229c9af259SGordon Ross 	}
723*613a2f6bSGordon Ross 
724*613a2f6bSGordon Ross 	err = smb_iod_vc_work(vcp, cr);
725*613a2f6bSGordon Ross 
726*613a2f6bSGordon Ross 	/* Caller wants state here. */
727*613a2f6bSGordon Ross 	vcp->vc_work.wk_out_state = vcp->vc_state;
728*613a2f6bSGordon Ross 
729*613a2f6bSGordon Ross 	(void) ddi_copyout(&vcp->vc_work, (void *)arg,
730*613a2f6bSGordon Ross 	    sizeof (smbioc_ssn_work_t), flags);
731*613a2f6bSGordon Ross 
732*613a2f6bSGordon Ross out:
733*613a2f6bSGordon Ross 	if (vcp->vc_mackey) {
734*613a2f6bSGordon Ross 		kmem_free(vcp->vc_mackey, vcp->vc_mackeylen);
735*613a2f6bSGordon Ross 		vcp->vc_mackey = NULL;
736*613a2f6bSGordon Ross 		vcp->vc_mackeylen = 0;
737*613a2f6bSGordon Ross 	}
738*613a2f6bSGordon Ross 
739*613a2f6bSGordon Ross 	/*
740*613a2f6bSGordon Ross 	 * The IOD thread is leaving the driver.  Clear iod_thr,
741*613a2f6bSGordon Ross 	 * and wake up anybody waiting for us to quit.
742*613a2f6bSGordon Ross 	 */
743*613a2f6bSGordon Ross 	SMB_VC_LOCK(vcp);
744*613a2f6bSGordon Ross 	vcp->iod_thr = NULL;
745*613a2f6bSGordon Ross 	cv_broadcast(&vcp->vc_statechg);
746*613a2f6bSGordon Ross 	SMB_VC_UNLOCK(vcp);
747*613a2f6bSGordon Ross 
748*613a2f6bSGordon Ross 	return (err);
7494bff34e3Sthurlow }
7504bff34e3Sthurlow 
7514bff34e3Sthurlow /*
752*613a2f6bSGordon Ross  * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL
753*613a2f6bSGordon Ross  *
754*613a2f6bSGordon Ross  * Wait for user-level requests to be enqueued on this session,
755*613a2f6bSGordon Ross  * and then return to the user-space helper, which will then
756*613a2f6bSGordon Ross  * initiate a reconnect, etc.
7574bff34e3Sthurlow  */
7584bff34e3Sthurlow int
759*613a2f6bSGordon Ross smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags)
7604bff34e3Sthurlow {
761*613a2f6bSGordon Ross 	struct smb_vc *vcp = NULL;
762*613a2f6bSGordon Ross 	int err = 0;
763*613a2f6bSGordon Ross 
764*613a2f6bSGordon Ross 	/* Must have a valid session. */
765*613a2f6bSGordon Ross 	if ((vcp = sdp->sd_vc) == NULL)
766*613a2f6bSGordon Ross 		return (EINVAL);
767*613a2f6bSGordon Ross 	if (vcp->vc_flags & SMBV_GONE)
768*613a2f6bSGordon Ross 		return (EINVAL);
769*613a2f6bSGordon Ross 
770*613a2f6bSGordon Ross 	/*
771*613a2f6bSGordon Ross 	 * Is there already an IOD for this VC?
772*613a2f6bSGordon Ross 	 * (Should never happen.)
773*613a2f6bSGordon Ross 	 */
774*613a2f6bSGordon Ross 	SMB_VC_LOCK(vcp);
775*613a2f6bSGordon Ross 	if (vcp->iod_thr == NULL)
776*613a2f6bSGordon Ross 		vcp->iod_thr = curthread;
777*613a2f6bSGordon Ross 	else
778*613a2f6bSGordon Ross 		err = EEXIST;
779*613a2f6bSGordon Ross 	SMB_VC_UNLOCK(vcp);
780*613a2f6bSGordon Ross 	if (err)
781*613a2f6bSGordon Ross 		return (err);
782*613a2f6bSGordon Ross 
783*613a2f6bSGordon Ross 	/* nothing to copyin */
7844bff34e3Sthurlow 
7854bff34e3Sthurlow 	switch (cmd) {
786*613a2f6bSGordon Ross 	case SMBIOC_IOD_IDLE:
787*613a2f6bSGordon Ross 		err = smb_iod_vc_idle(vcp);
7884bff34e3Sthurlow 		break;
789*613a2f6bSGordon Ross 
790*613a2f6bSGordon Ross 	case SMBIOC_IOD_RCFAIL:
791*613a2f6bSGordon Ross 		err = smb_iod_vc_rcfail(vcp);
7924bff34e3Sthurlow 		break;
793*613a2f6bSGordon Ross 
7944bff34e3Sthurlow 	default:
795*613a2f6bSGordon Ross 		err = ENOTTY;
796*613a2f6bSGordon Ross 		goto out;
7974bff34e3Sthurlow 	}
7984bff34e3Sthurlow 
799*613a2f6bSGordon Ross 	/* Both of these ioctls copy out the new state. */
800*613a2f6bSGordon Ross 	(void) ddi_copyout(&vcp->vc_state, (void *)arg,
801*613a2f6bSGordon Ross 	    sizeof (int), flags);
8024bff34e3Sthurlow 
803*613a2f6bSGordon Ross out:
8044bff34e3Sthurlow 	/*
805*613a2f6bSGordon Ross 	 * The IOD thread is leaving the driver.  Clear iod_thr,
806*613a2f6bSGordon Ross 	 * and wake up anybody waiting for us to quit.
8074bff34e3Sthurlow 	 */
808*613a2f6bSGordon Ross 	SMB_VC_LOCK(vcp);
809*613a2f6bSGordon Ross 	vcp->iod_thr = NULL;
810*613a2f6bSGordon Ross 	cv_broadcast(&vcp->vc_statechg);
811*613a2f6bSGordon Ross 	SMB_VC_UNLOCK(vcp);
8124bff34e3Sthurlow 
813*613a2f6bSGordon Ross 	return (err);
8144bff34e3Sthurlow }
815