xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_opipe.c (revision bde334a8dbd66dfa70ce4d7fc9dcad6e1ae45fe4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * This module provides the interface to NDR RPC.
28  */
29 
30 #include <sys/stat.h>
31 #include <sys/uio.h>
32 #include <sys/ksynch.h>
33 #include <sys/stropts.h>
34 #include <sys/socket.h>
35 #include <sys/filio.h>
36 #include <smbsrv/smb_kproto.h>
37 #include <smbsrv/smb_xdr.h>
38 #include <smb/winioctl.h>
39 
40 static uint32_t smb_opipe_transceive(smb_request_t *, smb_fsctl_t *);
41 
42 /*
43  * Allocate a new opipe and return it, or NULL, in which case
44  * the caller will report "internal error".
45  */
46 static smb_opipe_t *
47 smb_opipe_alloc(smb_request_t *sr)
48 {
49 	smb_server_t	*sv = sr->sr_server;
50 	smb_opipe_t	*opipe;
51 	ksocket_t	sock;
52 
53 	if (ksocket_socket(&sock, AF_UNIX, SOCK_STREAM, 0,
54 	    KSOCKET_SLEEP, sr->user_cr) != 0)
55 		return (NULL);
56 
57 	opipe = kmem_cache_alloc(smb_cache_opipe, KM_SLEEP);
58 
59 	bzero(opipe, sizeof (smb_opipe_t));
60 	mutex_init(&opipe->p_mutex, NULL, MUTEX_DEFAULT, NULL);
61 	cv_init(&opipe->p_cv, NULL, CV_DEFAULT, NULL);
62 	opipe->p_magic = SMB_OPIPE_MAGIC;
63 	opipe->p_server = sv;
64 	opipe->p_refcnt = 1;
65 	opipe->p_socket = sock;
66 
67 	return (opipe);
68 }
69 
70 /*
71  * Destroy an opipe.  This is normally called from smb_ofile_delete
72  * when the ofile has no more references and is about to be free'd.
73  * This is also called here in error handling code paths, before
74  * the opipe is installed under an ofile.
75  */
76 void
77 smb_opipe_dealloc(smb_opipe_t *opipe)
78 {
79 	smb_server_t *sv;
80 
81 	SMB_OPIPE_VALID(opipe);
82 	sv = opipe->p_server;
83 	SMB_SERVER_VALID(sv);
84 
85 	/*
86 	 * This is called in the error path when opening,
87 	 * in which case we close the socket here.
88 	 */
89 	if (opipe->p_socket != NULL)
90 		(void) ksocket_close(opipe->p_socket, zone_kcred());
91 
92 	opipe->p_magic = (uint32_t)~SMB_OPIPE_MAGIC;
93 	cv_destroy(&opipe->p_cv);
94 	mutex_destroy(&opipe->p_mutex);
95 
96 	kmem_cache_free(smb_cache_opipe, opipe);
97 }
98 
99 /*
100  * Unblock a request that might be blocked reading some
101  * pipe (AF_UNIX socket).  We don't have an easy way to
102  * interrupt just the thread servicing this request, so
103  * we shutdown(3socket) the socket, waking all readers.
104  * That's a bit heavy-handed, making the socket unusable
105  * after this, so we do this only when disconnecting a
106  * session (i.e. stopping the SMB service), and not when
107  * handling an SMB2_cancel or SMB_nt_cancel request.
108  */
109 static void
110 smb_opipe_cancel(smb_request_t *sr)
111 {
112 	ksocket_t so;
113 
114 	switch (sr->session->s_state) {
115 	case SMB_SESSION_STATE_DISCONNECTED:
116 	case SMB_SESSION_STATE_TERMINATED:
117 		if ((so = sr->cancel_arg2) != NULL)
118 			(void) ksocket_shutdown(so, SHUT_RDWR, sr->user_cr);
119 		break;
120 	}
121 }
122 
123 /*
124  * Helper for open: build pipe name and connect.
125  */
126 static int
127 smb_opipe_connect(smb_request_t *sr, smb_opipe_t *opipe)
128 {
129 	struct sockaddr_un saddr;
130 	smb_arg_open_t	*op = &sr->sr_open;
131 	const char *name;
132 	int rc;
133 
134 	name = op->fqi.fq_path.pn_path;
135 	name += strspn(name, "\\");
136 	if (smb_strcasecmp(name, "PIPE", 4) == 0) {
137 		name += 4;
138 		name += strspn(name, "\\");
139 	}
140 	(void) strlcpy(opipe->p_name, name, SMB_OPIPE_MAXNAME);
141 	(void) smb_strlwr(opipe->p_name);
142 
143 	bzero(&saddr, sizeof (saddr));
144 	saddr.sun_family = AF_UNIX;
145 	(void) snprintf(saddr.sun_path, sizeof (saddr.sun_path),
146 	    "%s/%s", SMB_PIPE_DIR, opipe->p_name);
147 	rc = ksocket_connect(opipe->p_socket, (struct sockaddr *)&saddr,
148 	    sizeof (saddr), sr->user_cr);
149 
150 	return (rc);
151 }
152 
153 /*
154  * Helper for open: encode and send the user info.
155  *
156  * We send information about this client + user to the
157  * pipe service so it can use it for access checks.
158  * The service MAY deny the open based on this info,
159  * (i.e. anonymous session trying to open a pipe that
160  * requires authentication) in which case we will read
161  * an error status from the service and return that.
162  */
163 static void
164 smb_opipe_send_userinfo(smb_request_t *sr, smb_opipe_t *opipe,
165     smb_error_t *errp)
166 {
167 	XDR xdrs;
168 	smb_netuserinfo_t nui;
169 	smb_pipehdr_t phdr;
170 	char *buf;
171 	uint32_t buflen;
172 	uint32_t status;
173 	size_t iocnt = 0;
174 	int rc;
175 
176 	/*
177 	 * Any errors building the XDR message etc.
178 	 */
179 	errp->status = NT_STATUS_INTERNAL_ERROR;
180 
181 	smb_user_netinfo_init(sr->uid_user, &nui);
182 	phdr.ph_magic = SMB_PIPE_HDR_MAGIC;
183 	phdr.ph_uilen = xdr_sizeof(smb_netuserinfo_xdr, &nui);
184 
185 	buflen = sizeof (phdr) + phdr.ph_uilen;
186 	buf = kmem_alloc(buflen, KM_SLEEP);
187 
188 	bcopy(&phdr, buf, sizeof (phdr));
189 	xdrmem_create(&xdrs, buf + sizeof (phdr),
190 	    buflen - (sizeof (phdr)), XDR_ENCODE);
191 	if (!smb_netuserinfo_xdr(&xdrs, &nui))
192 		goto out;
193 
194 	mutex_enter(&sr->sr_mutex);
195 	if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
196 		mutex_exit(&sr->sr_mutex);
197 		errp->status = NT_STATUS_CANCELLED;
198 		goto out;
199 	}
200 	sr->sr_state = SMB_REQ_STATE_WAITING_PIPE;
201 	sr->cancel_method = smb_opipe_cancel;
202 	sr->cancel_arg2 = opipe->p_socket;
203 	mutex_exit(&sr->sr_mutex);
204 
205 	rc = ksocket_send(opipe->p_socket, buf, buflen, 0,
206 	    &iocnt, sr->user_cr);
207 	if (rc == 0 && iocnt != buflen)
208 		rc = EIO;
209 	if (rc == 0)
210 		rc = ksocket_recv(opipe->p_socket, &status, sizeof (status),
211 		    0, &iocnt, sr->user_cr);
212 	if (rc == 0 && iocnt != sizeof (status))
213 		rc = EIO;
214 
215 	mutex_enter(&sr->sr_mutex);
216 	sr->cancel_method = NULL;
217 	sr->cancel_arg2 = NULL;
218 	switch (sr->sr_state) {
219 	case SMB_REQ_STATE_WAITING_PIPE:
220 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
221 		break;
222 	case SMB_REQ_STATE_CANCEL_PENDING:
223 		sr->sr_state = SMB_REQ_STATE_CANCELLED;
224 		rc = EINTR;
225 		break;
226 	default:
227 		/* keep rc from above */
228 		break;
229 	}
230 	mutex_exit(&sr->sr_mutex);
231 
232 
233 	/*
234 	 * Return the status we read from the pipe service,
235 	 * normally NT_STATUS_SUCCESS, but could be something
236 	 * else like NT_STATUS_ACCESS_DENIED.
237 	 */
238 	switch (rc) {
239 	case 0:
240 		errp->status = status;
241 		break;
242 	case EINTR:
243 		errp->status = NT_STATUS_CANCELLED;
244 		break;
245 	/*
246 	 * If we fail sending the netuserinfo or recv'ing the
247 	 * status reponse, we have probably run into the limit
248 	 * on the number of open pipes.  That's this status:
249 	 */
250 	default:
251 		errp->status = NT_STATUS_PIPE_NOT_AVAILABLE;
252 		break;
253 	}
254 
255 out:
256 	xdr_destroy(&xdrs);
257 	kmem_free(buf, buflen);
258 	smb_user_netinfo_fini(&nui);
259 }
260 
261 /*
262  * smb_opipe_open
263  *
264  * Open an RPC named pipe. This routine should be called if
265  * a file open is requested on a share of type STYPE_IPC.
266  * If we recognize the pipe, we setup a new ofile.
267  *
268  * Returns 0 on success, Otherwise an NT status code.
269  */
270 int
271 smb_opipe_open(smb_request_t *sr, smb_ofile_t *ofile)
272 {
273 	smb_arg_open_t	*op = &sr->sr_open;
274 	smb_attr_t *ap = &op->fqi.fq_fattr;
275 	smb_opipe_t *opipe;
276 	smb_error_t err;
277 
278 	opipe = smb_opipe_alloc(sr);
279 	if (opipe == NULL)
280 		return (NT_STATUS_INTERNAL_ERROR);
281 
282 	if (smb_opipe_connect(sr, opipe) != 0) {
283 		smb_opipe_dealloc(opipe);
284 		return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
285 	}
286 
287 	smb_opipe_send_userinfo(sr, opipe, &err);
288 	if (err.status != 0) {
289 		smb_opipe_dealloc(opipe);
290 		return (err.status);
291 	}
292 
293 	/*
294 	 * We might have blocked in smb_opipe_connect long enough so
295 	 * a tree disconnect might have happened.  In that case, we
296 	 * would be adding an ofile to a tree that's disconnecting,
297 	 * which would interfere with tear-down.
298 	 */
299 	if (!smb_tree_is_connected(sr->tid_tree)) {
300 		smb_opipe_dealloc(opipe);
301 		return (NT_STATUS_NETWORK_NAME_DELETED);
302 	}
303 
304 	/*
305 	 * Note: The new opipe is given to smb_ofile_open
306 	 * via op->pipe
307 	 */
308 	op->pipe = opipe;
309 	smb_ofile_open(sr, op, ofile);
310 	op->pipe = NULL;
311 
312 	/* An "up" pointer, for debug. */
313 	opipe->p_ofile = ofile;
314 
315 	/*
316 	 * Caller expects attributes in op->fqi
317 	 */
318 	(void) smb_opipe_getattr(ofile, &op->fqi.fq_fattr);
319 
320 	op->dsize = 0;
321 	op->dattr = ap->sa_dosattr;
322 	op->fileid = ap->sa_vattr.va_nodeid;
323 	op->ftype = SMB_FTYPE_MESG_PIPE;
324 	op->action_taken = SMB_OACT_OPLOCK | SMB_OACT_OPENED;
325 	op->devstate = SMB_PIPE_READMODE_MESSAGE
326 	    | SMB_PIPE_TYPE_MESSAGE
327 	    | SMB_PIPE_UNLIMITED_INSTANCES; /* 0x05ff */
328 
329 	sr->smb_fid = ofile->f_fid;
330 	sr->fid_ofile = ofile;
331 
332 	return (NT_STATUS_SUCCESS);
333 }
334 
335 /*
336  * smb_opipe_close
337  *
338  * Called by smb_ofile_close for pipes.
339  *
340  * Note: ksocket_close may block while waiting for
341  * any I/O threads with a hold to get out.
342  */
343 void
344 smb_opipe_close(smb_ofile_t *of)
345 {
346 	smb_opipe_t *opipe;
347 	ksocket_t sock;
348 
349 	ASSERT(of->f_state == SMB_OFILE_STATE_CLOSING);
350 	ASSERT(of->f_ftype == SMB_FTYPE_MESG_PIPE);
351 	opipe = of->f_pipe;
352 	SMB_OPIPE_VALID(opipe);
353 
354 	mutex_enter(&opipe->p_mutex);
355 	sock = opipe->p_socket;
356 	opipe->p_socket = NULL;
357 	mutex_exit(&opipe->p_mutex);
358 
359 	(void) ksocket_shutdown(sock, SHUT_RDWR, of->f_cr);
360 	(void) ksocket_close(sock, of->f_cr);
361 }
362 
363 /*
364  * smb_opipe_write
365  *
366  * Write RPC request data to the pipe.  The client should call smb_opipe_read
367  * to complete the exchange and obtain the RPC response.
368  *
369  * Returns 0 on success or an errno on failure.
370  */
371 int
372 smb_opipe_write(smb_request_t *sr, struct uio *uio)
373 {
374 	struct nmsghdr msghdr;
375 	smb_ofile_t *ofile;
376 	smb_opipe_t *opipe;
377 	ksocket_t sock;
378 	size_t sent = 0;
379 	int rc = 0;
380 
381 	ofile = sr->fid_ofile;
382 	ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
383 	opipe = ofile->f_pipe;
384 	SMB_OPIPE_VALID(opipe);
385 
386 	mutex_enter(&opipe->p_mutex);
387 	sock = opipe->p_socket;
388 	if (sock != NULL)
389 		ksocket_hold(sock);
390 	mutex_exit(&opipe->p_mutex);
391 	if (sock == NULL)
392 		return (EBADF);
393 
394 	bzero(&msghdr, sizeof (msghdr));
395 	msghdr.msg_iov = uio->uio_iov;
396 	msghdr.msg_iovlen = uio->uio_iovcnt;
397 
398 	/*
399 	 * This should block until we've sent it all,
400 	 * or given up due to errors (pipe closed).
401 	 */
402 	while (uio->uio_resid > 0) {
403 		rc = ksocket_sendmsg(sock, &msghdr, 0, &sent, ofile->f_cr);
404 		if (rc != 0)
405 			break;
406 		uio->uio_resid -= sent;
407 	}
408 
409 	ksocket_rele(sock);
410 
411 	return (rc);
412 }
413 
414 /*
415  * smb_opipe_read
416  *
417  * This interface may be called from smb_opipe_transact (write, read)
418  * or from smb_read / smb2_read to get the rest of an RPC response.
419  * The response data (and length) are returned via the uio.
420  */
421 int
422 smb_opipe_read(smb_request_t *sr, struct uio *uio)
423 {
424 	struct nmsghdr msghdr;
425 	smb_ofile_t *ofile;
426 	smb_opipe_t *opipe;
427 	ksocket_t sock;
428 	size_t recvcnt = 0;
429 	int rc;
430 
431 	ofile = sr->fid_ofile;
432 	ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
433 	opipe = ofile->f_pipe;
434 	SMB_OPIPE_VALID(opipe);
435 
436 	mutex_enter(&opipe->p_mutex);
437 	sock = opipe->p_socket;
438 	if (sock != NULL)
439 		ksocket_hold(sock);
440 	mutex_exit(&opipe->p_mutex);
441 	if (sock == NULL)
442 		return (EBADF);
443 
444 	mutex_enter(&sr->sr_mutex);
445 	if (sr->sr_state != SMB_REQ_STATE_ACTIVE) {
446 		mutex_exit(&sr->sr_mutex);
447 		rc = EINTR;
448 		goto out;
449 	}
450 	sr->sr_state = SMB_REQ_STATE_WAITING_PIPE;
451 	sr->cancel_method = smb_opipe_cancel;
452 	sr->cancel_arg2 = sock;
453 	mutex_exit(&sr->sr_mutex);
454 
455 	/*
456 	 * This should block only if there's no data.
457 	 * A single call to recvmsg does just that.
458 	 * (Intentionaly no recv loop here.)
459 	 */
460 	bzero(&msghdr, sizeof (msghdr));
461 	msghdr.msg_iov = uio->uio_iov;
462 	msghdr.msg_iovlen = uio->uio_iovcnt;
463 	rc = ksocket_recvmsg(sock, &msghdr, 0,
464 	    &recvcnt, ofile->f_cr);
465 
466 	mutex_enter(&sr->sr_mutex);
467 	sr->cancel_method = NULL;
468 	sr->cancel_arg2 = NULL;
469 	switch (sr->sr_state) {
470 	case SMB_REQ_STATE_WAITING_PIPE:
471 		sr->sr_state = SMB_REQ_STATE_ACTIVE;
472 		break;
473 	case SMB_REQ_STATE_CANCEL_PENDING:
474 		sr->sr_state = SMB_REQ_STATE_CANCELLED;
475 		rc = EINTR;
476 		break;
477 	default:
478 		/* keep rc from above */
479 		break;
480 	}
481 	mutex_exit(&sr->sr_mutex);
482 
483 	if (rc != 0)
484 		goto out;
485 
486 	if (recvcnt == 0) {
487 		/* Other side closed. */
488 		rc = EPIPE;
489 		goto out;
490 	}
491 	uio->uio_resid -= recvcnt;
492 
493 out:
494 	ksocket_rele(sock);
495 
496 	return (rc);
497 }
498 
499 int
500 smb_opipe_ioctl(smb_request_t *sr, int cmd, void *arg, int *rvalp)
501 {
502 	smb_ofile_t *ofile;
503 	smb_opipe_t *opipe;
504 	ksocket_t sock;
505 	int rc;
506 
507 	ofile = sr->fid_ofile;
508 	ASSERT(ofile->f_ftype == SMB_FTYPE_MESG_PIPE);
509 	opipe = ofile->f_pipe;
510 	SMB_OPIPE_VALID(opipe);
511 
512 	mutex_enter(&opipe->p_mutex);
513 	sock = opipe->p_socket;
514 	if (sock != NULL)
515 		ksocket_hold(sock);
516 	mutex_exit(&opipe->p_mutex);
517 	if (sock == NULL)
518 		return (EBADF);
519 
520 	rc = ksocket_ioctl(sock, cmd, (intptr_t)arg, rvalp, ofile->f_cr);
521 
522 	ksocket_rele(sock);
523 
524 	return (rc);
525 }
526 
527 /*
528  * Get the smb_attr_t for a named pipe.
529  * Caller has already cleared to zero.
530  */
531 int
532 smb_opipe_getattr(smb_ofile_t *of, smb_attr_t *ap)
533 {
534 
535 	if (of->f_pipe == NULL)
536 		return (EINVAL);
537 
538 	ap->sa_vattr.va_type = VFIFO;
539 	ap->sa_vattr.va_nlink = 1;
540 	ap->sa_vattr.va_nodeid = (uintptr_t)of->f_pipe;
541 	ap->sa_dosattr = FILE_ATTRIBUTE_NORMAL;
542 	ap->sa_allocsz = SMB_PIPE_MAX_MSGSIZE;
543 
544 	return (0);
545 }
546 
547 int
548 smb_opipe_getname(smb_ofile_t *of, char *buf, size_t buflen)
549 {
550 	smb_opipe_t *opipe;
551 
552 	if ((opipe = of->f_pipe) == NULL)
553 		return (EINVAL);
554 
555 	(void) snprintf(buf, buflen, "\\%s", opipe->p_name);
556 	return (0);
557 }
558 
559 /*
560  * Handler for smb2_ioctl
561  */
562 /* ARGSUSED */
563 uint32_t
564 smb_opipe_fsctl(smb_request_t *sr, smb_fsctl_t *fsctl)
565 {
566 	uint32_t status;
567 
568 	switch (fsctl->CtlCode) {
569 	case FSCTL_PIPE_TRANSCEIVE:
570 		status = smb_opipe_transceive(sr, fsctl);
571 		break;
572 
573 	case FSCTL_PIPE_PEEK:
574 	case FSCTL_PIPE_WAIT:
575 		/* XXX todo */
576 		status = NT_STATUS_NOT_SUPPORTED;
577 		break;
578 
579 	default:
580 		ASSERT(!"CtlCode");
581 		status = NT_STATUS_INTERNAL_ERROR;
582 		break;
583 	}
584 
585 	return (status);
586 }
587 
588 static uint32_t
589 smb_opipe_transceive(smb_request_t *sr, smb_fsctl_t *fsctl)
590 {
591 	smb_vdb_t	vdb;
592 	smb_ofile_t	*ofile;
593 	struct mbuf	*mb;
594 	uint32_t	status;
595 	int		len, rc;
596 
597 	/*
598 	 * Caller checked that this is the IPC$ share,
599 	 * and that this call has a valid open handle.
600 	 * Just check the type.
601 	 */
602 	ofile = sr->fid_ofile;
603 	if (ofile->f_ftype != SMB_FTYPE_MESG_PIPE)
604 		return (NT_STATUS_INVALID_HANDLE);
605 
606 	rc = smb_mbc_decodef(fsctl->in_mbc, "#B",
607 	    fsctl->InputCount, &vdb);
608 	if (rc != 0) {
609 		/* Not enough data sent. */
610 		return (NT_STATUS_INVALID_PARAMETER);
611 	}
612 
613 	rc = smb_opipe_write(sr, &vdb.vdb_uio);
614 	if (rc != 0)
615 		return (smb_errno2status(rc));
616 
617 	vdb.vdb_tag = 0;
618 	vdb.vdb_uio.uio_iov = &vdb.vdb_iovec[0];
619 	vdb.vdb_uio.uio_iovcnt = MAX_IOVEC;
620 	vdb.vdb_uio.uio_segflg = UIO_SYSSPACE;
621 	vdb.vdb_uio.uio_extflg = UIO_COPY_DEFAULT;
622 	vdb.vdb_uio.uio_loffset = (offset_t)0;
623 	vdb.vdb_uio.uio_resid = fsctl->MaxOutputResp;
624 	mb = smb_mbuf_allocate(&vdb.vdb_uio);
625 
626 	rc = smb_opipe_read(sr, &vdb.vdb_uio);
627 	if (rc != 0) {
628 		m_freem(mb);
629 		return (smb_errno2status(rc));
630 	}
631 
632 	len = fsctl->MaxOutputResp - vdb.vdb_uio.uio_resid;
633 	smb_mbuf_trim(mb, len);
634 	MBC_ATTACH_MBUF(fsctl->out_mbc, mb);
635 
636 	/*
637 	 * If the output buffer holds a partial pipe message,
638 	 * we're supposed to return NT_STATUS_BUFFER_OVERFLOW.
639 	 * As we don't have message boundary markers, the best
640 	 * we can do is return that status when we have ALL of:
641 	 *	Output buffer was < SMB_PIPE_MAX_MSGSIZE
642 	 *	We filled the output buffer (resid==0)
643 	 *	There's more data (ioctl FIONREAD)
644 	 */
645 	status = NT_STATUS_SUCCESS;
646 	if (fsctl->MaxOutputResp < SMB_PIPE_MAX_MSGSIZE &&
647 	    vdb.vdb_uio.uio_resid == 0) {
648 		int nread = 0, trval;
649 		rc = smb_opipe_ioctl(sr, FIONREAD, &nread, &trval);
650 		if (rc == 0 && nread != 0)
651 			status = NT_STATUS_BUFFER_OVERFLOW;
652 	}
653 
654 	return (status);
655 }
656