/* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * BSD 3 Clause License * * Copyright (c) 2007, The Storage Networking Industry Association. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * - Neither the name of The Storage Networking Industry Association (SNIA) * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* Copyright (c) 2007, The Storage Networking Industry Association. */ /* Copyright (c) 1996, 1997 PDC, Network Appliance. All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include "ndmpd.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tlm.h" /* * Mutex to protect Nlp */ mutex_t nlp_mtx; /* * Patchable socket buffer sizes in kilobytes. * ssb: send buffer size. * rsb: receive buffer size. */ int ndmp_sbs = 60; int ndmp_rbs = 60; /* * Force to backup all the intermediate directories leading to an object * to be backed up in 'dump' format backup. */ boolean_t ndmp_dump_path_node = FALSE; /* * Force to backup all the intermediate directories leading to an object * to be backed up in 'tar' format backup. */ boolean_t ndmp_tar_path_node = FALSE; /* * Should the 'st_ctime' be ignored during incremental level backup? */ boolean_t ndmp_ignore_ctime = FALSE; /* * Should the 'st_lmtime' be included during incremental level backup? */ boolean_t ndmp_include_lmtime = FALSE; /* * Force to send the file history node entries along with the file history * dir entries for all directories containing the changed files to the client * for incremental backup. * * Note: This variable is added to support Bakbone Software's Netvault DMA * which expects to get the FH ADD NODES for all upper directories which * contain the changed files in incremental backup along with the FH ADD DIRS. */ boolean_t ndmp_fhinode = FALSE; /* * Maximum permitted sequence number in the token-based backup. The * value of this variable can be changed by the administrator and is * saved in the NDMP configuration file. */ static int ndmp_max_tok_seq = NDMP_MAX_TOKSEQ; /* * Force backup directories in incremental backups. If the * directory is not modified itself, it's not backed up by * default. */ int ndmp_force_bk_dirs = 0; /* * Keeps track of the open SCSI (including tape and robot) devices. * When a SCSI device is opened its name must be added to this list and * when it's closed its name must be removed from this list. The main * purpose of this list is the robot device. If the robot devices are not * attached in SASD layer, Local Backup won't see them. If they are * attached and we open the robot devices, then wrong commands are sent * to robot by SASD since it assumes that the robot is a tape (sequential * access) device. */ struct open_list { LIST_ENTRY(open_list) ol_q; int ol_nref; char *ol_devnm; int ol_sid; int ol_lun; int ol_fd; ndmp_connection_t *cl_conn; }; LIST_HEAD(ol_head, open_list); /* * Head of the opened SCSI devices list. */ static struct ol_head ol_head; mutex_t ol_mutex = DEFAULTMUTEX; /* * List of things to be exluded from backup. */ static char *exls[] = { EXCL_PROC, EXCL_TMP, NULL, /* reserved for a copy of the "backup.directory" */ NULL }; /* * The counter for creating unique names with "ndmp.%d" format. */ #define NDMP_RCF_BASENAME "ndmp." static int ndmp_job_cnt = 0; static int scsi_test_unit_ready(int dev_id); /* * ndmpd_add_file_handler * * Adds a file handler to the file handler list. * The file handler list is used by ndmpd_api_dispatch. * * Parameters: * session (input) - session pointer. * cookie (input) - opaque data to be passed to file hander when called. * fd (input) - file descriptor. * mode (input) - bitmask of the following: * 1 = watch file for ready for reading * 2 = watch file for ready for writing * 4 = watch file for exception * class (input) - handler class. (HC_CLIENT, HC_MOVER, HC_MODULE) * func (input) - function to call when the file meets one of the * conditions specified by mode. * * Returns: * 0 - success. * -1 - error. */ int ndmpd_add_file_handler(ndmpd_session_t *session, void *cookie, int fd, ulong_t mode, ulong_t class, ndmpd_file_handler_func_t *func) { ndmpd_file_handler_t *new; new = ndmp_malloc(sizeof (ndmpd_file_handler_t)); if (new == 0) return (-1); new->fh_cookie = cookie; new->fh_fd = fd; new->fh_mode = mode; new->fh_class = class; new->fh_func = func; new->fh_next = session->ns_file_handler_list; session->ns_file_handler_list = new; return (0); } /* * ndmpd_remove_file_handler * * Removes a file handler from the file handler list. * * Parameters: * session (input) - session pointer. * fd (input) - file descriptor. * * Returns: * 0 - success. * -1 - error. */ int ndmpd_remove_file_handler(ndmpd_session_t *session, int fd) { ndmpd_file_handler_t **last; ndmpd_file_handler_t *handler; last = &session->ns_file_handler_list; while (*last != 0) { handler = *last; if (handler->fh_fd == fd) { *last = handler->fh_next; (void) free(handler); return (1); } last = &handler->fh_next; } return (0); } /* * ndmp_connection_closed * * If the connection closed or not. * * Parameters: * fd (input) : file descriptor * * Returns: * 0 - connection is still valid * 1 - connection is not valid anymore * -1 - Internal kernel error */ int ndmp_connection_closed(int fd) { fd_set fds; int closed, ret; struct timeval timeout; if (fd < 0) /* We are not using the mover */ return (-1); timeout.tv_sec = 0; timeout.tv_usec = 1000; FD_ZERO(&fds); FD_SET(fd, &fds); ret = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); closed = (ret == -1 && errno == EBADF); return (closed); } /* * ndmp_check_mover_state * * Checks the mover connection status and sends an appropriate * NDMP message to client based on that. * * Parameters: * ndmpd_session_t *session (input) : session pointer * * Returns: * void. */ void ndmp_check_mover_state(ndmpd_session_t *session) { int moverfd; /* * NDMPV3 Spec (Three-way restore): * Once all of the files have been recovered, NDMP DATA Server closes * the connection to the mover on the NDMP TAPE Server. THEN * The NDMP client should receive an NDMP_NOTIFY_MOVER_HALTED message * with an NDMP_MOVER_CONNECT_CLOSED reason from the NDMP TAPE Server */ moverfd = session->ns_mover.md_sock; /* If connection is closed by the peer */ if (moverfd >= 0 && session->ns_mover.md_mode == NDMP_MOVER_MODE_WRITE) { int closed, reason; closed = ndmp_connection_closed(moverfd); if (closed) { /* Connection closed or internal error */ if (closed > 0) { NDMP_LOG(LOG_DEBUG, "ndmp mover: connection closed by peer"); reason = NDMP_MOVER_HALT_CONNECT_CLOSED; } else { NDMP_LOG(LOG_DEBUG, "ndmp mover: Internal error"); reason = NDMP_MOVER_HALT_INTERNAL_ERROR; } ndmpd_mover_error(session, reason); } } } /* * ndmpd_select * * Calls select on the the set of file descriptors from the * file handler list masked by the fd_class argument. * Calls the file handler function for each * file descriptor that is ready for I/O. * * Parameters: * session (input) - session pointer. * block (input) - if TRUE, ndmpd_select waits until at least one * file descriptor is ready for I/O. Otherwise, * it returns immediately if no file descriptors are * ready for I/O. * class_mask (input) - bit mask of handler classes to be examined. * Provides for excluding some of the handlers from * being called. * * Returns: * -1 - error. * 0 - no handlers were called. * 1 - at least one handler was called. */ int ndmpd_select(ndmpd_session_t *session, boolean_t block, ulong_t class_mask) { fd_set rfds; fd_set wfds; fd_set efds; int n; ndmpd_file_handler_t *handler; struct timeval timeout; nlp_event_rv_set(session, 0); if (session->ns_file_handler_list == 0) return (0); /* * If select should be blocked, then we poll every ten seconds. * The reason is in case of three-way restore we should be able * to detect if the other end closed the connection or not. * NDMP client(DMA) does not send any information about the connection * that was closed in the other end. */ if (block == TRUE) timeout.tv_sec = 10; else timeout.tv_sec = 0; timeout.tv_usec = 0; do { /* Create the fd_sets for select. */ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); for (handler = session->ns_file_handler_list; handler != 0; handler = handler->fh_next) { if ((handler->fh_class & class_mask) == 0) continue; if (handler->fh_mode & NDMPD_SELECT_MODE_READ) FD_SET(handler->fh_fd, &rfds); if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE) FD_SET(handler->fh_fd, &wfds); if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION) FD_SET(handler->fh_fd, &efds); } ndmp_check_mover_state(session); n = select(FD_SETSIZE, &rfds, &wfds, &efds, &timeout); } while (n == 0 && block == TRUE); if (n < 0) { int connection_fd = ndmp_get_fd(session->ns_connection); if (errno == EINTR) return (0); NDMP_LOG(LOG_DEBUG, "Select error: %m"); for (handler = session->ns_file_handler_list; handler != 0; handler = handler->fh_next) { if ((handler->fh_class & class_mask) == 0) continue; if (handler->fh_mode & NDMPD_SELECT_MODE_READ) { if (FD_ISSET(handler->fh_fd, &rfds) && connection_fd == handler->fh_fd) session->ns_eof = TRUE; } if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE) { if (FD_ISSET(handler->fh_fd, &wfds) && connection_fd == handler->fh_fd) session->ns_eof = TRUE; } if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION) { if (FD_ISSET(handler->fh_fd, &efds) && connection_fd == handler->fh_fd) session->ns_eof = TRUE; } } nlp_event_rv_set(session, -1); return (-1); } if (n == 0) return (0); handler = session->ns_file_handler_list; while (handler != 0) { ulong_t mode = 0; if ((handler->fh_class & class_mask) == 0) { handler = handler->fh_next; continue; } if (handler->fh_mode & NDMPD_SELECT_MODE_READ) { if (FD_ISSET(handler->fh_fd, &rfds)) { mode |= NDMPD_SELECT_MODE_READ; FD_CLR(handler->fh_fd, &rfds); } } if (handler->fh_mode & NDMPD_SELECT_MODE_WRITE) { if (FD_ISSET(handler->fh_fd, &wfds)) { mode |= NDMPD_SELECT_MODE_WRITE; FD_CLR(handler->fh_fd, &wfds); } } if (handler->fh_mode & NDMPD_SELECT_MODE_EXCEPTION) { if (FD_ISSET(handler->fh_fd, &efds)) { mode |= NDMPD_SELECT_MODE_EXCEPTION; FD_CLR(handler->fh_fd, &efds); } } if (mode) { (*handler->fh_func) (handler->fh_cookie, handler->fh_fd, mode); /* * K.L. The list can be modified during the execution * of handler->fh_func. Therefore, handler will start * from the beginning of the handler list after * each execution. */ handler = session->ns_file_handler_list; /* * Release the thread which is waiting for a request * to be proccessed. */ nlp_event_nw(session); } else handler = handler->fh_next; } nlp_event_rv_set(session, 1); return (1); } /* * ndmpd_save_env * * Saves a copy of the environment variable list from the data_start_backup * request or data_start_recover request. * * Parameters: * session (input) - session pointer. * env (input) - environment variable list to be saved. * envlen (input) - length of variable array. * * Returns: * error code. */ ndmp_error ndmpd_save_env(ndmpd_session_t *session, ndmp_pval *env, ulong_t envlen) { ulong_t i; char *namebuf; char *valbuf; session->ns_data.dd_env_len = 0; if (envlen == 0) return (NDMP_NO_ERR); session->ns_data.dd_env = ndmp_malloc(sizeof (ndmp_pval) * envlen); if (session->ns_data.dd_env == 0) return (NDMP_NO_MEM_ERR); for (i = 0; i < envlen; i++) { namebuf = strdup(env[i].name); if (namebuf == 0) return (NDMP_NO_MEM_ERR); valbuf = strdup(env[i].value); if (valbuf == 0) { free(namebuf); return (NDMP_NO_MEM_ERR); } NDMP_LOG(LOG_DEBUG, "env(%s): \"%s\"", namebuf, valbuf); (void) mutex_lock(&session->ns_lock); session->ns_data.dd_env[i].name = namebuf; session->ns_data.dd_env[i].value = valbuf; session->ns_data.dd_env_len++; (void) mutex_unlock(&session->ns_lock); } return (NDMP_NO_ERR); } /* * ndmpd_free_env * * Free the previously saved environment variable array. * * Parameters: * session - NDMP session pointer. * * Returns: * void. */ void ndmpd_free_env(ndmpd_session_t *session) { ulong_t i; int count = session->ns_data.dd_env_len; (void) mutex_lock(&session->ns_lock); session->ns_data.dd_env_len = 0; for (i = 0; i < count; i++) { free(session->ns_data.dd_env[i].name); free(session->ns_data.dd_env[i].value); } free((char *)session->ns_data.dd_env); session->ns_data.dd_env = 0; (void) mutex_unlock(&session->ns_lock); } /* * ndmpd_save_nlist_v2 * * Save a copy of list of file names to be restored. * * Parameters: * nlist (input) - name list from data_start_recover request. * nlistlen (input) - length of name list. * * Returns: * array of file name pointers. * * Notes: * free_nlist should be called to free the returned list. * A null pointer indicates the end of the list. */ ndmp_error ndmpd_save_nlist_v2(ndmpd_session_t *session, ndmp_name *nlist, ulong_t nlistlen) { ulong_t i; char *namebuf; char *destbuf; if (nlistlen == 0) return (NDMP_NO_ERR); session->ns_data.dd_nlist_len = 0; session->ns_data.dd_nlist = ndmp_malloc(sizeof (ndmp_name)*nlistlen); if (session->ns_data.dd_nlist == 0) return (NDMP_NO_MEM_ERR); for (i = 0; i < nlistlen; i++) { namebuf = ndmp_malloc(strlen(nlist[i].name) + 1); if (namebuf == 0) return (NDMP_NO_MEM_ERR); destbuf = ndmp_malloc(strlen(nlist[i].dest) + 1); if (destbuf == 0) { free(namebuf); return (NDMP_NO_MEM_ERR); } (void) strlcpy(namebuf, nlist[i].name, strlen(nlist[i].name) + 1); (void) strlcpy(destbuf, nlist[i].dest, strlen(nlist[i].dest) + 1); session->ns_data.dd_nlist[i].name = namebuf; session->ns_data.dd_nlist[i].dest = destbuf; session->ns_data.dd_nlist[i].ssid = nlist[i].ssid; session->ns_data.dd_nlist[i].fh_info = nlist[i].fh_info; session->ns_data.dd_nlist_len++; } return (NDMP_NO_ERR); } /* * ndmpd_free_nlist_v2 * * Free a list created by ndmpd_save_nlist_v2. * * Parameters: * session (input) - session pointer. * * Returns: * void */ void ndmpd_free_nlist_v2(ndmpd_session_t *session) { ulong_t i; for (i = 0; i < session->ns_data.dd_nlist_len; i++) { free(session->ns_data.dd_nlist[i].name); free(session->ns_data.dd_nlist[i].dest); } if (session->ns_data.dd_nlist != NULL) free((char *)session->ns_data.dd_nlist); session->ns_data.dd_nlist = 0; session->ns_data.dd_nlist_len = 0; } /* * ndmpd_free_nlist_v3 * * Free a list created by ndmpd_save_nlist_v3. * * Parameters: * session (input) - session pointer. * * Returns: * void */ void ndmpd_free_nlist_v3(ndmpd_session_t *session) { ulong_t i; mem_ndmp_name_v3_t *tp; /* destination entry */ tp = session->ns_data.dd_nlist_v3; for (i = 0; i < session->ns_data.dd_nlist_len; tp++, i++) { NDMP_FREE(tp->nm3_opath); NDMP_FREE(tp->nm3_dpath); NDMP_FREE(tp->nm3_newnm); } NDMP_FREE(session->ns_data.dd_nlist_v3); session->ns_data.dd_nlist_len = 0; } /* * ndmpd_save_nlist_v3 * * Save a copy of list of file names to be restored. * * Parameters: * nlist (input) - name list from data_start_recover request. * nlistlen (input) - length of name list. * * Returns: * array of file name pointers. * * Notes: * free_nlist should be called to free the returned list. * A null pointer indicates the end of the list. */ ndmp_error ndmpd_save_nlist_v3(ndmpd_session_t *session, ndmp_name_v3 *nlist, ulong_t nlistlen) { ulong_t i; ndmp_error rv; ndmp_name_v3 *sp; /* source entry */ mem_ndmp_name_v3_t *tp; /* destination entry */ if (nlistlen == 0) return (NDMP_ILLEGAL_ARGS_ERR); session->ns_data.dd_nlist_len = 0; tp = session->ns_data.dd_nlist_v3 = ndmp_malloc(sizeof (mem_ndmp_name_v3_t) * nlistlen); if (session->ns_data.dd_nlist_v3 == 0) return (NDMP_NO_MEM_ERR); rv = NDMP_NO_ERR; sp = nlist; for (i = 0; i < nlistlen; tp++, sp++, i++) { tp->nm3_opath = strdup(sp->original_path); if (!tp->nm3_opath) { rv = NDMP_NO_MEM_ERR; break; } if (!*sp->destination_dir) { tp->nm3_dpath = NULL; /* In V4 destination dir cannot be NULL */ if (session->ns_protocol_version == NDMPV4) { rv = NDMP_ILLEGAL_ARGS_ERR; break; } } else if (!(tp->nm3_dpath = strdup(sp->destination_dir))) { rv = NDMP_NO_MEM_ERR; break; } if (!*sp->new_name) tp->nm3_newnm = NULL; else if (!(tp->nm3_newnm = strdup(sp->new_name))) { rv = NDMP_NO_MEM_ERR; break; } tp->nm3_node = quad_to_long_long(sp->node); tp->nm3_fh_info = quad_to_long_long(sp->fh_info); tp->nm3_err = NDMP_NO_ERR; session->ns_data.dd_nlist_len++; NDMP_LOG(LOG_DEBUG, "orig \"%s\"", tp->nm3_opath); NDMP_LOG(LOG_DEBUG, "dest \"%s\"", NDMP_SVAL(tp->nm3_dpath)); NDMP_LOG(LOG_DEBUG, "name \"%s\"", NDMP_SVAL(tp->nm3_newnm)); NDMP_LOG(LOG_DEBUG, "node %lld", tp->nm3_node); NDMP_LOG(LOG_DEBUG, "fh_info %lld", tp->nm3_fh_info); } if (rv != NDMP_NO_ERR) ndmpd_free_nlist_v3(session); return (rv); } /* * ndmpd_free_nlist * * Free the recovery list based on the version * * Parameters: * session (input) - session pointer. * * Returns: * void */ void ndmpd_free_nlist(ndmpd_session_t *session) { switch (session->ns_protocol_version) { case 1: case 2: ndmpd_free_nlist_v2(session); break; case 3: case 4: ndmpd_free_nlist_v3(session); break; default: NDMP_LOG(LOG_DEBUG, "Unknown version %d", session->ns_protocol_version); } } /* * fh_cmpv3 * * Comparison function used in sorting the Nlist based on their * file history info (offset of the entry on the tape) * * Parameters: * p (input) - pointer to P * q (input) - pointer to Q * * Returns: * -1: P < Q * 0: P = Q * 1: P > Q */ static int fh_cmpv3(const void *p, const void *q) { #define FH_INFOV3(p) (((mem_ndmp_name_v3_t *)p)->nm3_fh_info) if (FH_INFOV3(p) < FH_INFOV3(q)) return (-1); else if (FH_INFOV3(p) == FH_INFOV3(q)) return (0); else return (1); #undef FH_INFOV3 } /* * ndmp_sort_nlist_v3 * * Sort the recovery list based on their offset on the tape * * Parameters: * session (input) - session pointer. * * Returns: * void */ void ndmp_sort_nlist_v3(ndmpd_session_t *session) { if (!session || session->ns_data.dd_nlist_len == 0 || !session->ns_data.dd_nlist_v3) return; (void) qsort(session->ns_data.dd_nlist_v3, session->ns_data.dd_nlist_len, sizeof (mem_ndmp_name_v3_t), fh_cmpv3); } /* * ndmp_send_reply * * Send the reply, check for error and print the msg if any error * occured when sending the reply. * * Parameters: * connection (input) - connection pointer. * * Return: * void */ void ndmp_send_reply(ndmp_connection_t *connection, void *reply, char *msg) { if (ndmp_send_response(connection, NDMP_NO_ERR, reply) < 0) NDMP_LOG(LOG_DEBUG, "%s", msg); } /* * ndmp_mtioctl * * Performs numerous filemark operations. * * Parameters: * fd - file descriptor of the device * cmd - filemark or record command * count - the number of operations to be performed */ int ndmp_mtioctl(int fd, int cmd, int count) { struct mtop mp; mp.mt_op = cmd; mp.mt_count = count; if (ioctl(fd, MTIOCTOP, &mp) < 0) { NDMP_LOG(LOG_ERR, "Failed to send command to tape: %m."); return (-1); } return (0); } /* * quad_to_long_long * * Convert type quad to longlong_t */ u_longlong_t quad_to_long_long(ndmp_u_quad q) { u_longlong_t ull; ull = ((u_longlong_t)q.high << 32) + q.low; return (ull); } /* * long_long_to_quad * * Convert long long to quad type */ ndmp_u_quad long_long_to_quad(u_longlong_t ull) { ndmp_u_quad q; q.high = (ulong_t)(ull >> 32); q.low = (ulong_t)ull; return (q); } /* * ndmp_set_socket_nodelay * * Set the TCP socket option to nodelay mode */ void ndmp_set_socket_nodelay(int sock) { int flag = 1; (void) setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof (flag)); } /* * ndmp_set_socket_snd_buf * * Set the socket send buffer size */ void ndmp_set_socket_snd_buf(int sock, int size) { if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof (size)) < 0) NDMP_LOG(LOG_DEBUG, "SO_SNDBUF failed errno=%d", errno); } /* * ndmp_set_socket_rcv_buf * * Set the socket receive buffer size */ void ndmp_set_socket_rcv_buf(int sock, int size) { if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0) NDMP_LOG(LOG_DEBUG, "SO_RCVBUF failed errno=%d", errno); } /* * ndmp_get_max_tok_seq * * Get the maximum permitted token sequence for token-based * backups. * * Parameters: * void * * Returns: * ndmp_max_tok_seq */ int ndmp_get_max_tok_seq(void) { return (ndmp_max_tok_seq); } /* * ndmp_buffer_get_size * * Return the NDMP transfer buffer size * * Parameters: * session (input) - session pointer. * * Returns: * buffer size */ long ndmp_buffer_get_size(ndmpd_session_t *session) { long xfer_size; if (session == NULL) return (0); if (session->ns_data.dd_mover.addr_type == NDMP_ADDR_TCP) { xfer_size = atoi(ndmpd_get_prop_default(NDMP_MOVER_RECSIZE, "60")); if (xfer_size > 0) xfer_size *= KILOBYTE; else xfer_size = REMOTE_RECORD_SIZE; NDMP_LOG(LOG_DEBUG, "Remote operation: %d", xfer_size); } else { NDMP_LOG(LOG_DEBUG, "Local operation: %lu", session->ns_mover.md_record_size); if ((xfer_size = session->ns_mover.md_record_size) == 0) xfer_size = MAX_RECORD_SIZE; } NDMP_LOG(LOG_DEBUG, "xfer_size: %d", xfer_size); return (xfer_size); } /* * ndmp_lbr_init * * Initialize the LBR/NDMP backup parameters * * Parameters: * session (input) - session pointer. * * Returns: * 0: on success * -1: otherwise */ int ndmp_lbr_init(ndmpd_session_t *session) { if (session->ns_ndmp_lbr_params != NULL) { NDMP_LOG(LOG_DEBUG, "ndmp_lbr_params already allocated."); return (0); } session->ns_ndmp_lbr_params = ndmp_malloc(sizeof (ndmp_lbr_params_t)); if (session->ns_ndmp_lbr_params == NULL) return (-1); session->ns_ndmp_lbr_params->nlp_bkmap = -1; session->ns_ndmp_lbr_params->nlp_session = session; (void) cond_init(&session->ns_ndmp_lbr_params->nlp_cv, 0, NULL); (void) mutex_init(&session->ns_lock, 0, NULL); session->ns_nref = 0; return (0); } /* * ndmp_lbr_cleanup * * Deallocate and cleanup all NDMP/LBR parameters * * Parameters: * session (input) - session pointer. * * Returns: * 0: on success * -1: otherwise */ void ndmp_lbr_cleanup(ndmpd_session_t *session) { /* * If in 3-way restore, the connection close is detected after * check in tape_read(), the reader thread of mover may wait forever * for the tape to be changed. Force the reader thread to exit. */ nlp_event_rv_set(session, -2); nlp_event_nw(session); ndmpd_abort_marking_v2(session); ndmp_stop_buffer_worker(session); ndmp_waitfor_op(session); ndmp_free_reader_writer_ipc(session); if (session->ns_ndmp_lbr_params) { if (session->ns_ndmp_lbr_params->nlp_bkmap != -1) (void) dbm_free(session->ns_ndmp_lbr_params->nlp_bkmap); tlm_release_list(session->ns_ndmp_lbr_params->nlp_exl); tlm_release_list(session->ns_ndmp_lbr_params->nlp_inc); (void) cond_destroy(&session->ns_ndmp_lbr_params->nlp_cv); } NDMP_FREE(session->ns_ndmp_lbr_params); } /* * nlp_ref_nw * * Increase the references to the NDMP/LBR parameter to prevent * unwanted release * * Parameters: * session (input) - session pointer. * * Returns: * void */ void nlp_ref_nw(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; (void) mutex_lock(&nlp_mtx); if ((nlp = ndmp_get_nlp(session)) != NULL) { nlp->nlp_nw++; NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw); } else NDMP_LOG(LOG_DEBUG, "nlp == NULL"); (void) mutex_unlock(&nlp_mtx); } /* * nlp_unref_nw * * Decrease the references to the NDMP/LBR parameter before * release * * Parameters: * session (input) - session pointer. * * Returns: * void */ void nlp_unref_nw(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; (void) mutex_lock(&nlp_mtx); if ((nlp = ndmp_get_nlp(session)) != NULL) { NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw); if (nlp->nlp_nw > 0) nlp->nlp_nw--; } else NDMP_LOG(LOG_DEBUG, "nlp == NULL"); (void) mutex_unlock(&nlp_mtx); } /* * nlp_wait_nw * * Wait for a NDMP/LBR parameter to get available * * Parameters: * session (input) - session pointer. * * Returns: * void */ void nlp_wait_nw(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; (void) mutex_lock(&nlp_mtx); if ((nlp = ndmp_get_nlp(session)) != NULL) { NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw); if (nlp->nlp_nw > 0) { NDMP_LOG(LOG_DEBUG, "Waiting"); while ((nlp->nlp_flag & NLP_READY) == 0) (void) cond_wait(&nlp->nlp_cv, &nlp_mtx); } } else NDMP_LOG(LOG_DEBUG, "nlp == NULL"); (void) mutex_unlock(&nlp_mtx); } /* * nlp_event_nw * * Signal that a NDMP/LBR parameter is available to wake up the * threads waiting on that * * Parameters: * session (input) - session pointer. * * Returns: * void */ void nlp_event_nw(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; (void) mutex_lock(&nlp_mtx); if ((nlp = ndmp_get_nlp(session)) != NULL) { if (nlp->nlp_nw > 0) { NDMP_LOG(LOG_DEBUG, "nw: %d", nlp->nlp_nw); nlp->nlp_flag |= NLP_READY; (void) cond_signal(&nlp->nlp_cv); } } else NDMP_LOG(LOG_DEBUG, "nlp == NULL"); (void) mutex_unlock(&nlp_mtx); } /* * nlp_event_rv_get * * Get the return value for each NLP * * Parameters: * session (input) - session pointer. * * Returns: * return value */ int nlp_event_rv_get(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; if ((nlp = ndmp_get_nlp(session)) == NULL) { NDMP_LOG(LOG_DEBUG, "nlp == NULL"); return (0); } return (nlp->nlp_rv); } /* * nlp_event_rv_set * * Set the return value for an NLP * * Parameters: * session (input) - session pointer. * rv (input) - return value * * Returns: * void */ void nlp_event_rv_set(ndmpd_session_t *session, int rv) { ndmp_lbr_params_t *nlp; (void) mutex_lock(&nlp_mtx); if (rv != 0) NDMP_LOG(LOG_DEBUG, "rv: %d", rv); if ((nlp = ndmp_get_nlp(session)) != NULL) nlp->nlp_rv = rv; else NDMP_LOG(LOG_DEBUG, "nlp == NULL"); (void) mutex_unlock(&nlp_mtx); } /* * is_buffer_erroneous * * Run a sanity check on the buffer * * returns: * TRUE: if the buffer seems to have error * FALSE: if the buffer is full and has valid data. */ boolean_t is_buffer_erroneous(tlm_buffer_t *buf) { boolean_t rv; rv = (buf == NULL || buf->tb_eot || buf->tb_eof || buf->tb_errno != 0); if (rv) { if (buf == NULL) { NDMP_LOG(LOG_DEBUG, "buf == NULL"); } else { NDMP_LOG(LOG_DEBUG, "eot: %u, eof: %u, errno: %d", buf->tb_eot, buf->tb_eof, buf->tb_errno); } } return (rv); } /* * ndmp_execute_cdb * * Main SCSI CDB execution program, this is used by message handler * for the NDMP tape/SCSI execute CDB requests. This function uses * USCSI interface to run the CDB command and sets all the CDB parameters * in the SCSI query before calling the USCSI ioctl. The result of the * CDB is returned in two places: * cmd.uscsi_status The status of CDB execution * cmd.uscsi_rqstatus The status of sense requests * reply.error The general errno (ioctl) * * Parameters: * session (input) - session pointer * adapter_name (input) - name of SCSI adapter * sid (input) - SCSI target ID * lun (input) - LUN number * request (input) - NDMP client CDB request * * Returns: * void */ /*ARGSUSED*/ void ndmp_execute_cdb(ndmpd_session_t *session, char *adapter_name, int sid, int lun, ndmp_execute_cdb_request *request) { ndmp_execute_cdb_reply reply; struct uscsi_cmd cmd; int fd; struct open_list *olp; char rq_buf[255]; (void) memset((void *)&cmd, 0, sizeof (cmd)); (void) memset((void *)&reply, 0, sizeof (reply)); (void) memset((void *)rq_buf, 0, sizeof (rq_buf)); if (request->flags == NDMP_SCSI_DATA_IN) { cmd.uscsi_flags = USCSI_READ | USCSI_RQENABLE; if ((cmd.uscsi_bufaddr = ndmp_malloc(request->datain_len)) == 0) { reply.error = NDMP_NO_MEM_ERR; if (ndmp_send_response(session->ns_connection, NDMP_NO_ERR, (void *)&reply) < 0) NDMP_LOG(LOG_DEBUG, "error sending" " scsi_execute_cdb reply."); return; } cmd.uscsi_buflen = request->datain_len; cmd.uscsi_rqlen = sizeof (rq_buf); cmd.uscsi_rqbuf = rq_buf; } else if (request->flags == NDMP_SCSI_DATA_OUT) { cmd.uscsi_flags = USCSI_WRITE; cmd.uscsi_bufaddr = request->dataout.dataout_val; cmd.uscsi_buflen = request->dataout.dataout_len; } else { cmd.uscsi_flags = USCSI_RQENABLE; cmd.uscsi_bufaddr = 0; cmd.uscsi_buflen = 0; cmd.uscsi_rqlen = sizeof (rq_buf); cmd.uscsi_rqbuf = rq_buf; } cmd.uscsi_timeout = (request->timeout < 1000) ? 1 : (request->timeout / 1000); cmd.uscsi_cdb = (caddr_t)request->cdb.cdb_val; cmd.uscsi_cdblen = request->cdb.cdb_len; NDMP_LOG(LOG_DEBUG, "cmd: 0x%x, len: %d, flags: %d, datain_len: %d", request->cdb.cdb_val[0] & 0xff, request->cdb.cdb_len, request->flags, request->datain_len); NDMP_LOG(LOG_DEBUG, "dataout_len: %d, timeout: %d", request->dataout.dataout_len, request->timeout); if (request->cdb.cdb_len > 12) { reply.error = NDMP_ILLEGAL_ARGS_ERR; ndmp_send_reply(session->ns_connection, (void *) &reply, "sending execute_cdb reply"); if (request->flags == NDMP_SCSI_DATA_IN) free(cmd.uscsi_bufaddr); return; } reply.error = NDMP_NO_ERR; if ((olp = ndmp_open_list_find(adapter_name, sid, lun)) != NULL) { fd = olp->ol_fd; } else { reply.error = NDMP_DEV_NOT_OPEN_ERR; ndmp_send_reply(session->ns_connection, (void *) &reply, "sending execute_cdb reply"); if (request->flags == NDMP_SCSI_DATA_IN) free(cmd.uscsi_bufaddr); return; } if (ioctl(fd, USCSICMD, &cmd) < 0) { if (errno != EIO && errno != 0) NDMP_LOG(LOG_ERR, "Failed to send command to device: %m"); NDMP_LOG(LOG_DEBUG, "ioctl(USCSICMD) error: %m"); if (cmd.uscsi_status == 0) reply.error = NDMP_IO_ERR; } reply.status = cmd.uscsi_status; if (request->flags == NDMP_SCSI_DATA_IN) { reply.datain.datain_len = cmd.uscsi_buflen; reply.datain.datain_val = cmd.uscsi_bufaddr; } else { reply.dataout_len = request->dataout.dataout_len; } reply.ext_sense.ext_sense_len = cmd.uscsi_rqlen - cmd.uscsi_rqresid; reply.ext_sense.ext_sense_val = rq_buf; if (ndmp_send_response(session->ns_connection, NDMP_NO_ERR, (void *)&reply) < 0) NDMP_LOG(LOG_DEBUG, "Error sending scsi_execute_cdb reply."); if (request->flags == NDMP_SCSI_DATA_IN) free(cmd.uscsi_bufaddr); } /* * ndmp_stop_local_reader * * Stops a mover reader thread (for local backup only) * * Parameters: * session (input) - session pointer * cmds (input) - reader/writer command struct * * Returns: * void */ void ndmp_stop_local_reader(ndmpd_session_t *session, tlm_commands_t *cmds) { if (session != NULL) { if (session->ns_data.dd_sock == -1) { /* * 2-way restore. */ NDMP_LOG(LOG_DEBUG, "2-way restore"); if (cmds != NULL && cmds->tcs_reader_count > 0) { nlp_event_rv_set(session, -2); nlp_event_nw(session); } } } } /* * Stops a mover reader thread (for remote backup only) * * Parameters: * session (input) - session pointer * cmds (input) - reader/writer command struct * * Returns: * void */ void ndmp_stop_remote_reader(ndmpd_session_t *session) { if (session != NULL) { if (session->ns_data.dd_sock >= 0) { /* * 3-way restore. */ NDMP_LOG(LOG_DEBUG, "data.sock: %d", session->ns_data.dd_sock); (void) close(session->ns_data.dd_sock); session->ns_data.dd_sock = -1; } } } /* * ndmp_wait_for_reader * * Wait for a reader until get done (busy wait) */ void ndmp_wait_for_reader(tlm_commands_t *cmds) { if (cmds == NULL) { NDMP_LOG(LOG_DEBUG, "cmds == NULL"); } else { NDMP_LOG(LOG_DEBUG, "reader_count: %d", cmds->tcs_reader_count); while (cmds->tcs_reader_count > 0) (void) sleep(1); } } /* * ndmp_open_list_find * * Find a specific device in the open list * * Parameters: * dev (input) - device name * sid (input) - SCSI target ID * lun (input) - LUN number * * Returns: * pointer to the open list entry */ struct open_list * ndmp_open_list_find(char *dev, int sid, int lun) { struct ol_head *olhp; struct open_list *olp; if (dev == NULL || *dev == '\0') { NDMP_LOG(LOG_DEBUG, "Invalid argument"); return (NULL); } (void) mutex_lock(&ol_mutex); olhp = &ol_head; for (olp = LIST_FIRST(olhp); olp != NULL; olp = LIST_NEXT(olp, ol_q)) if (strcmp(olp->ol_devnm, dev) == 0 && olp->ol_sid == sid && olp->ol_lun == lun) { (void) mutex_unlock(&ol_mutex); return (olp); } (void) mutex_unlock(&ol_mutex); return (NULL); } /* * ndmp_open_list_add * * Add a specific device to the open list * * Parameters: * conn (input) - connection pointer * dev (input) - device name * sid (input) - SCSI target ID * lun (input) - LUN number * fd (input) - the device file descriptor * * Returns: * errno */ int ndmp_open_list_add(ndmp_connection_t *conn, char *dev, int sid, int lun, int fd) { int err; struct ol_head *olhp; struct open_list *olp; if (dev == NULL || *dev == '\0') { NDMP_LOG(LOG_DEBUG, "Invalid argument"); return (EINVAL); } NDMP_LOG(LOG_DEBUG, "conn: 0x%08x, dev: %s, sid: %d, lun: %d", conn, dev, sid, lun); err = 0; olhp = &ol_head; if ((olp = ndmp_open_list_find(dev, sid, lun)) != NULL) { NDMP_LOG(LOG_DEBUG, "already in list"); /* * The adapter handle can be opened many times by the clients. * Only when the target is set, we must check and reject the * open request if the device is already being used by another * session. */ if (sid == -1) olp->ol_nref++; else err = EBUSY; } else if ((olp = ndmp_malloc(sizeof (struct open_list))) == NULL) { err = ENOMEM; } else if ((olp->ol_devnm = strdup(dev)) == NULL) { NDMP_LOG(LOG_ERR, "Out of memory."); free(olp); err = ENOMEM; } else { olp->cl_conn = conn; olp->ol_nref = 1; olp->ol_sid = sid; olp->ol_lun = lun; if (fd > 0) olp->ol_fd = fd; else olp->ol_fd = -1; (void) mutex_lock(&ol_mutex); LIST_INSERT_HEAD(olhp, olp, ol_q); (void) mutex_unlock(&ol_mutex); } return (err); } /* * ndmp_open_list_del * * Delete a specific device from the open list * * Parameters: * dev (input) - device name * sid (input) - SCSI target ID * lun (input) - LUN number * * Returns: * errno */ int ndmp_open_list_del(char *dev, int sid, int lun) { struct open_list *olp; if (dev == NULL || *dev == '\0') { NDMP_LOG(LOG_DEBUG, "Invalid argument"); return (EINVAL); } if ((olp = ndmp_open_list_find(dev, sid, lun)) == NULL) { NDMP_LOG(LOG_DEBUG, "%s not found", dev); return (ENOENT); } (void) mutex_lock(&ol_mutex); if (--olp->ol_nref <= 0) { NDMP_LOG(LOG_DEBUG, "Removed dev: %s, sid: %d, lun: %d", dev, sid, lun); LIST_REMOVE(olp, ol_q); free(olp->ol_devnm); free(olp); } (void) mutex_unlock(&ol_mutex); return (0); } /* * ndmp_open_list_release * * Close all the resources belonging to this connection. * * Parameters: * ndmp_connection_t *conn : connection identifier * * Returns: * void */ void ndmp_open_list_release(ndmp_connection_t *conn) { struct ol_head *olhp = &ol_head; struct open_list *olp; struct open_list *next; (void) mutex_lock(&ol_mutex); olp = LIST_FIRST(olhp); while (olp != NULL) { next = LIST_NEXT(olp, ol_q); NDMP_LOG(LOG_DEBUG, "olp->conn 0x%08x", olp->cl_conn); if (olp->cl_conn == conn) { NDMP_LOG(LOG_DEBUG, "Removed dev: %s, sid: %d, lun: %d", olp->ol_devnm, olp->ol_sid, olp->ol_lun); LIST_REMOVE(olp, ol_q); if (olp->ol_fd > 0) (void) close(olp->ol_fd); free(olp->ol_devnm); free(olp); } olp = next; } (void) mutex_unlock(&ol_mutex); } /* * ndmp_stop_buffer_worker * * Stop all reader and writer threads for a specific buffer. * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_stop_buffer_worker(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; tlm_commands_t *cmds; session->ns_tape.td_pos = 0; if ((nlp = ndmp_get_nlp(session)) == NULL) { NDMP_LOG(LOG_DEBUG, "nlp == NULL"); } else { cmds = &nlp->nlp_cmds; if (cmds->tcs_command == NULL) { NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL"); } else { cmds->tcs_reader = cmds->tcs_writer = TLM_ABORT; cmds->tcs_command->tc_reader = TLM_ABORT; cmds->tcs_command->tc_writer = TLM_ABORT; while (cmds->tcs_reader_count > 0 || cmds->tcs_writer_count > 0) { NDMP_LOG(LOG_DEBUG, "trying to stop buffer worker"); (void) sleep(1); } } } } /* * ndmp_stop_reader_thread * * Stop only the reader threads of a specific buffer * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_stop_reader_thread(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; tlm_commands_t *cmds; if ((nlp = ndmp_get_nlp(session)) == NULL) { NDMP_LOG(LOG_DEBUG, "nlp == NULL"); } else { cmds = &nlp->nlp_cmds; if (cmds->tcs_command == NULL) { NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL"); } else { cmds->tcs_reader = TLM_ABORT; cmds->tcs_command->tc_reader = TLM_ABORT; while (cmds->tcs_reader_count > 0) { NDMP_LOG(LOG_DEBUG, "trying to stop reader thread"); (void) sleep(1); } } } } /* * ndmp_stop_reader_thread * * Stop only the writer threads of a specific buffer * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_stop_writer_thread(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; tlm_commands_t *cmds; if ((nlp = ndmp_get_nlp(session)) == NULL) { NDMP_LOG(LOG_DEBUG, "nlp == NULL"); } else { cmds = &nlp->nlp_cmds; if (cmds->tcs_command == NULL) { NDMP_LOG(LOG_DEBUG, "cmds->tcs_command == NULL"); } else { cmds->tcs_writer = TLM_ABORT; cmds->tcs_command->tc_writer = TLM_ABORT; while (cmds->tcs_writer_count > 0) { NDMP_LOG(LOG_DEBUG, "trying to stop writer thread"); (void) sleep(1); } } } } /* * ndmp_free_reader_writer_ipc * * Free and release the reader/writer buffers and the IPC structure * for reader and writer threads. * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_free_reader_writer_ipc(ndmpd_session_t *session) { ndmp_lbr_params_t *nlp; tlm_commands_t *cmds; if ((nlp = ndmp_get_nlp(session)) != NULL) { cmds = &nlp->nlp_cmds; if (cmds->tcs_command != NULL) { NDMP_LOG(LOG_DEBUG, "cmds->tcs_command->tc_ref: %d", cmds->tcs_command->tc_ref); tlm_release_reader_writer_ipc(cmds->tcs_command); } } } /* * ndmp_waitfor_op * * Wait for a session reference count to drop to zero * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_waitfor_op(ndmpd_session_t *session) { if (session != NULL) { while (session->ns_nref > 0) { (void) sleep(1); NDMP_LOG(LOG_DEBUG, "waiting for session nref: %d", session->ns_nref); } } } /* * ndmp_session_ref * * Increment the reference count of the session * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_session_ref(ndmpd_session_t *session) { (void) mutex_lock(&session->ns_lock); session->ns_nref++; (void) mutex_unlock(&session->ns_lock); } /* * ndmp_session_unref * * Decrement the reference count of the session * * Parameters: * session (input) - session pointer * * Returns: * void */ void ndmp_session_unref(ndmpd_session_t *session) { (void) mutex_lock(&session->ns_lock); session->ns_nref--; (void) mutex_unlock(&session->ns_lock); } /* * ndmp_addr2str_v3 * * Convert the address type to a string * * Parameters: * type (input) - address type * * Returns: * type in string */ char * ndmp_addr2str_v3(ndmp_addr_type type) { char *rv; switch (type) { case NDMP_ADDR_LOCAL: rv = "Local"; break; case NDMP_ADDR_TCP: rv = "TCP"; break; case NDMP_ADDR_FC: rv = "FC"; break; case NDMP_ADDR_IPC: rv = "IPC"; break; default: rv = "Unknown"; } return (rv); } /* * ndmp_valid_v3addr_type * * Make sure that the NDMP address is from any of the * valid types * * Parameters: * type (input) - address type * * Returns: * 1: valid * 0: invalid */ boolean_t ndmp_valid_v3addr_type(ndmp_addr_type type) { boolean_t rv; switch (type) { case NDMP_ADDR_LOCAL: case NDMP_ADDR_TCP: case NDMP_ADDR_FC: case NDMP_ADDR_IPC: rv = TRUE; break; default: rv = FALSE; } return (rv); } /* * ndmp_copy_addr_v3 * * Copy NDMP address from source to destination (V2 and V3 only) * * Parameters: * dst (ouput) - destination address * src (input) - source address * * Returns: * void */ void ndmp_copy_addr_v3(ndmp_addr_v3 *dst, ndmp_addr_v3 *src) { dst->addr_type = src->addr_type; switch (src->addr_type) { case NDMP_ADDR_LOCAL: /* nothing */ break; case NDMP_ADDR_TCP: dst->tcp_ip_v3 = htonl(src->tcp_ip_v3); dst->tcp_port_v3 = src->tcp_port_v3; break; case NDMP_ADDR_FC: case NDMP_ADDR_IPC: default: break; } } /* * ndmp_copy_addr_v4 * * Copy NDMP address from source to destination. V4 has a extra * environment list inside the address too which needs to be copied. * * Parameters: * dst (ouput) - destination address * src (input) - source address * * Returns: * void */ void ndmp_copy_addr_v4(ndmp_addr_v4 *dst, ndmp_addr_v4 *src) { int i; dst->addr_type = src->addr_type; dst->tcp_len_v4 = src->tcp_len_v4; switch (src->addr_type) { case NDMP_ADDR_LOCAL: /* nothing */ break; case NDMP_ADDR_TCP: dst->tcp_addr_v4 = ndmp_malloc(sizeof (ndmp_tcp_addr_v4) * src->tcp_len_v4); if (dst->tcp_addr_v4 == 0) return; for (i = 0; i < src->tcp_len_v4; i++) { dst->tcp_ip_v4(i) = htonl(src->tcp_ip_v4(i)); dst->tcp_port_v4(i) = src->tcp_port_v4(i); dst->tcp_env_v4(i).addr_env_len = 0; /* Solaris */ dst->tcp_env_v4(i).addr_env_val = 0; /* Solaris */ } break; case NDMP_ADDR_FC: case NDMP_ADDR_IPC: default: break; } } /* * ndmp_connect_sock_v3 * * Creates a socket and connects to the specified address/port * * Parameters: * addr (input) - IP address * port (input) - port number * * Returns: * 0: on success * -1: otherwise */ int ndmp_connect_sock_v3(ulong_t addr, ushort_t port) { int sock; struct sockaddr_in sin; int flag = 1; NDMP_LOG(LOG_DEBUG, "addr %s:%d", inet_ntoa(IN_ADDR(addr)), port); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { NDMP_LOG(LOG_DEBUG, "Socket error: %m"); return (-1); } (void) memset((void *) &sin, 0, sizeof (sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(addr); sin.sin_port = htons(port); if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) < 0) { NDMP_LOG(LOG_DEBUG, "Connect error: %m"); (void) close(sock); sock = -1; } else { if (ndmp_sbs > 0) ndmp_set_socket_snd_buf(sock, ndmp_sbs*KILOBYTE); if (ndmp_rbs > 0) ndmp_set_socket_rcv_buf(sock, ndmp_rbs*KILOBYTE); ndmp_set_socket_nodelay(sock); (void) setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof (flag)); NDMP_LOG(LOG_DEBUG, "sock %d", sock); } return (sock); } /* * ndmp_create_socket * * Creates a socket for listening for accepting data connections. * * Parameters: * session (input) - session pointer. * addr (output) - location to store address of socket. * port (output) - location to store port of socket. * * Returns: * 0 - success. * -1 - error. */ int ndmp_create_socket(ulong_t *addr, ushort_t *port) { char *p; int length; int sd; struct sockaddr_in sin; /* Try the user's prefered NIC IP address */ p = ndmpd_get_prop(NDMP_MOVER_NIC); /* Try host's IP address */ if (!p || *p == 0) p = gethostaddr(); /* Try default NIC's IP address (if DNS failed) */ if (!p) p = get_default_nic_addr(); /* Fail if no IP can be obtained */ if (!p) { NDMP_LOG(LOG_ERR, "Undetermined network port."); return (-1); } *addr = inet_addr(p); sd = socket(AF_INET, SOCK_STREAM, 0); if (sd < 0) { NDMP_LOG(LOG_DEBUG, "Socket error: %m"); return (-1); } sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = 0; length = sizeof (sin); if (bind(sd, (struct sockaddr *)&sin, sizeof (sin)) < 0) { NDMP_LOG(LOG_DEBUG, "Bind error: %m"); (void) close(sd); sd = -1; } else if (getsockname(sd, (struct sockaddr *)&sin, &length) < 0) { NDMP_LOG(LOG_DEBUG, "getsockname error: %m"); (void) close(sd); sd = -1; } else if (listen(sd, 5) < 0) { NDMP_LOG(LOG_DEBUG, "Listen error: %m"); (void) close(sd); sd = -1; } else *port = sin.sin_port; return (sd); } /* * cctime * * Convert the specified time into a string. It's like * ctime(), but: * - chops the trailing '\n' of ctime. * - and returns "the epoch" if time is 0. * * Returns: * "": invalid argument. * "the epoch": if time is 0. * string format of the time. */ char * cctime(time_t *t) { char *bp, *cp; static char tbuf[BUFSIZ]; if (!t) return (""); if (*t == (time_t)0) return ("the epoch"); if ((bp = ctime_r(t, tbuf, BUFSIZ)) == NULL) return (""); cp = strchr(bp, '\n'); if (cp) *cp = '\0'; return (bp); } /* * ndmp_new_job_name * * Create a job name for each backup/restore to keep track * * Parameters: * jname (output) - job name * * Returns: * jname */ char * ndmp_new_job_name(char *jname) { if (jname != NULL) { (void) snprintf(jname, TLM_MAX_BACKUP_JOB_NAME, "%s%d", NDMP_RCF_BASENAME, ndmp_job_cnt++); NDMP_LOG(LOG_DEBUG, "jname: \"%s\"", jname); } return (jname); } /* * fs_is_valid_logvol * * Check if the log path exists * * Parameters: * path (input) - log path * * Returns: * FALSE: invalid * TRUE: valid */ boolean_t fs_is_valid_logvol(char *path) { struct stat64 st; if (stat64(path, &st) < 0) return (FALSE); return (TRUE); } /* * ndmpd_mk_temp * * Make a temporary file using the working directory path and the * jobname * * Parameters: * buf (output) - the temporary file name path * * Returns: * buf */ char * ndmpd_mk_temp(char *buf) { char fname[TLM_MAX_BACKUP_JOB_NAME]; const char *dir; char *rv; if (!buf) return (NULL); dir = ndmpd_get_prop(NDMP_DEBUG_PATH); if (dir == 0 || *dir == '\0') { NDMP_LOG(LOG_DEBUG, "NDMP work path not specified"); return (0); } if (!fs_is_valid_logvol((char *)dir)) { NDMP_LOG(LOG_ERR, "Log file path cannot be on system volumes."); return (0); } dir += strspn(dir, " \t"); if (!*dir) { NDMP_LOG(LOG_DEBUG, "NDMP work path not specified"); return (0); } rv = buf; (void) ndmp_new_job_name(fname); (void) tlm_cat_path(buf, (char *)dir, fname); return (rv); } /* * ndmpd_make_bk_dir_path * * Make a directory path for temporary files under the NDMP * working directory. * * Parameters: * buf (output) - result path * fname (input) - the file name * * Returns: * buf */ char * ndmpd_make_bk_dir_path(char *buf, char *fname) { const char *p; char *name; char path[PATH_MAX]; if (!buf || !fname || !*fname) return (NULL); p = ndmpd_get_prop(NDMP_DEBUG_PATH); if (p == NULL || *p == '\0' || !fs_is_valid_logvol((char *)p)) { return (NULL); } (void) strlcpy(path, (char *)p, PATH_MAX); (void) trim_whitespace(path); if ((name = strrchr(fname, '/')) == 0) name = fname; (void) tlm_cat_path(buf, path, name); return (buf); } /* * ndmp_is_chkpnt_root * * Is this a root checkpoint (snapshot) directory. * Note: a temporary function */ boolean_t ndmp_is_chkpnt_root(char *path) { struct stat64 st; if (stat64(path, &st) != 0) { NDMP_LOG(LOG_DEBUG, "Couldn't stat path \"%s\"", path); return (TRUE); } return (FALSE); } /* * ndmpd_make_exc_list * * Make a list of files that should not be backed up. * * Parameters: * void * * Returns: * list - array of character strings */ char ** ndmpd_make_exc_list(void) { char *val, **cpp; int i, n; n = sizeof (exls); if ((cpp = ndmp_malloc(n)) != NULL) { for (i = 0; exls[i] != NULL; i++) cpp[i] = exls[i]; /* * If ndmpd_get_prop returns NULL, the array will be * null-terminated. */ val = ndmpd_get_prop(NDMP_DEBUG_PATH); cpp[i] = val; } return (cpp); } /* * ndmp_get_bk_dir_ino * * Get the inode number of the backup directory */ int ndmp_get_bk_dir_ino(ndmp_lbr_params_t *nlp) { int rv; struct stat64 st; if (stat64(nlp->nlp_backup_path, &st) != 0) { rv = -1; NDMP_LOG(LOG_DEBUG, "Getting inode # of \"%s\"", nlp->nlp_backup_path); } else { rv = 0; nlp->nlp_bkdirino = st.st_ino; NDMP_LOG(LOG_DEBUG, "nlp_bkdirino: %lu", (uint_t)nlp->nlp_bkdirino); } return (rv); } /* * ndmp_check_utf8magic * * Check if the magic string for exists in the tar header. This * magic string (which also indicates that the file names are in * UTF8 format) is used as a crest to indetify our own tapes. * This checking is always done before all restores except DAR * restores. */ boolean_t ndmp_check_utf8magic(tlm_cmd_t *cmd) { char *cp; int err, len, actual_size; if (cmd == NULL) { NDMP_LOG(LOG_DEBUG, "cmd == NULL"); return (FALSE); } if (cmd->tc_buffers == NULL) { NDMP_LOG(LOG_DEBUG, "cmd->tc_buffers == NULL"); return (FALSE); } /* wait until the first buffer gets full. */ tlm_buffer_in_buf_wait(cmd->tc_buffers); err = actual_size = 0; cp = tlm_get_read_buffer(RECORDSIZE, &err, cmd->tc_buffers, &actual_size); if (cp == NULL) { NDMP_LOG(LOG_DEBUG, "Can't read from buffers, err: %d", err); return (FALSE); } len = strlen(NDMPUTF8MAGIC); if (actual_size < len) { NDMP_LOG(LOG_DEBUG, "Not enough data in the buffers"); return (FALSE); } return ((strncmp(cp, NDMPUTF8MAGIC, len) == 0) ? TRUE : FALSE); } /* * ndmp_get_cur_bk_time * * Get the backup checkpoint time. */ int ndmp_get_cur_bk_time(ndmp_lbr_params_t *nlp, time_t *tp, char *jname) { int err; if (!nlp || !nlp->nlp_backup_path || !tp) { NDMP_LOG(LOG_DEBUG, "Invalid argument"); return (-1); } if (!fs_is_chkpnt_enabled(nlp->nlp_backup_path)) { NDMP_LOG(LOG_DEBUG, "Not a chkpnt volume %s", nlp->nlp_backup_path); *tp = time(NULL); return (0); } err = tlm_get_chkpnt_time(nlp->nlp_backup_path, !NLP_ISCHKPNTED(nlp), tp, jname); if (err != 0) { NDMP_LOG(LOG_DEBUG, "Can't checkpoint time"); } else { NDMP_LOG(LOG_DEBUG, "%s", cctime(tp)); } return (err); } /* * get_relative_path */ char * ndmp_get_relative_path(char *base, char *fullpath) { char *p = fullpath; if (!base || !*base) return (fullpath); while (*base) { if (*base != *p) break; p++; base++; } if (*p == '/') p++; return ((*base) ? fullpath : p); } /* * ndmp_get_nlp * * Get NDMP local backup parameters * * Parameter: * session cooke * * Returns: * LBR structure */ ndmp_lbr_params_t * ndmp_get_nlp(void *cookie) { if (cookie == NULL) return (NULL); return (((ndmpd_session_t *)cookie)->ns_ndmp_lbr_params); } /* * is_tape_unit_ready * * Check if the tape device is ready or not */ boolean_t is_tape_unit_ready(char *adptnm, int dev_id) { int try; int fd = 0; try = TUR_MAX_TRY; if (dev_id <= 0) { if ((fd = open(adptnm, O_RDONLY | O_NDELAY)) < 0) return (FALSE); } else { fd = dev_id; } do { if (scsi_test_unit_ready(fd) >= 0) { NDMP_LOG(LOG_DEBUG, "Unit is ready"); if (dev_id <= 0) (void) close(fd); return (TRUE); } NDMP_LOG(LOG_DEBUG, "Unit not ready"); (void) usleep(TUR_WAIT); } while (--try > 0); if (dev_id <= 0) (void) close(fd); NDMP_LOG(LOG_DEBUG, "Unit didn't get ready"); return (FALSE); } /* * scsi_test_unit_ready * * This is for Test Unit Read, without this function, the only * impact is getting EBUSY's before each operation which we have * busy waiting loops checking EBUSY error code. */ static int scsi_test_unit_ready(int dev_id) { struct uscsi_cmd ucmd; union scsi_cdb cdb; int retval; (void) memset(&ucmd, 0, sizeof (struct uscsi_cmd)); (void) memset(&cdb, 0, sizeof (union scsi_cdb)); cdb.scc_cmd = SCMD_TEST_UNIT_READY; ucmd.uscsi_cdb = (caddr_t)&cdb; ucmd.uscsi_cdblen = CDB_GROUP0; ucmd.uscsi_flags |= USCSI_SILENT; ucmd.uscsi_timeout = 60; /* Allow maximum 1 min */ retval = ioctl(dev_id, USCSICMD, &ucmd); if (retval != 0 && errno != EIO) { NDMP_LOG(LOG_ERR, "Failed to send inquiry request to device: %m."); NDMP_LOG(LOG_DEBUG, "Inquiry request failed for" " dev_id:%d err=%d -%m", dev_id, errno); retval = -errno; } else retval = -(ucmd.uscsi_status); return (retval); } /* * ndmp_load_params * * Load the parameters. * * Parameter: * void * * Returns: * void */ void ndmp_load_params(void) { ndmp_dump_path_node = ndmpd_get_prop_yorn(NDMP_DUMP_PATHNODE_ENV) ? TRUE : FALSE; ndmp_tar_path_node = ndmpd_get_prop_yorn(NDMP_TAR_PATHNODE_ENV) ? TRUE : FALSE; ndmp_ignore_ctime = ndmpd_get_prop_yorn(NDMP_IGNCTIME_ENV) ? TRUE : FALSE; ndmp_include_lmtime = ndmpd_get_prop_yorn(NDMP_INCLMTIME_ENV) ? TRUE : FALSE; ndmp_max_tok_seq = atoi(ndmpd_get_prop_default(NDMP_MAXSEQ_ENV, "9")); ndmp_full_restore_path = ndmpd_get_prop_yorn(NDMP_FULL_RESTORE_PATH) ? TRUE : FALSE; ndmp_fhinode = ndmpd_get_prop_yorn(NDMP_FHIST_INCR_ENV) ? TRUE : FALSE; /* Get the value from ndmp SMF property. */ ndmp_dar_support = ndmpd_get_prop_yorn(NDMP_DAR_SUPPORT); if ((ndmp_ver = atoi(ndmpd_get_prop(NDMP_VERSION_ENV))) == 0) ndmp_ver = NDMPVER; } /* * randomize * * Randomize the contents of a buffer * * Parameter: * buffer (output) - destination buffer * size (input) - buffer size * * Returns: * void */ void randomize(unsigned char *buffer, int size) { /* LINTED improper alignment */ unsigned int *p = (unsigned int *)buffer; unsigned int dwlen = size / sizeof (unsigned int); unsigned int remlen = size % sizeof (unsigned int); unsigned int tmp; unsigned int i; for (i = 0; i < dwlen; i++) *p++ = random(); if (remlen) { tmp = random(); (void) memcpy(p, &tmp, remlen); } } /* * ndmpd_get_file_entry_type * * Converts the mode to the NDMP file type * * Parameter: * mode (input) - file mode * ftype (output) - file type * * Returns: * void */ void ndmpd_get_file_entry_type(int mode, ndmp_file_type *ftype) { switch (mode & S_IFMT) { case S_IFIFO: *ftype = NDMP_FILE_FIFO; break; case S_IFCHR: *ftype = NDMP_FILE_CSPEC; break; case S_IFDIR: *ftype = NDMP_FILE_DIR; break; case S_IFBLK: *ftype = NDMP_FILE_BSPEC; break; case S_IFREG: *ftype = NDMP_FILE_REG; break; case S_IFLNK: *ftype = NDMP_FILE_SLINK; break; default: *ftype = NDMP_FILE_SOCK; break; } } /* * Set a private data in the plugin context */ void ndmp_context_set_specific(ndmp_context_t *nctx, void *ptr) { nctx->nc_pldata = ptr; } /* * Get a private data in the plugin context */ void * ndmp_context_get_specific(ndmp_context_t *nctx) { return (nctx->nc_pldata); } ndmpd_backup_type_t ndmp_get_backup_type(ndmp_context_t *ctx) { ndmpd_session_t *session = (ndmpd_session_t *)ctx->nc_ddata; return (session->ns_butype); }