xref: /onnv-gate/usr/src/lib/libsmbfs/smb/nb_ssn.c (revision 12140:3d7db9f327ef)
110023SGordon.Ross@Sun.COM /*
210023SGordon.Ross@Sun.COM  * CDDL HEADER START
310023SGordon.Ross@Sun.COM  *
410023SGordon.Ross@Sun.COM  * The contents of this file are subject to the terms of the
510023SGordon.Ross@Sun.COM  * Common Development and Distribution License (the "License").
610023SGordon.Ross@Sun.COM  * You may not use this file except in compliance with the License.
710023SGordon.Ross@Sun.COM  *
810023SGordon.Ross@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910023SGordon.Ross@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010023SGordon.Ross@Sun.COM  * See the License for the specific language governing permissions
1110023SGordon.Ross@Sun.COM  * and limitations under the License.
1210023SGordon.Ross@Sun.COM  *
1310023SGordon.Ross@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410023SGordon.Ross@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510023SGordon.Ross@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610023SGordon.Ross@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710023SGordon.Ross@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810023SGordon.Ross@Sun.COM  *
1910023SGordon.Ross@Sun.COM  * CDDL HEADER END
2010023SGordon.Ross@Sun.COM  */
2110023SGordon.Ross@Sun.COM 
2210023SGordon.Ross@Sun.COM /*
23*12140SGordon.Ross@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410023SGordon.Ross@Sun.COM  */
2510023SGordon.Ross@Sun.COM 
2610023SGordon.Ross@Sun.COM /*
2710023SGordon.Ross@Sun.COM  * NetBIOS session service functions
2810023SGordon.Ross@Sun.COM  */
2910023SGordon.Ross@Sun.COM 
3010023SGordon.Ross@Sun.COM #include <errno.h>
3110023SGordon.Ross@Sun.COM #include <stdio.h>
3210023SGordon.Ross@Sun.COM #include <stdlib.h>
3310023SGordon.Ross@Sun.COM #include <string.h>
3410023SGordon.Ross@Sun.COM #include <strings.h>
3510023SGordon.Ross@Sun.COM #include <libintl.h>
3610023SGordon.Ross@Sun.COM #include <xti.h>
3710023SGordon.Ross@Sun.COM #include <assert.h>
3810023SGordon.Ross@Sun.COM 
3910023SGordon.Ross@Sun.COM #include <sys/types.h>
4010023SGordon.Ross@Sun.COM #include <sys/socket.h>
4110023SGordon.Ross@Sun.COM #include <sys/poll.h>
4210023SGordon.Ross@Sun.COM 
4310023SGordon.Ross@Sun.COM #include <netsmb/netbios.h>
4410023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
4510023SGordon.Ross@Sun.COM #include <netsmb/nb_lib.h>
4610023SGordon.Ross@Sun.COM #include <netsmb/mchain.h>
4710023SGordon.Ross@Sun.COM 
4810023SGordon.Ross@Sun.COM #include "private.h"
4910023SGordon.Ross@Sun.COM #include "charsets.h"
5010023SGordon.Ross@Sun.COM 
5110023SGordon.Ross@Sun.COM static int nb_ssn_send(struct smb_ctx *, struct mbdata *, int, int);
5210023SGordon.Ross@Sun.COM static int nb_ssn_recv(struct smb_ctx *, struct mbdata *, int *, int *);
5310023SGordon.Ross@Sun.COM static int nb_ssn_pollin(struct smb_ctx *, int);
5410023SGordon.Ross@Sun.COM 
5510023SGordon.Ross@Sun.COM /*
5610023SGordon.Ross@Sun.COM  * Send a data message.
5710023SGordon.Ross@Sun.COM  */
5810023SGordon.Ross@Sun.COM int
smb_ssn_send(struct smb_ctx * ctx,struct mbdata * mbp)5910023SGordon.Ross@Sun.COM smb_ssn_send(struct smb_ctx *ctx, struct mbdata *mbp)
6010023SGordon.Ross@Sun.COM {
6110023SGordon.Ross@Sun.COM 	return (nb_ssn_send(ctx, mbp, 0, mbp->mb_count));
6210023SGordon.Ross@Sun.COM }
6310023SGordon.Ross@Sun.COM 
6410023SGordon.Ross@Sun.COM /*
6510023SGordon.Ross@Sun.COM  * Send a NetBIOS message, after
6610023SGordon.Ross@Sun.COM  * prepending the 4-byte header.
6710023SGordon.Ross@Sun.COM  */
6810023SGordon.Ross@Sun.COM static int
nb_ssn_send(struct smb_ctx * ctx,struct mbdata * mbp,int mtype,int mlen)6910023SGordon.Ross@Sun.COM nb_ssn_send(struct smb_ctx *ctx, struct mbdata *mbp,
7010023SGordon.Ross@Sun.COM 	    int mtype, int mlen)
7110023SGordon.Ross@Sun.COM {
72*12140SGordon.Ross@Sun.COM 	mbuf_t *m;
7310023SGordon.Ross@Sun.COM 	uint32_t hdr, hdrbuf;
74*12140SGordon.Ross@Sun.COM 	int err;
7510023SGordon.Ross@Sun.COM 
76*12140SGordon.Ross@Sun.COM 	m = mbp->mb_top;
7710023SGordon.Ross@Sun.COM 	if (m == NULL)
7810023SGordon.Ross@Sun.COM 		return (EINVAL);
7910023SGordon.Ross@Sun.COM 
8010023SGordon.Ross@Sun.COM 	/*
8110023SGordon.Ross@Sun.COM 	 * Prepend the NetBIOS header.
82*12140SGordon.Ross@Sun.COM 	 * Our mbufs leave space for this.
8310023SGordon.Ross@Sun.COM 	 */
8410023SGordon.Ross@Sun.COM 	hdr = (mtype << 24) | mlen;
8510023SGordon.Ross@Sun.COM 	hdrbuf = htonl(hdr);
8610023SGordon.Ross@Sun.COM 	m->m_data -= 4;
8710023SGordon.Ross@Sun.COM 	m->m_len  += 4;
8810023SGordon.Ross@Sun.COM 	bcopy(&hdrbuf, m->m_data, 4);
8910023SGordon.Ross@Sun.COM 
90*12140SGordon.Ross@Sun.COM 	/*
91*12140SGordon.Ross@Sun.COM 	 * Get contiguous data (so TCP won't fragment)
92*12140SGordon.Ross@Sun.COM 	 * Note: replaces mb_top.
93*12140SGordon.Ross@Sun.COM 	 */
94*12140SGordon.Ross@Sun.COM 	err = m_lineup(mbp->mb_top, &mbp->mb_top);
95*12140SGordon.Ross@Sun.COM 	if (err)
96*12140SGordon.Ross@Sun.COM 		return (err);
97*12140SGordon.Ross@Sun.COM 	m = mbp->mb_top;
98*12140SGordon.Ross@Sun.COM 
99*12140SGordon.Ross@Sun.COM 	/*
100*12140SGordon.Ross@Sun.COM 	 * Send it.
101*12140SGordon.Ross@Sun.COM 	 */
102*12140SGordon.Ross@Sun.COM 	if (t_snd(ctx->ct_tran_fd, m->m_data, m->m_len, 0) < 0) {
103*12140SGordon.Ross@Sun.COM 		if (t_errno == TSYSERR)
104*12140SGordon.Ross@Sun.COM 			err = errno;
105*12140SGordon.Ross@Sun.COM 		else
106*12140SGordon.Ross@Sun.COM 			err = EPROTO;
107*12140SGordon.Ross@Sun.COM 		DPRINT("t_snd: t_errno %d, err %d", t_errno, err);
108*12140SGordon.Ross@Sun.COM 		return (err);
10910023SGordon.Ross@Sun.COM 	}
110*12140SGordon.Ross@Sun.COM 
11110023SGordon.Ross@Sun.COM 	return (0);
11210023SGordon.Ross@Sun.COM }
11310023SGordon.Ross@Sun.COM 
11410023SGordon.Ross@Sun.COM /*
11510023SGordon.Ross@Sun.COM  * Receive a data message.  Discard anything else.
11610023SGordon.Ross@Sun.COM  * Caller must deal with EAGAIN, EINTR.
11710023SGordon.Ross@Sun.COM  */
11810023SGordon.Ross@Sun.COM int
smb_ssn_recv(struct smb_ctx * ctx,struct mbdata * mbp)11910023SGordon.Ross@Sun.COM smb_ssn_recv(struct smb_ctx *ctx, struct mbdata *mbp)
12010023SGordon.Ross@Sun.COM {
12110023SGordon.Ross@Sun.COM 	int err, mtype, mlen;
12210023SGordon.Ross@Sun.COM 	err = nb_ssn_recv(ctx, mbp, &mtype, &mlen);
12310023SGordon.Ross@Sun.COM 	if (err)
12410023SGordon.Ross@Sun.COM 		return (err);
12510023SGordon.Ross@Sun.COM 	if (mtype != NB_SSN_MESSAGE) {
12610023SGordon.Ross@Sun.COM 		DPRINT("discard type 0x%x", mtype);
12710023SGordon.Ross@Sun.COM 		mb_done(mbp);
12810023SGordon.Ross@Sun.COM 		return (EAGAIN);
12910023SGordon.Ross@Sun.COM 	}
13010023SGordon.Ross@Sun.COM 	if (mlen == 0) {
13110023SGordon.Ross@Sun.COM 		DPRINT("zero length");
13210023SGordon.Ross@Sun.COM 		mb_done(mbp);
13310023SGordon.Ross@Sun.COM 		return (EAGAIN);
13410023SGordon.Ross@Sun.COM 	}
13510023SGordon.Ross@Sun.COM 
13610023SGordon.Ross@Sun.COM 	return (0);
13710023SGordon.Ross@Sun.COM }
13810023SGordon.Ross@Sun.COM 
13910023SGordon.Ross@Sun.COM /*
14010023SGordon.Ross@Sun.COM  * Receive a NetBIOS message, any type.
14110023SGordon.Ross@Sun.COM  * Give caller type and length.
14210023SGordon.Ross@Sun.COM  */
14310023SGordon.Ross@Sun.COM static int
nb_ssn_recv(struct smb_ctx * ctx,struct mbdata * mb,int * mtype,int * mlen)14410023SGordon.Ross@Sun.COM nb_ssn_recv(struct smb_ctx *ctx, struct mbdata *mb,
14510023SGordon.Ross@Sun.COM 	    int *mtype, int *mlen)
14610023SGordon.Ross@Sun.COM {
14710023SGordon.Ross@Sun.COM 	char *buf;
14810023SGordon.Ross@Sun.COM 	uint32_t hdr, hdrbuf;
14910023SGordon.Ross@Sun.COM 	int cnt, len, err, moreflag;
15010023SGordon.Ross@Sun.COM 	int fd = ctx->ct_tran_fd;
15110023SGordon.Ross@Sun.COM 	int tmo = smb_recv_timeout * 1000;
15210023SGordon.Ross@Sun.COM 
15310023SGordon.Ross@Sun.COM 	/*
15410023SGordon.Ross@Sun.COM 	 * Start by getting the header
15510023SGordon.Ross@Sun.COM 	 * (four bytes)
15610023SGordon.Ross@Sun.COM 	 */
15710023SGordon.Ross@Sun.COM 	if ((err = nb_ssn_pollin(ctx, tmo)) != 0) {
15810023SGordon.Ross@Sun.COM 		DPRINT("pollin err %d", err);
15910023SGordon.Ross@Sun.COM 		return (err);
16010023SGordon.Ross@Sun.COM 	}
16110023SGordon.Ross@Sun.COM 	moreflag = 0;
16210023SGordon.Ross@Sun.COM 	cnt = t_rcv(fd, &hdrbuf, sizeof (hdrbuf), &moreflag);
16310023SGordon.Ross@Sun.COM 	if (cnt < 0) {
16410023SGordon.Ross@Sun.COM 		err = get_xti_err(fd);
16510023SGordon.Ross@Sun.COM 		DPRINT("t_errno %d err %d", t_errno, err);
16610023SGordon.Ross@Sun.COM 		return (err);
16710023SGordon.Ross@Sun.COM 	}
16810023SGordon.Ross@Sun.COM 
16910023SGordon.Ross@Sun.COM 	if (cnt != sizeof (hdrbuf)) {
17010023SGordon.Ross@Sun.COM 		DPRINT("hdr cnt %d", cnt);
17110023SGordon.Ross@Sun.COM 		return (EPROTO);
17210023SGordon.Ross@Sun.COM 	}
17310023SGordon.Ross@Sun.COM 
17410023SGordon.Ross@Sun.COM 	/*
17510023SGordon.Ross@Sun.COM 	 * Decode the header, get the length.
17610023SGordon.Ross@Sun.COM 	 */
17710023SGordon.Ross@Sun.COM 	hdr = ntohl(hdrbuf);
17810023SGordon.Ross@Sun.COM 	*mtype = (hdr >> 24) & 0xff;
17910023SGordon.Ross@Sun.COM 	*mlen = hdr & 0xffffff;
18010023SGordon.Ross@Sun.COM 
18110023SGordon.Ross@Sun.COM 	if (mlen == 0)
18210023SGordon.Ross@Sun.COM 		return (0);
18310023SGordon.Ross@Sun.COM 
18410023SGordon.Ross@Sun.COM 	/*
18510023SGordon.Ross@Sun.COM 	 * Get a message buffer, read the payload
18610023SGordon.Ross@Sun.COM 	 */
18711332SGordon.Ross@Sun.COM 	if ((err = mb_init_sz(mb, *mlen)) != 0)
18810023SGordon.Ross@Sun.COM 		return (err);
18910023SGordon.Ross@Sun.COM 	buf = mb->mb_top->m_data;
19010023SGordon.Ross@Sun.COM 	len = *mlen;
19110023SGordon.Ross@Sun.COM 	while (len > 0) {
19210023SGordon.Ross@Sun.COM 		if (!moreflag) {
19310023SGordon.Ross@Sun.COM 			if ((err = nb_ssn_pollin(ctx, tmo)) != 0) {
19410023SGordon.Ross@Sun.COM 				DPRINT("pollin err %d", err);
19510023SGordon.Ross@Sun.COM 				return (err);
19610023SGordon.Ross@Sun.COM 			}
19710023SGordon.Ross@Sun.COM 		}
19810023SGordon.Ross@Sun.COM 
19910023SGordon.Ross@Sun.COM 		moreflag = 0;
20010023SGordon.Ross@Sun.COM 		cnt = t_rcv(fd, buf, len, &moreflag);
20110023SGordon.Ross@Sun.COM 		if (cnt < 0) {
20210023SGordon.Ross@Sun.COM 			err = get_xti_err(fd);
20310023SGordon.Ross@Sun.COM 			DPRINT("t_errno %d err %d", t_errno, err);
20410023SGordon.Ross@Sun.COM 			return (err);
20510023SGordon.Ross@Sun.COM 		}
20610023SGordon.Ross@Sun.COM 		buf += cnt;
20710023SGordon.Ross@Sun.COM 		len -= cnt;
20810023SGordon.Ross@Sun.COM 	}
20910023SGordon.Ross@Sun.COM 	mb->mb_top->m_len = *mlen;
21010023SGordon.Ross@Sun.COM 	mb->mb_count = *mlen;
21110023SGordon.Ross@Sun.COM 
21210023SGordon.Ross@Sun.COM 	return (0);
21310023SGordon.Ross@Sun.COM }
21410023SGordon.Ross@Sun.COM 
21510023SGordon.Ross@Sun.COM int
get_xti_err(int fd)21610023SGordon.Ross@Sun.COM get_xti_err(int fd)
21710023SGordon.Ross@Sun.COM {
21810023SGordon.Ross@Sun.COM 	int look;
21910023SGordon.Ross@Sun.COM 	if (t_errno == TSYSERR)
22010023SGordon.Ross@Sun.COM 		return (errno);
22110023SGordon.Ross@Sun.COM 
22210023SGordon.Ross@Sun.COM 	if (t_errno == TLOOK) {
22310023SGordon.Ross@Sun.COM 		look = t_look(fd);
22410023SGordon.Ross@Sun.COM 		switch (look) {
22510023SGordon.Ross@Sun.COM 		case T_DISCONNECT:
22610023SGordon.Ross@Sun.COM 			(void) t_rcvdis(fd, NULL);
22710023SGordon.Ross@Sun.COM 			(void) t_snddis(fd, NULL);
22810023SGordon.Ross@Sun.COM 			return (ECONNRESET);
22910023SGordon.Ross@Sun.COM 		case T_ORDREL:
23010023SGordon.Ross@Sun.COM 			/* Received orderly release indication */
23110023SGordon.Ross@Sun.COM 			(void) t_rcvrel(fd);
23210023SGordon.Ross@Sun.COM 			/* Send orderly release indicator */
23310023SGordon.Ross@Sun.COM 			(void) t_sndrel(fd);
23410023SGordon.Ross@Sun.COM 			return (ECONNRESET);
23510023SGordon.Ross@Sun.COM 		}
23610023SGordon.Ross@Sun.COM 	}
23710023SGordon.Ross@Sun.COM 	return (EPROTO);
23810023SGordon.Ross@Sun.COM }
23910023SGordon.Ross@Sun.COM 
24010023SGordon.Ross@Sun.COM /*
24110023SGordon.Ross@Sun.COM  * Wait for data we can receive.
24210023SGordon.Ross@Sun.COM  * Timeout is mSec., as for poll(2)
24310023SGordon.Ross@Sun.COM  */
24410023SGordon.Ross@Sun.COM static int
nb_ssn_pollin(struct smb_ctx * ctx,int tmo)24510023SGordon.Ross@Sun.COM nb_ssn_pollin(struct smb_ctx *ctx, int tmo)
24610023SGordon.Ross@Sun.COM {
24710023SGordon.Ross@Sun.COM 	struct pollfd pfd[1];
24810023SGordon.Ross@Sun.COM 	int cnt, err;
24910023SGordon.Ross@Sun.COM 
25010023SGordon.Ross@Sun.COM 	pfd[0].fd = ctx->ct_tran_fd;
25110023SGordon.Ross@Sun.COM 	pfd[0].events = POLLIN | POLLPRI;
25210023SGordon.Ross@Sun.COM 	pfd[0].revents = 0;
25310023SGordon.Ross@Sun.COM 	cnt = poll(pfd, 1, tmo);
25410023SGordon.Ross@Sun.COM 	switch (cnt) {
25510023SGordon.Ross@Sun.COM 	case 0:
25610023SGordon.Ross@Sun.COM 		err = ETIME;
25710023SGordon.Ross@Sun.COM 		break;
25810023SGordon.Ross@Sun.COM 	case -1:
25910023SGordon.Ross@Sun.COM 		err = errno;
26010023SGordon.Ross@Sun.COM 		break;
26110023SGordon.Ross@Sun.COM 	default:
26210023SGordon.Ross@Sun.COM 		err = 0;
26310023SGordon.Ross@Sun.COM 		break;
26410023SGordon.Ross@Sun.COM 	}
26510023SGordon.Ross@Sun.COM 	return (err);
26610023SGordon.Ross@Sun.COM }
26710023SGordon.Ross@Sun.COM 
26810023SGordon.Ross@Sun.COM /*
26910023SGordon.Ross@Sun.COM  * Send a NetBIOS session request and
27010023SGordon.Ross@Sun.COM  * wait for the response.
27110023SGordon.Ross@Sun.COM  */
27210023SGordon.Ross@Sun.COM int
nb_ssn_request(struct smb_ctx * ctx,char * srvname)27310023SGordon.Ross@Sun.COM nb_ssn_request(struct smb_ctx *ctx, char *srvname)
27410023SGordon.Ross@Sun.COM {
27510023SGordon.Ross@Sun.COM 	struct mbdata req, res;
27610023SGordon.Ross@Sun.COM 	struct nb_name lcl, srv;
27710023SGordon.Ross@Sun.COM 	int err, mtype, mlen;
27810023SGordon.Ross@Sun.COM 	char *ucwks;
27910023SGordon.Ross@Sun.COM 
28010023SGordon.Ross@Sun.COM 	bzero(&req, sizeof (req));
28110023SGordon.Ross@Sun.COM 	bzero(&res, sizeof (res));
28210023SGordon.Ross@Sun.COM 
28311332SGordon.Ross@Sun.COM 	if ((err = mb_init(&req)) != 0)
28410023SGordon.Ross@Sun.COM 		goto errout;
28510023SGordon.Ross@Sun.COM 
28610023SGordon.Ross@Sun.COM 	ucwks = utf8_str_toupper(ctx->ct_locname);
28710023SGordon.Ross@Sun.COM 	if (ucwks == NULL) {
28810023SGordon.Ross@Sun.COM 		err = ENOMEM;
28910023SGordon.Ross@Sun.COM 		goto errout;
29010023SGordon.Ross@Sun.COM 	}
29110023SGordon.Ross@Sun.COM 
29210023SGordon.Ross@Sun.COM 	/* Local NB name. */
29310023SGordon.Ross@Sun.COM 	snprintf(lcl.nn_name, NB_NAMELEN, "%-15.15s", ucwks);
29410023SGordon.Ross@Sun.COM 	lcl.nn_type = NBT_WKSTA;
29510023SGordon.Ross@Sun.COM 	lcl.nn_scope = ctx->ct_nb->nb_scope;
29610023SGordon.Ross@Sun.COM 
29710023SGordon.Ross@Sun.COM 	/* Server NB name */
29810023SGordon.Ross@Sun.COM 	snprintf(srv.nn_name, NB_NAMELEN, "%-15.15s", srvname);
29910023SGordon.Ross@Sun.COM 	srv.nn_type = NBT_SERVER;
30010023SGordon.Ross@Sun.COM 	srv.nn_scope = ctx->ct_nb->nb_scope;
30110023SGordon.Ross@Sun.COM 
30210023SGordon.Ross@Sun.COM 	/*
30310023SGordon.Ross@Sun.COM 	 * Build the request.  Header is prepended later.
30410023SGordon.Ross@Sun.COM 	 */
30510023SGordon.Ross@Sun.COM 	if ((err = nb_name_encode(&req, &srv)) != 0)
30610023SGordon.Ross@Sun.COM 		goto errout;
30710023SGordon.Ross@Sun.COM 	if ((err = nb_name_encode(&req, &lcl)) != 0)
30810023SGordon.Ross@Sun.COM 		goto errout;
30910023SGordon.Ross@Sun.COM 
31010023SGordon.Ross@Sun.COM 	/*
31110023SGordon.Ross@Sun.COM 	 * Send it, wait for the reply.
31210023SGordon.Ross@Sun.COM 	 */
31310023SGordon.Ross@Sun.COM 	err = nb_ssn_send(ctx, &req, NB_SSN_REQUEST, req.mb_count);
31410023SGordon.Ross@Sun.COM 	if (err) {
31510023SGordon.Ross@Sun.COM 		DPRINT("send, err %d", err);
31610023SGordon.Ross@Sun.COM 		goto errout;
31710023SGordon.Ross@Sun.COM 	}
31810023SGordon.Ross@Sun.COM 	err = nb_ssn_recv(ctx, &res, &mtype, &mlen);
31910023SGordon.Ross@Sun.COM 	if (err) {
32010023SGordon.Ross@Sun.COM 		DPRINT("recv, err %d", err);
32110023SGordon.Ross@Sun.COM 		goto errout;
32210023SGordon.Ross@Sun.COM 	}
32310023SGordon.Ross@Sun.COM 
32410023SGordon.Ross@Sun.COM 	if (mtype != NB_SSN_POSRESP) {
32510023SGordon.Ross@Sun.COM 		DPRINT("recv, mtype 0x%x", mtype);
32610023SGordon.Ross@Sun.COM 		err = ECONNREFUSED;
32710023SGordon.Ross@Sun.COM 		goto errout;
32810023SGordon.Ross@Sun.COM 	}
32910023SGordon.Ross@Sun.COM 
33010023SGordon.Ross@Sun.COM 	return (0);
33110023SGordon.Ross@Sun.COM 
33210023SGordon.Ross@Sun.COM errout:
33310023SGordon.Ross@Sun.COM 	mb_done(&res);
33410023SGordon.Ross@Sun.COM 	mb_done(&req);
33510023SGordon.Ross@Sun.COM 	return (err);
33610023SGordon.Ross@Sun.COM }
337