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