/* * ndmp_create_connection * * Allocate and initialize a connection structure. * * Parameters: * handler_tbl (input) - message handlers. * * Returns: * NULL - error * connection pointer * * Notes: * The returned connection should be destroyed using * ndmp_destroy_connection(). */ ndmp_connection_t * ndmp_create_connection(void) { ndmp_connection_t *connection; connection = ndmp_malloc(sizeof (ndmp_connection_t)); if (connection == NULL) return (NULL); connection->conn_sock = -1; connection->conn_my_sequence = 0; connection->conn_authorized = FALSE; connection->conn_eof = FALSE; connection->conn_msginfo.mi_body = 0; connection->conn_version = ndmp_ver; connection->conn_client_data = 0; (void) mutex_init(&connection->conn_lock, 0, NULL); connection->conn_xdrs.x_ops = 0; xdrrec_create(&connection->conn_xdrs, 0, 0, (caddr_t)connection, ndmp_readit, ndmp_writeit); if (connection->conn_xdrs.x_ops == 0) { NDMP_LOG(LOG_DEBUG, "xdrrec_create failed"); (void) mutex_destroy(&connection->conn_lock); (void) close(connection->conn_sock); free(connection); return (0); } return ((ndmp_connection_t *)connection); }
/* * ndmpd_config_update * * Updates the specified config param with the given value. * This function is called both on (re)load and set. */ static int ndmpd_config_update(ndmpd_cfg_param_t *cfg, char *value) { char *curval; int rc = 0; int len; if (value) { len = strlen(value); if (cfg->sc_value) { curval = realloc(cfg->sc_value, (len + 1)); } else { curval = ndmp_malloc(len + 1); } if (curval) { cfg->sc_value = curval; (void) strcpy(cfg->sc_value, value); cfg->sc_flags |= NDMP_CF_DEFINED; } else { syslog(LOG_ERR, "Out of memory."); rc = -1; } } else if (cfg->sc_value) { free(cfg->sc_value); cfg->sc_value = 0; cfg->sc_flags &= ~NDMP_CF_DEFINED; } return (rc); }
/* * malloc_paths * * Allocate the path names (direct and checkpointed paths) */ static boolean_t malloc_paths(ndmp_run_args_t *np) { boolean_t rv; rv = TRUE; np->nr_chkp_nm = ndmp_malloc(TLM_MAX_PATH_NAME); np->nr_unchkp_nm = ndmp_malloc(TLM_MAX_PATH_NAME); if (!np->nr_chkp_nm || !np->nr_unchkp_nm) { free_paths(np); rv = FALSE; } else if ((np->nr_excls = ndmpd_make_exc_list()) == NULL) { free_paths(np); rv = FALSE; } return (rv); }
/* * cstack_new * * Allocate and initialize a new stack, which is just an empty cstack_t. * A pointer to the new stack is returned. This should be treated as an * opaque handle by the caller. */ cstack_t * cstack_new(void) { cstack_t *stk; if ((stk = ndmp_malloc(sizeof (cstack_t))) == NULL) return (NULL); return (stk); }
/* * dup_dir_info * * Make and return a copy of the directory info. */ struct full_dir_info * dup_dir_info(struct full_dir_info *old_dir_info) { struct full_dir_info *new_dir_info; new_dir_info = ndmp_malloc(sizeof (struct full_dir_info)); if (new_dir_info) { bcopy(old_dir_info, new_dir_info, sizeof (struct full_dir_info)); } return (new_dir_info); }
/* * ndmp_run * * Creates a socket for listening and accepting connections * from NDMP clients. * Accepts connections and passes each connection to the connection * handler. * * Parameters: * port (input) - NDMP server port. * If 0, the port number will be retrieved from * the network service database. If not found there, * the default NDMP port number (from ndmp.x) * will be used. * handler (input) - connection handler function. * * Returns: * This function normally never returns unless there's error. * -1 : error * * Notes: * This function does not return unless encountering an error * related to the listen socket. */ int ndmp_run(ulong_t port, ndmp_con_handler_func_t con_handler_func) { int ns; int on; int server_socket; unsigned int ipaddr; struct sockaddr_in sin; ndmpd_worker_arg_t *argp; sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { NDMP_LOG(LOG_DEBUG, "Socket error: %m"); return (-1); } on = 1; (void) setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)); if (bind(server_socket, (struct sockaddr *)&sin, sizeof (sin)) < 0) { NDMP_LOG(LOG_DEBUG, "bind error: %m"); (void) close(server_socket); return (-1); } if (listen(server_socket, 5) < 0) { NDMP_LOG(LOG_DEBUG, "listen error: %m"); (void) close(server_socket); return (-1); } for (; ; ) { if ((ns = tcp_accept(server_socket, &ipaddr)) < 0) { NDMP_LOG(LOG_DEBUG, "tcp_accept error: %m"); continue; } NDMP_LOG(LOG_DEBUG, "connection fd: %d", ns); set_socket_options(ns); if ((argp = ndmp_malloc(sizeof (ndmpd_worker_arg_t))) != NULL) { argp->nw_sock = ns; argp->nw_ipaddr = ipaddr; argp->nw_con_handler_func = con_handler_func; (void) ndmp_start_worker(argp); } } }
/* * correct_ents * * Correct the entries in the restore list by appending the appropriate * path to them */ static int correct_ents(ndmpd_module_params_t *params, int n, char *bkpath) { char *cp, *pathname; int i, len, rv; ndmp_name *ent; if ((pathname = ndmp_malloc(TLM_MAX_PATH_NAME)) == NULL) { MOD_LOG(params, "Error: insufficient memory.\n"); return (-1); } rv = 0; /* Append the backup path to all the "ent[].name"s. */ for (i = 0; i < n; i++) { ent = (ndmp_name *)MOD_GETNAME(params, i); NDMP_LOG(LOG_DEBUG, "Old: ent[%d].name: \"%s\"", i, ent->name); NDMP_LOG(LOG_DEBUG, "Old: ent[%d].dest: \"%s\"", i, ent->dest); /* remove trailing slash */ len = strlen(ent->name); if (ent->name[len - 1] == '/') ent->name[len - 1] = '\0'; if (!tlm_cat_path(pathname, bkpath, ent->name)) { MOD_LOG(params, "Error: path too long.\n"); rv = -1; break; } /* Make a copy of the new string and save it in ent->name. */ cp = strdup(pathname); if (cp == NULL) { MOD_LOG(params, "Error: insufficient memory.\n"); rv = -1; break; } free(ent->name); ent->name = cp; NDMP_LOG(LOG_DEBUG, "New: ent[%d].name: \"%s\"", i, ent->name); } free(pathname); return (rv); }
/* * restore_create_structs * * Allocate structures for performing a restore * * Parameters: * sesison (input) - session handle * jname (input) - backup job name * * Returns: * 0: on success * -1: otherwise */ static int restore_create_structs(ndmpd_session_t *session, char *jname) { int i; long xfer_size; ndmp_lbr_params_t *nlp; tlm_commands_t *cmds; if ((nlp = ndmp_get_nlp(session)) == NULL) { NDMP_LOG(LOG_DEBUG, "nlp == NULL"); return (-1); } if ((nlp->nlp_jstat = tlm_new_job_stats(jname)) == NULL) { NDMP_LOG(LOG_DEBUG, "Creating job stats"); return (-1); } cmds = &nlp->nlp_cmds; (void) memset(cmds, 0, sizeof (*cmds)); xfer_size = ndmp_buffer_get_size(session); cmds->tcs_command = tlm_create_reader_writer_ipc(FALSE, xfer_size); if (cmds->tcs_command == NULL) { NDMP_LOG(LOG_DEBUG, "Error creating ipc buffers"); tlm_un_ref_job_stats(jname); return (-1); } nlp->nlp_logcallbacks = lbrlog_callbacks_init(session, ndmpd_path_restored, NULL, NULL); if (nlp->nlp_logcallbacks == NULL) { tlm_release_reader_writer_ipc(cmds->tcs_command); tlm_un_ref_job_stats(jname); return (-1); } nlp->nlp_jstat->js_callbacks = (void *)(nlp->nlp_logcallbacks); nlp->nlp_restored = ndmp_malloc(sizeof (boolean_t) * nlp->nlp_nfiles); if (nlp->nlp_restored == NULL) { lbrlog_callbacks_done(nlp->nlp_logcallbacks); tlm_release_reader_writer_ipc(cmds->tcs_command); tlm_un_ref_job_stats(jname); return (-1); } for (i = 0; i < (int)nlp->nlp_nfiles; i++) nlp->nlp_restored[i] = FALSE; return (0); }
/* * Initialize the file history callback functions */ lbr_fhlog_call_backs_t * lbrlog_callbacks_init(void *cookie, path_hist_func_t log_pname_func, dir_hist_func_t log_dir_func, node_hist_func_t log_node_func) { lbr_fhlog_call_backs_t *p; p = ndmp_malloc(sizeof (lbr_fhlog_call_backs_t)); if (p == NULL) return (NULL); p->fh_cookie = cookie; p->fh_logpname = (func_t)log_pname_func; p->fh_log_dir = (func_t)log_dir_func; p->fh_log_node = (func_t)log_node_func; return (p); }
/* * Creates a new traversing state based on the path passed to it. */ static traverse_state_t * new_tsp(char *path) { traverse_state_t *tsp; tsp = ndmp_malloc(sizeof (traverse_state_t)); if (!tsp) return (NULL); tsp->ts_end = strchr(path, '\0'); if (*(tsp->ts_end-1) == '/') *--tsp->ts_end = '\0'; tsp->ts_ent = NULL; tsp->ts_dpos = 0; return (tsp); }
/* * tlm_new_dir_info * * Create a new structure, set fh field to what is specified and the path * to the concatenation of directory and the component */ struct full_dir_info * tlm_new_dir_info(struct fs_fhandle *fhp, char *dir, char *nm) { struct full_dir_info *fdip; if (!(fdip = ndmp_malloc(sizeof (struct full_dir_info)))) return (NULL); (void) memcpy(&fdip->fd_dir_fh, fhp, sizeof (fs_fhandle_t)); if (!tlm_cat_path(fdip->fd_dir_name, dir, nm)) { free(fdip); NDMP_LOG(LOG_DEBUG, "TAPE BACKUP Find> path too long [%s][%s]", dir, nm); return (NULL); } return (fdip); }
/* * cstack_push * * Push an element onto the stack. Allocate a new node and assign the * data and len values. We don't care what about the real values of * data or len and we never try to access them. The stack head will * point to the new node. * * Returns 0 on success. Otherwise returns -1 to indicate overflow. */ int cstack_push(cstack_t *stk, void *data, int len) { cstack_t *stk_node; if (stk == NULL) { NDMP_LOG(LOG_DEBUG, "cstack_push: invalid stack"); return (-1); } if ((stk_node = ndmp_malloc(sizeof (cstack_t))) == NULL) return (-1); stk_node->data = data; stk_node->len = len; stk_node->next = stk->next; stk->next = stk_node; NDMP_LOG(LOG_DEBUG, "cstack_push(0x%p): 0x%p", stk, stk_node); return (0); }
/* * output_humongus_header * * output a special header record for HUGE files * output is: 1) a TAR "HUGE" header redord * 2) a "file" of size, name */ static int output_humongus_header(char *fullname, longlong_t file_size, tlm_cmd_t *local_commands) { char *buf; int len; long actual_size; tlm_tar_hdr_t *tar_hdr; /* * buf will contain: "%llu %s": * - 20 is the maximum length of 'ulong_tlong' decimal notation. * - The first '1' is for the ' ' between the "%llu" and the fullname. * - The last '1' is for the null-terminator of fullname. */ len = 20 + 1 + strlen(fullname) + 1; if ((buf = ndmp_malloc(sizeof (char) * len)) == NULL) return (-1); tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE, &actual_size, TRUE, local_commands); if (!tar_hdr) { free(buf); return (0); } tar_hdr->th_linkflag = LF_HUMONGUS; (void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ", len); tlm_build_header_checksum(tar_hdr); (void) snprintf(buf, len, "%lld %s", file_size, fullname); (void) output_mem(local_commands, buf, len); free(buf); return (0); }
/* * Attach the SCSI device by updating the structures */ void scsi_sasd_attach(scsi_adapter_t *sa, int sid, int lun, char *name, int type) { scsi_link_t *sl, *next; scsi_sasd_drive_t *ssd; ssd = ndmp_malloc(sizeof (scsi_sasd_drive_t)); if (ssd == NULL) return; scsi_sasd_drives[sasd_drive_count++] = ssd; switch (type) { case DTYPE_CHANGER: (void) snprintf(ssd->ss_sd.sd_name, sizeof (ssd->ss_sd.sd_name), "%s/%s", SCSI_CHANGER_DIR, name); break; case DTYPE_SEQUENTIAL: (void) snprintf(ssd->ss_sd.sd_name, sizeof (ssd->ss_sd.sd_name), "%s/%s", SCSI_TAPE_DIR, name); break; } sl = &ssd->ss_slink; sl->sl_type = type; sl->sl_sa = sa; sl->sl_lun = lun; sl->sl_sid = sid; sl->sl_requested_max_active = 1; /* Insert slink */ next = sa->sa_link_head.sl_next; sa->sa_link_head.sl_next = sl; sl->sl_next = next; }
/* * create the IPC area between the reader and writer */ tlm_cmd_t * tlm_create_reader_writer_ipc(boolean_t write, long data_transfer_size) { tlm_cmd_t *cmd; cmd = ndmp_malloc(sizeof (tlm_cmd_t)); if (cmd == NULL) return (NULL); cmd->tc_reader = TLM_BACKUP_RUN; cmd->tc_writer = TLM_BACKUP_RUN; cmd->tc_ref = 1; cmd->tc_buffers = tlm_allocate_buffers(write, data_transfer_size); if (cmd->tc_buffers == NULL) { free(cmd); return (NULL); } (void) mutex_init(&cmd->tc_mtx, 0, NULL); (void) cond_init(&cmd->tc_cv, 0, NULL); return (cmd); }
/* * ndmpd_api_file_history_file_v3 * * Add a file history file entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * name (input) - file name. * NULL forces buffered data to be sent. * file_stat (input) - file status pointer. * fh_info (input) - data stream position of file data used during * fast restore. * * Returns: * 0 - success * -1 - error */ int ndmpd_api_file_history_file_v3(void *cookie, char *name, struct stat64 *file_stat, u_longlong_t fh_info) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_file_v3 *file_entry; ndmp_file_name_v3 *file_name_entry; ndmp_file_stat_v3 *file_stat_entry; ndmp_fh_add_file_request_v3 request; if (name == NULL && session->ns_fh_v3.fh_file_index == 0) return (0); /* * If the buffer does not have space * for the current entry, send the buffered data to the client. * A NULL name indicates that any buffered data should be sent. */ if (name == NULL || session->ns_fh_v3.fh_file_index == N_FILE_ENTRIES || session->ns_fh_v3.fh_file_name_buf_index + strlen(name) + 1 > PATH_NAMEBUF_SIZE) { syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh_v3.fh_file_index); request.files.files_len = session->ns_fh_v3.fh_file_index; request.files.files_val = session->ns_fh_v3.fh_files; if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_FILE, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_ERR, "Sending ndmp_fh_add_file request failed"); return (-1); } session->ns_fh_v3.fh_file_index = 0; session->ns_fh_v3.fh_file_name_buf_index = 0; } if (name == NULL) return (0); if (session->ns_fh_v3.fh_files == 0) { session->ns_fh_v3.fh_files = ndmp_malloc(sizeof (ndmp_file_v3) * N_FILE_ENTRIES); if (session->ns_fh_v3.fh_files == 0) return (-1); } if (session->ns_fh_v3.fh_file_names == 0) { session->ns_fh_v3.fh_file_names = ndmp_malloc(sizeof (ndmp_file_name_v3) * N_FILE_ENTRIES); if (session->ns_fh_v3.fh_file_names == 0) return (-1); } if (session->ns_fh_v3.fh_file_name_buf == 0) { session->ns_fh_v3.fh_file_name_buf = ndmp_malloc(sizeof (char) * PATH_NAMEBUF_SIZE); if (session->ns_fh_v3.fh_file_name_buf == 0) return (-1); } if (session->ns_fh_v3.fh_file_stats == 0) { session->ns_fh_v3.fh_file_stats = ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_FILE_ENTRIES); if (session->ns_fh_v3.fh_file_stats == 0) return (-1); } file_entry = &session->ns_fh_v3.fh_files[session->ns_fh_v3.fh_file_index]; file_name_entry = &session->ns_fh_v3.fh_file_names[session->ns_fh_v3.fh_file_index]; file_stat_entry = &session->ns_fh_v3.fh_file_stats[session->ns_fh_v3.fh_file_index]; file_entry->names.names_len = 1; file_entry->names.names_val = file_name_entry; file_entry->stats.stats_len = 1; file_entry->stats.stats_val = file_stat_entry; file_entry->node = long_long_to_quad(file_stat->st_ino); file_entry->fh_info = long_long_to_quad(fh_info); file_name_entry->fs_type = NDMP_FS_UNIX; file_name_entry->ndmp_file_name_v3_u.unix_name = &session->ns_fh_v3.fh_file_name_buf[session-> ns_fh_v3.fh_file_name_buf_index]; (void) strlcpy(&session->ns_fh_v3.fh_file_name_buf[session-> ns_fh_v3.fh_file_name_buf_index], name, PATH_NAMEBUF_SIZE); session->ns_fh_v3.fh_file_name_buf_index += strlen(name) + 1; ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype); file_stat_entry->invalid = 0; file_stat_entry->fs_type = NDMP_FS_UNIX; file_stat_entry->mtime = file_stat->st_mtime; file_stat_entry->atime = file_stat->st_atime; file_stat_entry->ctime = file_stat->st_ctime; file_stat_entry->owner = file_stat->st_uid; file_stat_entry->group = file_stat->st_gid; file_stat_entry->fattr = file_stat->st_mode & 0x0fff; file_stat_entry->size = long_long_to_quad((u_longlong_t)file_stat->st_size); file_stat_entry->links = file_stat->st_nlink; session->ns_fh_v3.fh_file_index++; return (0); }
/* * ndmpd_api_file_history_node_v2 * * Add a file history node entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * node (input) - file inode. * must match a node from a prior ndmpd_api_file_history_dir() * call. * file_stat (input) - file status pointer. * 0 forces buffered data to be sent. * fh_info (input) - data stream position of file data used during * fast restore. * * Returns: * 0 - success * -1 - error. */ int ndmpd_api_file_history_node_v2(void *cookie, ulong_t node, struct stat64 *file_stat, u_longlong_t fh_info) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_fh_unix_node *entry; if (file_stat == NULL && session->ns_fh.fh_node_index == 0) return (-1); /* * If the buffer does not have space * for the current entry, send the buffered data to the client. * A 0 file_stat pointer indicates that any buffered data should * be sent. */ if (file_stat == NULL || (ndmp_syncfh && session->ns_fh.fh_node_index != 0) || session->ns_fh.fh_node_index == N_NODE_ENTRIES) { ndmp_fh_add_unix_node_request request; syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh.fh_node_index); request.nodes.nodes_val = session->ns_fh.fh_node_entries; request.nodes.nodes_len = session->ns_fh.fh_node_index; /* * Need to send Dir entry as well. Since Dir entry is more than * Node entry, we may send a Node entry that hasn't have * its dir entry sent. Therefore, we need to flush Dir entry * as well everytime the Dir entry is send. */ (void) ndmpd_api_file_history_dir_v2(session, 0, 0, 0); if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_UNIX_NODE, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_ERR, "Sending file history data failed"); return (-1); } session->ns_fh.fh_node_index = 0; } if (file_stat == NULL) return (0); if (session->ns_fh.fh_node_entries == 0) { session->ns_fh.fh_node_entries = ndmp_malloc(N_NODE_ENTRIES * sizeof (ndmp_fh_unix_node)); if (session->ns_fh.fh_node_entries == 0) return (-1); } entry = &session->ns_fh.fh_node_entries[session->ns_fh.fh_node_index]; ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype); entry->node = node; entry->fstat.mtime = (ulong_t)file_stat->st_mtime; entry->fstat.atime = (ulong_t)file_stat->st_atime; entry->fstat.ctime = (ulong_t)file_stat->st_ctime; entry->fstat.uid = file_stat->st_uid; entry->fstat.gid = file_stat->st_gid; entry->fstat.mode = (file_stat->st_mode) & 0x0fff; entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size); entry->fstat.fh_info = long_long_to_quad(fh_info); session->ns_fh.fh_node_index++; return (0); }
/* * ndmpd_api_file_history_dir_v2 * * Add a file history dir entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * name (input) - file name. * NULL forces buffered data to be sent. * node (input) - file inode. * parent (input) - file parent inode. * Should equal node if the file is the root of * the filesystem and has no parent. * * Returns: * 0 - success * -1 - error */ int ndmpd_api_file_history_dir_v2(void *cookie, char *name, ulong_t node, ulong_t parent) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_fh_unix_dir *entry; if (name == NULL && session->ns_fh.fh_dir_index == 0) return (0); /* * If the buffer does not have space for the current entry, * send the buffered data to the client. A NULL name indicates * that any buffered data should be sent. */ if (name == NULL || (ndmp_syncfh && session->ns_fh.fh_dir_index != 0) || session->ns_fh.fh_dir_index == N_DIR_ENTRIES || session->ns_fh.fh_dir_name_buf_index + strlen(name) + 1 > DIR_NAMEBUF_SIZE) { ndmp_fh_add_unix_dir_request request; syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh.fh_dir_index); request.dirs.dirs_val = session->ns_fh.fh_dir_entries; request.dirs.dirs_len = session->ns_fh.fh_dir_index; if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_UNIX_DIR, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_DEBUG, "Sending file history data"); return (-1); } session->ns_fh.fh_dir_index = 0; session->ns_fh.fh_dir_name_buf_index = 0; } if (name == NULL) return (0); if (session->ns_fh.fh_dir_entries == 0) { session->ns_fh.fh_dir_entries = ndmp_malloc(N_DIR_ENTRIES * sizeof (ndmp_fh_unix_dir)); if (session->ns_fh.fh_dir_entries == 0) return (-1); } if (session->ns_fh.fh_dir_name_buf == 0) { session->ns_fh.fh_dir_name_buf = ndmp_malloc(DIR_NAMEBUF_SIZE); if (session->ns_fh.fh_dir_name_buf == 0) return (-1); } entry = &session->ns_fh.fh_dir_entries[session->ns_fh.fh_dir_index]; entry->name = &session-> ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index]; (void) strlcpy(&session-> ns_fh.fh_dir_name_buf[session->ns_fh.fh_dir_name_buf_index], name, PATH_NAMEBUF_SIZE); session->ns_fh.fh_dir_name_buf_index += strlen(name) + 1; entry->node = node; entry->parent = parent; session->ns_fh.fh_dir_index++; return (0); }
/* * ndmpd_api_file_history_path_v2 * * Add a file history path entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * name (input) - file name. * NULL forces buffered data to be sent. * file_stat (input) - file status pointer. * fh_info (input) - data stream position of file data used during * fast restore. * * Returns: * 0 - success * -1 - error */ int ndmpd_api_file_history_path_v2(void *cookie, char *name, struct stat64 *file_stat, u_longlong_t fh_info) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_fh_unix_path *entry; if (name == NULL && session->ns_fh.fh_path_index == 0) return (0); /* * If the buffer does not have space * for the current entry, send the buffered data to the client. * A NULL name indicates that any buffered data should be sent. */ if (name == NULL || (ndmp_syncfh && session->ns_fh.fh_path_index != 0) || session->ns_fh.fh_path_index == N_PATH_ENTRIES || session->ns_fh.fh_path_name_buf_index + strlen(name) + 1 > PATH_NAMEBUF_SIZE) { ndmp_fh_add_unix_path_request request; syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh.fh_path_index); request.paths.paths_val = session->ns_fh.fh_path_entries; request.paths.paths_len = session->ns_fh.fh_path_index; if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_UNIX_PATH, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_ERR, "Sending file history data failed"); return (-1); } session->ns_fh.fh_path_index = 0; session->ns_fh.fh_path_name_buf_index = 0; } if (name == NULL) return (0); if (session->ns_fh.fh_path_entries == 0) { session->ns_fh.fh_path_entries = ndmp_malloc(N_PATH_ENTRIES * sizeof (ndmp_fh_unix_path)); if (session->ns_fh.fh_path_entries == 0) return (-1); } if (session->ns_fh.fh_path_name_buf == 0) { session->ns_fh.fh_path_name_buf = ndmp_malloc(PATH_NAMEBUF_SIZE); if (session->ns_fh.fh_path_name_buf == 0) return (-1); } entry = &session->ns_fh.fh_path_entries[session->ns_fh.fh_path_index]; ndmpd_get_file_entry_type(file_stat->st_mode, &entry->fstat.ftype); entry->name = &session-> ns_fh.fh_path_name_buf[session->ns_fh.fh_path_name_buf_index]; (void) strlcpy(entry->name, name, PATH_NAMEBUF_SIZE); session->ns_fh.fh_path_name_buf_index += strlen(name) + 1; entry->fstat.mtime = (ulong_t)file_stat->st_mtime; entry->fstat.atime = (ulong_t)file_stat->st_atime; entry->fstat.ctime = (ulong_t)file_stat->st_ctime; entry->fstat.uid = file_stat->st_uid; entry->fstat.gid = file_stat->st_gid; entry->fstat.mode = (file_stat->st_mode) & 0x0fff; entry->fstat.size = long_long_to_quad((u_longlong_t)file_stat->st_size); entry->fstat.fh_info = long_long_to_quad((u_longlong_t)fh_info); session->ns_fh.fh_path_index++; return (0); }
/* * backup_work * * Start the NDMP backup (V2 only). */ int backup_work(char *bk_path, tlm_job_stats_t *job_stats, ndmp_run_args_t *np, tlm_commands_t *commands, ndmp_lbr_params_t *nlp) { struct full_dir_info dir_info; /* the blob to push/pop with cstack_t */ struct full_dir_info *t_dir_info, *p_dir_info; struct stat64 ret_attr; /* attributes of current file name */ fs_fhandle_t ret_fh; char *first_name; /* where the first name is located */ char *dname; int erc; int retval; cstack_t *stk; unsigned long fileid; tlm_acls_t tlm_acls; int dname_size; longlong_t fsize; bk_selector_t bks; tlm_cmd_t *local_commands; long dpos; NDMP_LOG(LOG_DEBUG, "nr_chkpnted %d nr_ldate: %u bk_path: \"%s\"", NLP_ISCHKPNTED(nlp), nlp->nlp_ldate, bk_path); /* Get every name in this directory */ dname = ndmp_malloc(TLM_MAX_PATH_NAME); if (dname == NULL) return (-ENOMEM); local_commands = commands->tcs_command; retval = 0; (void) memset(&bks, 0, sizeof (bks)); bks.bs_cookie = (void *)nlp; bks.bs_level = nlp->nlp_clevel; bks.bs_ldate = nlp->nlp_ldate; bks.bs_fn = timecmp; /* * should we skip the whole thing? */ if (tlm_is_excluded("", bk_path, np->nr_excls)) { NDMP_LOG(LOG_DEBUG, "%s excluded", bk_path); free(dname); return (0); } /* * Search for the top-level file-directory */ if (NLP_ISCHKPNTED(nlp)) { first_name = np->nr_chkp_nm; (void) strlcpy(first_name, bk_path, TLM_MAX_PATH_NAME); } else { first_name = tlm_build_snapshot_name(bk_path, np->nr_chkp_nm, nlp->nlp_jstat->js_job_name); } (void) memset(&ret_fh, 0, sizeof (ret_fh)); erc = fs_getstat(first_name, &ret_fh, &ret_attr); if (erc != 0) { NDMP_LOG(LOG_ERR, "Path %s not found.", first_name); free(dname); return (-EINVAL); } if ((stk = cstack_new()) == NULL) { free(dname); NDMP_LOG(LOG_DEBUG, "cstack_new failed"); return (-ENOMEM); } (void) strlcpy(dir_info.fd_dir_name, first_name, TLM_MAX_PATH_NAME); (void) memcpy(&dir_info.fd_dir_fh, &ret_fh, sizeof (fs_fhandle_t)); p_dir_info = dup_dir_info(&dir_info); /* * Push the first name onto the stack so that we can pop it back * off as part of the normal cycle */ if (cstack_push(stk, p_dir_info, 0)) { free(dname); free(p_dir_info); cstack_delete(stk); NDMP_LOG(LOG_DEBUG, "cstack_push failed"); return (-ENOMEM); } (void) memset(&tlm_acls, 0, sizeof (tlm_acls)); /* * Did NDMP create a checkpoint? */ if (NLP_ISCHKPNTED(nlp) || fs_is_rdonly(bk_path)) { tlm_acls.acl_checkpointed = FALSE; } else { /* Use the checkpoint created by NDMP */ tlm_acls.acl_checkpointed = TRUE; } /* * This is level-backup. It never resets the archive bit. */ tlm_acls.acl_clear_archive = FALSE; NDMP_LOG(LOG_DEBUG, "acls.chkpnt: %c acls.clear_arcbit: %c", NDMP_YORN(tlm_acls.acl_checkpointed), NDMP_YORN(tlm_acls.acl_clear_archive)); while (commands->tcs_reader == TLM_BACKUP_RUN && local_commands->tc_reader == TLM_BACKUP_RUN && cstack_pop(stk, (void **)&p_dir_info, 0) == 0) { if (NLP_ISCHKPNTED(nlp)) (void) strlcpy(np->nr_unchkp_nm, p_dir_info->fd_dir_name, TLM_MAX_PATH_NAME); else (void) tlm_remove_checkpoint(p_dir_info->fd_dir_name, np->nr_unchkp_nm); (void) backup_dir(np->nr_unchkp_nm, &tlm_acls, local_commands, job_stats, &bks); while (commands->tcs_reader == TLM_BACKUP_RUN && local_commands->tc_reader == TLM_BACKUP_RUN) { dname_size = TLM_MAX_PATH_NAME - 1; NDMP_LOG(LOG_DEBUG, "dir_name: %s", p_dir_info->fd_dir_name); (void) memset(&ret_fh, 0, sizeof (ret_fh)); erc = fs_readdir(&p_dir_info->fd_dir_fh, p_dir_info->fd_dir_name, &dpos, dname, &dname_size, &ret_fh, &ret_attr); if (erc == 0) { fileid = ret_fh.fh_fid; } else { NDMP_LOG(LOG_DEBUG, "Filesystem readdir in [%s]", p_dir_info->fd_dir_name); retval = -ENOENT; break; } /* an empty name size marks the end of the list */ if (dname_size == 0) break; dname[dname_size] = '\0'; NDMP_LOG(LOG_DEBUG, "dname: \"%s\"", dname); /* * If name refers to a directory, push its file * handle onto the stack (skip "." and ".."). */ if (rootfs_dot_or_dotdot(dname)) { fileid = 0; continue; } /* * Skip the: * non-dir entries which should not be backed up * Or * dir-type entries which have have nothing under * their hierarchy to be backed up. */ if (!dbm_getone(nlp->nlp_bkmap, (u_longlong_t)fileid)) { NDMP_LOG(LOG_DEBUG, "Skipping %s/%s", p_dir_info->fd_dir_name, dname); fileid = 0; continue; } if (tlm_is_excluded(np->nr_unchkp_nm, dname, np->nr_excls)) { fileid = 0; continue; } if (S_ISDIR(ret_attr.st_mode)) { /* * only directories get pushed onto this stack, * so we do not have to test for regular files. */ t_dir_info = tlm_new_dir_info(&ret_fh, p_dir_info->fd_dir_name, dname); if (t_dir_info == NULL) { NDMP_LOG(LOG_DEBUG, "While backing up [%s][%s]", p_dir_info->fd_dir_name, dname); } else if (cstack_push(stk, t_dir_info, 0) != 0) { NDMP_LOG(LOG_DEBUG, "No enough memory stack_push"); retval = -ENOMEM; break; } } else if (S_ISREG(ret_attr.st_mode) || S_ISLNK(ret_attr.st_mode)) { fsize = backup_file(np->nr_unchkp_nm, dname, &tlm_acls, commands, local_commands, job_stats, &bks); if (fsize >= 0) { job_stats->js_files_so_far++; job_stats->js_bytes_total += fsize; } else job_stats->js_errors++; fileid = 0; } } fileid = 0; free(p_dir_info); if (retval != 0) break; } free(dname); while (cstack_pop(stk, (void **)&p_dir_info, 0) == 0) { free(p_dir_info); } cstack_delete(stk); return (retval); }
/* * get_dir_acl_info * * load up all ACL and attr info about a directory */ static int get_dir_acl_info(char *dir, tlm_acls_t *tlm_acls, tlm_job_stats_t *js) { int erc; char *checkpointed_dir; char root_dir[TLM_VOLNAME_MAX_LENGTH]; char *spot; char *fil; acl_t *aclp = NULL; char *acltp; checkpointed_dir = ndmp_malloc(TLM_MAX_PATH_NAME); if (checkpointed_dir == NULL) return (-1); if (tlm_acls->acl_checkpointed) fil = tlm_build_snapshot_name(dir, checkpointed_dir, js->js_job_name); else fil = dir; erc = lstat64(fil, &tlm_acls->acl_attr); if (erc != 0) { NDMP_LOG(LOG_ERR, "Could not find directory %s.", dir); free(checkpointed_dir); return (-1); } spot = strchr(&fil[1], '/'); if (spot == NULL) { (void) strlcpy(root_dir, fil, TLM_VOLNAME_MAX_LENGTH); } else { *spot = 0; (void) strlcpy(root_dir, fil, TLM_VOLNAME_MAX_LENGTH); *spot = '/'; } if (strcmp(root_dir, tlm_acls->acl_root_dir) != 0) { struct stat64 attr; erc = lstat64(root_dir, &attr); if (erc != 0) { NDMP_LOG(LOG_ERR, "Cannot find root directory %s.", root_dir); free(checkpointed_dir); return (-1); } (void) strlcpy(tlm_acls->acl_root_dir, root_dir, TLM_VOLNAME_MAX_LENGTH); } erc = acl_get(fil, ACL_NO_TRIVIAL, &aclp); if (erc != 0) { NDMP_LOG(LOG_DEBUG, "Could not read metadata for directory [%s]", dir); free(checkpointed_dir); return (-1); } if (aclp && (acltp = acl_totext(aclp, ACL_APPEND_ID | ACL_SID_FMT | ACL_COMPACT_FMT)) != NULL) { (void) strlcpy(tlm_acls->acl_info.attr_info, acltp, TLM_MAX_ACL_TXT); acl_free(aclp); free(acltp); } free(checkpointed_dir); return (0); }
/* * output_file_header * * output the TAR header record */ static int output_file_header(char *name, char *link, tlm_acls_t *tlm_acls, int section, tlm_cmd_t *local_commands) { static longlong_t file_count = 0; struct stat64 *attr = &tlm_acls->acl_attr; tlm_tar_hdr_t *tar_hdr; long actual_size; boolean_t long_name = FALSE; boolean_t long_link = FALSE; char *section_name = ndmp_malloc(TLM_MAX_PATH_NAME); int nmlen, lnklen; uid_t uid; gid_t gid; char *uname = ""; char *gname = ""; struct passwd *pwd; struct group *grp; if (section_name == NULL) return (-TLM_NO_SCRATCH_SPACE); /* * if the file has to go out in sections, * we must mung the name. */ if (section == 0) { (void) strlcpy(section_name, name, TLM_MAX_PATH_NAME); } else { (void) snprintf(section_name, TLM_MAX_PATH_NAME, "%s.%03d", name, section); } if ((pwd = getpwuid(attr->st_uid)) != NULL) uname = pwd->pw_name; if ((grp = getgrgid(attr->st_gid)) != NULL) gname = grp->gr_name; if ((ulong_t)(uid = attr->st_uid) > (ulong_t)OCTAL7CHAR) uid = UID_NOBODY; if ((ulong_t)(gid = attr->st_gid) > (ulong_t)OCTAL7CHAR) gid = GID_NOBODY; nmlen = strlen(section_name); if (nmlen >= NAMSIZ) { /* * file name is too big, it must go out * in its own data file */ tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE, &actual_size, TRUE, local_commands); if (!tar_hdr) { free(section_name); return (0); } (void) snprintf(tar_hdr->th_name, sizeof (tar_hdr->th_name), "%s%08qd.fil", LONGNAME_PREFIX, file_count++); tar_hdr->th_linkflag = LF_LONGNAME; (void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ", nmlen); (void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ", attr->st_mode & 07777); (void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ", uid); (void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ", gid); (void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname), "%.31s", uname); (void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname), "%.31s", gname); (void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime), "%011o ", attr->st_mtime); (void) strlcpy(tar_hdr->th_magic, TLM_MAGIC, sizeof (tar_hdr->th_magic)); tlm_build_header_checksum(tar_hdr); (void) output_mem(local_commands, (void *)section_name, nmlen); long_name = TRUE; } lnklen = strlen(link); if (lnklen >= NAMSIZ) { /* * link name is too big, it must go out * in its own data file */ tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE, &actual_size, TRUE, local_commands); if (!tar_hdr) { free(section_name); return (0); } (void) snprintf(tar_hdr->th_linkname, sizeof (tar_hdr->th_name), "%s%08qd.slk", LONGNAME_PREFIX, file_count++); tar_hdr->th_linkflag = LF_LONGLINK; (void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ", lnklen); (void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ", attr->st_mode & 07777); (void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ", uid); (void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ", gid); (void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname), "%.31s", uname); (void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname), "%.31s", gname); (void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime), "%011o ", attr->st_mtime); (void) strlcpy(tar_hdr->th_magic, TLM_MAGIC, sizeof (tar_hdr->th_magic)); tlm_build_header_checksum(tar_hdr); (void) output_mem(local_commands, (void *)link, lnklen); long_link = TRUE; } tar_hdr = (tlm_tar_hdr_t *)get_write_buffer(RECORDSIZE, &actual_size, TRUE, local_commands); if (!tar_hdr) { free(section_name); return (0); } if (long_name) { (void) snprintf(tar_hdr->th_name, sizeof (tar_hdr->th_name), "%s%08qd.fil", LONGNAME_PREFIX, file_count++); } else { (void) strlcpy(tar_hdr->th_name, section_name, TLM_NAME_SIZE); } if (long_link) { (void) snprintf(tar_hdr->th_linkname, sizeof (tar_hdr->th_name), "%s%08qd.slk", LONGNAME_PREFIX, file_count++); } else { (void) strlcpy(tar_hdr->th_linkname, link, TLM_NAME_SIZE); } switch (attr->st_mode & S_IFMT) { case S_IFDIR: tar_hdr->th_linkflag = LF_DIR; break; case S_IFIFO: tar_hdr->th_linkflag = LF_FIFO; break; case S_IFBLK: case S_IFCHR: if (S_ISBLK(attr->st_mode)) tar_hdr->th_linkflag = LF_BLK; else tar_hdr->th_linkflag = LF_CHR; (void) snprintf(tar_hdr->th_shared.th_dev.th_devmajor, sizeof (tar_hdr->th_shared.th_dev.th_devmajor), "%06o ", major(attr->st_rdev)); (void) snprintf(tar_hdr->th_shared.th_dev.th_devminor, sizeof (tar_hdr->th_shared.th_dev.th_devminor), "%06o ", minor(attr->st_rdev)); break; default: if (attr->st_nlink > 1) { /* mark file with hardlink LF_LINK */ tar_hdr->th_linkflag = LF_LINK; (void) snprintf(tar_hdr->th_shared.th_hlink_ino, sizeof (tar_hdr->th_shared.th_hlink_ino), "%011llo ", attr->st_ino); } else { tar_hdr->th_linkflag = *link == 0 ? LF_NORMAL : LF_SYMLINK; } } (void) snprintf(tar_hdr->th_size, sizeof (tar_hdr->th_size), "%011o ", (long)attr->st_size); (void) snprintf(tar_hdr->th_mode, sizeof (tar_hdr->th_mode), "%06o ", attr->st_mode & 07777); (void) snprintf(tar_hdr->th_uid, sizeof (tar_hdr->th_uid), "%06o ", uid); (void) snprintf(tar_hdr->th_gid, sizeof (tar_hdr->th_gid), "%06o ", gid); (void) snprintf(tar_hdr->th_uname, sizeof (tar_hdr->th_uname), "%.31s", uname); (void) snprintf(tar_hdr->th_gname, sizeof (tar_hdr->th_gname), "%.31s", gname); (void) snprintf(tar_hdr->th_mtime, sizeof (tar_hdr->th_mtime), "%011o ", attr->st_mtime); (void) strlcpy(tar_hdr->th_magic, TLM_MAGIC, sizeof (tar_hdr->th_magic)); tlm_build_header_checksum(tar_hdr); if (long_name || long_link) { if (file_count > 99999990) { file_count = 0; } } free(section_name); return (0); }
/*ARGSUSED*/ longlong_t tlm_output_xattr(char *dir, char *name, char *chkdir, tlm_acls_t *tlm_acls, tlm_commands_t *commands, tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats) { char *fullname; /* directory + name */ char *snapname; /* snapshot name */ int section; /* section of a huge file */ int fd; int afd = 0; longlong_t seek_spot = 0; /* location in the file */ /* for Multi Volume record */ DIR *dp; struct dirent *dtp; char *attrname; char *fnamep; int rv = 0; if (S_ISPECIAL(tlm_acls->acl_attr.st_mode)) { return (TLM_NO_SOURCE_FILE); } fullname = ndmp_malloc(TLM_MAX_PATH_NAME); if (fullname == NULL) { free(fullname); return (-TLM_NO_SCRATCH_SPACE); } if (!tlm_cat_path(fullname, dir, name)) { syslog(LOG_ERR, "Path too long."); free(fullname); return (-TLM_NO_SCRATCH_SPACE); } if (pathconf(fullname, _PC_XATTR_EXISTS) != 1 && sysattr_support(fullname, _PC_SATTR_EXISTS) != 1) { free(fullname); return (0); } attrname = ndmp_malloc(TLM_MAX_PATH_NAME); snapname = ndmp_malloc(TLM_MAX_PATH_NAME); if (attrname == NULL || snapname == NULL) { rv = -TLM_NO_SCRATCH_SPACE; goto err_out; } if (!tlm_cat_path(snapname, chkdir, name)) { syslog(LOG_ERR, "Path too long."); rv = -TLM_NO_SCRATCH_SPACE; goto err_out; } fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname; /* * Open the file for reading. */ fd = attropen(fnamep, ".", O_RDONLY); if (fd == -1) { syslog(LOG_ERR, "BACKUP> Can't open file [%s][%s]", fullname, fnamep); rv = TLM_NO_SOURCE_FILE; goto err_out; } section = 0; dp = (DIR *)fdopendir(fd); if (dp == NULL) { syslog(LOG_ERR, "BACKUP> Can't open file [%s]", fullname); (void) close(fd); rv = TLM_NO_SOURCE_FILE; goto err_out; } while ((dtp = readdir(dp)) != NULL) { int section_size; if (*dtp->d_name == '.') continue; if (sysattr_rdonly(dtp->d_name)) continue; afd = attropen(fnamep, dtp->d_name, O_RDONLY); if (afd == -1) { syslog(LOG_ERR, "problem(%d) opening xattr file [%s][%s]", errno, fullname, fnamep); goto tear_down; } (void) output_xattr_header(fullname, dtp->d_name, afd, tlm_acls, section, local_commands); (void) snprintf(attrname, TLM_MAX_PATH_NAME, "/dev/null/%s", dtp->d_name); (void) output_file_header(attrname, "", tlm_acls, 0, local_commands); section_size = (long)llmin(tlm_acls->acl_attr.st_size, (longlong_t)TLM_MAX_TAR_IMAGE); /* We only can read upto one section extended attribute */ while (section_size > 0) { char *buf; long actual_size; int read_size; int sysattr_read = 0; char *rec; int size; /* * check for Abort commands */ if (commands->tcs_reader != TLM_BACKUP_RUN) { local_commands->tc_writer = TLM_ABORT; goto tear_down; } local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in]. tb_file_size = section_size; local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in]. tb_seek_spot = seek_spot; buf = get_write_buffer(section_size, &actual_size, FALSE, local_commands); if (!buf) goto tear_down; if ((actual_size < section_size) && sysattr_rw(dtp->d_name)) { rec = buf; buf = ndmp_malloc(section_size); if (!buf) goto tear_down; size = actual_size; actual_size = section_size; sysattr_read = 1; } /* * check for Abort commands */ if (commands->tcs_reader != TLM_BACKUP_RUN) { local_commands->tc_writer = TLM_ABORT; goto tear_down; } read_size = min(section_size, actual_size); if ((actual_size = read(afd, buf, read_size)) < 0) break; if (sysattr_read) { if (get_write_one_buf(buf, rec, read_size, size, local_commands) == 0) { free(buf); goto tear_down; } free(buf); } NS_ADD(rdisk, actual_size); NS_INC(rfile); if (actual_size == -1) { syslog(LOG_ERR, "problem(%d) reading file [%s][%s]", errno, fullname, snapname); goto tear_down; } seek_spot += actual_size; section_size -= actual_size; } (void) close(afd); afd = -1; } tear_down: local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0; if (afd > 0) (void) close(afd); /* closedir closes fd too */ (void) closedir(dp); err_out: free(fullname); free(attrname); free(snapname); return (rv); }
/* * Traverse the file system in the level-order way. The description * and example is in the header file. */ int traverse_level(struct fs_traverse *ftp) { char path[PATH_MAX + 1]; /* full path name of the current dir */ char nm[NAME_MAX + 1]; /* directory entry name */ char *lp; /* last position on the path */ int next_dir, rv; int pl, el; /* path and directory entry length */ cstack_t *sp; fs_fhandle_t pfh, efh; struct stat64 pst, est; traverse_state_t *tsp; struct fst_node pn, en; /* parent and entry nodes */ dent_arg_t darg; if (!ftp || !ftp->ft_path || !*ftp->ft_path || !ftp->ft_callbk) { NDMP_LOG(LOG_DEBUG, "Invalid argument"); errno = EINVAL; return (-1); } /* set the default log function if it's not already set */ if (!ftp->ft_logfp) { ftp->ft_logfp = (ft_log_t)syslog; NDMP_LOG(LOG_DEBUG, "Log to system log \"%s\"", ftp->ft_path); } if (!ftp->ft_lpath) { NDMP_LOG(LOG_DEBUG, "report the same paths \"%s\"", ftp->ft_path); ftp->ft_lpath = ftp->ft_path; } pl = strlen(ftp->ft_lpath); if (pl + 1 > PATH_MAX) { /* +1 for the '/' */ NDMP_LOG(LOG_DEBUG, "lpath too long \"%s\"", ftp->ft_path); errno = ENAMETOOLONG; return (-1); } (void) strcpy(path, ftp->ft_lpath); (void) memset(&pfh, 0, sizeof (pfh)); rv = fs_getstat(ftp->ft_lpath, &pfh, &pst); if (rv != 0) { NDMP_LOG(LOG_DEBUG, "Error %d on fs_getstat(%s)", rv, ftp->ft_path); return (-1); } en.tn_path = NULL; en.tn_fh = NULL; en.tn_st = NULL; if (!S_ISDIR(pst.st_mode)) { pn.tn_path = ftp->ft_lpath; pn.tn_fh = &pfh; pn.tn_st = &pst; rv = CALLBACK(&pn, &en); if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "CALLBACK(%s): %d", pn.tn_path, rv); free(pfh.fh_fpath); return (rv); } sp = cstack_new(); if (!sp) { free(pfh.fh_fpath); errno = ENOMEM; return (-1); } tsp = new_tsp(path); if (!tsp) { cstack_delete(sp); free(pfh.fh_fpath); errno = ENOMEM; return (-1); } darg.da_buf = ndmp_malloc(MAX_DENT_BUF_SIZE); if (!darg.da_buf) { cstack_delete(sp); free(pfh.fh_fpath); free(tsp); errno = ENOMEM; return (-1); } darg.da_size = MAX_DENT_BUF_SIZE; tsp->ts_ent = tsp->ts_end; tsp->ts_fh = pfh; tsp->ts_st = pst; pn.tn_path = path; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; /* call the callback function on the path itself */ traverse_stats.fss_dir_calls++; rv = CALLBACK(&pn, &en); if (rv < 0) { free(tsp); goto end; } if (rv == FST_SKIP) { traverse_stats.fss_dir_skipped++; free(tsp); rv = 0; goto end; } rv = 0; next_dir = 1; do { if (next_dir) { traverse_stats.fss_newdirs++; *tsp->ts_end = '\0'; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "pl %d \"%s\"", pl, path); rv = traverse_level_nondir(ftp, tsp, &pn, &darg); if (rv < 0) { NEGATE(rv); free(tsp->ts_fh.fh_fpath); free(tsp); break; } /* * If skipped by the callback function or * error happened reading the information */ if (rv == FST_SKIP || rv == SKIP_ENTRY) { /* * N.B. next_dir should be set to 0 as * well. This prevents the infinite loop. * If it's not set the same directory will * be poped from the stack and will be * scanned again. */ next_dir = 0; rv = 0; goto skip_dir; } /* re-start reading entries of the directory */ tsp->ts_dpos = 0; } next_dir = 0; do { el = NAME_MAX; rv = fs_readdir(&tsp->ts_fh, pn.tn_path, &tsp->ts_dpos, nm, &el, &efh, &est); if (rv != 0) { traverse_stats.fss_readdir_err++; NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", rv, path, tsp->ts_dpos); if (STOP_ONERR(ftp)) break; rv = SKIP_ENTRY; continue; } /* done with this directory */ if (el == 0) break; nm[el] = '\0'; if (rootfs_dot_or_dotdot(nm)) { free(efh.fh_fpath); continue; } if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "%u dname: \"%s\"", tsp->ts_dpos, nm); if (pl + 1 + el > PATH_MAX) { /* * The long paths were already encountered * when processing non-dir entries in. * traverse_level_nondir. * We don't increase fss_longpath_err * counter for them again here. */ NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", path, nm); if (STOP_ONLONG(ftp)) rv = ENAMETOOLONG; free(efh.fh_fpath); continue; } if (!S_ISDIR(est.st_mode)) continue; /* * Call the callback function for the new * directory found, then push the current * directory on to the stack. Then dive * into the entry found. */ traverse_stats.fss_dir_calls++; en.tn_path = nm; en.tn_fh = &efh; en.tn_st = &est; rv = CALLBACK(&pn, &en); if (rv < 0) { NEGATE(rv); free(efh.fh_fpath); break; } if (rv == FST_SKIP) { traverse_stats.fss_dir_skipped++; free(efh.fh_fpath); rv = 0; continue; } /* * Push the current directory on to the stack and * dive into the entry found. */ if (cstack_push(sp, tsp, 0)) { rv = ENOMEM; } else { traverse_stats.fss_pushes++; lp = tsp->ts_end; *tsp->ts_end = '/'; (void) strcpy(tsp->ts_end + 1, nm); tsp = new_tsp(path); if (!tsp) rv = ENOMEM; else { next_dir = 1; pl += el + 1; tsp->ts_fh = efh; tsp->ts_st = est; tsp->ts_ent = lp; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; } } break; } while (rv == 0); /* * A new directory must be processed, go to the start of * the loop, open it and process it. */ if (next_dir) continue; skip_dir: if (tsp) { free(tsp->ts_fh.fh_fpath); free(tsp); } if (rv == SKIP_ENTRY) rv = 0; if (rv == 0) { if (cstack_pop(sp, (void **)&tsp, (int *)NULL)) break; traverse_stats.fss_pops++; if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "Poped pl %d \"%s\"", pl, path); *tsp->ts_end = '\0'; pl = tsp->ts_end - path; pn.tn_fh = &tsp->ts_fh; pn.tn_st = &tsp->ts_st; } } while (rv == 0); /* * Pop and free all the remaining entries on the stack. */ while (!cstack_pop(sp, (void **)&tsp, (int *)NULL)) { traverse_stats.fss_stack_residue++; free(tsp->ts_fh.fh_fpath); free(tsp); } end: free(darg.da_buf); cstack_delete(sp); return (rv); }
/* * In one pass, read all the directory entries of the specified * directory and call the callback function for non-directory * entries. * * On return: * 0: Lets the directory to be scanned for directory entries. * < 0: Completely stops traversing. * FST_SKIP: stops further scanning of the directory. Traversing * will continue with the next directory in the hierarchy. * SKIP_ENTRY: Failed to get the directory entries, so the caller * should skip this entry. */ static int traverse_level_nondir(struct fs_traverse *ftp, traverse_state_t *tsp, struct fst_node *pnp, dent_arg_t *darg) { int pl; /* path length */ int rv; struct fst_node en; /* entry node */ longlong_t cookie_verf; fs_dent_info_t *dent; struct dirent *buf; size_t len = 0; int fd; rv = 0; pl = strlen(pnp->tn_path); buf = ndmp_malloc(MAX_DENT_BUF_SIZE); if (buf == NULL) return (errno); fd = open(tsp->ts_fh.fh_fpath, O_RDONLY); if (fd == -1) { free(buf); return (errno); } while (rv == 0) { long i, n_entries; darg->da_end = 0; n_entries = 0; rv = fs_getdents(fd, buf, &len, pnp->tn_path, &tsp->ts_dpos, &cookie_verf, &n_entries, darg); if (rv < 0) { traverse_stats.fss_readdir_err++; NDMP_LOG(LOG_DEBUG, "Error %d on readdir(%s) pos %d", rv, pnp->tn_path, tsp->ts_dpos); if (STOP_ONERR(ftp)) break; /* * We cannot read the directory entry, we should * skip to the next directory. */ rv = SKIP_ENTRY; continue; } else { /* Break at the end of directory */ if (rv > 0) rv = 0; else break; } /* LINTED imporper alignment */ dent = (fs_dent_info_t *)darg->da_buf; /* LINTED imporper alignment */ for (i = 0; i < n_entries; i++, dent = (fs_dent_info_t *) ((char *)dent + dent->fd_len)) { if (VERBOSE(ftp)) NDMP_LOG(LOG_DEBUG, "i %u dname: \"%s\"", dent->fd_fh.fh_fid, dent->fd_name); if ((pl + strlen(dent->fd_name)) > PATH_MAX) { traverse_stats.fss_longpath_err++; NDMP_LOG(LOG_ERR, "Path %s/%s is too long.", pnp->tn_path, dent->fd_name); if (STOP_ONLONG(ftp)) rv = -ENAMETOOLONG; free(dent->fd_fh.fh_fpath); continue; } /* * The entry is not a directory so the callback * function must be called. */ if (!S_ISDIR(dent->fd_attr.st_mode)) { traverse_stats.fss_nondir_calls++; en.tn_path = dent->fd_name; en.tn_fh = &dent->fd_fh; en.tn_st = &dent->fd_attr; rv = CALLBACK(pnp, &en); dent->fd_fh.fh_fpath = NULL; if (rv < 0) break; if (rv == FST_SKIP) { traverse_stats.fss_nondir_skipped++; break; } } } } free(buf); (void) close(fd); return (rv); }
/* * tlm_output_file * * Put this file into the output buffers. */ longlong_t tlm_output_file(char *dir, char *name, char *chkdir, tlm_acls_t *tlm_acls, tlm_commands_t *commands, tlm_cmd_t *local_commands, tlm_job_stats_t *job_stats, struct hardlink_q *hardlink_q) { char *fullname; /* directory + name */ char *snapname; /* snapshot name */ char *linkname; /* where this file points */ int section = 0; /* section of a huge file */ int fd; longlong_t real_size; /* the origional file size */ longlong_t file_size; /* real size of this file */ longlong_t seek_spot = 0; /* location in the file */ /* for Multi Volume record */ u_longlong_t pos; char *fnamep; /* Indicate whether a file with the same inode has been backed up. */ int hardlink_done = 0; /* * If a file with the same inode has been backed up, hardlink_pos holds * the tape offset of the data record. */ u_longlong_t hardlink_pos = 0; if (tlm_is_too_long(tlm_acls->acl_checkpointed, dir, name)) { syslog(LOG_ERR, "Path too long [%s][%s]", dir, name); return (-TLM_NO_SCRATCH_SPACE); } fullname = ndmp_malloc(TLM_MAX_PATH_NAME); linkname = ndmp_malloc(TLM_MAX_PATH_NAME); snapname = ndmp_malloc(TLM_MAX_PATH_NAME); if (fullname == NULL || linkname == NULL || snapname == NULL) { real_size = -TLM_NO_SCRATCH_SPACE; goto err_out; } if (!tlm_cat_path(fullname, dir, name) || !tlm_cat_path(snapname, chkdir, name)) { syslog(LOG_ERR, "Path too long."); real_size = -TLM_NO_SCRATCH_SPACE; goto err_out; } pos = tlm_get_data_offset(local_commands); if (S_ISPECIAL(tlm_acls->acl_attr.st_mode)) { if (S_ISLNK(tlm_acls->acl_attr.st_mode)) { file_size = tlm_readlink(fullname, snapname, linkname, TLM_MAX_PATH_NAME-1); if (file_size < 0) { real_size = -ENOENT; goto err_out; } } /* * Since soft links can not be read(2), we should only * backup the file header. */ (void) output_file_header(fullname, linkname, tlm_acls, section, local_commands); (void) tlm_log_fhnode(job_stats, dir, name, &tlm_acls->acl_attr, pos); (void) tlm_log_fhpath_name(job_stats, fullname, &tlm_acls->acl_attr, pos); free(fullname); free(linkname); free(snapname); return (0); } fnamep = (tlm_acls->acl_checkpointed) ? snapname : fullname; /* * For hardlink, only read the data if no other link * belonging to the same inode has been backed up. */ if (tlm_acls->acl_attr.st_nlink > 1) { hardlink_done = !hardlink_q_get(hardlink_q, tlm_acls->acl_attr.st_ino, &hardlink_pos, NULL); } if (!hardlink_done) { /* * Open the file for reading. */ fd = open(fnamep, O_RDONLY); if (fd == -1) { syslog(LOG_ERR, "BACKUP> Can't open file [%s][%s] err(%d)", fullname, fnamep, errno); real_size = -TLM_NO_SOURCE_FILE; goto err_out; } } else { syslog(LOG_DEBUG, "found hardlink, inode = %llu, pos = %llu ", tlm_acls->acl_attr.st_ino, hardlink_pos); fd = -1; } linkname[0] = 0; real_size = tlm_acls->acl_attr.st_size; (void) output_acl_header(&tlm_acls->acl_info, local_commands); /* * section = 0: file is small enough for TAR * section > 0: file goes out in TLM_MAX_TAR_IMAGE sized chunks * and the file name gets munged */ file_size = real_size; if (file_size > TLM_MAX_TAR_IMAGE) { if (output_humongus_header(fullname, file_size, local_commands) < 0) { (void) close(fd); real_size = -TLM_NO_SCRATCH_SPACE; goto err_out; } section = 1; } else { section = 0; } /* * For hardlink, if other link belonging to the same inode * has been backed up, only backup an empty record. */ if (hardlink_done) file_size = 0; /* * work */ if (file_size == 0) { (void) output_file_header(fullname, linkname, tlm_acls, section, local_commands); /* * this can fall right through since zero size files * will be skipped by the WHILE loop anyway */ } while (file_size > 0) { int section_size = llmin(file_size, (longlong_t)TLM_MAX_TAR_IMAGE); tlm_acls->acl_attr.st_size = (longlong_t)section_size; (void) output_file_header(fullname, linkname, tlm_acls, section, local_commands); while (section_size > 0) { char *buf; long actual_size; int read_size; /* * check for Abort commands */ if (commands->tcs_reader != TLM_BACKUP_RUN) { local_commands->tc_writer = TLM_ABORT; goto tear_down; } local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in]. tb_file_size = section_size; local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in]. tb_seek_spot = seek_spot; buf = get_write_buffer(section_size, &actual_size, FALSE, local_commands); if (!buf) goto tear_down; /* * check for Abort commands */ if (commands->tcs_reader != TLM_BACKUP_RUN) { local_commands->tc_writer = TLM_ABORT; goto tear_down; } read_size = min(section_size, actual_size); actual_size = read(fd, buf, read_size); NS_ADD(rdisk, actual_size); NS_INC(rfile); if (actual_size == 0) break; if (actual_size == -1) { syslog(LOG_ERR, "problem(%d) reading file [%s][%s]", errno, fullname, snapname); goto tear_down; } seek_spot += actual_size; file_size -= actual_size; section_size -= actual_size; } section++; } /* * If data belonging to this hardlink has been backed up, add the link * to hardlink queue. */ if (tlm_acls->acl_attr.st_nlink > 1 && !hardlink_done) { (void) hardlink_q_add(hardlink_q, tlm_acls->acl_attr.st_ino, pos, NULL, 0); syslog(LOG_DEBUG, "backed up hardlink file %s, inode = %llu, pos = %llu ", fullname, tlm_acls->acl_attr.st_ino, pos); } /* * For hardlink, if other link belonging to the same inode has been * backed up, no add_node entry should be sent for this link. */ if (hardlink_done) { syslog(LOG_DEBUG, "backed up hardlink link %s, inode = %llu, pos = %llu ", fullname, tlm_acls->acl_attr.st_ino, hardlink_pos); } else { (void) tlm_log_fhnode(job_stats, dir, name, &tlm_acls->acl_attr, pos); } (void) tlm_log_fhpath_name(job_stats, fullname, &tlm_acls->acl_attr, pos); tear_down: local_commands->tc_buffers->tbs_buffer[ local_commands->tc_buffers->tbs_buffer_in].tb_seek_spot = 0; (void) close(fd); err_out: free(fullname); free(linkname); free(snapname); return (real_size); }
/* * ndmpd_api_file_history_dir_v3 * * Add a file history dir entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * name (input) - file name. * NULL forces buffered data to be sent. * node (input) - file inode. * parent (input) - file parent inode. * Should equal node if the file is the root of * the filesystem and has no parent. * * Returns: * 0 - success * -1 - error */ int ndmpd_api_file_history_dir_v3(void *cookie, char *name, ulong_t node, ulong_t parent) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_dir_v3 *dir_entry; ndmp_file_name_v3 *dir_name_entry; ndmp_fh_add_dir_request_v3 request; if (name == NULL && session->ns_fh_v3.fh_dir_index == 0) return (0); /* * If the buffer does not have space * for the current entry, send the buffered data to the client. * A NULL name indicates that any buffered data should be sent. */ if (name == NULL || session->ns_fh_v3.fh_dir_index == N_DIR_ENTRIES || session->ns_fh_v3.fh_dir_name_buf_index + strlen(name) + 1 > DIR_NAMEBUF_SIZE) { syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh_v3.fh_dir_index); request.dirs.dirs_val = session->ns_fh_v3.fh_dirs; request.dirs.dirs_len = session->ns_fh_v3.fh_dir_index; if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_DIR, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_ERR, "Sending ndmp_fh_add_dir request failed"); return (-1); } session->ns_fh_v3.fh_dir_index = 0; session->ns_fh_v3.fh_dir_name_buf_index = 0; } if (name == NULL) return (0); if (session->ns_fh_v3.fh_dirs == 0) { session->ns_fh_v3.fh_dirs = ndmp_malloc(sizeof (ndmp_dir_v3) * N_DIR_ENTRIES); if (session->ns_fh_v3.fh_dirs == 0) return (-1); } if (session->ns_fh_v3.fh_dir_names == 0) { session->ns_fh_v3.fh_dir_names = ndmp_malloc(sizeof (ndmp_file_name_v3) * N_DIR_ENTRIES); if (session->ns_fh_v3.fh_dir_names == 0) return (-1); } if (session->ns_fh_v3.fh_dir_name_buf == 0) { session->ns_fh_v3.fh_dir_name_buf = ndmp_malloc(sizeof (char) * DIR_NAMEBUF_SIZE); if (session->ns_fh_v3.fh_dir_name_buf == 0) return (-1); } dir_entry = &session->ns_fh_v3.fh_dirs[session->ns_fh_v3.fh_dir_index]; dir_name_entry = &session->ns_fh_v3.fh_dir_names[session->ns_fh_v3.fh_dir_index]; dir_name_entry->fs_type = NDMP_FS_UNIX; dir_name_entry->ndmp_file_name_v3_u.unix_name = &session->ns_fh_v3.fh_dir_name_buf[session-> ns_fh_v3.fh_dir_name_buf_index]; (void) strlcpy(&session->ns_fh_v3.fh_dir_name_buf[session-> ns_fh_v3.fh_dir_name_buf_index], name, PATH_NAMEBUF_SIZE); session->ns_fh_v3.fh_dir_name_buf_index += strlen(name) + 1; dir_entry->names.names_len = 1; dir_entry->names.names_val = dir_name_entry; dir_entry->node = long_long_to_quad(node); dir_entry->parent = long_long_to_quad(parent); session->ns_fh_v3.fh_dir_index++; return (0); }
/* * ndmp_recv_msg * * Read the next message. * * Parameters: * connection (input) - connection pointer. * msg (output) - received message. * * Returns: * 0 - Message successfully received. * error number - Message related error. * -1 - Error decoding the message header. */ static int ndmp_recv_msg(ndmp_connection_t *connection) { bool_t(*xdr_func) (XDR *, ...) = NULL; /* Decode the header. */ connection->conn_xdrs.x_op = XDR_DECODE; (void) xdrrec_skiprecord(&connection->conn_xdrs); if (!xdr_ndmp_header(&connection->conn_xdrs, &connection->conn_msginfo.mi_hdr)) return (-1); /* Lookup info necessary for processing this message. */ if ((connection->conn_msginfo.mi_handler = ndmp_get_handler(connection, connection->conn_msginfo.mi_hdr.message)) == 0) { NDMP_LOG(LOG_DEBUG, "Message 0x%x not supported", connection->conn_msginfo.mi_hdr.message); return (NDMP_NOT_SUPPORTED_ERR); } connection->conn_msginfo.mi_body = 0; if (connection->conn_msginfo.mi_hdr.error != NDMP_NO_ERR) return (0); /* Determine body type */ if (connection->conn_msginfo.mi_hdr.message_type == NDMP_MESSAGE_REQUEST) { if (ndmp_check_auth_required( connection->conn_msginfo.mi_hdr.message) && !connection->conn_authorized) { NDMP_LOG(LOG_DEBUG, "Processing request 0x%x:connection not authorized", connection->conn_msginfo.mi_hdr.message); return (NDMP_NOT_AUTHORIZED_ERR); } if (connection->conn_msginfo.mi_handler->mh_sizeof_request > 0) { xdr_func = connection->conn_msginfo.mi_handler->mh_xdr_request; if (xdr_func == NULL) { NDMP_LOG(LOG_DEBUG, "Processing request 0x%x: no xdr function " "in handler table", connection->conn_msginfo.mi_hdr.message); return (NDMP_NOT_SUPPORTED_ERR); } connection->conn_msginfo.mi_body = ndmp_malloc( connection->conn_msginfo.mi_handler-> mh_sizeof_request); if (connection->conn_msginfo.mi_body == NULL) return (NDMP_NO_MEM_ERR); (void) memset(connection->conn_msginfo.mi_body, 0, connection->conn_msginfo.mi_handler-> mh_sizeof_request); } } else { if (connection->conn_msginfo.mi_handler->mh_sizeof_reply > 0) { xdr_func = connection->conn_msginfo.mi_handler->mh_xdr_reply; if (xdr_func == NULL) { NDMP_LOG(LOG_DEBUG, "Processing reply 0x%x: no xdr function " "in handler table", connection->conn_msginfo.mi_hdr.message); return (NDMP_NOT_SUPPORTED_ERR); } connection->conn_msginfo.mi_body = ndmp_malloc( connection->conn_msginfo.mi_handler-> mh_sizeof_reply); if (connection->conn_msginfo.mi_body == NULL) return (NDMP_NO_MEM_ERR); (void) memset(connection->conn_msginfo.mi_body, 0, connection->conn_msginfo.mi_handler-> mh_sizeof_reply); } } /* Decode message arguments if needed */ if (xdr_func) { if (!(*xdr_func)(&connection->conn_xdrs, connection->conn_msginfo.mi_body)) { NDMP_LOG(LOG_DEBUG, "Processing message 0x%x: error decoding arguments", connection->conn_msginfo.mi_hdr.message); free(connection->conn_msginfo.mi_body); connection->conn_msginfo.mi_body = 0; return (NDMP_XDR_DECODE_ERR); } } return (0); }
/* * ndmpd_api_file_history_node_v3 * * Add a file history node entry to the buffer. * History data is buffered until the buffer is filled. * Full buffers are then sent to the client. * * Parameters: * cookie (input) - session pointer. * node (input) - file inode. * must match a node from a prior ndmpd_api_file_history_dir() * call. * file_stat (input) - file status pointer. * 0 forces buffered data to be sent. * fh_info (input) - data stream position of file data used during * fast restore. * * Returns: * 0 - success * -1 - error. */ int ndmpd_api_file_history_node_v3(void *cookie, ulong_t node, struct stat64 *file_stat, u_longlong_t fh_info) { ndmpd_session_t *session = (ndmpd_session_t *)cookie; ndmp_node_v3 *node_entry; ndmp_file_stat_v3 *file_stat_entry; ndmp_fh_add_node_request_v3 request; if (file_stat == NULL && session->ns_fh_v3.fh_node_index == 0) return (0); /* * If the buffer does not have space * for the current entry, send the buffered data to the client. * A 0 file_stat pointer indicates that any buffered data should * be sent. */ if (file_stat == NULL || session->ns_fh_v3.fh_node_index == N_NODE_ENTRIES) { syslog(LOG_DEBUG, "sending %ld entries", session->ns_fh_v3.fh_node_index); /* * Need to send Dir entry as well. Since Dir entry is more * than a Node entry, we may send a Node entry that hasn't * had its Dir entry sent. Therefore, we need to flush Dir * entry as well every time the Dir entry is sent. */ (void) ndmpd_api_file_history_dir_v3(session, 0, 0, 0); request.nodes.nodes_len = session->ns_fh_v3.fh_node_index; request.nodes.nodes_val = session->ns_fh_v3.fh_nodes; if (ndmp_send_request_lock(session->ns_connection, NDMP_FH_ADD_NODE, NDMP_NO_ERR, (void *) &request, 0) < 0) { syslog(LOG_ERR, "Sending ndmp_fh_add_node request failed"); return (-1); } session->ns_fh_v3.fh_node_index = 0; } if (file_stat == NULL) return (0); if (session->ns_fh_v3.fh_nodes == 0) { session->ns_fh_v3.fh_nodes = ndmp_malloc(sizeof (ndmp_node_v3) * N_NODE_ENTRIES); if (session->ns_fh_v3.fh_nodes == 0) return (-1); } if (session->ns_fh_v3.fh_node_stats == 0) { session->ns_fh_v3.fh_node_stats = ndmp_malloc(sizeof (ndmp_file_stat_v3) * N_NODE_ENTRIES); if (session->ns_fh_v3.fh_node_stats == 0) return (-1); } node_entry = &session->ns_fh_v3.fh_nodes[session->ns_fh_v3.fh_node_index]; file_stat_entry = &session->ns_fh_v3.fh_node_stats[session->ns_fh_v3.fh_node_index]; ndmpd_get_file_entry_type(file_stat->st_mode, &file_stat_entry->ftype); file_stat_entry->invalid = 0; file_stat_entry->fs_type = NDMP_FS_UNIX; file_stat_entry->mtime = file_stat->st_mtime; file_stat_entry->atime = file_stat->st_atime; file_stat_entry->ctime = file_stat->st_ctime; file_stat_entry->owner = file_stat->st_uid; file_stat_entry->group = file_stat->st_gid; file_stat_entry->fattr = file_stat->st_mode & 0x0fff; file_stat_entry->size = long_long_to_quad((u_longlong_t)file_stat->st_size); file_stat_entry->links = file_stat->st_nlink; node_entry->stats.stats_len = 1; node_entry->stats.stats_val = file_stat_entry; node_entry->node = long_long_to_quad((u_longlong_t)node); node_entry->fh_info = long_long_to_quad(fh_info); session->ns_fh_v3.fh_node_index++; return (0); }
int filecopy(char *dest, char *src) { FILE *src_fh = 0; FILE *dst_fh = 0; struct stat64 src_attr; struct stat64 dst_attr; char *buf = 0; u_longlong_t bytes_to_copy; size_t nbytes; int file_copied = 0; buf = ndmp_malloc(BUFSIZE); if (!buf) return (-1); src_fh = fopen(src, "r"); if (src_fh == 0) { free(buf); return (-2); } dst_fh = fopen(dest, "w"); if (dst_fh == NULL) { free(buf); (void) fclose(src_fh); return (-3); } if (stat64(src, &src_attr) < 0) { free(buf); (void) fclose(src_fh); (void) fclose(dst_fh); return (-2); } bytes_to_copy = src_attr.st_size; while (bytes_to_copy) { if (bytes_to_copy > BUFSIZE) nbytes = BUFSIZE; else nbytes = bytes_to_copy; if ((fread(buf, nbytes, 1, src_fh) != 1) || (fwrite(buf, nbytes, 1, dst_fh) != 1)) break; bytes_to_copy -= nbytes; } (void) fclose(src_fh); (void) fclose(dst_fh); if (bytes_to_copy > 0) { free(buf); /* short read/write, remove the partial file */ return (-4); } if (stat64(src, &dst_attr) < 0) { free(buf); return (-2); } free(buf); if (!file_copied) return (-5); /* source modified during copy */ else return (0); }