xref: /titanic_52/usr/src/cmd/ndmpd/ndmp/ndmpd_util.c (revision e0731422366620894c16c1ee6515551c5f00733d)
1 /*
2  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * BSD 3 Clause License
7  *
8  * Copyright (c) 2007, The Storage Networking Industry Association.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 	- Redistributions of source code must retain the above copyright
14  *	  notice, this list of conditions and the following disclaimer.
15  *
16  * 	- Redistributions in binary form must reproduce the above copyright
17  *	  notice, this list of conditions and the following disclaimer in
18  *	  the documentation and/or other materials provided with the
19  *	  distribution.
20  *
21  *	- Neither the name of The Storage Networking Industry Association (SNIA)
22  *	  nor the names of its contributors may be used to endorse or promote
23  *	  products derived from this software without specific prior written
24  *	  permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 /* Copyright (c) 2007, The Storage Networking Industry Association. */
39 /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */
40 
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <assert.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <unistd.h>
49 #include <strings.h>
50 #include <time.h>
51 #include "ndmpd.h"
52 #include <bitmap.h>
53 #include <sys/queue.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <netinet/tcp.h>
57 #include <arpa/inet.h>
58 #include <sys/socketvar.h>
59 #include <net/if.h>
60 #include <netdb.h>
61 #include <sys/filio.h>
62 #include <sys/mtio.h>
63 #include <sys/scsi/impl/uscsi.h>
64 #include <sys/scsi/scsi.h>
65 #include "tlm.h"
66 
67 /*
68  * Mutex to protect Nlp
69  */
70 mutex_t nlp_mtx;
71 
72 /*
73  * Patchable socket buffer sizes in kilobytes.
74  * ssb: send buffer size.
75  * rsb: receive buffer size.
76  */
77 int ndmp_sbs = 60;
78 int ndmp_rbs = 60;
79 
80 
81 /*
82  * Force to backup all the intermediate directories leading to an object
83  * to be backed up in 'dump' format backup.
84  */
85 boolean_t ndmp_dump_path_node = FALSE;
86 
87 
88 /*
89  * Force to backup all the intermediate directories leading to an object
90  * to be backed up in 'tar' format backup.
91  */
92 boolean_t ndmp_tar_path_node = FALSE;
93 
94 
95 /*
96  * Should the 'st_ctime' be ignored during incremental level backup?
97  */
98 boolean_t ndmp_ignore_ctime = FALSE;
99 
100 /*
101  * Should the 'st_lmtime' be included during incremental level backup?
102  */
103 boolean_t ndmp_include_lmtime = FALSE;
104 
105 /*
106  * Force to send the file history node entries along with the file history
107  * dir entries for all directories containing the changed files to the client
108  * for incremental backup.
109  *
110  * Note: This variable is added to support Bakbone Software's Netvault DMA
111  * which expects to get the FH ADD NODES for all upper directories which
112  * contain the changed files in incremental backup along with the FH ADD DIRS.
113  */
114 boolean_t ndmp_fhinode = FALSE;
115 
116 /*
117  * Maximum permitted sequence number in the token-based backup.  The
118  * value of this variable can be changed by the administrator and is
119  * saved in the NDMP configuration file.
120  */
121 static int ndmp_max_tok_seq = NDMP_MAX_TOKSEQ;
122 
123 /*
124  * Force backup directories in incremental backups.  If the
125  * directory is not modified itself, it's not backed up by
126  * default.
127  */
128 int ndmp_force_bk_dirs = 0;
129 
130 /*
131  * Keeps track of the open SCSI (including tape and robot) devices.
132  * When a SCSI device is opened its name must be added to this list and
133  * when it's closed its name must be removed from this list.  The main
134  * purpose of this list is the robot device.  If the robot devices are not
135  * attached in SASD layer, Local Backup won't see them. If they are
136  * attached and we open the robot devices, then wrong commands are sent
137  * to robot by SASD since it assumes that the robot is a tape (sequential
138  * access) device.
139  */
140 struct open_list {
141 	LIST_ENTRY(open_list) ol_q;
142 	int ol_nref;
143 	char *ol_devnm;
144 	int ol_sid;
145 	int ol_lun;
146 	int ol_fd;
147 	ndmp_connection_t *cl_conn;
148 };
149 LIST_HEAD(ol_head, open_list);
150 
151 
152 /*
153  * Head of the opened SCSI devices list.
154  */
155 static struct ol_head ol_head;
156 
157 mutex_t ol_mutex = DEFAULTMUTEX;
158 
159 
160 /*
161  * List of things to be exluded from backup.
162  */
163 static char *exls[] = {
164 	EXCL_PROC,
165 	EXCL_TMP,
166 	NULL, /* reserved for a copy of the "backup.directory" */
167 	NULL
168 };
169 
170 
171 /*
172  * The counter for creating unique names with "ndmp.%d" format.
173  */
174 #define	NDMP_RCF_BASENAME	"ndmp."
175 static int ndmp_job_cnt = 0;
176 
177 static int scsi_test_unit_ready(int dev_id);
178 
179 /*
180  * ndmpd_add_file_handler
181  *
182  * Adds a file handler to the file handler list.
183  * The file handler list is used by ndmpd_api_dispatch.
184  *
185  * Parameters:
186  *   session (input) - session pointer.
187  *   cookie  (input) - opaque data to be passed to file hander when called.
188  *   fd      (input) - file descriptor.
189  *   mode    (input) - bitmask of the following:
190  *		     1 = watch file for ready for reading
191  *		     2 = watch file for ready for writing
192  *		     4 = watch file for exception
193  *   class   (input) - handler class. (HC_CLIENT, HC_MOVER, HC_MODULE)
194  *   func    (input) - function to call when the file meets one of the
195  *		     conditions specified by mode.
196  *
197  * Returns:
198  *   0 - success.
199  *  -1 - error.
200  */
201 int
202 ndmpd_add_file_handler(ndmpd_session_t *session, void *cookie, int fd,
203     ulong_t mode, ulong_t class, ndmpd_file_handler_func_t *func)
204 {
205 	ndmpd_file_handler_t *new;
206 
207 	new = ndmp_malloc(sizeof (ndmpd_file_handler_t));
208 	if (new == 0)
209 		return (-1);
210 
211 	new->fh_cookie = cookie;
212 	new->fh_fd = fd;
213 	new->fh_mode = mode;
214 	new->fh_class = class;
215 	new->fh_func = func;
216 	new->fh_next = session->ns_file_handler_list;
217 	session->ns_file_handler_list = new;
218 	return (0);
219 }
220 
221 
222 /*
223  * ndmpd_remove_file_handler
224  *
225  * Removes a file handler from the file handler list.
226  *
227  * Parameters:
228  *   session (input) - session pointer.
229  *   fd      (input) - file descriptor.
230  *
231  * Returns:
232  *   0 - success.
233  *  -1 - error.
234  */
235 int
236 ndmpd_remove_file_handler(ndmpd_session_t *session, int fd)
237 {
238 	ndmpd_file_handler_t **last;
239 	ndmpd_file_handler_t *handler;
240 
241 	last = &session->ns_file_handler_list;
242 	while (*last != 0) {
243 		handler = *last;
244 
245 		if (handler->fh_fd == fd) {
246 			*last = handler->fh_next;
247 			(void) free(handler);
248 			return (1);
249 		}
250 		last = &handler->fh_next;
251 	}
252 
253 	return (0);
254 }
255 
256 
257 /*
258  * ndmp_connection_closed
259  *
260  * If the connection closed or not.
261  *
262  * Parameters:
263  *   fd (input) : file descriptor
264  *
265  * Returns:
266  *   0  - connection is still valid
267  *   1  - connection is not valid anymore
268  *   -1 - Internal kernel error
269  */
270 int
271 ndmp_connection_closed(int fd)
272 {
273 	fd_set fds;
274 	int closed, ret;
275 	struct timeval timeout;
276 
277 	if (fd < 0) /* We are not using the mover */
278 		return (-1);
279 
280 	timeout.tv_sec = 0;
281 	timeout.tv_usec = 1000;
282 
283 	FD_ZERO(&fds);
284 	FD_SET(fd, &fds);
285 	ret = select(FD_SETSIZE, &fds, NULL, NULL, &timeout);
286 
287 	closed = (ret == -1 && errno == EBADF);
288 
289 	return (closed);
290 }
291 
292 /*
293  * ndmp_check_mover_state
294  *
295  * Checks the mover connection status and sends an appropriate
296  * NDMP message to client based on that.
297  *
298  * Parameters:
299  *   ndmpd_session_t *session (input) : session pointer
300  *
301  * Returns:
302  *   void.
303  */
304 void
305 ndmp_check_mover_state(ndmpd_session_t *session)
306 {
307 	int moverfd;
308 	/*
309 	 * NDMPV3 Spec (Three-way restore):
310 	 * Once all of the files have been recovered, NDMP DATA Server closes
311 	 * the connection to the mover on the NDMP TAPE Server. THEN
312 	 * The NDMP client should receive an NDMP_NOTIFY_MOVER_HALTED message
313 	 * with an NDMP_MOVER_CONNECT_CLOSED reason from the NDMP TAPE Server
314 	 */
315 	moverfd = session->ns_mover.md_sock;
316 	/* If connection is closed by the peer */
317 	if (moverfd >= 0 &&
318 	    session->ns_mover.md_mode == NDMP_MOVER_MODE_WRITE) {
319 		int closed, reason;
320 
321 		closed = ndmp_connection_closed(moverfd);
322 		if (closed) {
323 			/* Connection closed or internal error */
324 			if (closed > 0) {
325 				NDMP_LOG(LOG_DEBUG,
326 				    "ndmp mover: connection closed by peer");
327 				reason = NDMP_MOVER_HALT_CONNECT_CLOSED;
328 			} else {
329 				NDMP_LOG(LOG_DEBUG,
330 				    "ndmp mover: Internal error");
331 				reason = NDMP_MOVER_HALT_INTERNAL_ERROR;
332 			}
333 			ndmpd_mover_error(session, reason);
334 
335 		}
336 	}
337 }
338 
339 
340 /*
341  * ndmpd_select
342  *
343  * Calls select on the the set of file descriptors from the
344  * file handler list masked by the fd_class argument.
345  * Calls the file handler function for each
346  * file descriptor that is ready for I/O.
347  *
348  * Parameters:
349  *   session (input) - session pointer.
350  *   block   (input) - if TRUE, ndmpd_select waits until at least one
351  *		     file descriptor is ready for I/O. Otherwise,
352  *		     it returns immediately if no file descriptors are
353  *		     ready for I/O.
354  *   class_mask (input) - bit mask of handler classes to be examined.
355  *		     Provides for excluding some of the handlers from
356  *		     being called.
357  *
358  * Returns:
359  *  -1 - error.
360  *   0 - no handlers were called.
361  *   1 - at least one handler was called.
362  */
363 int
364 ndmpd_select(ndmpd_session_t *session, boolean_t block, ulong_t class_mask)
365 {
366 	fd_set rfds;
367 	fd_set wfds;
368 	fd_set efds;
369 	int n;
370 	ndmpd_file_handler_t *handler;
371 	struct timeval timeout;
372 
373 	nlp_event_rv_set(session, 0);
374 
375 	if (session->ns_file_handler_list == 0)
376 		return (0);
377 
378 
379 	/*
380 	 * If select should be blocked, then we poll every ten seconds.
381 	 * The reason is in case of three-way restore we should be able
382 	 * to detect if the other end closed the connection or not.
383 	 * NDMP client(DMA) does not send any information about the connection
384 	 * that was closed in the other end.
385 	 */
386 
387 	if (block == TRUE)
388 		timeout.tv_sec = 10;
389 	else
390 		timeout.tv_sec = 0;
391 	timeout.tv_usec = 0;
392 
393 	do {
394 		/* Create the fd_sets for select. */
395 		FD_ZERO(&rfds);
396 		FD_ZERO(&wfds);
397 		FD_ZERO(&efds);
398 
399 		for (handler = session->ns_file_handler_list; handler != 0;
400 		    handler = handler->fh_next) {
401 			if ((handler->fh_class & class_mask) == 0)
402 				continue;
403 
404 			if (handler->fh_mode & NDMPD_SELECT_MODE_READ)
405 				FD_SET(handler->fh_fd, &rfds);
406 			if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE)
407 				FD_SET(handler->fh_fd, &wfds);
408 			if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION)
409 				FD_SET(handler->fh_fd, &efds);
410 		}
411 		ndmp_check_mover_state(session);
412 		n = select(FD_SETSIZE, &rfds, &wfds, &efds, &timeout);
413 	} while (n == 0 && block == TRUE);
414 
415 	if (n < 0) {
416 		int connection_fd = ndmp_get_fd(session->ns_connection);
417 
418 		if (errno == EINTR)
419 			return (0);
420 
421 		NDMP_LOG(LOG_DEBUG, "Select error: %m");
422 
423 		for (handler = session->ns_file_handler_list; handler != 0;
424 		    handler = handler->fh_next) {
425 			if ((handler->fh_class & class_mask) == 0)
426 				continue;
427 
428 			if (handler->fh_mode & NDMPD_SELECT_MODE_READ) {
429 				if (FD_ISSET(handler->fh_fd, &rfds) &&
430 				    connection_fd == handler->fh_fd)
431 					session->ns_eof = TRUE;
432 			}
433 			if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE) {
434 				if (FD_ISSET(handler->fh_fd, &wfds) &&
435 				    connection_fd == handler->fh_fd)
436 					session->ns_eof = TRUE;
437 			}
438 			if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION) {
439 				if (FD_ISSET(handler->fh_fd, &efds) &&
440 				    connection_fd == handler->fh_fd)
441 					session->ns_eof = TRUE;
442 			}
443 		}
444 
445 		nlp_event_rv_set(session, -1);
446 		return (-1);
447 	}
448 	if (n == 0)
449 		return (0);
450 
451 	handler = session->ns_file_handler_list;
452 	while (handler != 0) {
453 		ulong_t mode = 0;
454 
455 		if ((handler->fh_class & class_mask) == 0) {
456 			handler = handler->fh_next;
457 			continue;
458 		}
459 		if (handler->fh_mode & NDMPD_SELECT_MODE_READ) {
460 			if (FD_ISSET(handler->fh_fd, &rfds)) {
461 				mode |= NDMPD_SELECT_MODE_READ;
462 				FD_CLR(handler->fh_fd, &rfds);
463 			}
464 		}
465 		if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE) {
466 			if (FD_ISSET(handler->fh_fd, &wfds)) {
467 				mode |= NDMPD_SELECT_MODE_WRITE;
468 				FD_CLR(handler->fh_fd, &wfds);
469 			}
470 		}
471 		if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION) {
472 			if (FD_ISSET(handler->fh_fd, &efds)) {
473 				mode |= NDMPD_SELECT_MODE_EXCEPTION;
474 				FD_CLR(handler->fh_fd, &efds);
475 			}
476 		}
477 		if (mode) {
478 			(*handler->fh_func) (handler->fh_cookie,
479 			    handler->fh_fd, mode);
480 
481 			/*
482 			 * K.L. The list can be modified during the execution
483 			 * of handler->fh_func. Therefore, handler will start
484 			 * from the beginning of the handler list after
485 			 * each execution.
486 			 */
487 			handler = session->ns_file_handler_list;
488 
489 			/*
490 			 * Release the thread which is waiting for a request
491 			 * to be proccessed.
492 			 */
493 			nlp_event_nw(session);
494 		} else
495 			handler = handler->fh_next;
496 
497 	}
498 
499 	nlp_event_rv_set(session, 1);
500 	return (1);
501 }
502 
503 
504 /*
505  * ndmpd_save_env
506  *
507  * Saves a copy of the environment variable list from the data_start_backup
508  * request or data_start_recover request.
509  *
510  * Parameters:
511  *   session (input) - session pointer.
512  *   env     (input) - environment variable list to be saved.
513  *   envlen  (input) - length of variable array.
514  *
515  * Returns:
516  *   error code.
517  */
518 ndmp_error
519 ndmpd_save_env(ndmpd_session_t *session, ndmp_pval *env, ulong_t envlen)
520 {
521 	ulong_t i;
522 	char *namebuf;
523 	char *valbuf;
524 
525 	session->ns_data.dd_env_len = 0;
526 
527 	if (envlen == 0)
528 		return (NDMP_NO_ERR);
529 
530 	session->ns_data.dd_env = ndmp_malloc(sizeof (ndmp_pval) * envlen);
531 	if (session->ns_data.dd_env == 0)
532 		return (NDMP_NO_MEM_ERR);
533 
534 	for (i = 0; i < envlen; i++) {
535 		namebuf = strdup(env[i].name);
536 		if (namebuf == 0)
537 			return (NDMP_NO_MEM_ERR);
538 
539 		valbuf = strdup(env[i].value);
540 		if (valbuf == 0) {
541 			free(namebuf);
542 			return (NDMP_NO_MEM_ERR);
543 		}
544 
545 		NDMP_LOG(LOG_DEBUG, "env(%s): \"%s\"",
546 		    namebuf, valbuf);
547 
548 		(void) mutex_lock(&session->ns_lock);
549 		session->ns_data.dd_env[i].name = namebuf;
550 		session->ns_data.dd_env[i].value = valbuf;
551 		session->ns_data.dd_env_len++;
552 		(void) mutex_unlock(&session->ns_lock);
553 	}
554 
555 	return (NDMP_NO_ERR);
556 }
557 
558 
559 /*
560  * ndmpd_free_env
561  *
562  * Free the previously saved environment variable array.
563  *
564  * Parameters:
565  *   session - NDMP session pointer.
566  *
567  * Returns:
568  *   void.
569  */
570 void
571 ndmpd_free_env(ndmpd_session_t *session)
572 {
573 	ulong_t i;
574 	int count = session->ns_data.dd_env_len;
575 
576 	(void) mutex_lock(&session->ns_lock);
577 	session->ns_data.dd_env_len = 0;
578 	for (i = 0; i < count; i++) {
579 		free(session->ns_data.dd_env[i].name);
580 		free(session->ns_data.dd_env[i].value);
581 	}
582 
583 	free((char *)session->ns_data.dd_env);
584 	session->ns_data.dd_env = 0;
585 	(void) mutex_unlock(&session->ns_lock);
586 }
587 
588 
589 /*
590  * ndmpd_save_nlist_v2
591  *
592  * Save a copy of list of file names to be restored.
593  *
594  * Parameters:
595  *   nlist    (input) - name list from data_start_recover request.
596  *   nlistlen (input) - length of name list.
597  *
598  * Returns:
599  *   array of file name pointers.
600  *
601  * Notes:
602  *   free_nlist should be called to free the returned list.
603  *   A null pointer indicates the end of the list.
604  */
605 ndmp_error
606 ndmpd_save_nlist_v2(ndmpd_session_t *session, ndmp_name *nlist,
607     ulong_t nlistlen)
608 {
609 	ulong_t i;
610 	char *namebuf;
611 	char *destbuf;
612 
613 	if (nlistlen == 0)
614 		return (NDMP_NO_ERR);
615 
616 	session->ns_data.dd_nlist_len = 0;
617 	session->ns_data.dd_nlist = ndmp_malloc(sizeof (ndmp_name)*nlistlen);
618 	if (session->ns_data.dd_nlist == 0)
619 		return (NDMP_NO_MEM_ERR);
620 
621 	for (i = 0; i < nlistlen; i++) {
622 		namebuf = ndmp_malloc(strlen(nlist[i].name) + 1);
623 		if (namebuf == 0)
624 			return (NDMP_NO_MEM_ERR);
625 
626 		destbuf = ndmp_malloc(strlen(nlist[i].dest) + 1);
627 		if (destbuf == 0) {
628 			free(namebuf);
629 			return (NDMP_NO_MEM_ERR);
630 		}
631 		(void) strlcpy(namebuf, nlist[i].name,
632 		    strlen(nlist[i].name) + 1);
633 		(void) strlcpy(destbuf, nlist[i].dest,
634 		    strlen(nlist[i].dest) + 1);
635 
636 		session->ns_data.dd_nlist[i].name = namebuf;
637 		session->ns_data.dd_nlist[i].dest = destbuf;
638 		session->ns_data.dd_nlist[i].ssid = nlist[i].ssid;
639 		session->ns_data.dd_nlist[i].fh_info = nlist[i].fh_info;
640 		session->ns_data.dd_nlist_len++;
641 	}
642 
643 	return (NDMP_NO_ERR);
644 }
645 
646 
647 /*
648  * ndmpd_free_nlist_v2
649  *
650  * Free a list created by ndmpd_save_nlist_v2.
651  *
652  * Parameters:
653  *   session (input) - session pointer.
654  *
655  * Returns:
656  *   void
657  */
658 void
659 ndmpd_free_nlist_v2(ndmpd_session_t *session)
660 {
661 	ulong_t i;
662 
663 	for (i = 0; i < session->ns_data.dd_nlist_len; i++) {
664 		free(session->ns_data.dd_nlist[i].name);
665 		free(session->ns_data.dd_nlist[i].dest);
666 	}
667 
668 	if (session->ns_data.dd_nlist != NULL)
669 		free((char *)session->ns_data.dd_nlist);
670 	session->ns_data.dd_nlist = 0;
671 	session->ns_data.dd_nlist_len = 0;
672 }
673 
674 
675 /*
676  * ndmpd_free_nlist_v3
677  *
678  * Free a list created by ndmpd_save_nlist_v3.
679  *
680  * Parameters:
681  *   session (input) - session pointer.
682  *
683  * Returns:
684  *   void
685  */
686 void
687 ndmpd_free_nlist_v3(ndmpd_session_t *session)
688 {
689 	ulong_t i;
690 	mem_ndmp_name_v3_t *tp; /* destination entry */
691 
692 	tp = session->ns_data.dd_nlist_v3;
693 	for (i = 0; i < session->ns_data.dd_nlist_len; tp++, i++) {
694 		NDMP_FREE(tp->nm3_opath);
695 		NDMP_FREE(tp->nm3_dpath);
696 		NDMP_FREE(tp->nm3_newnm);
697 	}
698 
699 	NDMP_FREE(session->ns_data.dd_nlist_v3);
700 	session->ns_data.dd_nlist_len = 0;
701 }
702 
703 
704 /*
705  * ndmpd_save_nlist_v3
706  *
707  * Save a copy of list of file names to be restored.
708  *
709  * Parameters:
710  *   nlist    (input) - name list from data_start_recover request.
711  *   nlistlen (input) - length of name list.
712  *
713  * Returns:
714  *   array of file name pointers.
715  *
716  * Notes:
717  *   free_nlist should be called to free the returned list.
718  *   A null pointer indicates the end of the list.
719  */
720 ndmp_error
721 ndmpd_save_nlist_v3(ndmpd_session_t *session, ndmp_name_v3 *nlist,
722     ulong_t nlistlen)
723 {
724 	ulong_t i;
725 	ndmp_error rv;
726 	ndmp_name_v3 *sp; /* source entry */
727 	mem_ndmp_name_v3_t *tp; /* destination entry */
728 
729 	if (nlistlen == 0)
730 		return (NDMP_ILLEGAL_ARGS_ERR);
731 
732 	session->ns_data.dd_nlist_len = 0;
733 	tp = session->ns_data.dd_nlist_v3 =
734 	    ndmp_malloc(sizeof (mem_ndmp_name_v3_t) * nlistlen);
735 	if (session->ns_data.dd_nlist_v3 == 0)
736 		return (NDMP_NO_MEM_ERR);
737 
738 	rv = NDMP_NO_ERR;
739 	sp = nlist;
740 	for (i = 0; i < nlistlen; tp++, sp++, i++) {
741 		tp->nm3_opath = strdup(sp->original_path);
742 		if (!tp->nm3_opath) {
743 			rv = NDMP_NO_MEM_ERR;
744 			break;
745 		}
746 		if (!*sp->destination_dir) {
747 			tp->nm3_dpath = NULL;
748 			/* In V4 destination dir cannot be NULL */
749 			if (session->ns_protocol_version == NDMPV4) {
750 				rv = NDMP_ILLEGAL_ARGS_ERR;
751 				break;
752 			}
753 		} else if (!(tp->nm3_dpath = strdup(sp->destination_dir))) {
754 			rv = NDMP_NO_MEM_ERR;
755 			break;
756 		}
757 		if (!*sp->new_name)
758 			tp->nm3_newnm = NULL;
759 		else if (!(tp->nm3_newnm = strdup(sp->new_name))) {
760 			rv = NDMP_NO_MEM_ERR;
761 			break;
762 		}
763 
764 		tp->nm3_node = quad_to_long_long(sp->node);
765 		tp->nm3_fh_info = quad_to_long_long(sp->fh_info);
766 		tp->nm3_err = NDMP_NO_ERR;
767 		session->ns_data.dd_nlist_len++;
768 
769 		NDMP_LOG(LOG_DEBUG, "orig \"%s\"", tp->nm3_opath);
770 		NDMP_LOG(LOG_DEBUG, "dest \"%s\"", NDMP_SVAL(tp->nm3_dpath));
771 		NDMP_LOG(LOG_DEBUG, "name \"%s\"", NDMP_SVAL(tp->nm3_newnm));
772 		NDMP_LOG(LOG_DEBUG, "node %lld", tp->nm3_node);
773 		NDMP_LOG(LOG_DEBUG, "fh_info %lld", tp->nm3_fh_info);
774 	}
775 
776 	if (rv != NDMP_NO_ERR)
777 		ndmpd_free_nlist_v3(session);
778 
779 	return (rv);
780 }
781 
782 
783 /*
784  * ndmpd_free_nlist
785  *
786  * Free the recovery list based on the version
787  *
788  * Parameters:
789  *   session (input) - session pointer.
790  *
791  * Returns:
792  *   void
793  */
794 void
795 ndmpd_free_nlist(ndmpd_session_t *session)
796 {
797 	switch (session->ns_protocol_version) {
798 	case 1:
799 	case 2:
800 		ndmpd_free_nlist_v2(session);
801 		break;
802 	case 3:
803 	case 4:
804 		ndmpd_free_nlist_v3(session);
805 		break;
806 
807 	default:
808 		NDMP_LOG(LOG_DEBUG, "Unknown version %d",
809 		    session->ns_protocol_version);
810 	}
811 }
812 
813 
814 /*
815  * fh_cmpv3
816  *
817  * Comparison function used in sorting the Nlist based on their
818  * file history info (offset of the entry on the tape)
819  *
820  * Parameters:
821  *   p (input) - pointer to P
822  *   q (input) - pointer to Q
823  *
824  * Returns:
825  *  -1: P < Q
826  *   0: P = Q
827  *   1: P > Q
828  */
829 static int
830 fh_cmpv3(const void *p,
831 		const void *q)
832 {
833 #define	FH_INFOV3(p)	(((mem_ndmp_name_v3_t *)p)->nm3_fh_info)
834 
835 	if (FH_INFOV3(p) < FH_INFOV3(q))
836 		return (-1);
837 	else if (FH_INFOV3(p) == FH_INFOV3(q))
838 		return (0);
839 	else
840 		return (1);
841 
842 #undef FH_INFOV3
843 }
844 
845 
846 /*
847  * ndmp_sort_nlist_v3
848  *
849  * Sort the recovery list based on their offset on the tape
850  *
851  * Parameters:
852  *   session (input) - session pointer.
853  *
854  * Returns:
855  *   void
856  */
857 void
858 ndmp_sort_nlist_v3(ndmpd_session_t *session)
859 {
860 	if (!session || session->ns_data.dd_nlist_len == 0 ||
861 	    !session->ns_data.dd_nlist_v3)
862 		return;
863 
864 	(void) qsort(session->ns_data.dd_nlist_v3,
865 	    session->ns_data.dd_nlist_len,
866 	    sizeof (mem_ndmp_name_v3_t), fh_cmpv3);
867 }
868 
869 
870 /*
871  * ndmp_send_reply
872  *
873  * Send the reply, check for error and print the msg if any error
874  * occured when sending the reply.
875  *
876  *   Parameters:
877  *     connection (input) - connection pointer.
878  *
879  *   Return:
880  *     void
881  */
882 void
883 ndmp_send_reply(ndmp_connection_t *connection, void *reply, char *msg)
884 {
885 	if (ndmp_send_response(connection, NDMP_NO_ERR, reply) < 0)
886 		NDMP_LOG(LOG_DEBUG, "%s", msg);
887 }
888 
889 
890 /*
891  * ndmp_mtioctl
892  *
893  * Performs numerous filemark operations.
894  *
895  * Parameters:
896  * 	fd - file descriptor of the device
897  *	cmd - filemark or record command
898  * 	count - the number of operations to be performed
899  */
900 int
901 ndmp_mtioctl(int fd, int cmd, int count)
902 {
903 	struct mtop mp;
904 
905 	mp.mt_op = cmd;
906 	mp.mt_count = count;
907 	if (ioctl(fd, MTIOCTOP, &mp) < 0) {
908 		NDMP_LOG(LOG_ERR, "Failed to send command to tape: %m.");
909 		return (-1);
910 	}
911 
912 	return (0);
913 }
914 
915 
916 /*
917  * quad_to_long_long
918  *
919  * Convert type quad to longlong_t
920  */
921 u_longlong_t
922 quad_to_long_long(ndmp_u_quad q)
923 {
924 	u_longlong_t ull;
925 
926 	ull = ((u_longlong_t)q.high << 32) + q.low;
927 	return (ull);
928 }
929 
930 
931 /*
932  * long_long_to_quad
933  *
934  * Convert long long to quad type
935  */
936 ndmp_u_quad
937 long_long_to_quad(u_longlong_t ull)
938 {
939 	ndmp_u_quad q;
940 
941 	q.high = (ulong_t)(ull >> 32);
942 	q.low = (ulong_t)ull;
943 	return (q);
944 }
945 
946 
947 /*
948  * ndmp_set_socket_nodelay
949  *
950  * Set the TCP socket option to nodelay mode
951  */
952 void
953 ndmp_set_socket_nodelay(int sock)
954 {
955 	int flag = 1;
956 
957 	(void) setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof (flag));
958 }
959 
960 
961 /*
962  * ndmp_set_socket_snd_buf
963  *
964  * Set the socket send buffer size
965  */
966 void
967 ndmp_set_socket_snd_buf(int sock, int size)
968 {
969 	if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0)
970 		NDMP_LOG(LOG_DEBUG, "SO_SNDBUF failed errno=%d", errno);
971 }
972 
973 
974 /*
975  * ndmp_set_socket_rcv_buf
976  *
977  * Set the socket receive buffer size
978  */
979 void
980 ndmp_set_socket_rcv_buf(int sock, int size)
981 {
982 	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
983 		NDMP_LOG(LOG_DEBUG, "SO_RCVBUF failed errno=%d", errno);
984 }
985 
986 /*
987  * ndmp_get_max_tok_seq
988  *
989  * Get the maximum permitted token sequence for token-based
990  * backups.
991  *
992  * Parameters:
993  *   void
994  *
995  * Returns:
996  *   ndmp_max_tok_seq
997  */
998 int
999 ndmp_get_max_tok_seq(void)
1000 {
1001 	return (ndmp_max_tok_seq);
1002 }
1003 
1004 /*
1005  * ndmp_buffer_get_size
1006  *
1007  * Return the NDMP transfer buffer size
1008  *
1009  * Parameters:
1010  *   session (input) - session pointer.
1011  *
1012  * Returns:
1013  *   buffer size
1014  */
1015 long
1016 ndmp_buffer_get_size(ndmpd_session_t *session)
1017 {
1018 	long xfer_size;
1019 
1020 	if (session == NULL)
1021 		return (0);
1022 
1023 	if (session->ns_data.dd_mover.addr_type == NDMP_ADDR_TCP) {
1024 		xfer_size = atoi(ndmpd_get_prop_default(NDMP_MOVER_RECSIZE,
1025 		    "60"));
1026 		if (xfer_size > 0)
1027 			xfer_size *= KILOBYTE;
1028 		else
1029 			xfer_size = REMOTE_RECORD_SIZE;
1030 		NDMP_LOG(LOG_DEBUG, "Remote operation: %d", xfer_size);
1031 	} else {
1032 		NDMP_LOG(LOG_DEBUG,
1033 		    "Local operation: %lu", session->ns_mover.md_record_size);
1034 		if ((xfer_size = session->ns_mover.md_record_size) == 0)
1035 			xfer_size = MAX_RECORD_SIZE;
1036 	}
1037 
1038 	NDMP_LOG(LOG_DEBUG, "xfer_size: %d", xfer_size);
1039 	return (xfer_size);
1040 }
1041 
1042 
1043 /*
1044  * ndmp_lbr_init
1045  *
1046  * Initialize the LBR/NDMP backup parameters
1047  *
1048  * Parameters:
1049  *   session (input) - session pointer.
1050  *
1051  * Returns:
1052  *   0: on success
1053  *  -1: otherwise
1054  */
1055 int
1056 ndmp_lbr_init(ndmpd_session_t *session)
1057 {
1058 	if (session->ns_ndmp_lbr_params != NULL) {
1059 		NDMP_LOG(LOG_DEBUG, "ndmp_lbr_params already allocated.");
1060 		return (0);
1061 	}
1062 
1063 	session->ns_ndmp_lbr_params = ndmp_malloc(sizeof (ndmp_lbr_params_t));
1064 	if (session->ns_ndmp_lbr_params == NULL)
1065 		return (-1);
1066 
1067 	session->ns_ndmp_lbr_params->nlp_bkmap = -1;
1068 	session->ns_ndmp_lbr_params->nlp_session = session;
1069 	(void) cond_init(&session->ns_ndmp_lbr_params->nlp_cv, 0, NULL);
1070 	(void) mutex_init(&session->ns_lock, 0, NULL);
1071 	session->ns_nref = 0;
1072 	return (0);
1073 }
1074 
1075 
1076 /*
1077  * ndmp_lbr_cleanup
1078  *
1079  * Deallocate and cleanup all NDMP/LBR parameters
1080  *
1081  * Parameters:
1082  *   session (input) - session pointer.
1083  *
1084  * Returns:
1085  *   0: on success
1086  *  -1: otherwise
1087  */
1088 void
1089 ndmp_lbr_cleanup(ndmpd_session_t *session)
1090 {
1091 	/*
1092 	 * If in 3-way restore, the connection close is detected after
1093 	 * check in tape_read(), the reader thread of mover may wait forever
1094 	 * for the tape to be changed.  Force the reader thread to exit.
1095 	 */
1096 	nlp_event_rv_set(session, -2);
1097 	nlp_event_nw(session);
1098 
1099 	ndmpd_abort_marking_v2(session);
1100 	ndmp_stop_buffer_worker(session);
1101 	ndmp_waitfor_op(session);
1102 	ndmp_free_reader_writer_ipc(session);
1103 	if (session->ns_ndmp_lbr_params) {
1104 		if (session->ns_ndmp_lbr_params->nlp_bkmap != -1)
1105 			(void) dbm_free(session->ns_ndmp_lbr_params->nlp_bkmap);
1106 		tlm_release_list(session->ns_ndmp_lbr_params->nlp_exl);
1107 		tlm_release_list(session->ns_ndmp_lbr_params->nlp_inc);
1108 		(void) cond_destroy(&session->ns_ndmp_lbr_params->nlp_cv);
1109 	}
1110 
1111 	NDMP_FREE(session->ns_ndmp_lbr_params);
1112 }
1113 
1114 
1115 /*
1116  * nlp_ref_nw
1117  *
1118  * Increase the references to the NDMP/LBR parameter to prevent
1119  * unwanted release
1120  *
1121  * Parameters:
1122  *   session (input) - session pointer.
1123  *
1124  * Returns:
1125  *   void
1126  */
1127 void
1128 nlp_ref_nw(ndmpd_session_t *session)
1129 {
1130 	ndmp_lbr_params_t *nlp;
1131 
1132 	(void) mutex_lock(&nlp_mtx);
1133 	if ((nlp = ndmp_get_nlp(session)) != NULL) {
1134 		nlp->nlp_nw++;
1135 		NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw);
1136 	} else
1137 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1138 	(void) mutex_unlock(&nlp_mtx);
1139 }
1140 
1141 
1142 /*
1143  * nlp_unref_nw
1144  *
1145  * Decrease the references to the NDMP/LBR parameter before
1146  * release
1147  *
1148  * Parameters:
1149  *   session (input) - session pointer.
1150  *
1151  * Returns:
1152  *   void
1153  */
1154 void
1155 nlp_unref_nw(ndmpd_session_t *session)
1156 {
1157 	ndmp_lbr_params_t *nlp;
1158 
1159 	(void) mutex_lock(&nlp_mtx);
1160 	if ((nlp = ndmp_get_nlp(session)) != NULL) {
1161 		NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw);
1162 		if (nlp->nlp_nw > 0)
1163 			nlp->nlp_nw--;
1164 	} else
1165 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1166 	(void) mutex_unlock(&nlp_mtx);
1167 }
1168 
1169 
1170 /*
1171  * nlp_wait_nw
1172  *
1173  * Wait for a NDMP/LBR parameter to get available
1174  *
1175  * Parameters:
1176  *   session (input) - session pointer.
1177  *
1178  * Returns:
1179  *   void
1180  */
1181 void
1182 nlp_wait_nw(ndmpd_session_t *session)
1183 {
1184 	ndmp_lbr_params_t *nlp;
1185 
1186 	(void) mutex_lock(&nlp_mtx);
1187 	if ((nlp = ndmp_get_nlp(session)) != NULL) {
1188 		NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw);
1189 		if (nlp->nlp_nw > 0) {
1190 			NDMP_LOG(LOG_DEBUG, "Waiting");
1191 			while ((nlp->nlp_flag & NLP_READY) == 0)
1192 				(void) cond_wait(&nlp->nlp_cv, &nlp_mtx);
1193 		}
1194 	} else
1195 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1196 	(void) mutex_unlock(&nlp_mtx);
1197 }
1198 
1199 
1200 /*
1201  * nlp_event_nw
1202  *
1203  * Signal that a NDMP/LBR parameter is available to wake up the
1204  * threads waiting on that
1205  *
1206  * Parameters:
1207  *   session (input) - session pointer.
1208  *
1209  * Returns:
1210  *   void
1211  */
1212 void
1213 nlp_event_nw(ndmpd_session_t *session)
1214 {
1215 	ndmp_lbr_params_t *nlp;
1216 
1217 	(void) mutex_lock(&nlp_mtx);
1218 	if ((nlp = ndmp_get_nlp(session)) != NULL) {
1219 		if (nlp->nlp_nw > 0) {
1220 			NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw);
1221 			nlp->nlp_flag |= NLP_READY;
1222 			(void) cond_signal(&nlp->nlp_cv);
1223 		}
1224 	} else
1225 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1226 	(void) mutex_unlock(&nlp_mtx);
1227 }
1228 
1229 
1230 /*
1231  * nlp_event_rv_get
1232  *
1233  * Get the return value for each NLP
1234  *
1235  * Parameters:
1236  *   session (input) - session pointer.
1237  *
1238  * Returns:
1239  *   return value
1240  */
1241 int
1242 nlp_event_rv_get(ndmpd_session_t *session)
1243 {
1244 	ndmp_lbr_params_t *nlp;
1245 
1246 	if ((nlp = ndmp_get_nlp(session)) == NULL) {
1247 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1248 		return (0);
1249 	}
1250 
1251 	return (nlp->nlp_rv);
1252 }
1253 
1254 
1255 /*
1256  * nlp_event_rv_set
1257  *
1258  * Set the return value for an NLP
1259  *
1260  * Parameters:
1261  *   session (input) - session pointer.
1262  *   rv (input) - return value
1263  *
1264  * Returns:
1265  *   void
1266  */
1267 void
1268 nlp_event_rv_set(ndmpd_session_t *session,
1269     int rv)
1270 {
1271 	ndmp_lbr_params_t *nlp;
1272 
1273 	(void) mutex_lock(&nlp_mtx);
1274 	if (rv != 0)
1275 		NDMP_LOG(LOG_DEBUG, "rv: %d", rv);
1276 
1277 	if ((nlp = ndmp_get_nlp(session)) != NULL)
1278 		nlp->nlp_rv = rv;
1279 	else
1280 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1281 	(void) mutex_unlock(&nlp_mtx);
1282 }
1283 
1284 /*
1285  * is_buffer_erroneous
1286  *
1287  * Run a sanity check on the buffer
1288  *
1289  * returns:
1290  *   TRUE: if the buffer seems to have error
1291  *   FALSE: if the buffer is full and has valid data.
1292  */
1293 boolean_t
1294 is_buffer_erroneous(tlm_buffer_t *buf)
1295 {
1296 	boolean_t rv;
1297 
1298 	rv = (buf == NULL || buf->tb_eot || buf->tb_eof ||
1299 	    buf->tb_errno != 0);
1300 	if (rv) {
1301 		if (buf == NULL) {
1302 			NDMP_LOG(LOG_DEBUG, "buf == NULL");
1303 		} else {
1304 			NDMP_LOG(LOG_DEBUG, "eot: %u, eof: %u, errno: %d",
1305 			    buf->tb_eot, buf->tb_eof, buf->tb_errno);
1306 		}
1307 	}
1308 
1309 	return (rv);
1310 }
1311 
1312 /*
1313  * ndmp_execute_cdb
1314  *
1315  * Main SCSI CDB execution program, this is used by message handler
1316  * for the NDMP tape/SCSI execute CDB requests. This function uses
1317  * USCSI interface to run the CDB command and sets all the CDB parameters
1318  * in the SCSI query before calling the USCSI ioctl. The result of the
1319  * CDB is returned in two places:
1320  *    cmd.uscsi_status		The status of CDB execution
1321  *    cmd.uscsi_rqstatus	The status of sense requests
1322  *    reply.error		The general errno (ioctl)
1323  *
1324  * Parameters:
1325  *   session (input) - session pointer
1326  *   adapter_name (input) - name of SCSI adapter
1327  *   sid (input) - SCSI target ID
1328  *   lun (input) - LUN number
1329  *   request (input) - NDMP client CDB request
1330  *
1331  * Returns:
1332  *   void
1333  */
1334 /*ARGSUSED*/
1335 void
1336 ndmp_execute_cdb(ndmpd_session_t *session, char *adapter_name, int sid, int lun,
1337     ndmp_execute_cdb_request *request)
1338 {
1339 	ndmp_execute_cdb_reply reply;
1340 	struct uscsi_cmd cmd;
1341 	int fd;
1342 	struct open_list *olp;
1343 	char rq_buf[255];
1344 
1345 	(void) memset((void *)&cmd, 0, sizeof (cmd));
1346 	(void) memset((void *)&reply, 0, sizeof (reply));
1347 	(void) memset((void *)rq_buf, 0, sizeof (rq_buf));
1348 
1349 	if (request->flags == NDMP_SCSI_DATA_IN) {
1350 		cmd.uscsi_flags = USCSI_READ | USCSI_RQENABLE;
1351 		if ((cmd.uscsi_bufaddr =
1352 		    ndmp_malloc(request->datain_len)) == 0) {
1353 			reply.error = NDMP_NO_MEM_ERR;
1354 			if (ndmp_send_response(session->ns_connection,
1355 			    NDMP_NO_ERR, (void *)&reply) < 0)
1356 				NDMP_LOG(LOG_DEBUG, "error sending"
1357 				    " scsi_execute_cdb reply.");
1358 			return;
1359 		}
1360 
1361 		cmd.uscsi_buflen = request->datain_len;
1362 	} else if (request->flags == NDMP_SCSI_DATA_OUT) {
1363 		cmd.uscsi_flags = USCSI_WRITE | USCSI_RQENABLE;
1364 		cmd.uscsi_bufaddr = request->dataout.dataout_val;
1365 		cmd.uscsi_buflen = request->dataout.dataout_len;
1366 	} else {
1367 		cmd.uscsi_flags = USCSI_RQENABLE;
1368 		cmd.uscsi_bufaddr = 0;
1369 		cmd.uscsi_buflen = 0;
1370 	}
1371 	cmd.uscsi_rqlen = sizeof (rq_buf);
1372 	cmd.uscsi_rqbuf = rq_buf;
1373 
1374 	cmd.uscsi_timeout = (request->timeout < 1000) ?
1375 	    1 : (request->timeout / 1000);
1376 
1377 	cmd.uscsi_cdb = (caddr_t)request->cdb.cdb_val;
1378 	cmd.uscsi_cdblen = request->cdb.cdb_len;
1379 
1380 	NDMP_LOG(LOG_DEBUG, "cmd: 0x%x, len: %d, flags: %d, datain_len: %d",
1381 	    request->cdb.cdb_val[0] & 0xff, request->cdb.cdb_len,
1382 	    request->flags, request->datain_len);
1383 	NDMP_LOG(LOG_DEBUG, "dataout_len: %d, timeout: %d",
1384 	    request->dataout.dataout_len, request->timeout);
1385 
1386 	if (request->cdb.cdb_len > 12) {
1387 		reply.error = NDMP_ILLEGAL_ARGS_ERR;
1388 		ndmp_send_reply(session->ns_connection, (void *) &reply,
1389 		    "sending execute_cdb reply");
1390 		if (request->flags == NDMP_SCSI_DATA_IN)
1391 			free(cmd.uscsi_bufaddr);
1392 		return;
1393 	}
1394 
1395 	reply.error = NDMP_NO_ERR;
1396 
1397 	if ((olp = ndmp_open_list_find(adapter_name, sid, lun)) != NULL) {
1398 		fd = olp->ol_fd;
1399 	} else {
1400 		reply.error = NDMP_DEV_NOT_OPEN_ERR;
1401 		ndmp_send_reply(session->ns_connection, (void *) &reply,
1402 		    "sending execute_cdb reply");
1403 		if (request->flags == NDMP_SCSI_DATA_IN)
1404 			free(cmd.uscsi_bufaddr);
1405 		return;
1406 	}
1407 
1408 	if (ioctl(fd, USCSICMD, &cmd) < 0) {
1409 		if (errno != EIO && errno != 0)
1410 			NDMP_LOG(LOG_ERR,
1411 			    "Failed to send command to device: %m");
1412 		NDMP_LOG(LOG_DEBUG, "ioctl(USCSICMD) error: %m");
1413 		if (cmd.uscsi_status == 0)
1414 			reply.error = NDMP_IO_ERR;
1415 	}
1416 
1417 	reply.status = cmd.uscsi_status;
1418 
1419 	if (request->flags == NDMP_SCSI_DATA_IN) {
1420 		reply.datain.datain_len = cmd.uscsi_buflen;
1421 		reply.datain.datain_val = cmd.uscsi_bufaddr;
1422 	} else {
1423 		reply.dataout_len = request->dataout.dataout_len;
1424 	}
1425 
1426 	reply.ext_sense.ext_sense_len = cmd.uscsi_rqlen - cmd.uscsi_rqresid;
1427 	reply.ext_sense.ext_sense_val = rq_buf;
1428 
1429 	if (ndmp_send_response(session->ns_connection, NDMP_NO_ERR,
1430 	    (void *)&reply) < 0)
1431 		NDMP_LOG(LOG_DEBUG, "Error sending scsi_execute_cdb reply.");
1432 
1433 	if (request->flags == NDMP_SCSI_DATA_IN)
1434 		free(cmd.uscsi_bufaddr);
1435 }
1436 
1437 
1438 /*
1439  * ndmp_stop_local_reader
1440  *
1441  * Stops a mover reader thread (for local backup only)
1442  *
1443  * Parameters:
1444  *   session (input) - session pointer
1445  *   cmds (input) - reader/writer command struct
1446  *
1447  * Returns:
1448  *   void
1449  */
1450 void
1451 ndmp_stop_local_reader(ndmpd_session_t *session, tlm_commands_t *cmds)
1452 {
1453 	if (session != NULL) {
1454 		if (session->ns_data.dd_sock == -1) {
1455 			/*
1456 			 * 2-way restore.
1457 			 */
1458 			NDMP_LOG(LOG_DEBUG, "2-way restore");
1459 			if (cmds != NULL && cmds->tcs_reader_count > 0) {
1460 				nlp_event_rv_set(session, -2);
1461 				nlp_event_nw(session);
1462 			}
1463 		}
1464 	}
1465 }
1466 
1467 
1468 /*
1469  * Stops a mover reader thread (for remote backup only)
1470  *
1471  * Parameters:
1472  *   session (input) - session pointer
1473  *   cmds (input) - reader/writer command struct
1474  *
1475  * Returns:
1476  *   void
1477  */
1478 void
1479 ndmp_stop_remote_reader(ndmpd_session_t *session)
1480 {
1481 	if (session != NULL) {
1482 		if (session->ns_data.dd_sock >= 0) {
1483 			/*
1484 			 * 3-way restore.
1485 			 */
1486 			NDMP_LOG(LOG_DEBUG,
1487 			    "data.sock: %d", session->ns_data.dd_sock);
1488 			(void) close(session->ns_data.dd_sock);
1489 			session->ns_data.dd_sock = -1;
1490 		}
1491 	}
1492 }
1493 
1494 
1495 /*
1496  * ndmp_wait_for_reader
1497  *
1498  * Wait for a reader until get done (busy wait)
1499  */
1500 void
1501 ndmp_wait_for_reader(tlm_commands_t *cmds)
1502 {
1503 	if (cmds == NULL) {
1504 		NDMP_LOG(LOG_DEBUG, "cmds == NULL");
1505 	} else {
1506 		NDMP_LOG(LOG_DEBUG,
1507 		    "reader_count: %d", cmds->tcs_reader_count);
1508 
1509 		while (cmds->tcs_reader_count > 0)
1510 			(void) sleep(1);
1511 	}
1512 }
1513 
1514 
1515 /*
1516  * ndmp_open_list_find
1517  *
1518  * Find a specific device in the open list
1519  *
1520  * Parameters:
1521  *   dev (input) - device name
1522  *   sid (input) - SCSI target ID
1523  *   lun (input) - LUN number
1524  *
1525  * Returns:
1526  *   pointer to the open list entry
1527  */
1528 struct open_list *
1529 ndmp_open_list_find(char *dev, int sid, int lun)
1530 {
1531 	struct ol_head *olhp;
1532 	struct open_list *olp;
1533 
1534 	if (dev == NULL || *dev == '\0') {
1535 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
1536 		return (NULL);
1537 	}
1538 
1539 	(void) mutex_lock(&ol_mutex);
1540 	olhp = &ol_head;
1541 	for (olp = LIST_FIRST(olhp); olp != NULL; olp = LIST_NEXT(olp, ol_q))
1542 		if (strcmp(olp->ol_devnm, dev) == 0 && olp->ol_sid == sid &&
1543 		    olp->ol_lun == lun) {
1544 			(void) mutex_unlock(&ol_mutex);
1545 			return (olp);
1546 		}
1547 
1548 	(void) mutex_unlock(&ol_mutex);
1549 	return (NULL);
1550 }
1551 
1552 
1553 /*
1554  * ndmp_open_list_add
1555  *
1556  * Add a specific device to the open list
1557  *
1558  * Parameters:
1559  *   conn (input) - connection pointer
1560  *   dev (input) - device name
1561  *   sid (input) - SCSI target ID
1562  *   lun (input) - LUN number
1563  *   fd (input) - the device file descriptor
1564  *
1565  * Returns:
1566  *   errno
1567  */
1568 int
1569 ndmp_open_list_add(ndmp_connection_t *conn, char *dev, int sid, int lun, int fd)
1570 {
1571 	int err;
1572 	struct ol_head *olhp;
1573 	struct open_list *olp;
1574 
1575 	if (dev == NULL || *dev == '\0') {
1576 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
1577 		return (EINVAL);
1578 	}
1579 	NDMP_LOG(LOG_DEBUG,
1580 	    "conn: 0x%08x, dev: %s, sid: %d, lun: %d", conn, dev, sid, lun);
1581 
1582 	err = 0;
1583 	olhp = &ol_head;
1584 
1585 	if ((olp = ndmp_open_list_find(dev, sid, lun)) != NULL) {
1586 		NDMP_LOG(LOG_DEBUG, "already in list");
1587 		/*
1588 		 * The adapter handle can be opened many times by the clients.
1589 		 * Only when the target is set, we must check and reject the
1590 		 * open request if the device is already being used by another
1591 		 * session.
1592 		 */
1593 		if (sid == -1)
1594 			olp->ol_nref++;
1595 		else
1596 			err = EBUSY;
1597 	} else if ((olp = ndmp_malloc(sizeof (struct open_list))) == NULL) {
1598 		err = ENOMEM;
1599 	} else if ((olp->ol_devnm = strdup(dev)) == NULL) {
1600 		NDMP_LOG(LOG_ERR, "Out of memory.");
1601 		free(olp);
1602 		err = ENOMEM;
1603 	} else {
1604 		olp->cl_conn = conn;
1605 		olp->ol_nref = 1;
1606 		olp->ol_sid = sid;
1607 		olp->ol_lun = lun;
1608 		if (fd > 0)
1609 			olp->ol_fd = fd;
1610 		else
1611 			olp->ol_fd = -1;
1612 		(void) mutex_lock(&ol_mutex);
1613 		LIST_INSERT_HEAD(olhp, olp, ol_q);
1614 		(void) mutex_unlock(&ol_mutex);
1615 	}
1616 
1617 	return (err);
1618 }
1619 
1620 
1621 /*
1622  * ndmp_open_list_del
1623  *
1624  * Delete a specific device from the open list
1625  *
1626  * Parameters:
1627  *   dev (input) - device name
1628  *   sid (input) - SCSI target ID
1629  *   lun (input) - LUN number
1630  *
1631  * Returns:
1632  *   errno
1633  */
1634 int
1635 ndmp_open_list_del(char *dev, int sid, int lun)
1636 {
1637 	struct open_list *olp;
1638 
1639 	if (dev == NULL || *dev == '\0') {
1640 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
1641 		return (EINVAL);
1642 	}
1643 	if ((olp = ndmp_open_list_find(dev, sid, lun)) == NULL) {
1644 		NDMP_LOG(LOG_DEBUG, "%s not found", dev);
1645 		return (ENOENT);
1646 	}
1647 
1648 	(void) mutex_lock(&ol_mutex);
1649 	if (--olp->ol_nref <= 0) {
1650 		NDMP_LOG(LOG_DEBUG,
1651 		    "Removed dev: %s, sid: %d, lun: %d", dev, sid, lun);
1652 		LIST_REMOVE(olp, ol_q);
1653 		free(olp->ol_devnm);
1654 		free(olp);
1655 	}
1656 	(void) mutex_unlock(&ol_mutex);
1657 
1658 	return (0);
1659 }
1660 
1661 
1662 /*
1663  * ndmp_open_list_release
1664  *
1665  * Close all the resources belonging to this connection.
1666  *
1667  * Parameters:
1668  *    ndmp_connection_t *conn : connection identifier
1669  *
1670  * Returns:
1671  *   void
1672  */
1673 void
1674 ndmp_open_list_release(ndmp_connection_t *conn)
1675 {
1676 	struct ol_head *olhp = &ol_head;
1677 	struct open_list *olp;
1678 	struct open_list *next;
1679 
1680 	(void) mutex_lock(&ol_mutex);
1681 	olp = LIST_FIRST(olhp);
1682 	while (olp != NULL) {
1683 		next = LIST_NEXT(olp, ol_q);
1684 		NDMP_LOG(LOG_DEBUG, "olp->conn 0x%08x", olp->cl_conn);
1685 		if (olp->cl_conn == conn) {
1686 			NDMP_LOG(LOG_DEBUG,
1687 			    "Removed dev: %s, sid: %d, lun: %d",
1688 			    olp->ol_devnm, olp->ol_sid, olp->ol_lun);
1689 			LIST_REMOVE(olp, ol_q);
1690 			if (olp->ol_fd > 0)
1691 				(void) close(olp->ol_fd);
1692 			free(olp->ol_devnm);
1693 			free(olp);
1694 		}
1695 		olp = next;
1696 	}
1697 	(void) mutex_unlock(&ol_mutex);
1698 }
1699 
1700 
1701 /*
1702  * ndmp_stop_buffer_worker
1703  *
1704  * Stop all reader and writer threads for a specific buffer.
1705  *
1706  * Parameters:
1707  *   session (input) - session pointer
1708  *
1709  * Returns:
1710  *   void
1711  */
1712 void
1713 ndmp_stop_buffer_worker(ndmpd_session_t *session)
1714 {
1715 	ndmp_lbr_params_t *nlp;
1716 	tlm_commands_t *cmds;
1717 
1718 	session->ns_tape.td_pos = 0;
1719 	if ((nlp = ndmp_get_nlp(session)) == NULL) {
1720 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1721 	} else {
1722 		cmds = &nlp->nlp_cmds;
1723 		if (cmds->tcs_command == NULL) {
1724 			NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL");
1725 		} else {
1726 			cmds->tcs_reader = cmds->tcs_writer = TLM_ABORT;
1727 			cmds->tcs_command->tc_reader = TLM_ABORT;
1728 			cmds->tcs_command->tc_writer = TLM_ABORT;
1729 			while (cmds->tcs_reader_count > 0 ||
1730 			    cmds->tcs_writer_count > 0) {
1731 				NDMP_LOG(LOG_DEBUG,
1732 				    "trying to stop buffer worker");
1733 				(void) sleep(1);
1734 			}
1735 		}
1736 	}
1737 }
1738 
1739 
1740 /*
1741  * ndmp_stop_reader_thread
1742  *
1743  * Stop only the reader threads of a specific buffer
1744  *
1745  * Parameters:
1746  *   session (input) - session pointer
1747  *
1748  * Returns:
1749  *   void
1750  */
1751 void
1752 ndmp_stop_reader_thread(ndmpd_session_t *session)
1753 {
1754 	ndmp_lbr_params_t *nlp;
1755 	tlm_commands_t *cmds;
1756 
1757 	if ((nlp = ndmp_get_nlp(session)) == NULL) {
1758 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1759 	} else {
1760 		cmds = &nlp->nlp_cmds;
1761 		if (cmds->tcs_command == NULL) {
1762 			NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL");
1763 		} else {
1764 			cmds->tcs_reader = TLM_ABORT;
1765 			cmds->tcs_command->tc_reader = TLM_ABORT;
1766 			while (cmds->tcs_reader_count > 0) {
1767 				NDMP_LOG(LOG_DEBUG,
1768 				    "trying to stop reader thread");
1769 				(void) sleep(1);
1770 			}
1771 		}
1772 	}
1773 }
1774 
1775 
1776 /*
1777  * ndmp_stop_reader_thread
1778  *
1779  * Stop only the writer threads of a specific buffer
1780  *
1781  * Parameters:
1782  *   session (input) - session pointer
1783  *
1784  * Returns:
1785  *   void
1786  */
1787 void
1788 ndmp_stop_writer_thread(ndmpd_session_t *session)
1789 {
1790 	ndmp_lbr_params_t *nlp;
1791 	tlm_commands_t *cmds;
1792 
1793 	if ((nlp = ndmp_get_nlp(session)) == NULL) {
1794 		NDMP_LOG(LOG_DEBUG, "nlp == NULL");
1795 	} else {
1796 		cmds = &nlp->nlp_cmds;
1797 		if (cmds->tcs_command == NULL) {
1798 			NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL");
1799 		} else {
1800 			cmds->tcs_writer = TLM_ABORT;
1801 			cmds->tcs_command->tc_writer = TLM_ABORT;
1802 			while (cmds->tcs_writer_count > 0) {
1803 				NDMP_LOG(LOG_DEBUG,
1804 				    "trying to stop writer thread");
1805 				(void) sleep(1);
1806 			}
1807 		}
1808 	}
1809 }
1810 
1811 
1812 /*
1813  * ndmp_free_reader_writer_ipc
1814  *
1815  * Free and release the reader/writer buffers and the IPC structure
1816  * for reader and writer threads.
1817  *
1818  * Parameters:
1819  *   session (input) - session pointer
1820  *
1821  * Returns:
1822  *   void
1823  */
1824 void
1825 ndmp_free_reader_writer_ipc(ndmpd_session_t *session)
1826 {
1827 	ndmp_lbr_params_t *nlp;
1828 	tlm_commands_t *cmds;
1829 
1830 	if ((nlp = ndmp_get_nlp(session)) != NULL) {
1831 		cmds = &nlp->nlp_cmds;
1832 		if (cmds->tcs_command != NULL) {
1833 			NDMP_LOG(LOG_DEBUG, "cmds->tcs_command->tc_ref: %d",
1834 			    cmds->tcs_command->tc_ref);
1835 			tlm_release_reader_writer_ipc(cmds->tcs_command);
1836 		}
1837 	}
1838 }
1839 
1840 
1841 /*
1842  * ndmp_waitfor_op
1843  *
1844  * Wait for a session reference count to drop to zero
1845  *
1846  * Parameters:
1847  *   session (input) - session pointer
1848  *
1849  * Returns:
1850  *   void
1851  */
1852 void
1853 ndmp_waitfor_op(ndmpd_session_t *session)
1854 {
1855 	if (session != NULL) {
1856 		while (session->ns_nref > 0) {
1857 			(void) sleep(1);
1858 			NDMP_LOG(LOG_DEBUG,
1859 			    "waiting for session nref: %d", session->ns_nref);
1860 		}
1861 	}
1862 }
1863 
1864 
1865 /*
1866  * ndmp_session_ref
1867  *
1868  * Increment the reference count of the session
1869  *
1870  * Parameters:
1871  *   session (input) - session pointer
1872  *
1873  * Returns:
1874  *   void
1875  */
1876 void
1877 ndmp_session_ref(ndmpd_session_t *session)
1878 {
1879 	(void) mutex_lock(&session->ns_lock);
1880 	session->ns_nref++;
1881 	(void) mutex_unlock(&session->ns_lock);
1882 }
1883 
1884 
1885 /*
1886  * ndmp_session_unref
1887  *
1888  * Decrement the reference count of the session
1889  *
1890  * Parameters:
1891  *   session (input) - session pointer
1892  *
1893  * Returns:
1894  *   void
1895  */
1896 void
1897 ndmp_session_unref(ndmpd_session_t *session)
1898 {
1899 	(void) mutex_lock(&session->ns_lock);
1900 	session->ns_nref--;
1901 	(void) mutex_unlock(&session->ns_lock);
1902 }
1903 
1904 
1905 /*
1906  * ndmp_addr2str_v3
1907  *
1908  * Convert the address type to a string
1909  *
1910  * Parameters:
1911  *   type (input) - address type
1912  *
1913  * Returns:
1914  *   type in string
1915  */
1916 char *
1917 ndmp_addr2str_v3(ndmp_addr_type type)
1918 {
1919 	char *rv;
1920 
1921 	switch (type) {
1922 	case NDMP_ADDR_LOCAL:
1923 		rv = "Local";
1924 		break;
1925 	case NDMP_ADDR_TCP:
1926 		rv = "TCP";
1927 		break;
1928 	case NDMP_ADDR_FC:
1929 		rv = "FC";
1930 		break;
1931 	case NDMP_ADDR_IPC:
1932 		rv = "IPC";
1933 		break;
1934 	default:
1935 		rv = "Unknown";
1936 	}
1937 
1938 	return (rv);
1939 }
1940 
1941 
1942 /*
1943  * ndmp_valid_v3addr_type
1944  *
1945  * Make sure that the NDMP address is from any of the
1946  * valid types
1947  *
1948  * Parameters:
1949  *   type (input) - address type
1950  *
1951  * Returns:
1952  *   1: valid
1953  *   0: invalid
1954  */
1955 boolean_t
1956 ndmp_valid_v3addr_type(ndmp_addr_type type)
1957 {
1958 	boolean_t rv;
1959 
1960 	switch (type) {
1961 	case NDMP_ADDR_LOCAL:
1962 	case NDMP_ADDR_TCP:
1963 	case NDMP_ADDR_FC:
1964 	case NDMP_ADDR_IPC:
1965 		rv = TRUE;
1966 		break;
1967 	default:
1968 		rv = FALSE;
1969 	}
1970 
1971 	return (rv);
1972 }
1973 
1974 
1975 /*
1976  * ndmp_copy_addr_v3
1977  *
1978  * Copy NDMP address from source to destination (V2 and V3 only)
1979  *
1980  * Parameters:
1981  *   dst (ouput) - destination address
1982  *   src (input) - source address
1983  *
1984  * Returns:
1985  *   void
1986  */
1987 void
1988 ndmp_copy_addr_v3(ndmp_addr_v3 *dst, ndmp_addr_v3 *src)
1989 {
1990 	dst->addr_type = src->addr_type;
1991 	switch (src->addr_type) {
1992 	case NDMP_ADDR_LOCAL:
1993 		/* nothing */
1994 		break;
1995 	case NDMP_ADDR_TCP:
1996 		dst->tcp_ip_v3 = htonl(src->tcp_ip_v3);
1997 		dst->tcp_port_v3 = src->tcp_port_v3;
1998 		break;
1999 	case NDMP_ADDR_FC:
2000 	case NDMP_ADDR_IPC:
2001 	default:
2002 		break;
2003 	}
2004 }
2005 
2006 
2007 /*
2008  * ndmp_copy_addr_v4
2009  *
2010  * Copy NDMP address from source to destination. V4 has a extra
2011  * environment list inside the address too which needs to be copied.
2012  *
2013  * Parameters:
2014  *   dst (ouput) - destination address
2015  *   src (input) - source address
2016  *
2017  * Returns:
2018  *   void
2019  */
2020 void
2021 ndmp_copy_addr_v4(ndmp_addr_v4 *dst, ndmp_addr_v4 *src)
2022 {
2023 	int i;
2024 
2025 	dst->addr_type = src->addr_type;
2026 	dst->tcp_len_v4 = src->tcp_len_v4;
2027 	switch (src->addr_type) {
2028 	case NDMP_ADDR_LOCAL:
2029 		/* nothing */
2030 		break;
2031 	case NDMP_ADDR_TCP:
2032 		dst->tcp_addr_v4 = ndmp_malloc(sizeof (ndmp_tcp_addr_v4) *
2033 		    src->tcp_len_v4);
2034 		if (dst->tcp_addr_v4 == 0)
2035 			return;
2036 
2037 		for (i = 0; i < src->tcp_len_v4; i++) {
2038 			dst->tcp_ip_v4(i) = htonl(src->tcp_ip_v4(i));
2039 			dst->tcp_port_v4(i) = src->tcp_port_v4(i);
2040 			dst->tcp_env_v4(i).addr_env_len = 0; /* Solaris */
2041 			dst->tcp_env_v4(i).addr_env_val = 0; /* Solaris */
2042 		}
2043 		break;
2044 	case NDMP_ADDR_FC:
2045 	case NDMP_ADDR_IPC:
2046 	default:
2047 		break;
2048 	}
2049 }
2050 
2051 
2052 /*
2053  * ndmp_connect_sock_v3
2054  *
2055  * Creates a socket and connects to the specified address/port
2056  *
2057  * Parameters:
2058  *   addr (input) - IP address
2059  *   port (input) - port number
2060  *
2061  * Returns:
2062  *   0: on success
2063  *  -1: otherwise
2064  */
2065 int
2066 ndmp_connect_sock_v3(ulong_t addr, ushort_t port)
2067 {
2068 	int sock;
2069 	struct sockaddr_in sin;
2070 	int flag = 1;
2071 
2072 	NDMP_LOG(LOG_DEBUG, "addr %s:%d", inet_ntoa(IN_ADDR(addr)), port);
2073 
2074 	sock = socket(AF_INET, SOCK_STREAM, 0);
2075 	if (sock < 0) {
2076 		NDMP_LOG(LOG_DEBUG, "Socket error: %m");
2077 		return (-1);
2078 	}
2079 
2080 	(void) memset((void *) &sin, 0, sizeof (sin));
2081 	sin.sin_family = AF_INET;
2082 	sin.sin_addr.s_addr = htonl(addr);
2083 	sin.sin_port = htons(port);
2084 	if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
2085 		NDMP_LOG(LOG_DEBUG, "Connect error: %m");
2086 		(void) close(sock);
2087 		sock = -1;
2088 	} else {
2089 		if (ndmp_sbs > 0)
2090 			ndmp_set_socket_snd_buf(sock, ndmp_sbs*KILOBYTE);
2091 		if (ndmp_rbs > 0)
2092 			ndmp_set_socket_rcv_buf(sock, ndmp_rbs*KILOBYTE);
2093 
2094 		ndmp_set_socket_nodelay(sock);
2095 		(void) setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag,
2096 		    sizeof (flag));
2097 
2098 		NDMP_LOG(LOG_DEBUG, "sock %d", sock);
2099 	}
2100 
2101 	return (sock);
2102 }
2103 
2104 /*
2105  * ndmp_create_socket
2106  *
2107  * Creates a socket for listening for accepting data connections.
2108  *
2109  * Parameters:
2110  *   session (input)  - session pointer.
2111  *   addr    (output) - location to store address of socket.
2112  *   port    (output) - location to store port of socket.
2113  *
2114  * Returns:
2115  *   0 - success.
2116  *  -1 - error.
2117  */
2118 int
2119 ndmp_create_socket(ulong_t *addr, ushort_t *port)
2120 {
2121 	char *p;
2122 	int length;
2123 	int sd;
2124 	struct sockaddr_in sin;
2125 
2126 	/* Try the user's prefered NIC IP address */
2127 	p = ndmpd_get_prop(NDMP_MOVER_NIC);
2128 
2129 	/* Try host's IP address */
2130 	if (!p || *p == 0)
2131 		p = gethostaddr();
2132 
2133 	/* Try default NIC's IP address (if DNS failed) */
2134 	if (!p)
2135 		p = get_default_nic_addr();
2136 
2137 	/* Fail if no IP can be obtained */
2138 	if (!p) {
2139 		NDMP_LOG(LOG_ERR, "Undetermined network port.");
2140 		return (-1);
2141 	}
2142 
2143 	*addr = inet_addr(p);
2144 
2145 	sd = socket(AF_INET, SOCK_STREAM, 0);
2146 	if (sd < 0) {
2147 		NDMP_LOG(LOG_DEBUG, "Socket error: %m");
2148 		return (-1);
2149 	}
2150 	sin.sin_family = AF_INET;
2151 	sin.sin_addr.s_addr = INADDR_ANY;
2152 	sin.sin_port = 0;
2153 	length = sizeof (sin);
2154 
2155 	if (bind(sd, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
2156 		NDMP_LOG(LOG_DEBUG, "Bind error: %m");
2157 		(void) close(sd);
2158 		sd = -1;
2159 	} else if (getsockname(sd, (struct sockaddr *)&sin, &length) < 0) {
2160 		NDMP_LOG(LOG_DEBUG, "getsockname error: %m");
2161 		(void) close(sd);
2162 		sd = -1;
2163 	} else if (listen(sd, 5) < 0) {
2164 		NDMP_LOG(LOG_DEBUG, "Listen error: %m");
2165 		(void) close(sd);
2166 		sd = -1;
2167 	} else
2168 		*port = sin.sin_port;
2169 
2170 	return (sd);
2171 }
2172 
2173 
2174 /*
2175  * cctime
2176  *
2177  * Convert the specified time into a string.  It's like
2178  * ctime(), but:
2179  *     - chops the trailing '\n' of ctime.
2180  *     - and returns "the epoch" if time is 0.
2181  *
2182  * Returns:
2183  *     "": invalid argument.
2184  *     "the epoch": if time is 0.
2185  *     string format of the time.
2186  */
2187 char *
2188 cctime(time_t *t)
2189 {
2190 	char *bp, *cp;
2191 	static char tbuf[BUFSIZ];
2192 
2193 	if (!t)
2194 		return ("");
2195 
2196 	if (*t == (time_t)0)
2197 		return ("the epoch");
2198 
2199 	if ((bp = ctime_r(t, tbuf, BUFSIZ)) == NULL)
2200 		return ("");
2201 
2202 	cp = strchr(bp, '\n');
2203 	if (cp)
2204 		*cp = '\0';
2205 
2206 	return (bp);
2207 }
2208 
2209 
2210 /*
2211  * ndmp_new_job_name
2212  *
2213  * Create a job name for each backup/restore to keep track
2214  *
2215  * Parameters:
2216  *   jname (output) - job name
2217  *
2218  * Returns:
2219  *   jname
2220  */
2221 char *
2222 ndmp_new_job_name(char *jname)
2223 {
2224 	if (jname != NULL) {
2225 		(void) snprintf(jname, TLM_MAX_BACKUP_JOB_NAME, "%s%d",
2226 		    NDMP_RCF_BASENAME, ndmp_job_cnt++);
2227 		NDMP_LOG(LOG_DEBUG, "jname: \"%s\"", jname);
2228 	}
2229 
2230 	return (jname);
2231 }
2232 
2233 
2234 /*
2235  * fs_is_valid_logvol
2236  *
2237  * Check if the log path exists
2238  *
2239  * Parameters:
2240  *   path (input) - log path
2241  *
2242  * Returns:
2243  *   FALSE: invalid
2244  *   TRUE: valid
2245  */
2246 boolean_t
2247 fs_is_valid_logvol(char *path)
2248 {
2249 	struct stat64 st;
2250 
2251 	if (stat64(path, &st) < 0)
2252 		return (FALSE);
2253 
2254 	return (TRUE);
2255 }
2256 
2257 
2258 /*
2259  * ndmpd_mk_temp
2260  *
2261  * Make a temporary file using the working directory path and the
2262  * jobname
2263  *
2264  * Parameters:
2265  *   buf (output) - the temporary file name path
2266  *
2267  * Returns:
2268  *   buf
2269  */
2270 char *
2271 ndmpd_mk_temp(char *buf)
2272 {
2273 	char fname[TLM_MAX_BACKUP_JOB_NAME];
2274 	const char *dir;
2275 	char *rv;
2276 
2277 	if (!buf)
2278 		return (NULL);
2279 
2280 	dir = ndmpd_get_prop(NDMP_DEBUG_PATH);
2281 	if (dir == 0 || *dir == '\0') {
2282 		NDMP_LOG(LOG_DEBUG, "NDMP work path not specified");
2283 		return (0);
2284 	}
2285 
2286 	if (!fs_is_valid_logvol((char *)dir)) {
2287 		NDMP_LOG(LOG_ERR,
2288 		    "Log file path cannot be on system volumes.");
2289 		return (0);
2290 	}
2291 
2292 	dir += strspn(dir, " \t");
2293 	if (!*dir) {
2294 		NDMP_LOG(LOG_DEBUG, "NDMP work path not specified");
2295 		return (0);
2296 	}
2297 
2298 	rv = buf;
2299 	(void) ndmp_new_job_name(fname);
2300 	(void) tlm_cat_path(buf, (char *)dir, fname);
2301 
2302 	return (rv);
2303 }
2304 
2305 
2306 /*
2307  * ndmpd_make_bk_dir_path
2308  *
2309  * Make a directory path for temporary files under the NDMP
2310  * working directory.
2311  *
2312  * Parameters:
2313  *   buf (output) - result path
2314  *   fname (input) - the file name
2315  *
2316  * Returns:
2317  *   buf
2318  */
2319 char *
2320 ndmpd_make_bk_dir_path(char *buf, char *fname)
2321 {
2322 	const char *p;
2323 	char *name;
2324 	char path[PATH_MAX];
2325 
2326 	if (!buf || !fname || !*fname)
2327 		return (NULL);
2328 
2329 	p = ndmpd_get_prop(NDMP_DEBUG_PATH);
2330 	if (p == NULL || *p == '\0' || !fs_is_valid_logvol((char *)p)) {
2331 		return (NULL);
2332 	}
2333 
2334 	(void) strlcpy(path, (char *)p, PATH_MAX);
2335 	(void) trim_whitespace(path);
2336 
2337 	if ((name = strrchr(fname, '/')) == 0)
2338 		name = fname;
2339 
2340 	(void) tlm_cat_path(buf, path, name);
2341 	return (buf);
2342 }
2343 
2344 
2345 /*
2346  * ndmp_is_chkpnt_root
2347  *
2348  * Is this a root checkpoint (snapshot) directory.
2349  * Note: a temporary function
2350  */
2351 boolean_t
2352 ndmp_is_chkpnt_root(char *path)
2353 {
2354 	struct stat64 st;
2355 
2356 	if (stat64(path, &st) != 0) {
2357 		NDMP_LOG(LOG_DEBUG, "Couldn't stat path \"%s\"", path);
2358 		return (TRUE);
2359 	}
2360 	return (FALSE);
2361 }
2362 
2363 
2364 /*
2365  * ndmpd_make_exc_list
2366  *
2367  * Make a list of files that should not be backed up.
2368  *
2369  * Parameters:
2370  *   void
2371  *
2372  * Returns:
2373  *   list - array of character strings
2374  */
2375 char **
2376 ndmpd_make_exc_list(void)
2377 {
2378 	char *val, **cpp;
2379 	int i, n;
2380 
2381 	n = sizeof (exls);
2382 	if ((cpp = ndmp_malloc(n)) != NULL) {
2383 		for (i = 0; exls[i] != NULL; i++)
2384 			cpp[i] = exls[i];
2385 
2386 		/*
2387 		 * If ndmpd_get_prop returns NULL, the array will be
2388 		 * null-terminated.
2389 		 */
2390 		val = ndmpd_get_prop(NDMP_DEBUG_PATH);
2391 		cpp[i] = val;
2392 	}
2393 
2394 	return (cpp);
2395 }
2396 
2397 
2398 /*
2399  * ndmp_get_bk_dir_ino
2400  *
2401  * Get the inode number of the backup directory
2402  */
2403 int
2404 ndmp_get_bk_dir_ino(ndmp_lbr_params_t *nlp)
2405 {
2406 	int rv;
2407 	struct stat64 st;
2408 
2409 	if (stat64(nlp->nlp_backup_path, &st) != 0) {
2410 		rv = -1;
2411 		NDMP_LOG(LOG_DEBUG, "Getting inode # of \"%s\"",
2412 		    nlp->nlp_backup_path);
2413 	} else {
2414 		rv = 0;
2415 		nlp->nlp_bkdirino = st.st_ino;
2416 		NDMP_LOG(LOG_DEBUG, "nlp_bkdirino: %lu",
2417 		    (uint_t)nlp->nlp_bkdirino);
2418 	}
2419 
2420 	return (rv);
2421 }
2422 
2423 
2424 /*
2425  * ndmp_check_utf8magic
2426  *
2427  * Check if the magic string for exists in the tar header. This
2428  * magic string (which also indicates that the file names are in
2429  * UTF8 format) is used as a crest to indetify our own tapes.
2430  * This checking is always done before all restores except DAR
2431  * restores.
2432  */
2433 boolean_t
2434 ndmp_check_utf8magic(tlm_cmd_t *cmd)
2435 {
2436 	char *cp;
2437 	int err, len, actual_size;
2438 
2439 	if (cmd == NULL) {
2440 		NDMP_LOG(LOG_DEBUG, "cmd == NULL");
2441 		return (FALSE);
2442 	}
2443 	if (cmd->tc_buffers == NULL) {
2444 		NDMP_LOG(LOG_DEBUG, "cmd->tc_buffers == NULL");
2445 		return (FALSE);
2446 	}
2447 
2448 	/* wait until the first buffer gets full. */
2449 	tlm_buffer_in_buf_wait(cmd->tc_buffers);
2450 
2451 	err = actual_size = 0;
2452 	cp = tlm_get_read_buffer(RECORDSIZE, &err, cmd->tc_buffers,
2453 	    &actual_size);
2454 	if (cp == NULL) {
2455 		NDMP_LOG(LOG_DEBUG, "Can't read from buffers, err: %d", err);
2456 		return (FALSE);
2457 	}
2458 	len = strlen(NDMPUTF8MAGIC);
2459 	if (actual_size < len) {
2460 		NDMP_LOG(LOG_DEBUG, "Not enough data in the buffers");
2461 		return (FALSE);
2462 	}
2463 
2464 	return ((strncmp(cp, NDMPUTF8MAGIC, len) == 0) ? TRUE : FALSE);
2465 }
2466 
2467 
2468 /*
2469  * ndmp_get_cur_bk_time
2470  *
2471  * Get the backup checkpoint time.
2472  */
2473 int
2474 ndmp_get_cur_bk_time(ndmp_lbr_params_t *nlp, time_t *tp, char *jname)
2475 {
2476 	int err;
2477 
2478 	if (!nlp || !nlp->nlp_backup_path || !tp) {
2479 		NDMP_LOG(LOG_DEBUG, "Invalid argument");
2480 		return (-1);
2481 	}
2482 
2483 	if (!fs_is_chkpnt_enabled(nlp->nlp_backup_path)) {
2484 		NDMP_LOG(LOG_DEBUG, "Not a chkpnt volume %s",
2485 		    nlp->nlp_backup_path);
2486 		*tp = time(NULL);
2487 		return (0);
2488 	}
2489 
2490 	err = tlm_get_chkpnt_time(nlp->nlp_backup_path, !NLP_ISCHKPNTED(nlp),
2491 	    tp, jname);
2492 	if (err != 0) {
2493 		NDMP_LOG(LOG_DEBUG, "Can't checkpoint time");
2494 	} else {
2495 		NDMP_LOG(LOG_DEBUG, "%s", cctime(tp));
2496 	}
2497 
2498 	return (err);
2499 }
2500 
2501 
2502 /*
2503  * get_relative_path
2504  */
2505 char *
2506 ndmp_get_relative_path(char *base, char *fullpath)
2507 {
2508 	char *p = fullpath;
2509 
2510 	if (!base || !*base)
2511 		return (fullpath);
2512 
2513 	while (*base) {
2514 		if (*base != *p)
2515 			break;
2516 		p++; base++;
2517 	}
2518 
2519 	if (*p == '/')
2520 		p++;
2521 
2522 	return ((*base) ? fullpath : p);
2523 }
2524 
2525 
2526 /*
2527  * ndmp_get_nlp
2528  *
2529  * Get NDMP local backup parameters
2530  *
2531  * Parameter:
2532  *   session cooke
2533  *
2534  * Returns:
2535  *   LBR structure
2536  */
2537 ndmp_lbr_params_t *
2538 ndmp_get_nlp(void *cookie)
2539 {
2540 	if (cookie == NULL)
2541 		return (NULL);
2542 
2543 	return (((ndmpd_session_t *)cookie)->ns_ndmp_lbr_params);
2544 }
2545 
2546 
2547 /*
2548  * is_tape_unit_ready
2549  *
2550  * Check if the tape device is ready or not
2551  */
2552 boolean_t
2553 is_tape_unit_ready(char *adptnm, int dev_id)
2554 {
2555 	int try;
2556 	int fd = 0;
2557 
2558 	try = TUR_MAX_TRY;
2559 	if (dev_id <= 0) {
2560 		if ((fd = open(adptnm, O_RDONLY | O_NDELAY)) < 0)
2561 			return (FALSE);
2562 	} else {
2563 		fd = dev_id;
2564 	}
2565 	do {
2566 		if (scsi_test_unit_ready(fd) >= 0) {
2567 			NDMP_LOG(LOG_DEBUG, "Unit is ready");
2568 
2569 			if (dev_id <= 0)
2570 				(void) close(fd);
2571 
2572 			return (TRUE);
2573 		}
2574 
2575 		NDMP_LOG(LOG_DEBUG, "Unit not ready");
2576 		(void) usleep(TUR_WAIT);
2577 
2578 	} while (--try > 0);
2579 
2580 	if (dev_id <= 0)
2581 		(void) close(fd);
2582 
2583 	NDMP_LOG(LOG_DEBUG, "Unit didn't get ready");
2584 	return (FALSE);
2585 }
2586 
2587 
2588 /*
2589  * scsi_test_unit_ready
2590  *
2591  * This is for Test Unit Read, without this function, the only
2592  * impact is getting EBUSY's before each operation which we have
2593  * busy waiting loops checking EBUSY error code.
2594  */
2595 static int
2596 scsi_test_unit_ready(int dev_id)
2597 {
2598 	struct uscsi_cmd ucmd;
2599 	union scsi_cdb cdb;
2600 	int retval;
2601 
2602 	(void) memset(&ucmd, 0, sizeof (struct uscsi_cmd));
2603 	(void) memset(&cdb, 0, sizeof (union scsi_cdb));
2604 	cdb.scc_cmd = SCMD_TEST_UNIT_READY;
2605 	ucmd.uscsi_cdb = (caddr_t)&cdb;
2606 	ucmd.uscsi_cdblen = CDB_GROUP0;
2607 	ucmd.uscsi_flags |= USCSI_SILENT;
2608 	ucmd.uscsi_timeout = 60;	/* Allow maximum 1 min */
2609 
2610 	retval = ioctl(dev_id, USCSICMD, &ucmd);
2611 
2612 	if (retval != 0 && errno != EIO) {
2613 		NDMP_LOG(LOG_ERR,
2614 		    "Failed to send inquiry request to device: %m.");
2615 		NDMP_LOG(LOG_DEBUG, "Inquiry request failed for"
2616 		    " dev_id:%d err=%d -%m", dev_id, errno);
2617 		retval = -errno;
2618 	} else
2619 		retval = -(ucmd.uscsi_status);
2620 
2621 	return (retval);
2622 }
2623 
2624 
2625 /*
2626  * ndmp_load_params
2627  *
2628  * Load the parameters.
2629  *
2630  * Parameter:
2631  *   void
2632  *
2633  * Returns:
2634  *   void
2635  */
2636 void
2637 ndmp_load_params(void)
2638 {
2639 	ndmp_dump_path_node = ndmpd_get_prop_yorn(NDMP_DUMP_PATHNODE_ENV) ?
2640 	    TRUE : FALSE;
2641 	ndmp_tar_path_node = ndmpd_get_prop_yorn(NDMP_TAR_PATHNODE_ENV) ?
2642 	    TRUE : FALSE;
2643 	ndmp_ignore_ctime =
2644 	    ndmpd_get_prop_yorn(NDMP_IGNCTIME_ENV) ? TRUE : FALSE;
2645 	ndmp_include_lmtime = ndmpd_get_prop_yorn(NDMP_INCLMTIME_ENV) ?
2646 	    TRUE : FALSE;
2647 	ndmp_max_tok_seq = atoi(ndmpd_get_prop_default(NDMP_MAXSEQ_ENV, "9"));
2648 
2649 	ndmp_full_restore_path = ndmpd_get_prop_yorn(NDMP_FULL_RESTORE_PATH) ?
2650 	    TRUE : FALSE;
2651 
2652 	ndmp_fhinode = ndmpd_get_prop_yorn(NDMP_FHIST_INCR_ENV) ? TRUE : FALSE;
2653 
2654 	/* Get the value from ndmp SMF property. */
2655 	ndmp_dar_support = ndmpd_get_prop_yorn(NDMP_DAR_SUPPORT);
2656 
2657 	if ((ndmp_ver = atoi(ndmpd_get_prop(NDMP_VERSION_ENV))) == 0)
2658 		ndmp_ver = NDMPVER;
2659 }
2660 
2661 /*
2662  * randomize
2663  *
2664  * Randomize the contents of a buffer
2665  *
2666  * Parameter:
2667  *   buffer (output) - destination buffer
2668  *   size (input) - buffer size
2669  *
2670  * Returns:
2671  *   void
2672  */
2673 void
2674 randomize(unsigned char *buffer, int size)
2675 {
2676 	/* LINTED improper alignment */
2677 	unsigned int *p = (unsigned int *)buffer;
2678 	unsigned int dwlen = size / sizeof (unsigned int);
2679 	unsigned int remlen = size % sizeof (unsigned int);
2680 	unsigned int tmp;
2681 	unsigned int i;
2682 
2683 	for (i = 0; i < dwlen; i++)
2684 		*p++ = random();
2685 
2686 	if (remlen) {
2687 		tmp = random();
2688 		(void) memcpy(p, &tmp, remlen);
2689 	}
2690 }
2691 
2692 /*
2693  * ndmpd_get_file_entry_type
2694  *
2695  * Converts the mode to the NDMP file type
2696  *
2697  * Parameter:
2698  *   mode (input) - file mode
2699  *   ftype (output) - file type
2700  *
2701  * Returns:
2702  *   void
2703  */
2704 void
2705 ndmpd_get_file_entry_type(int mode, ndmp_file_type *ftype)
2706 {
2707 	switch (mode & S_IFMT) {
2708 	case S_IFIFO:
2709 		*ftype = NDMP_FILE_FIFO;
2710 		break;
2711 	case S_IFCHR:
2712 		*ftype = NDMP_FILE_CSPEC;
2713 		break;
2714 	case S_IFDIR:
2715 		*ftype = NDMP_FILE_DIR;
2716 		break;
2717 	case S_IFBLK:
2718 		*ftype = NDMP_FILE_BSPEC;
2719 		break;
2720 	case S_IFREG:
2721 		*ftype = NDMP_FILE_REG;
2722 		break;
2723 	case S_IFLNK:
2724 		*ftype = NDMP_FILE_SLINK;
2725 		break;
2726 	default:
2727 		*ftype = NDMP_FILE_SOCK;
2728 		break;
2729 	}
2730 }
2731 
2732 /*
2733  * Set a private data in the plugin context
2734  */
2735 void
2736 ndmp_context_set_specific(ndmp_context_t *nctx, void *ptr)
2737 {
2738 	nctx->nc_pldata = ptr;
2739 }
2740 
2741 /*
2742  * Get a private data in the plugin context
2743  */
2744 void *
2745 ndmp_context_get_specific(ndmp_context_t *nctx)
2746 {
2747 	return (nctx->nc_pldata);
2748 }
2749 
2750 ndmpd_backup_type_t
2751 ndmp_get_backup_type(ndmp_context_t *ctx)
2752 {
2753 	ndmpd_session_t *session = (ndmpd_session_t *)ctx->nc_ddata;
2754 
2755 	return (session->ns_butype);
2756 }
2757