xref: /onnv-gate/usr/src/uts/common/fs/smbsrv/smb_opipe.c (revision 12890:16985853e3aa)
17052Samw /*
27052Samw  * CDDL HEADER START
37052Samw  *
47052Samw  * The contents of this file are subject to the terms of the
57052Samw  * Common Development and Distribution License (the "License").
67052Samw  * You may not use this file except in compliance with the License.
77052Samw  *
87052Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97052Samw  * or http://www.opensolaris.org/os/licensing.
107052Samw  * See the License for the specific language governing permissions
117052Samw  * and limitations under the License.
127052Samw  *
137052Samw  * When distributing Covered Code, include this CDDL HEADER in each
147052Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157052Samw  * If applicable, add the following below this CDDL HEADER, with the
167052Samw  * fields enclosed by brackets "[]" replaced with your own identifying
177052Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
187052Samw  *
197052Samw  * CDDL HEADER END
207052Samw  */
217052Samw /*
2212508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
237052Samw  */
247052Samw 
257052Samw /*
267052Samw  * This module provides the interface to NDR RPC.
277052Samw  */
287052Samw 
297052Samw #include <sys/stat.h>
307052Samw #include <sys/door.h>
317052Samw #include <sys/door_data.h>
327052Samw #include <sys/uio.h>
337052Samw #include <sys/ksynch.h>
3410966SJordan.Brown@Sun.COM #include <smbsrv/smb_kproto.h>
357052Samw #include <smbsrv/smb_xdr.h>
367052Samw 
377052Samw #define	SMB_OPIPE_ISOPEN(OPIPE)	\
3811963SAfshin.Ardakani@Sun.COM 	(((OPIPE)->p_hdr.dh_magic == SMB_OPIPE_HDR_MAGIC) && \
3911963SAfshin.Ardakani@Sun.COM 	((OPIPE)->p_hdr.dh_fid))
407052Samw 
417052Samw extern volatile uint32_t smb_fids;
427052Samw 
437052Samw static int smb_opipe_do_open(smb_request_t *, smb_opipe_t *);
4411963SAfshin.Ardakani@Sun.COM static char *smb_opipe_lookup(const char *);
4511963SAfshin.Ardakani@Sun.COM static int smb_opipe_sethdr(smb_opipe_t *, uint32_t, uint32_t);
4611963SAfshin.Ardakani@Sun.COM static int smb_opipe_exec(smb_opipe_t *);
477052Samw static void smb_opipe_enter(smb_opipe_t *);
487052Samw static void smb_opipe_exit(smb_opipe_t *);
497052Samw 
507052Samw static door_handle_t smb_opipe_door_hd = NULL;
517052Samw static int smb_opipe_door_id = -1;
527052Samw static uint64_t smb_opipe_door_ncall = 0;
537052Samw static kmutex_t smb_opipe_door_mutex;
547052Samw static kcondvar_t smb_opipe_door_cv;
557052Samw 
567052Samw static int smb_opipe_door_call(smb_opipe_t *);
577052Samw static int smb_opipe_door_upcall(smb_opipe_t *);
587052Samw 
5911963SAfshin.Ardakani@Sun.COM smb_opipe_t *
smb_opipe_alloc(smb_server_t * sv)6011963SAfshin.Ardakani@Sun.COM smb_opipe_alloc(smb_server_t *sv)
6111963SAfshin.Ardakani@Sun.COM {
6211963SAfshin.Ardakani@Sun.COM 	smb_opipe_t	*opipe;
6311963SAfshin.Ardakani@Sun.COM 
6411963SAfshin.Ardakani@Sun.COM 	opipe = kmem_cache_alloc(sv->si_cache_opipe, KM_SLEEP);
6511963SAfshin.Ardakani@Sun.COM 
6611963SAfshin.Ardakani@Sun.COM 	bzero(opipe, sizeof (smb_opipe_t));
6711963SAfshin.Ardakani@Sun.COM 	mutex_init(&opipe->p_mutex, NULL, MUTEX_DEFAULT, NULL);
6811963SAfshin.Ardakani@Sun.COM 	cv_init(&opipe->p_cv, NULL, CV_DEFAULT, NULL);
6911963SAfshin.Ardakani@Sun.COM 	opipe->p_magic = SMB_OPIPE_MAGIC;
7011963SAfshin.Ardakani@Sun.COM 	opipe->p_server = sv;
7111963SAfshin.Ardakani@Sun.COM 
7211963SAfshin.Ardakani@Sun.COM 	smb_llist_enter(&sv->sv_opipe_list, RW_WRITER);
7311963SAfshin.Ardakani@Sun.COM 	smb_llist_insert_tail(&sv->sv_opipe_list, opipe);
7411963SAfshin.Ardakani@Sun.COM 	smb_llist_exit(&sv->sv_opipe_list);
7511963SAfshin.Ardakani@Sun.COM 
7611963SAfshin.Ardakani@Sun.COM 	return (opipe);
7711963SAfshin.Ardakani@Sun.COM }
7811963SAfshin.Ardakani@Sun.COM 
7911963SAfshin.Ardakani@Sun.COM void
smb_opipe_dealloc(smb_opipe_t * opipe)8011963SAfshin.Ardakani@Sun.COM smb_opipe_dealloc(smb_opipe_t *opipe)
8111963SAfshin.Ardakani@Sun.COM {
8211963SAfshin.Ardakani@Sun.COM 	smb_server_t *sv;
8311963SAfshin.Ardakani@Sun.COM 
8411963SAfshin.Ardakani@Sun.COM 	SMB_OPIPE_VALID(opipe);
8511963SAfshin.Ardakani@Sun.COM 	sv = opipe->p_server;
8611963SAfshin.Ardakani@Sun.COM 	SMB_SERVER_VALID(sv);
8711963SAfshin.Ardakani@Sun.COM 
8811963SAfshin.Ardakani@Sun.COM 	smb_llist_enter(&sv->sv_opipe_list, RW_WRITER);
8911963SAfshin.Ardakani@Sun.COM 	smb_llist_remove(&sv->sv_opipe_list, opipe);
9011963SAfshin.Ardakani@Sun.COM 	smb_llist_exit(&sv->sv_opipe_list);
9111963SAfshin.Ardakani@Sun.COM 
9211963SAfshin.Ardakani@Sun.COM 	opipe->p_magic = (uint32_t)~SMB_OPIPE_MAGIC;
9311963SAfshin.Ardakani@Sun.COM 	smb_event_destroy(opipe->p_event);
9411963SAfshin.Ardakani@Sun.COM 	cv_destroy(&opipe->p_cv);
9511963SAfshin.Ardakani@Sun.COM 	mutex_destroy(&opipe->p_mutex);
9611963SAfshin.Ardakani@Sun.COM 
9711963SAfshin.Ardakani@Sun.COM 	kmem_cache_free(sv->si_cache_opipe, opipe);
9811963SAfshin.Ardakani@Sun.COM }
9911963SAfshin.Ardakani@Sun.COM 
1007052Samw /*
1017052Samw  * smb_opipe_open
1027052Samw  *
1037052Samw  * Open a well-known RPC named pipe. This routine should be called if
1047052Samw  * a file open is requested on a share of type STYPE_IPC.
1057052Samw  * If we recognize the pipe, we setup a new ofile.
1067052Samw  *
1077052Samw  * Returns 0 on success, Otherwise an NT status is returned to indicate
1087052Samw  * an error.
1097052Samw  */
1107052Samw int
smb_opipe_open(smb_request_t * sr)1117052Samw smb_opipe_open(smb_request_t *sr)
1127052Samw {
11312508Samw@Sun.COM 	smb_arg_open_t	*op = &sr->sr_open;
1147052Samw 	smb_ofile_t *of;
1157052Samw 	smb_opipe_t *opipe;
11611963SAfshin.Ardakani@Sun.COM 	smb_doorhdr_t hdr;
1177052Samw 	smb_error_t err;
1187052Samw 	char *pipe_name;
1197052Samw 
1209343SAfshin.Ardakani@Sun.COM 	if ((pipe_name = smb_opipe_lookup(op->fqi.fq_path.pn_path)) == NULL)
1217052Samw 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1227052Samw 
1237348SJose.Borrego@Sun.COM 	op->create_options = 0;
1247348SJose.Borrego@Sun.COM 
1257348SJose.Borrego@Sun.COM 	of = smb_ofile_open(sr->tid_tree, NULL, sr->smb_pid, op,
1267052Samw 	    SMB_FTYPE_MESG_PIPE, SMB_UNIQ_FID(), &err);
1279231SAfshin.Ardakani@Sun.COM 
1287052Samw 	if (of == NULL)
1297052Samw 		return (err.status);
1307052Samw 
1319231SAfshin.Ardakani@Sun.COM 	if (!smb_tree_is_connected(sr->tid_tree)) {
1329231SAfshin.Ardakani@Sun.COM 		smb_ofile_close(of, 0);
1339231SAfshin.Ardakani@Sun.COM 		smb_ofile_release(of);
1349231SAfshin.Ardakani@Sun.COM 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
1359231SAfshin.Ardakani@Sun.COM 	}
1369231SAfshin.Ardakani@Sun.COM 
1377052Samw 	op->dsize = 0x01000;
1387052Samw 	op->dattr = FILE_ATTRIBUTE_NORMAL;
1397052Samw 	op->ftype = SMB_FTYPE_MESG_PIPE;
1407052Samw 	op->action_taken = SMB_OACT_LOCK | SMB_OACT_OPENED; /* 0x8001 */
1417052Samw 	op->devstate = SMB_PIPE_READMODE_MESSAGE
1427052Samw 	    | SMB_PIPE_TYPE_MESSAGE
1437052Samw 	    | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */
1447052Samw 	op->fileid = of->f_fid;
1457052Samw 
1467052Samw 	sr->smb_fid = of->f_fid;
1477052Samw 	sr->fid_ofile = of;
1487052Samw 
1497052Samw 	opipe = of->f_pipe;
1507052Samw 	smb_opipe_enter(opipe);
1517052Samw 
15211963SAfshin.Ardakani@Sun.COM 	opipe->p_server = of->f_server;
1537052Samw 	opipe->p_name = pipe_name;
1547052Samw 	opipe->p_doorbuf = kmem_zalloc(SMB_OPIPE_DOOR_BUFSIZE, KM_SLEEP);
1557052Samw 
1567052Samw 	/*
1577052Samw 	 * p_data points to the offset within p_doorbuf at which
1587052Samw 	 * data will be written or read.
1597052Samw 	 */
16011963SAfshin.Ardakani@Sun.COM 	opipe->p_data = opipe->p_doorbuf + xdr_sizeof(smb_doorhdr_xdr, &hdr);
1617052Samw 
1627052Samw 	if (smb_opipe_do_open(sr, opipe) != 0) {
1637052Samw 		/*
1647052Samw 		 * On error, reset the header to clear the fid,
1657052Samw 		 * which avoids confusion when smb_opipe_close() is
1667052Samw 		 * called by smb_ofile_close().
1677052Samw 		 */
16811963SAfshin.Ardakani@Sun.COM 		bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
1697052Samw 		kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE);
1707052Samw 		smb_opipe_exit(opipe);
1717348SJose.Borrego@Sun.COM 		smb_ofile_close(of, 0);
1727052Samw 		return (NT_STATUS_NO_MEMORY);
1737052Samw 	}
1747052Samw 	smb_opipe_exit(opipe);
1757052Samw 	return (NT_STATUS_SUCCESS);
1767052Samw }
1777052Samw 
1787052Samw /*
1797052Samw  * smb_opipe_lookup
1807052Samw  *
1817052Samw  * Lookup a path to see if it's a well-known RPC named pipe that we support.
1827052Samw  * The full pipe path will be in the form \\PIPE\\SERVICE.  The first part
1837052Samw  * can be assumed, so all we need here are the service names.
1847052Samw  *
18510122SJordan.Brown@Sun.COM  * Returns a pointer to the pipe name (without any leading \'s) on success.
1867052Samw  * Otherwise returns a null pointer.
1877052Samw  */
1887052Samw static char *
smb_opipe_lookup(const char * path)1897052Samw smb_opipe_lookup(const char *path)
1907052Samw {
1917052Samw 	static char *named_pipes[] = {
19210122SJordan.Brown@Sun.COM 		"lsass",
1937052Samw 		"LSARPC",
1947052Samw 		"NETLOGON",
1957052Samw 		"SAMR",
1967052Samw 		"SPOOLSS",
1977052Samw 		"SRVSVC",
1987052Samw 		"SVCCTL",
1997052Samw 		"WINREG",
2007052Samw 		"WKSSVC",
20111963SAfshin.Ardakani@Sun.COM 		"EVENTLOG",
20211963SAfshin.Ardakani@Sun.COM 		"NETDFS"
2037052Samw 	};
2047052Samw 
2057052Samw 	const char *name;
2067052Samw 	int i;
2077052Samw 
2087052Samw 	if (path == NULL)
2097052Samw 		return (NULL);
2107052Samw 
2117052Samw 	name = path;
2127052Samw 	name += strspn(name, "\\");
21310966SJordan.Brown@Sun.COM 	if (smb_strcasecmp(name, "PIPE", 4) == 0) {
2147052Samw 		path += 4;
2157052Samw 		name += strspn(name, "\\");
2167052Samw 	}
2177052Samw 
2187052Samw 	for (i = 0; i < sizeof (named_pipes) / sizeof (named_pipes[0]); ++i) {
21910966SJordan.Brown@Sun.COM 		if (smb_strcasecmp(name, named_pipes[i], 0) == 0)
2207052Samw 			return (named_pipes[i]);
2217052Samw 	}
2227052Samw 
2237052Samw 	return (NULL);
2247052Samw }
2257052Samw 
2267052Samw /*
2277052Samw  * Initialize the opipe header and context, and make the door call.
2287052Samw  */
2297052Samw static int
smb_opipe_do_open(smb_request_t * sr,smb_opipe_t * opipe)2307052Samw smb_opipe_do_open(smb_request_t *sr, smb_opipe_t *opipe)
2317052Samw {
23210122SJordan.Brown@Sun.COM 	smb_netuserinfo_t *userinfo = &opipe->p_user;
2337052Samw 	smb_user_t *user = sr->uid_user;
2347052Samw 	uint8_t *buf = opipe->p_doorbuf;
2357052Samw 	uint32_t buflen = SMB_OPIPE_DOOR_BUFSIZE;
2367052Samw 	uint32_t len;
2377052Samw 
238*12890SJoyce.McIntosh@Sun.COM 	if ((opipe->p_event = smb_event_create(SMB_EVENT_TIMEOUT)) == NULL)
23911963SAfshin.Ardakani@Sun.COM 		return (-1);
24011963SAfshin.Ardakani@Sun.COM 
24110122SJordan.Brown@Sun.COM 	smb_user_netinfo_init(user, userinfo);
24210122SJordan.Brown@Sun.COM 	len = xdr_sizeof(smb_netuserinfo_xdr, userinfo);
2437052Samw 
24411963SAfshin.Ardakani@Sun.COM 	bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
24511963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_magic = SMB_OPIPE_HDR_MAGIC;
24611963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_flags = SMB_DF_SYSSPACE;
24711963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_fid = smb_event_txid(opipe->p_event);
2487052Samw 
24911963SAfshin.Ardakani@Sun.COM 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_OPEN, len) == -1)
2507052Samw 		return (-1);
2517052Samw 
25211963SAfshin.Ardakani@Sun.COM 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
2537052Samw 	buf += len;
2547052Samw 	buflen -= len;
2557052Samw 
25610122SJordan.Brown@Sun.COM 	if (smb_netuserinfo_encode(userinfo, buf, buflen, NULL) == -1)
2577052Samw 		return (-1);
2587052Samw 
2597052Samw 	return (smb_opipe_door_call(opipe));
2607052Samw }
2617052Samw 
2627052Samw /*
2637052Samw  * smb_opipe_close
2647052Samw  *
2657052Samw  * Called whenever an IPC file/pipe is closed.
2667052Samw  */
2677052Samw void
smb_opipe_close(smb_ofile_t * of)2687052Samw smb_opipe_close(smb_ofile_t *of)
2697052Samw {
2707052Samw 	smb_opipe_t *opipe;
2717052Samw 
2727052Samw 	ASSERT(of);
2737052Samw 	ASSERT(of->f_ftype == SMB_FTYPE_MESG_PIPE);
2747052Samw 
2757052Samw 	opipe = of->f_pipe;
27611963SAfshin.Ardakani@Sun.COM 	SMB_OPIPE_VALID(opipe);
27711963SAfshin.Ardakani@Sun.COM 
27811963SAfshin.Ardakani@Sun.COM 	(void) smb_server_cancel_event(opipe->p_hdr.dh_fid);
2797052Samw 	smb_opipe_enter(opipe);
2807052Samw 
2817052Samw 	if (SMB_OPIPE_ISOPEN(opipe)) {
28211963SAfshin.Ardakani@Sun.COM 		(void) smb_opipe_sethdr(opipe, SMB_OPIPE_CLOSE, 0);
2837052Samw 		(void) smb_opipe_door_call(opipe);
28411963SAfshin.Ardakani@Sun.COM 		bzero(&opipe->p_hdr, sizeof (smb_doorhdr_t));
2857052Samw 		kmem_free(opipe->p_doorbuf, SMB_OPIPE_DOOR_BUFSIZE);
2867052Samw 	}
2877052Samw 
28810122SJordan.Brown@Sun.COM 	smb_user_netinfo_fini(&opipe->p_user);
2897052Samw 	smb_opipe_exit(opipe);
2907052Samw }
2917052Samw 
2927052Samw static int
smb_opipe_sethdr(smb_opipe_t * opipe,uint32_t cmd,uint32_t datalen)29311963SAfshin.Ardakani@Sun.COM smb_opipe_sethdr(smb_opipe_t *opipe, uint32_t cmd, uint32_t datalen)
2947052Samw {
29511963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_op = cmd;
29611963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_txid = opipe->p_hdr.dh_fid;
29711963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_datalen = datalen;
29811963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_resid = 0;
29911963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_door_rc = EINVAL;
3007052Samw 
30111963SAfshin.Ardakani@Sun.COM 	return (smb_doorhdr_encode(&opipe->p_hdr, opipe->p_doorbuf,
3027052Samw 	    SMB_OPIPE_DOOR_BUFSIZE));
3037052Samw }
3047052Samw 
3057052Samw /*
3067052Samw  * smb_opipe_transact
3077052Samw  *
3087052Samw  * This is the entry point for RPC bind and request transactions.
3097052Samw  * The fid is an arbitrary id used to associate RPC requests with a
3107052Samw  * particular binding handle.
3117052Samw  *
3127052Samw  * If the data to be returned is larger than the client expects, we
3137052Samw  * return as much as the client can handle and report a buffer overflow
3147052Samw  * warning, which informs the client that we have more data to return.
3157052Samw  * The residual data remains in the pipe until the client claims it or
3167052Samw  * closes the pipe.
3177052Samw  */
3187052Samw smb_sdrc_t
smb_opipe_transact(smb_request_t * sr,struct uio * uio)3197052Samw smb_opipe_transact(smb_request_t *sr, struct uio *uio)
3207052Samw {
3217052Samw 	smb_xa_t *xa;
3227052Samw 	smb_opipe_t *opipe;
3237052Samw 	struct mbuf *mhead;
3247052Samw 	int mdrcnt;
3257052Samw 	int nbytes;
3267052Samw 	int rc;
3277052Samw 
3287052Samw 	if ((rc = smb_opipe_write(sr, uio)) != 0) {
3297052Samw 		if (rc == EBADF)
3307052Samw 			smbsr_error(sr, NT_STATUS_INVALID_HANDLE,
3317052Samw 			    ERRDOS, ERROR_INVALID_HANDLE);
3327052Samw 		else
3337052Samw 			smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
3347052Samw 			    ERRDOS, ERROR_INTERNAL_ERROR);
3357052Samw 		return (SDRC_ERROR);
3367052Samw 	}
3377052Samw 
33811963SAfshin.Ardakani@Sun.COM 	opipe = sr->fid_ofile->f_pipe;
33911963SAfshin.Ardakani@Sun.COM 
34011963SAfshin.Ardakani@Sun.COM 	if ((rc = smb_opipe_exec(opipe)) != 0) {
34111963SAfshin.Ardakani@Sun.COM 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
34211963SAfshin.Ardakani@Sun.COM 		    ERRDOS, ERROR_INTERNAL_ERROR);
34311963SAfshin.Ardakani@Sun.COM 		return (SDRC_ERROR);
34411963SAfshin.Ardakani@Sun.COM 	}
34511963SAfshin.Ardakani@Sun.COM 
3467052Samw 	xa = sr->r_xa;
3477052Samw 	mdrcnt = xa->smb_mdrcnt;
3487052Samw 	smb_opipe_enter(opipe);
3497052Samw 
35011963SAfshin.Ardakani@Sun.COM 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, mdrcnt) == -1) {
3517052Samw 		smb_opipe_exit(opipe);
3527052Samw 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
3537052Samw 		    ERRDOS, ERROR_INTERNAL_ERROR);
3547052Samw 		return (SDRC_ERROR);
3557052Samw 	}
3567052Samw 
3577052Samw 	rc = smb_opipe_door_call(opipe);
35811963SAfshin.Ardakani@Sun.COM 	nbytes = opipe->p_hdr.dh_datalen;
3597052Samw 
3607052Samw 	if (rc != 0) {
3617052Samw 		smb_opipe_exit(opipe);
3627052Samw 		smbsr_error(sr, NT_STATUS_INTERNAL_ERROR,
3637052Samw 		    ERRDOS, ERROR_INTERNAL_ERROR);
3647052Samw 		return (SDRC_ERROR);
3657052Samw 	}
3667052Samw 
3677052Samw 	if (nbytes) {
3687052Samw 		mhead = smb_mbuf_get(opipe->p_data, nbytes);
3697052Samw 		xa->rep_data_mb.max_bytes = nbytes;
3707052Samw 		MBC_ATTACH_MBUF(&xa->rep_data_mb, mhead);
3717052Samw 	}
3727052Samw 
37311963SAfshin.Ardakani@Sun.COM 	if (opipe->p_hdr.dh_resid) {
3747052Samw 		/*
3757052Samw 		 * The pipe contains more data than mdrcnt, warn the
3767052Samw 		 * client that there is more data in the pipe.
3777052Samw 		 * Typically, the client will call SmbReadX, which
3787052Samw 		 * will call smb_opipe_read, to get the data.
3797052Samw 		 */
3807052Samw 		smbsr_warn(sr, NT_STATUS_BUFFER_OVERFLOW,
3817052Samw 		    ERRDOS, ERROR_MORE_DATA);
3827052Samw 	}
3837052Samw 
3847052Samw 	smb_opipe_exit(opipe);
3857052Samw 	return (SDRC_SUCCESS);
3867052Samw }
3877052Samw 
3887052Samw /*
3897052Samw  * smb_opipe_write
3907052Samw  *
3917052Samw  * Write RPC request data to the pipe.  The client should call smb_opipe_read
3927052Samw  * to complete the exchange and obtain the RPC response.
3937052Samw  *
3947052Samw  * Returns 0 on success or an errno on failure.
3957052Samw  */
3967052Samw int
smb_opipe_write(smb_request_t * sr,struct uio * uio)3977052Samw smb_opipe_write(smb_request_t *sr, struct uio *uio)
3987052Samw {
3997052Samw 	smb_opipe_t *opipe;
4007052Samw 	uint32_t buflen;
4017052Samw 	uint32_t len;
4027052Samw 	int rc;
4037052Samw 
4047052Samw 	ASSERT(sr->fid_ofile);
4057052Samw 	ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
4067052Samw 
4077052Samw 	opipe = sr->fid_ofile->f_pipe;
40811963SAfshin.Ardakani@Sun.COM 	SMB_OPIPE_VALID(opipe);
4097052Samw 	smb_opipe_enter(opipe);
4107052Samw 
4117052Samw 	if (!SMB_OPIPE_ISOPEN(opipe)) {
4127052Samw 		smb_opipe_exit(opipe);
4137052Samw 		return (EBADF);
4147052Samw 	}
4157052Samw 
41611963SAfshin.Ardakani@Sun.COM 	rc = smb_opipe_sethdr(opipe, SMB_OPIPE_WRITE, uio->uio_resid);
41711963SAfshin.Ardakani@Sun.COM 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
4187052Samw 	if (rc == -1 || len == 0) {
4197052Samw 		smb_opipe_exit(opipe);
4207052Samw 		return (ENOMEM);
4217052Samw 	}
4227052Samw 
4237052Samw 	buflen = SMB_OPIPE_DOOR_BUFSIZE - len;
4247052Samw 	(void) uiomove((caddr_t)opipe->p_data, buflen, UIO_WRITE, uio);
4257052Samw 
4267052Samw 	rc = smb_opipe_door_call(opipe);
4277052Samw 
4287052Samw 	smb_opipe_exit(opipe);
4297052Samw 	return ((rc == 0) ? 0 : EIO);
4307052Samw }
4317052Samw 
4327052Samw /*
4337052Samw  * smb_opipe_read
4347052Samw  *
4357052Samw  * This interface may be called because smb_opipe_transact could not return
4367052Samw  * all of the data in the original transaction or to form the second half
4377052Samw  * of a transaction set up using smb_opipe_write.  Either way, we just need
4387052Samw  * to read data from the pipe and return it.
4397052Samw  *
4407052Samw  * The response data is encoded into raw_data as required by the smb_read
4417052Samw  * functions.  The uio_resid value indicates the number of bytes read.
4427052Samw  */
4437052Samw int
smb_opipe_read(smb_request_t * sr,struct uio * uio)4447052Samw smb_opipe_read(smb_request_t *sr, struct uio *uio)
4457052Samw {
4467052Samw 	smb_opipe_t *opipe;
4477052Samw 	struct mbuf *mhead;
4487052Samw 	uint32_t nbytes;
4497052Samw 	int rc;
4507052Samw 
4517052Samw 	ASSERT(sr->fid_ofile);
4527052Samw 	ASSERT(sr->fid_ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
4537052Samw 
4547052Samw 	opipe = sr->fid_ofile->f_pipe;
45511963SAfshin.Ardakani@Sun.COM 	SMB_OPIPE_VALID(opipe);
45611963SAfshin.Ardakani@Sun.COM 
45711963SAfshin.Ardakani@Sun.COM 	if ((rc = smb_opipe_exec(opipe)) != 0)
45811963SAfshin.Ardakani@Sun.COM 		return (EIO);
45911963SAfshin.Ardakani@Sun.COM 
4607052Samw 	smb_opipe_enter(opipe);
4617052Samw 
4627052Samw 	if (!SMB_OPIPE_ISOPEN(opipe)) {
4637052Samw 		smb_opipe_exit(opipe);
4647052Samw 		return (EBADF);
4657052Samw 	}
4667052Samw 
46711963SAfshin.Ardakani@Sun.COM 	if (smb_opipe_sethdr(opipe, SMB_OPIPE_READ, uio->uio_resid) == -1) {
4687052Samw 		smb_opipe_exit(opipe);
4697052Samw 		return (ENOMEM);
4707052Samw 	}
4717052Samw 
4727052Samw 	rc = smb_opipe_door_call(opipe);
47311963SAfshin.Ardakani@Sun.COM 	nbytes = opipe->p_hdr.dh_datalen;
4747052Samw 
4757052Samw 	if (rc != 0 || nbytes > uio->uio_resid) {
4767052Samw 		smb_opipe_exit(opipe);
4777052Samw 		return (EIO);
4787052Samw 	}
4797052Samw 
4807052Samw 	if (nbytes) {
4817052Samw 		mhead = smb_mbuf_get(opipe->p_data, nbytes);
4827052Samw 		MBC_SETUP(&sr->raw_data, nbytes);
4837052Samw 		MBC_ATTACH_MBUF(&sr->raw_data, mhead);
4847052Samw 		uio->uio_resid -= nbytes;
4857052Samw 	}
4867052Samw 
4877052Samw 	smb_opipe_exit(opipe);
4887052Samw 	return (rc);
4897052Samw }
4907052Samw 
49111963SAfshin.Ardakani@Sun.COM static int
smb_opipe_exec(smb_opipe_t * opipe)49211963SAfshin.Ardakani@Sun.COM smb_opipe_exec(smb_opipe_t *opipe)
49311963SAfshin.Ardakani@Sun.COM {
49411963SAfshin.Ardakani@Sun.COM 	uint32_t	len;
49511963SAfshin.Ardakani@Sun.COM 	int		rc;
49611963SAfshin.Ardakani@Sun.COM 
49711963SAfshin.Ardakani@Sun.COM 	smb_opipe_enter(opipe);
49811963SAfshin.Ardakani@Sun.COM 
49911963SAfshin.Ardakani@Sun.COM 	rc = smb_opipe_sethdr(opipe, SMB_OPIPE_EXEC, 0);
50011963SAfshin.Ardakani@Sun.COM 	len = xdr_sizeof(smb_doorhdr_xdr, &opipe->p_hdr);
50111963SAfshin.Ardakani@Sun.COM 	if (rc == -1 || len == 0) {
50211963SAfshin.Ardakani@Sun.COM 		smb_opipe_exit(opipe);
50311963SAfshin.Ardakani@Sun.COM 		return (ENOMEM);
50411963SAfshin.Ardakani@Sun.COM 	}
50511963SAfshin.Ardakani@Sun.COM 
50611963SAfshin.Ardakani@Sun.COM 	if ((rc = smb_opipe_door_call(opipe)) == 0)
50711963SAfshin.Ardakani@Sun.COM 		rc = smb_event_wait(opipe->p_event);
50811963SAfshin.Ardakani@Sun.COM 
50911963SAfshin.Ardakani@Sun.COM 	smb_opipe_exit(opipe);
51011963SAfshin.Ardakani@Sun.COM 	return (rc);
51111963SAfshin.Ardakani@Sun.COM }
51211963SAfshin.Ardakani@Sun.COM 
5137052Samw /*
5147052Samw  * Named pipe I/O is serialized per fid to ensure that each request
5157052Samw  * has exclusive opipe access for the duration of the request.
5167052Samw  */
5177052Samw static void
smb_opipe_enter(smb_opipe_t * opipe)5187052Samw smb_opipe_enter(smb_opipe_t *opipe)
5197052Samw {
5207052Samw 	mutex_enter(&opipe->p_mutex);
5217052Samw 
5227052Samw 	while (opipe->p_busy)
5237052Samw 		cv_wait(&opipe->p_cv, &opipe->p_mutex);
5247052Samw 
5257052Samw 	opipe->p_busy = 1;
5267052Samw 	mutex_exit(&opipe->p_mutex);
5277052Samw }
5287052Samw 
52911963SAfshin.Ardakani@Sun.COM /*
53011963SAfshin.Ardakani@Sun.COM  * Exit busy state.  If we have exec'd an RPC, we may have
53111963SAfshin.Ardakani@Sun.COM  * to wait for notification that processing has completed.
53211963SAfshin.Ardakani@Sun.COM  */
5337052Samw static void
smb_opipe_exit(smb_opipe_t * opipe)5347052Samw smb_opipe_exit(smb_opipe_t *opipe)
5357052Samw {
5367052Samw 	mutex_enter(&opipe->p_mutex);
5377052Samw 	opipe->p_busy = 0;
5387052Samw 	cv_signal(&opipe->p_cv);
5397052Samw 	mutex_exit(&opipe->p_mutex);
5407052Samw }
5417052Samw 
5427052Samw /*
5437052Samw  * opipe door client (to user space door server).
5447052Samw  */
5457052Samw void
smb_opipe_door_init(void)5467052Samw smb_opipe_door_init(void)
5477052Samw {
5487052Samw 	mutex_init(&smb_opipe_door_mutex, NULL, MUTEX_DEFAULT, NULL);
5497052Samw 	cv_init(&smb_opipe_door_cv, NULL, CV_DEFAULT, NULL);
5507052Samw }
5517052Samw 
5527052Samw void
smb_opipe_door_fini(void)5537052Samw smb_opipe_door_fini(void)
5547052Samw {
5557052Samw 	smb_opipe_door_close();
5567052Samw 	cv_destroy(&smb_opipe_door_cv);
5577052Samw 	mutex_destroy(&smb_opipe_door_mutex);
5587052Samw }
5597052Samw 
5607052Samw /*
5617052Samw  * Open the (user space) door.  If the door is already open,
5627052Samw  * close it first because the door-id has probably changed.
5637052Samw  */
5647052Samw int
smb_opipe_door_open(int door_id)5657052Samw smb_opipe_door_open(int door_id)
5667052Samw {
5677052Samw 	smb_opipe_door_close();
5687052Samw 
5697052Samw 	mutex_enter(&smb_opipe_door_mutex);
5707052Samw 	smb_opipe_door_ncall = 0;
5717052Samw 
5727052Samw 	if (smb_opipe_door_hd == NULL) {
5737052Samw 		smb_opipe_door_id = door_id;
5747052Samw 		smb_opipe_door_hd = door_ki_lookup(door_id);
5757052Samw 	}
5767052Samw 
5777052Samw 	mutex_exit(&smb_opipe_door_mutex);
5787052Samw 	return ((smb_opipe_door_hd == NULL)  ? -1 : 0);
5797052Samw }
5807052Samw 
5817052Samw /*
5827052Samw  * Close the (user space) door.
5837052Samw  */
5847052Samw void
smb_opipe_door_close(void)5857052Samw smb_opipe_door_close(void)
5867052Samw {
5877052Samw 	mutex_enter(&smb_opipe_door_mutex);
5887052Samw 
5897052Samw 	if (smb_opipe_door_hd != NULL) {
5907052Samw 		while (smb_opipe_door_ncall > 0)
5917052Samw 			cv_wait(&smb_opipe_door_cv, &smb_opipe_door_mutex);
5927052Samw 
5937052Samw 		door_ki_rele(smb_opipe_door_hd);
5947052Samw 		smb_opipe_door_hd = NULL;
5957052Samw 	}
5967052Samw 
5977052Samw 	mutex_exit(&smb_opipe_door_mutex);
5987052Samw }
5997052Samw 
6007052Samw /*
6017052Samw  * opipe door call interface.
6027052Samw  * Door serialization and call reference accounting is handled here.
6037052Samw  */
6047052Samw static int
smb_opipe_door_call(smb_opipe_t * opipe)6057052Samw smb_opipe_door_call(smb_opipe_t *opipe)
6067052Samw {
6077052Samw 	int rc;
6087052Samw 
6097052Samw 	mutex_enter(&smb_opipe_door_mutex);
6107052Samw 
6117052Samw 	if (smb_opipe_door_hd == NULL) {
6127052Samw 		mutex_exit(&smb_opipe_door_mutex);
6137052Samw 
6147052Samw 		if (smb_opipe_door_open(smb_opipe_door_id) != 0)
6157052Samw 			return (-1);
6167052Samw 
6177052Samw 		mutex_enter(&smb_opipe_door_mutex);
6187052Samw 	}
6197052Samw 
6207052Samw 	++smb_opipe_door_ncall;
6217052Samw 	mutex_exit(&smb_opipe_door_mutex);
6227052Samw 
6237052Samw 	rc = smb_opipe_door_upcall(opipe);
6247052Samw 
6257052Samw 	mutex_enter(&smb_opipe_door_mutex);
62611963SAfshin.Ardakani@Sun.COM 	if ((--smb_opipe_door_ncall) == 0)
62711963SAfshin.Ardakani@Sun.COM 		cv_signal(&smb_opipe_door_cv);
6287052Samw 	mutex_exit(&smb_opipe_door_mutex);
6297052Samw 	return (rc);
6307052Samw }
6317052Samw 
6327052Samw /*
6337052Samw  * Door upcall wrapper - handles data marshalling.
6347052Samw  * This function should only be called by smb_opipe_door_call.
6357052Samw  */
6367052Samw static int
smb_opipe_door_upcall(smb_opipe_t * opipe)6377052Samw smb_opipe_door_upcall(smb_opipe_t *opipe)
6387052Samw {
6397052Samw 	door_arg_t da;
64011963SAfshin.Ardakani@Sun.COM 	smb_doorhdr_t hdr;
6417052Samw 	int i;
6427052Samw 	int rc;
6437052Samw 
6447052Samw 	da.data_ptr = (char *)opipe->p_doorbuf;
6457052Samw 	da.data_size = SMB_OPIPE_DOOR_BUFSIZE;
6467052Samw 	da.desc_ptr = NULL;
6477052Samw 	da.desc_num = 0;
6487052Samw 	da.rbuf = (char *)opipe->p_doorbuf;
6497052Samw 	da.rsize = SMB_OPIPE_DOOR_BUFSIZE;
6507052Samw 
6517052Samw 	for (i = 0; i < 3; ++i) {
65211963SAfshin.Ardakani@Sun.COM 		if (smb_server_is_stopping())
65311963SAfshin.Ardakani@Sun.COM 			return (-1);
65411963SAfshin.Ardakani@Sun.COM 
6557052Samw 		if ((rc = door_ki_upcall_limited(smb_opipe_door_hd, &da,
6567052Samw 		    NULL, SIZE_MAX, 0)) == 0)
6577052Samw 			break;
6587052Samw 
6597052Samw 		if (rc != EAGAIN && rc != EINTR)
6607052Samw 			return (-1);
6617052Samw 	}
6627052Samw 
66311963SAfshin.Ardakani@Sun.COM 	/* Check for door_return(NULL, 0, NULL, 0) */
66411963SAfshin.Ardakani@Sun.COM 	if (rc != 0 || da.data_size == 0 || da.rsize == 0)
6657052Samw 		return (-1);
6667052Samw 
66711963SAfshin.Ardakani@Sun.COM 	if (smb_doorhdr_decode(&hdr, (uint8_t *)da.data_ptr, da.rsize) == -1)
66811963SAfshin.Ardakani@Sun.COM 		return (-1);
66911963SAfshin.Ardakani@Sun.COM 
67011963SAfshin.Ardakani@Sun.COM 	if ((hdr.dh_magic != SMB_OPIPE_HDR_MAGIC) ||
67111963SAfshin.Ardakani@Sun.COM 	    (hdr.dh_fid != opipe->p_hdr.dh_fid) ||
67211963SAfshin.Ardakani@Sun.COM 	    (hdr.dh_op != opipe->p_hdr.dh_op) ||
67311963SAfshin.Ardakani@Sun.COM 	    (hdr.dh_door_rc != 0) ||
67411963SAfshin.Ardakani@Sun.COM 	    (hdr.dh_datalen > SMB_OPIPE_DOOR_BUFSIZE)) {
6757052Samw 		return (-1);
6767052Samw 	}
6777052Samw 
67811963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_datalen = hdr.dh_datalen;
67911963SAfshin.Ardakani@Sun.COM 	opipe->p_hdr.dh_resid = hdr.dh_resid;
6807052Samw 	return (0);
6817052Samw }
682