/* * Called here before the job is run to do the job * specific setup. */ bool do_native_vbackup_init(JCR *jcr) { const char *storage_source; if (!get_or_create_fileset_record(jcr)) { Dmsg1(dbglevel, "JobId=%d no FileSet\n", (int)jcr->JobId); return false; } apply_pool_overrides(jcr); if (!allow_duplicate_job(jcr)) { return false; } jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->res.pool->name()); if (jcr->jr.PoolId == 0) { Dmsg1(dbglevel, "JobId=%d no PoolId\n", (int)jcr->JobId); Jmsg(jcr, M_FATAL, 0, _("Could not get or create a Pool record.\n")); return false; } /* * Note, at this point, pool is the pool for this job. We * transfer it to rpool (read pool), and a bit later, * pool will be changed to point to the write pool, * which comes from pool->NextPool. */ jcr->res.rpool = jcr->res.pool; /* save read pool */ pm_strcpy(jcr->res.rpool_source, jcr->res.pool_source); /* If pool storage specified, use it for restore */ copy_rstorage(jcr, jcr->res.pool->storage, _("Pool resource")); Dmsg2(dbglevel, "Read pool=%s (From %s)\n", jcr->res.rpool->name(), jcr->res.rpool_source); jcr->start_time = time(NULL); jcr->jr.StartTime = jcr->start_time; if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); } /* * See if there is a next pool override. */ if (jcr->res.run_next_pool_override) { pm_strcpy(jcr->res.npool_source, _("Run NextPool override")); pm_strcpy(jcr->res.pool_source, _("Run NextPool override")); storage_source = _("Storage from Run NextPool override"); } else { /* * See if there is a next pool override in the Job definition. */ if (jcr->res.job->next_pool) { jcr->res.next_pool = jcr->res.job->next_pool; pm_strcpy(jcr->res.npool_source, _("Job's NextPool resource")); pm_strcpy(jcr->res.pool_source, _("Job's NextPool resource")); storage_source = _("Storage from Job's NextPool resource"); } else { /* * Fall back to the pool's NextPool definition. */ jcr->res.next_pool = jcr->res.pool->NextPool; pm_strcpy(jcr->res.npool_source, _("Job Pool's NextPool resource")); pm_strcpy(jcr->res.pool_source, _("Job Pool's NextPool resource")); storage_source = _("Storage from Pool's NextPool resource"); } } /* * If the original backup pool has a NextPool, make sure a * record exists in the database. Note, in this case, we * will be migrating from pool to pool->NextPool. */ if (jcr->res.next_pool) { jcr->jr.PoolId = get_or_create_pool_record(jcr, jcr->res.next_pool->name()); if (jcr->jr.PoolId == 0) { return false; } } if (!set_migration_wstorage(jcr, jcr->res.pool, jcr->res.next_pool, storage_source)) { return false; } jcr->res.pool = jcr->res.next_pool; Dmsg2(dbglevel, "Write pool=%s read rpool=%s\n", jcr->res.pool->name(), jcr->res.rpool->name()); // create_clones(jcr); return true; }
bool decompress_data(JCR *jcr, const char *last_fname, int32_t stream, char **data, uint32_t *length, bool want_data_stream) { Dmsg1(400, "Stream found in decompress_data(): %d\n", stream); switch (stream) { case STREAM_COMPRESSED_DATA: case STREAM_SPARSE_COMPRESSED_DATA: case STREAM_WIN32_COMPRESSED_DATA: case STREAM_ENCRYPTED_FILE_COMPRESSED_DATA: case STREAM_ENCRYPTED_WIN32_COMPRESSED_DATA: { uint32_t comp_magic, comp_len; uint16_t comp_level, comp_version; /* * Read compress header */ unser_declare; unser_begin(*data, sizeof(comp_stream_header)); unser_uint32(comp_magic); unser_uint32(comp_len); unser_uint16(comp_level); unser_uint16(comp_version); unser_end(*data, sizeof(comp_stream_header)); Dmsg4(400, "Compressed data stream found: magic=0x%x, len=%d, level=%d, ver=0x%x\n", comp_magic, comp_len, comp_level, comp_version); /* * Version check */ if (comp_version != COMP_HEAD_VERSION) { Qmsg(jcr, M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version); return false; } /* * Size check */ if (comp_len + sizeof(comp_stream_header) != *length) { Qmsg(jcr, M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"), comp_len, *length); return false; } /* * Based on the compression used perform the actual decompression of the data. */ switch (comp_magic) { #ifdef HAVE_LIBZ case COMPRESS_GZIP: switch (stream) { case STREAM_SPARSE_COMPRESSED_DATA: return decompress_with_zlib(jcr, last_fname, data, length, true, true, want_data_stream); default: return decompress_with_zlib(jcr, last_fname, data, length, false, true, want_data_stream); } #endif #ifdef HAVE_LZO case COMPRESS_LZO1X: switch (stream) { case STREAM_SPARSE_COMPRESSED_DATA: return decompress_with_lzo(jcr, last_fname, data, length, true, want_data_stream); default: return decompress_with_lzo(jcr, last_fname, data, length, false, want_data_stream); } #endif #ifdef HAVE_FASTLZ case COMPRESS_FZFZ: case COMPRESS_FZ4L: case COMPRESS_FZ4H: switch (stream) { case STREAM_SPARSE_COMPRESSED_DATA: return decompress_with_fastlz(jcr, last_fname, data, length, comp_magic, true, want_data_stream); default: return decompress_with_fastlz(jcr, last_fname, data, length, comp_magic, false, want_data_stream); } #endif default: Qmsg(jcr, M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic); return false; } break; } default: #ifdef HAVE_LIBZ switch (stream) { case STREAM_SPARSE_GZIP_DATA: return decompress_with_zlib(jcr, last_fname, data, length, true, false, want_data_stream); default: return decompress_with_zlib(jcr, last_fname, data, length, false, false, want_data_stream); } #else Qmsg(jcr, M_ERROR, 0, _("Compression algorithm GZIP found, but not supported!\n")); return false; #endif } }
/* * Restore files * */ int restore_cmd(UAContext *ua, const char *cmd) { RESTORE_CTX rx; /* restore context */ POOL_MEM buf; JOB *job; int i; JCR *jcr = ua->jcr; char *escaped_bsr_name = NULL; char *escaped_where_name = NULL; char *strip_prefix, *add_prefix, *add_suffix, *regexp; strip_prefix = add_prefix = add_suffix = regexp = NULL; memset(&rx, 0, sizeof(rx)); rx.path = get_pool_memory(PM_FNAME); rx.fname = get_pool_memory(PM_FNAME); rx.JobIds = get_pool_memory(PM_FNAME); rx.JobIds[0] = 0; rx.BaseJobIds = get_pool_memory(PM_FNAME); rx.query = get_pool_memory(PM_FNAME); rx.bsr = new_bsr(); i = find_arg_with_value(ua, "comment"); if (i >= 0) { rx.comment = ua->argv[i]; if (!is_comment_legal(ua, rx.comment)) { goto bail_out; } } i = find_arg_with_value(ua, "where"); if (i >= 0) { rx.where = ua->argv[i]; } i = find_arg_with_value(ua, "replace"); if (i >= 0) { rx.replace = ua->argv[i]; } i = find_arg_with_value(ua, "strip_prefix"); if (i >= 0) { strip_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_prefix"); if (i >= 0) { add_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_suffix"); if (i >= 0) { add_suffix = ua->argv[i]; } i = find_arg_with_value(ua, "regexwhere"); if (i >= 0) { rx.RegexWhere = ua->argv[i]; } if (strip_prefix || add_suffix || add_prefix) { int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix); regexp = (char *)bmalloc(len * sizeof(char)); bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix); rx.RegexWhere = regexp; } /* TODO: add acl for regexwhere ? */ if (rx.RegexWhere) { if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere)) { ua->error_msg(_("\"RegexWhere\" specification not authorized.\n")); goto bail_out; } } if (rx.where) { if (!acl_access_ok(ua, Where_ACL, rx.where)) { ua->error_msg(_("\"where\" specification not authorized.\n")); goto bail_out; } } if (!open_client_db(ua)) { goto bail_out; } /* Ensure there is at least one Restore Job */ LockRes(); foreach_res(job, R_JOB) { if (job->JobType == JT_RESTORE) { if (!rx.restore_job) { rx.restore_job = job; } rx.restore_jobs++; } } UnlockRes(); if (!rx.restore_jobs) { ua->error_msg(_( "No Restore Job Resource found in bacula-dir.conf.\n" "You must create at least one before running this command.\n")); goto bail_out; } /* * Request user to select JobIds or files by various different methods * last 20 jobs, where File saved, most recent backup, ... * In the end, a list of files are pumped into * add_findex() */ switch (user_select_jobids_or_files(ua, &rx)) { case 0: /* error */ goto bail_out; case 1: /* selected by jobid */ get_and_display_basejobs(ua, &rx); if (!build_directory_tree(ua, &rx)) { ua->send_msg(_("Restore not done.\n")); goto bail_out; } break; case 2: /* selected by filename, no tree needed */ break; } if (rx.bsr->JobId) { char ed1[50]; if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n")); goto bail_out; } if (!(rx.selected_files = write_bsr_file(ua, rx))) { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } display_bsr_info(ua, rx); /* display vols needed, etc */ if (rx.selected_files==1) { ua->info_msg(_("\n1 file selected to be restored.\n\n")); } else { ua->info_msg(_("\n%s files selected to be restored.\n\n"), edit_uint64_with_commas(rx.selected_files, ed1)); } } else { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } if (rx.restore_jobs == 1) { job = rx.restore_job; } else { job = get_restore_job(ua); } if (!job) { goto bail_out; } get_client_name(ua, &rx); if (!rx.ClientName) { ua->error_msg(_("No Client resource found!\n")); goto bail_out; } get_restore_client_name(ua, rx); escaped_bsr_name = escape_filename(jcr->RestoreBootstrap); Mmsg(ua->cmd, "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\"" " bootstrap=\"%s\" files=%u catalog=\"%s\"", job->name(), rx.ClientName, rx.RestoreClientName, rx.store?rx.store->name():"", escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name()); /* Build run command */ pm_strcpy(buf, ""); if (rx.RegexWhere) { escaped_where_name = escape_filename(rx.RegexWhere); Mmsg(buf, " regexwhere=\"%s\"", escaped_where_name ? escaped_where_name : rx.RegexWhere); } else if (rx.where) { escaped_where_name = escape_filename(rx.where); Mmsg(buf," where=\"%s\"", escaped_where_name ? escaped_where_name : rx.where); } pm_strcat(ua->cmd, buf); if (rx.replace) { Mmsg(buf, " replace=%s", rx.replace); pm_strcat(ua->cmd, buf); } if (rx.comment) { Mmsg(buf, " comment=\"%s\"", rx.comment); pm_strcat(ua->cmd, buf); } if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } if (find_arg(ua, NT_("yes")) > 0) { pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */ } Dmsg1(200, "Submitting: %s\n", ua->cmd); /* Transfer jobids to jcr to for picking up restore objects */ jcr->JobIds = rx.JobIds; rx.JobIds = NULL; parse_ua_args(ua); run_cmd(ua, ua->cmd); free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return 1; bail_out: if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return 0; }
/* * Try to limit the bandwidth of a network connection */ void BSOCK::control_bwlimit(int bytes) { btime_t now, temp; int64_t usec_sleep; /* * If nothing written or read nothing todo. */ if (bytes == 0) { return; } /* * See if this is the first time we enter here. */ now = get_current_btime(); if (m_last_tick == 0) { m_nb_bytes = bytes; m_last_tick = now; return; } /* * Calculate the number of microseconds since the last check. */ temp = now - m_last_tick; /* * Less than 0.1ms since the last call, see the next time */ if (temp < 100) { m_nb_bytes += bytes; return; } /* * Keep track of how many bytes are written in this timeslice. */ m_nb_bytes += bytes; m_last_tick = now; if (debug_level >= 400) { Dmsg3(400, "control_bwlimit: now = %lld, since = %lld, nb_bytes = %d\n", now, temp, m_nb_bytes); } /* * Take care of clock problems (>10s) */ if (temp > 10000000) { return; } /* * Remove what was authorised to be written in temp usecs. */ m_nb_bytes -= (int64_t)(temp * ((double)m_bwlimit / 1000000.0)); if (m_nb_bytes < 0) { /* * If more was authorized then used but bursting is not enabled * reset the counter as these bytes cannot be used later on when * we are exceeding our bandwidth. */ if (!m_use_bursting) { m_nb_bytes = 0; } return; } /* * What exceed should be converted in sleep time */ usec_sleep = (int64_t)(m_nb_bytes /((double)m_bwlimit / 1000000.0)); if (usec_sleep > 100) { if (debug_level >= 400) { Dmsg1(400, "control_bwlimit: sleeping for %lld usecs\n", usec_sleep); } /* * Sleep the right number of usecs. */ while (1) { bmicrosleep(0, usec_sleep); now = get_current_btime(); /* * See if we slept enough or that bmicrosleep() returned early. */ if ((now - m_last_tick) < usec_sleep) { usec_sleep -= (now - m_last_tick); continue; } else { m_last_tick = now; break; } } /* * Subtract the number of bytes we could have sent during the sleep * time given the bandwidth limit set. We only do this when we are * allowed to burst e.g. use unused bytes from previous timeslices * to get an overall bandwidth limiting which may sometimes be below * the bandwidth and sometimes above it but the average will be near * the set bandwidth. */ if (m_use_bursting) { m_nb_bytes -= (int64_t)(usec_sleep * ((double)m_bwlimit / 1000000.0)); } else { m_nb_bytes = 0; } } }
/* * Authenticate File daemon connection */ int authenticate_file_daemon(JCR *jcr) { BSOCK *fd = jcr->file_bsock; CLIENT *client = jcr->client; char dirname[MAX_NAME_LENGTH]; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; int compatible = true; bool auth_success = false; /* * Send my name to the File daemon then do authentication */ bstrncpy(dirname, director->name(), sizeof(dirname)); bash_spaces(dirname); /* Timeout Hello after 1 min */ btimer_t *tid = start_bsock_timer(fd, AUTH_TIMEOUT); if (!fd->fsend(hello, dirname)) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to File daemon at \"%s:%d\". ERR=%s\n"), fd->host(), fd->port(), fd->bstrerror()); return 0; } Dmsg1(dbglvl, "Sent: %s", fd->msg); /* TLS Requirement */ if (client->tls_enable) { if (client->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (client->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } auth_success = cram_md5_respond(fd, client->password, &tls_remote_need, &compatible); if (auth_success) { auth_success = cram_md5_challenge(fd, client->password, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_auth failed for %s\n", fd->who()); } } else { Dmsg1(dbglvl, "cram_get_auth failed for %s\n", fd->who()); } if (!auth_success) { stop_bsock_timer(tid); Dmsg0(dbglvl, _("Director and File daemon passwords or names not the same.\n")); Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate with File daemon at \"%s:%d\". Possible causes:\n" "Passwords or names not the same or\n" "Maximum Concurrent Jobs exceeded on the FD or\n" "FD networking messed up (restart daemon).\n" "Please see " MANUAL_AUTH_URL " for help.\n"), fd->host(), fd->port()); return 0; } /* Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("Authorization problem: FD \"%s:%s\" did not advertise required TLS support.\n"), fd->who(), fd->host()); return 0; } /* Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("Authorization problem: FD at \"%s:%d\" requires TLS.\n"), fd->host(), fd->port()); return 0; } /* Is TLS Enabled? */ if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* Engage TLS! Full Speed Ahead! */ if (!bnet_tls_client(client->tls_ctx, fd, client->tls_allowed_cns)) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed with FD at \"%s:%d\".\n"), fd->host(), fd->port()); return 0; } if (client->tls_authenticate) { /* tls authentication only? */ fd->free_tls(); /* yes, shutdown tls */ } } Dmsg1(116, ">filed: %s", fd->msg); if (fd->recv() <= 0) { stop_bsock_timer(tid); Dmsg1(dbglvl, _("Bad response from File daemon to Hello command: ERR=%s\n"), bnet_strerror(fd)); Jmsg(jcr, M_FATAL, 0, _("Bad response from File daemon at \"%s:%d\" to Hello command: ERR=%s\n"), fd->host(), fd->port(), fd->bstrerror()); return 0; } Dmsg1(110, "<filed: %s", fd->msg); stop_bsock_timer(tid); jcr->FDVersion = 0; if (strncmp(fd->msg, FDOKhello, sizeof(FDOKhello)) != 0 && sscanf(fd->msg, FDOKnewHello, &jcr->FDVersion) != 1) { Dmsg0(dbglvl, _("File daemon rejected Hello command\n")); Jmsg(jcr, M_FATAL, 0, _("File daemon at \"%s:%d\" rejected Hello command\n"), fd->host(), fd->port()); return 0; } return 1; }
void b_free_jcr(const char *file, int line, JCR *jcr) { struct s_last_job *je; Dmsg3(dbglvl, "Enter free_jcr jid=%u from %s:%d\n", jcr->JobId, file, line); #else void free_jcr(JCR *jcr) { struct s_last_job *je; Dmsg3(dbglvl, "Enter free_jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); #endif lock_jcr_chain(); jcr->dec_use_count(); /* decrement use count */ if (jcr->use_count() < 0) { Jmsg2(jcr, M_ERROR, 0, _("JCR use_count=%d JobId=%d\n"), jcr->use_count(), jcr->JobId); } if (jcr->JobId > 0) { Dmsg3(dbglvl, "Dec free_jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } if (jcr->use_count() > 0) { /* if in use */ unlock_jcr_chain(); return; } if (jcr->JobId > 0) { Dmsg3(dbglvl, "remove jcr jid=%u use_count=%d Job=%s\n", jcr->JobId, jcr->use_count(), jcr->Job); } remove_jcr(jcr); /* remove Jcr from chain */ unlock_jcr_chain(); dequeue_messages(jcr); job_end_pop(jcr); /* pop and call hooked routines */ Dmsg1(dbglvl, "End job=%d\n", jcr->JobId); /* Keep some statistics */ switch (jcr->get_JobType()) { case JT_BACKUP: case JT_VERIFY: case JT_RESTORE: case JT_MIGRATE: case JT_COPY: case JT_ADMIN: /* Keep list of last jobs, but not Console where JobId==0 */ if (jcr->JobId > 0) { lock_last_jobs_list(); num_jobs_run++; je = (struct s_last_job *)malloc(sizeof(struct s_last_job)); memset(je, 0, sizeof(struct s_last_job)); /* zero in case unset fields */ je->Errors = jcr->JobErrors; je->JobType = jcr->get_JobType(); je->JobId = jcr->JobId; je->VolSessionId = jcr->VolSessionId; je->VolSessionTime = jcr->VolSessionTime; bstrncpy(je->Job, jcr->Job, sizeof(je->Job)); je->JobFiles = jcr->JobFiles; je->JobBytes = jcr->JobBytes; je->JobStatus = jcr->JobStatus; je->JobLevel = jcr->get_JobLevel(); je->start_time = jcr->start_time; je->end_time = time(NULL); if (!last_jobs) { init_last_jobs_list(); } last_jobs->append(je); if (last_jobs->size() > max_last_jobs) { je = (struct s_last_job *)last_jobs->first(); last_jobs->remove(je); free(je); } unlock_last_jobs_list(); } break; default: break; } if (jcr->daemon_free_jcr) { jcr->daemon_free_jcr(jcr); /* call daemon free routine */ } free_common_jcr(jcr); close_msg(NULL); /* flush any daemon messages */ garbage_collect_memory_pool(); Dmsg0(dbglvl, "Exit free_jcr\n"); } /* * Remove jcr from thread specific data, but * but make sure it is us who are attached. */ void remove_jcr_from_tsd(JCR *jcr) { JCR *tjcr = get_jcr_from_tsd(); if (tjcr == jcr) { set_jcr_in_tsd(INVALID_JCR); } }
/* * Find all the requested files and send them * to the Storage daemon. * * Note, we normally carry on a one-way * conversation from this point on with the SD, simply blasting * data to him. To properly know what is going on, we * also run a "heartbeat" monitor which reads the socket and * reacts accordingly (at the moment it has nothing to do * except echo the heartbeat to the Director). * */ bool blast_data_to_storage_daemon(JCR *jcr, char *addr) { BSOCK *sd; bool ok = true; // TODO landonf: Allow user to specify encryption algorithm sd = jcr->store_bsock; set_jcr_job_status(jcr, JS_Running); Dmsg1(300, "bfiled: opened data connection %d to stored\n", sd->m_fd); LockRes(); CLIENT *client = (CLIENT *)GetNextRes(R_CLIENT, NULL); UnlockRes(); uint32_t buf_size; if (client) { buf_size = client->max_network_buffer_size; } else { buf_size = 0; /* use default */ } if (!sd->set_buffer_size(buf_size, BNET_SETBUF_WRITE)) { set_jcr_job_status(jcr, JS_ErrorTerminated); Jmsg(jcr, M_FATAL, 0, _("Cannot set buffer size FD->SD.\n")); return false; } jcr->buf_size = sd->msglen; /* Adjust for compression so that output buffer is * 12 bytes + 0.1% larger than input buffer plus 18 bytes. * This gives a bit extra plus room for the sparse addr if any. * Note, we adjust the read size to be smaller so that the * same output buffer can be used without growing it. * * The zlib compression workset is initialized here to minimize * the "per file" load. The jcr member is only set, if the init * was successful. */ jcr->compress_buf_size = jcr->buf_size + ((jcr->buf_size+999) / 1000) + 30; jcr->compress_buf = get_memory(jcr->compress_buf_size); #ifdef HAVE_LIBZ z_stream *pZlibStream = (z_stream*)malloc(sizeof(z_stream)); if (pZlibStream) { pZlibStream->zalloc = Z_NULL; pZlibStream->zfree = Z_NULL; pZlibStream->opaque = Z_NULL; pZlibStream->state = Z_NULL; if (deflateInit(pZlibStream, Z_DEFAULT_COMPRESSION) == Z_OK) { jcr->pZLIB_compress_workset = pZlibStream; } else { free (pZlibStream); } } #endif if (!crypto_session_start(jcr)) { return false; } set_find_options((FF_PKT *)jcr->ff, jcr->incremental, jcr->mtime); /* in accurate mode, we overwrite the find_one check function */ if (jcr->accurate) { set_find_changed_function((FF_PKT *)jcr->ff, accurate_check_file); } start_heartbeat_monitor(jcr); jcr->acl_data = get_pool_memory(PM_MESSAGE); jcr->xattr_data = get_pool_memory(PM_MESSAGE); /* Subroutine save_file() is called for each file */ if (!find_files(jcr, (FF_PKT *)jcr->ff, save_file, plugin_save)) { ok = false; /* error */ set_jcr_job_status(jcr, JS_ErrorTerminated); } accurate_send_deleted_list(jcr); /* send deleted list to SD */ stop_heartbeat_monitor(jcr); sd->signal(BNET_EOD); /* end of sending data */ if (jcr->acl_data) { free_pool_memory(jcr->acl_data); jcr->acl_data = NULL; } if (jcr->xattr_data) { free_pool_memory(jcr->xattr_data); jcr->xattr_data = NULL; } if (jcr->big_buf) { free(jcr->big_buf); jcr->big_buf = NULL; } if (jcr->compress_buf) { free_pool_memory(jcr->compress_buf); jcr->compress_buf = NULL; } if (jcr->pZLIB_compress_workset) { /* Free the zlib stream */ #ifdef HAVE_LIBZ deflateEnd((z_stream *)jcr->pZLIB_compress_workset); #endif free (jcr->pZLIB_compress_workset); jcr->pZLIB_compress_workset = NULL; } crypto_session_end(jcr); Dmsg1(100, "end blast_data ok=%d\n", ok); return ok; }
/* * Close the device. */ bool DEVICE::close(DCR *dcr) { bool retval = true; int status; Dmsg1(100, "close_dev %s\n", print_name()); if (!norewindonclose) { offline_or_rewind(); } if (!is_open()) { Dmsg2(100, "device %s already closed vol=%s\n", print_name(), VolHdr.VolumeName); goto bail_out; /* already closed */ } switch (dev_type) { case B_VTL_DEV: case B_TAPE_DEV: unlock_door(); /* * Fall through wanted */ default: status = d_close(m_fd); if (status < 0) { berrno be; Mmsg2(errmsg, _("Unable to close device %s. ERR=%s\n"), print_name(), be.bstrerror()); dev_errno = errno; retval = false; } break; } unmount(dcr, 1); /* do unmount if required */ /* * Clean up device packet so it can be reused. */ clear_opened(); clear_bit(ST_LABEL, state); clear_bit(ST_READREADY, state); clear_bit(ST_APPENDREADY, state); clear_bit(ST_EOT, state); clear_bit(ST_WEOT, state); clear_bit(ST_EOF, state); clear_bit(ST_MOUNTED, state); clear_bit(ST_MEDIA, state); clear_bit(ST_SHORT, state); label_type = B_BAREOS_LABEL; file = block_num = 0; file_size = 0; file_addr = 0; EndFile = EndBlock = 0; open_mode = 0; clear_volhdr(); memset(&VolCatInfo, 0, sizeof(VolCatInfo)); if (tid) { stop_thread_timer(tid); tid = 0; } bail_out: return retval; }
bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) { BSOCK *sd = jcr->store_bsock; char attribs[MAXSTRING]; char attribsEx[MAXSTRING]; int attr_stream; int stat; #ifdef FD_NO_SEND_TEST return true; #endif Dmsg1(300, "encode_and_send_attrs fname=%s\n", ff_pkt->fname); /* Find what data stream we will use, then encode the attributes */ if ((data_stream = select_data_stream(ff_pkt)) == STREAM_NONE) { /* This should not happen */ Jmsg0(jcr, M_FATAL, 0, _("Invalid file flags, no supported data stream type.\n")); return false; } encode_stat(attribs, &ff_pkt->statp, ff_pkt->LinkFI, data_stream); /* Now possibly extend the attributes */ attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt); Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs, attribsEx); jcr->lock(); jcr->JobFiles++; /* increment number of files sent */ ff_pkt->FileIndex = jcr->JobFiles; /* return FileIndex */ pm_strcpy(jcr->last_fname, ff_pkt->fname); jcr->unlock(); /* * Send Attributes header to Storage daemon * <file-index> <stream> <info> */ if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); return false; } Dmsg1(300, ">stored: attrhdr %s\n", sd->msg); /* * Send file attributes to Storage daemon * File_index * File type * Filename (full path) * Encoded attributes * Link name (if type==FT_LNK or FT_LNKSAVED) * Encoded extended-attributes (for Win32) * * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. */ if (ff_pkt->type != FT_DELETED) { /* already stripped */ strip_path(ff_pkt); } if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) { Dmsg2(300, "Link %s to %s\n", ff_pkt->fname, ff_pkt->link); stat = sd->fsend("%ld %d %s%c%s%c%s%c%s%c", jcr->JobFiles, ff_pkt->type, ff_pkt->fname, 0, attribs, 0, ff_pkt->link, 0, attribsEx, 0); } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE) { /* Here link is the canonical filename (i.e. with trailing slash) */ stat = sd->fsend("%ld %d %s%c%s%c%c%s%c", jcr->JobFiles, ff_pkt->type, ff_pkt->link, 0, attribs, 0, 0, attribsEx, 0); } else { stat = sd->fsend("%ld %d %s%c%s%c%c%s%c", jcr->JobFiles, ff_pkt->type, ff_pkt->fname, 0, attribs, 0, 0, attribsEx, 0); } if (ff_pkt->type != FT_DELETED) { unstrip_path(ff_pkt); } Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); if (!stat) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); return false; } sd->signal(BNET_EOD); /* indicate end of attributes data */ return true; }
/* * Called here by find() for each file included. * This is a callback. The original is find_files() above. * * Send the file and its data to the Storage daemon. * * Returns: 1 if OK * 0 if error * -1 to ignore file/directory (not used here) */ int save_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { bool do_read = false; int stat, data_stream; int rtnstat = 0; DIGEST *digest = NULL; DIGEST *signing_digest = NULL; int digest_stream = STREAM_NONE; SIGNATURE *sig = NULL; bool has_file_data = false; // TODO landonf: Allow the user to specify the digest algorithm #ifdef HAVE_SHA2 crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA256; #else crypto_digest_t signing_algorithm = CRYPTO_DIGEST_SHA1; #endif BSOCK *sd = jcr->store_bsock; if (job_canceled(jcr)) { return 0; } jcr->num_files_examined++; /* bump total file count */ switch (ff_pkt->type) { case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(130, "FT_LNKSAVED hard link: %s => %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_REGE: Dmsg1(130, "FT_REGE saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_REG: Dmsg1(130, "FT_REG saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_LNK: Dmsg2(130, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_DIRBEGIN: jcr->num_files_examined--; /* correct file count */ return 1; /* not used */ case FT_NORECURSE: Jmsg(jcr, M_INFO, 1, _(" Recursion turned off. Will not descend from %s into %s\n"), ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_NOFSCHG: /* Suppress message for /dev filesystems */ if (!is_in_fileset(ff_pkt)) { Jmsg(jcr, M_INFO, 1, _(" %s is a different filesystem. Will not descend from %s into %s\n"), ff_pkt->fname, ff_pkt->top_fname, ff_pkt->fname); } ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDFS: Jmsg(jcr, M_INFO, 1, _(" Disallowed filesystem. Will not descend from %s into %s\n"), ff_pkt->top_fname, ff_pkt->fname); ff_pkt->type = FT_DIREND; /* Backup only the directory entry */ break; case FT_INVALIDDT: Jmsg(jcr, M_INFO, 1, _(" Disallowed drive type. Will not descend into %s\n"), ff_pkt->fname); break; case FT_REPARSE: case FT_DIREND: Dmsg1(130, "FT_DIREND: %s\n", ff_pkt->link); break; case FT_SPEC: Dmsg1(130, "FT_SPEC saving: %s\n", ff_pkt->fname); if (S_ISSOCK(ff_pkt->statp.st_mode)) { Jmsg(jcr, M_SKIPPED, 1, _(" Socket file skipped: %s\n"), ff_pkt->fname); return 1; } break; case FT_RAW: Dmsg1(130, "FT_RAW saving: %s\n", ff_pkt->fname); has_file_data = true; break; case FT_FIFO: Dmsg1(130, "FT_FIFO saving: %s\n", ff_pkt->fname); break; case FT_NOACCESS: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not access \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOFOLLOW: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not follow link \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_NOSTAT: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not stat \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } case FT_DIRNOCHG: case FT_NOCHG: Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); return 1; case FT_ISARCH: Jmsg(jcr, M_NOTSAVED, 0, _(" Archive file not saved: %s\n"), ff_pkt->fname); return 1; case FT_NOOPEN: { berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Could not open directory \"%s\": ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->ff_errno)); jcr->JobErrors++; return 1; } default: Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d; not saved: %s\n"), ff_pkt->type, ff_pkt->fname); jcr->JobErrors++; return 1; } Dmsg1(130, "bfiled: sending %s to stored\n", ff_pkt->fname); /* Digests and encryption are only useful if there's file data */ if (has_file_data) { /* * Setup for digest handling. If this fails, the digest will be set to NULL * and not used. Note, the digest (file hash) can be any one of the four * algorithms below. * * The signing digest is a single algorithm depending on * whether or not we have SHA2. * ****FIXME**** the signing algoritm should really be * determined a different way!!!!!! What happens if * sha2 was available during backup but not restore? */ if (ff_pkt->flags & FO_MD5) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_MD5); digest_stream = STREAM_MD5_DIGEST; } else if (ff_pkt->flags & FO_SHA1) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA1); digest_stream = STREAM_SHA1_DIGEST; } else if (ff_pkt->flags & FO_SHA256) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA256); digest_stream = STREAM_SHA256_DIGEST; } else if (ff_pkt->flags & FO_SHA512) { digest = crypto_digest_new(jcr, CRYPTO_DIGEST_SHA512); digest_stream = STREAM_SHA512_DIGEST; } /* Did digest initialization fail? */ if (digest_stream != STREAM_NONE && digest == NULL) { Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), stream_to_ascii(digest_stream)); } /* * Set up signature digest handling. If this fails, the signature digest will be set to * NULL and not used. */ // TODO landonf: We should really only calculate the digest once, for both verification and signing. if (jcr->crypto.pki_sign) { signing_digest = crypto_digest_new(jcr, signing_algorithm); /* Full-stop if a failure occurred initializing the signature digest */ if (signing_digest == NULL) { Jmsg(jcr, M_NOTSAVED, 0, _("%s signature digest initialization failed\n"), stream_to_ascii(signing_algorithm)); jcr->JobErrors++; goto good_rtn; } } /* Enable encryption */ if (jcr->crypto.pki_encrypt) { ff_pkt->flags |= FO_ENCRYPT; } } /* Initialize the file descriptor we use for data and other streams. */ binit(&ff_pkt->bfd); if (ff_pkt->flags & FO_PORTABLE) { set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */ } if (ff_pkt->cmd_plugin) { if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) { goto bail_out; } send_plugin_name(jcr, sd, true); /* signal start of plugin data */ } /* Send attributes -- must be done after binit() */ if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) { goto bail_out; } /* Set up the encryption context and send the session data to the SD */ if (has_file_data && jcr->crypto.pki_encrypt) { if (!crypto_session_send(jcr, sd)) { goto bail_out; } } /* * Open any file with data that we intend to save, then save it. * * Note, if is_win32_backup, we must open the Directory so that * the BackupRead will save its permissions and ownership streams. */ if (ff_pkt->type != FT_LNKSAVED && S_ISREG(ff_pkt->statp.st_mode)) { #ifdef HAVE_WIN32 do_read = !is_portable_backup(&ff_pkt->bfd) || ff_pkt->statp.st_size > 0; #else do_read = ff_pkt->statp.st_size > 0; #endif } else if (ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO || ff_pkt->type == FT_REPARSE || (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { do_read = true; } if (ff_pkt->cmd_plugin) { do_read = true; } Dmsg1(400, "do_read=%d\n", do_read); if (do_read) { btimer_t *tid; if (ff_pkt->type == FT_FIFO) { tid = start_thread_timer(jcr, pthread_self(), 60); } else { tid = NULL; } int noatime = ff_pkt->flags & FO_NOATIME ? O_NOATIME : 0; ff_pkt->bfd.reparse_point = ff_pkt->type == FT_REPARSE; if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0) < 0) { ff_pkt->ff_errno = errno; berrno be; Jmsg(jcr, M_NOTSAVED, 0, _(" Cannot open \"%s\": ERR=%s.\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; if (tid) { stop_thread_timer(tid); tid = NULL; } goto good_rtn; } if (tid) { stop_thread_timer(tid); tid = NULL; } stat = send_data(jcr, data_stream, ff_pkt, digest, signing_digest); if (ff_pkt->flags & FO_CHKCHANGES) { has_file_changed(jcr, ff_pkt); } bclose(&ff_pkt->bfd); if (!stat) { goto bail_out; } } #ifdef HAVE_DARWIN_OS /* Regular files can have resource forks and Finder Info */ if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && ff_pkt->flags & FO_HFSPLUS)) { if (ff_pkt->hfsinfo.rsrclength > 0) { int flags; int rsrc_stream; if (!bopen_rsrc(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY, 0) < 0) { ff_pkt->ff_errno = errno; berrno be; Jmsg(jcr, M_NOTSAVED, -1, _(" Cannot open resource fork for \"%s\": ERR=%s.\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; if (is_bopen(&ff_pkt->bfd)) { bclose(&ff_pkt->bfd); } goto good_rtn; } flags = ff_pkt->flags; ff_pkt->flags &= ~(FO_GZIP|FO_SPARSE); if (flags & FO_ENCRYPT) { rsrc_stream = STREAM_ENCRYPTED_MACOS_FORK_DATA; } else { rsrc_stream = STREAM_MACOS_FORK_DATA; } stat = send_data(jcr, rsrc_stream, ff_pkt, digest, signing_digest); ff_pkt->flags = flags; bclose(&ff_pkt->bfd); if (!stat) { goto bail_out; } } Dmsg1(300, "Saving Finder Info for \"%s\"\n", ff_pkt->fname); sd->fsend("%ld %d 0", jcr->JobFiles, STREAM_HFSPLUS_ATTRIBUTES); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); pm_memcpy(sd->msg, ff_pkt->hfsinfo.fndrinfo, 32); sd->msglen = 32; if (digest) { crypto_digest_update(digest, (uint8_t *)sd->msg, sd->msglen); } if (signing_digest) { crypto_digest_update(signing_digest, (uint8_t *)sd->msg, sd->msglen); } sd->send(); sd->signal(BNET_EOD); } #endif /* * Save ACLs for anything not being a symlink and not being a plugin. */ if (!ff_pkt->cmd_plugin) { if (ff_pkt->flags & FO_ACL && ff_pkt->type != FT_LNK) { if (!build_acl_streams(jcr, ff_pkt)) goto bail_out; } } /* * Save Extended Attributes for all files not being a plugin. */ if (!ff_pkt->cmd_plugin) { if (ff_pkt->flags & FO_XATTR) { if (!build_xattr_streams(jcr, ff_pkt)) goto bail_out; } } /* Terminate the signing digest and send it to the Storage daemon */ if (signing_digest) { uint32_t size = 0; if ((sig = crypto_sign_new(jcr)) == NULL) { Jmsg(jcr, M_FATAL, 0, _("Failed to allocate memory for crypto signature.\n")); goto bail_out; } if (!crypto_sign_add_signer(sig, signing_digest, jcr->crypto.pki_keypair)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } /* Get signature size */ if (!crypto_sign_encode(sig, NULL, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } /* Grow the bsock buffer to fit our message if necessary */ if (sizeof_pool_memory(sd->msg) < (int32_t)size) { sd->msg = realloc_pool_memory(sd->msg, size); } /* Send our header */ sd->fsend("%ld %ld 0", jcr->JobFiles, STREAM_SIGNED_DIGEST); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); /* Encode signature data */ if (!crypto_sign_encode(sig, (uint8_t *)sd->msg, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred while signing the stream.\n")); goto bail_out; } sd->msglen = size; sd->send(); sd->signal(BNET_EOD); /* end of checksum */ } /* Terminate any digest and send it to Storage daemon */ if (digest) { uint32_t size; sd->fsend("%ld %d 0", jcr->JobFiles, digest_stream); Dmsg1(300, "bfiled>stored:header %s\n", sd->msg); size = CRYPTO_DIGEST_MAX_SIZE; /* Grow the bsock buffer to fit our message if necessary */ if (sizeof_pool_memory(sd->msg) < (int32_t)size) { sd->msg = realloc_pool_memory(sd->msg, size); } if (!crypto_digest_finalize(digest, (uint8_t *)sd->msg, &size)) { Jmsg(jcr, M_FATAL, 0, _("An error occurred finalizing signing the stream.\n")); goto bail_out; } sd->msglen = size; sd->send(); sd->signal(BNET_EOD); /* end of checksum */ } if (ff_pkt->cmd_plugin) { send_plugin_name(jcr, sd, false); /* signal end of plugin data */ } good_rtn: rtnstat = 1; /* good return */ bail_out: if (digest) { crypto_digest_free(digest); } if (signing_digest) { crypto_digest_free(signing_digest); } if (sig) { crypto_sign_free(sig); } return rtnstat; }
/* Authorize other end * Codes that tls_local_need and tls_remote_need can take: * BNET_TLS_NONE I cannot do tls * BNET_TLS_OK I can do tls, but it is not required on my end * BNET_TLS_REQUIRED tls is required on my end * * Returns: false if authentication failed * true if OK */ bool cram_md5_challenge(BSOCK *bs, const char *password, int tls_local_need, int compatible) { struct timeval t1; struct timeval t2; struct timezone tz; int i; bool ok; char chal[MAXSTRING]; char host[MAXSTRING]; uint8_t hmac[20]; gettimeofday(&t1, &tz); for (i=0; i<4; i++) { gettimeofday(&t2, &tz); } srandom((t1.tv_sec&0xffff) * (t2.tv_usec&0xff)); if (!gethostname(host, sizeof(host))) { bstrncpy(host, my_name, sizeof(host)); } /* Send challenge -- no hashing yet */ bsnprintf(chal, sizeof(chal), "<%u.%u@%s>", (uint32_t)random(), (uint32_t)time(NULL), host); if (compatible) { Dmsg2(dbglvl, "send: auth cram-md5 %s ssl=%d\n", chal, tls_local_need); if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) { Dmsg1(dbglvl, "Bnet send challenge comm error. ERR=%s\n", bs->bstrerror()); return false; } } else { /* Old non-compatible system */ Dmsg2(dbglvl, "send: auth cram-md5 %s ssl=%d\n", chal, tls_local_need); if (!bs->fsend("auth cram-md5 %s ssl=%d\n", chal, tls_local_need)) { Dmsg1(dbglvl, "Bnet send challenge comm error. ERR=%s\n", bs->bstrerror()); return false; } } /* Read hashed response to challenge */ if (bs->wait_data(180) <= 0 || bs->recv() <= 0) { Dmsg1(dbglvl, "Bnet receive challenge response comm error. ERR=%s\n", bs->bstrerror()); bmicrosleep(5, 0); return false; } /* Attempt to duplicate hash with our password */ hmac_md5((uint8_t *)chal, strlen(chal), (uint8_t *)password, strlen(password), hmac); bin_to_base64(host, sizeof(host), (char *)hmac, 16, compatible); ok = bstrcmp(bs->msg, host); if (ok) { Dmsg1(dbglvl, "Authenticate OK %s\n", host); } else { bin_to_base64(host, sizeof(host), (char *)hmac, 16, false); ok = bstrcmp(bs->msg, host); if (!ok) { Dmsg2(dbglvl, "Authenticate NOT OK: wanted %s, got %s\n", host, bs->msg); } } if (ok) { bs->fsend("1000 OK auth\n"); } else { bs->fsend(_("1999 Authorization failed.\n")); bmicrosleep(5, 0); } return ok; }
static void read_and_process_input(FILE *input, BSOCK *UA_sock) { const char *prompt = "*"; bool at_prompt = false; int tty_input = isatty(fileno(input)); int status; btimer_t *tid = NULL; while (1) { if (at_prompt) { /* don't prompt multiple times */ prompt = ""; } else { prompt = "*"; at_prompt = true; } if (tty_input) { status = get_cmd(input, prompt, UA_sock, 30); if (usrbrk() == 1) { clrbrk(); } if (usrbrk()) { break; } } else { /* * Reading input from a file */ int len = sizeof_pool_memory(UA_sock->msg) - 1; if (usrbrk()) { break; } if (fgets(UA_sock->msg, len, input) == NULL) { status = -1; } else { sendit(UA_sock->msg); /* echo to terminal */ strip_trailing_junk(UA_sock->msg); UA_sock->msglen = strlen(UA_sock->msg); status = 1; } } if (status < 0) { break; /* error or interrupt */ } else if (status == 0) { /* timeout */ if (bstrcmp(prompt, "*")) { tid = start_bsock_timer(UA_sock, timeout); UA_sock->fsend(".messages"); stop_bsock_timer(tid); } else { continue; } } else { at_prompt = false; /* * @ => internal command for us */ if (UA_sock->msg[0] == '@') { parse_args(UA_sock->msg, args, &argc, argk, argv, MAX_CMD_ARGS); if (!do_a_command(input, UA_sock)) { break; } continue; } tid = start_bsock_timer(UA_sock, timeout); if (!UA_sock->send()) { /* send command */ stop_bsock_timer(tid); break; /* error */ } stop_bsock_timer(tid); } if (bstrcmp(UA_sock->msg, ".quit") || bstrcmp(UA_sock->msg, ".exit")) { break; } tid = start_bsock_timer(UA_sock, timeout); while ((status = UA_sock->recv()) >= 0 || ((status == BNET_SIGNAL) && ( (UA_sock->msglen != BNET_EOD) && (UA_sock->msglen != BNET_MAIN_PROMPT) && (UA_sock->msglen != BNET_SUB_PROMPT)))) { if (status == BNET_SIGNAL) { if (UA_sock->msglen == BNET_START_RTREE) { file_selection = true; } else if (UA_sock->msglen == BNET_END_RTREE) { file_selection = false; } continue; } if (at_prompt) { if (!stop) { sendit("\n"); } at_prompt = false; } /* * Suppress output if running in background or user hit ctl-c */ if (!stop && !usrbrk()) { if (UA_sock->msg) { sendit(UA_sock->msg); } } } stop_bsock_timer(tid); if (usrbrk() > 1) { break; } else { clrbrk(); } if (!stop) { fflush(stdout); } if (is_bnet_stop(UA_sock)) { break; /* error or term */ } else if (status == BNET_SIGNAL) { if (UA_sock->msglen == BNET_SUB_PROMPT) { at_prompt = true; } Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock)); } } }
/* * Called here by find() for each file. * * Find the file, compute the MD5 or SHA1 and send it back to the Director */ static int verify_file(JCR *jcr, FF_PKT *ff_pkt, bool top_level) { POOL_MEM attribs(PM_NAME), attribsEx(PM_NAME); int status; BSOCK *dir; if (job_canceled(jcr)) { return 0; } dir = jcr->dir_bsock; jcr->num_files_examined++; /* bump total file count */ switch (ff_pkt->type) { case FT_LNKSAVED: /* Hard linked, file already saved */ Dmsg2(30, "FT_LNKSAVED saving: %s => %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_REGE: Dmsg1(30, "FT_REGE saving: %s\n", ff_pkt->fname); break; case FT_REG: Dmsg1(30, "FT_REG saving: %s\n", ff_pkt->fname); break; case FT_LNK: Dmsg2(30, "FT_LNK saving: %s -> %s\n", ff_pkt->fname, ff_pkt->link); break; case FT_DIRBEGIN: jcr->num_files_examined--; /* correct file count */ return 1; /* ignored */ case FT_REPARSE: case FT_JUNCTION: case FT_DIREND: Dmsg1(30, "FT_DIR saving: %s\n", ff_pkt->fname); break; case FT_SPEC: Dmsg1(30, "FT_SPEC saving: %s\n", ff_pkt->fname); break; case FT_RAW: Dmsg1(30, "FT_RAW saving: %s\n", ff_pkt->fname); break; case FT_FIFO: Dmsg1(30, "FT_FIFO saving: %s\n", ff_pkt->fname); break; case FT_NOACCESS: { berrno be; be.set_errno(ff_pkt->ff_errno); Jmsg(jcr, M_NOTSAVED, 1, _(" Could not access %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; return 1; } case FT_NOFOLLOW: { berrno be; be.set_errno(ff_pkt->ff_errno); Jmsg(jcr, M_NOTSAVED, 1, _(" Could not follow link %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; return 1; } case FT_NOSTAT: { berrno be; be.set_errno(ff_pkt->ff_errno); Jmsg(jcr, M_NOTSAVED, 1, _(" Could not stat %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; return 1; } case FT_DIRNOCHG: case FT_NOCHG: Jmsg(jcr, M_SKIPPED, 1, _(" Unchanged file skipped: %s\n"), ff_pkt->fname); return 1; case FT_ISARCH: Jmsg(jcr, M_SKIPPED, 1, _(" Archive file skipped: %s\n"), ff_pkt->fname); return 1; case FT_NORECURSE: Jmsg(jcr, M_SKIPPED, 1, _(" Recursion turned off. Directory skipped: %s\n"), ff_pkt->fname); ff_pkt->type = FT_DIREND; /* directory entry was backed up */ break; case FT_NOFSCHG: Jmsg(jcr, M_SKIPPED, 1, _(" File system change prohibited. Directory skipped: %s\n"), ff_pkt->fname); return 1; case FT_PLUGIN_CONFIG: case FT_RESTORE_FIRST: return 1; /* silently skip */ case FT_NOOPEN: { berrno be; be.set_errno(ff_pkt->ff_errno); Jmsg(jcr, M_NOTSAVED, 1, _(" Could not open directory %s: ERR=%s\n"), ff_pkt->fname, be.bstrerror()); jcr->JobErrors++; return 1; } default: Jmsg(jcr, M_NOTSAVED, 0, _(" Unknown file type %d: %s\n"), ff_pkt->type, ff_pkt->fname); jcr->JobErrors++; return 1; } /* Encode attributes and possibly extend them */ encode_stat(attribs.c_str(), &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, 0); encode_attribsEx(jcr, attribsEx.c_str(), ff_pkt); jcr->lock(); jcr->JobFiles++; /* increment number of files sent */ pm_strcpy(jcr->last_fname, ff_pkt->fname); jcr->unlock(); /* * Send file attributes to Director * File_index * Stream * Verify Options * Filename (full path) * Encoded attributes * Link name (if type==FT_LNK) * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. */ /* * Send file attributes to Director (note different format than for Storage) */ Dmsg2(400, "send ATTR inx=%d fname=%s\n", jcr->JobFiles, ff_pkt->fname); if (ff_pkt->type == FT_LNK || ff_pkt->type == FT_LNKSAVED) { status = dir->fsend("%d %d %s %s%c%s%c%s%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, 0, attribs.c_str(), 0, ff_pkt->link, 0); } else if (ff_pkt->type == FT_DIREND || ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION) { /* * Here link is the canonical filename (i.e. with trailing slash) */ status = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->link, 0, attribs.c_str(), 0, 0); } else { status = dir->fsend("%d %d %s %s%c%s%c%c", jcr->JobFiles, STREAM_UNIX_ATTRIBUTES, ff_pkt->VerifyOpts, ff_pkt->fname, 0, attribs.c_str(), 0, 0); } Dmsg2(20, "filed>dir: attribs len=%d: msg=%s\n", dir->msglen, dir->msg); if (!status) { Jmsg(jcr, M_FATAL, 0, _("Network error in send to Director: ERR=%s\n"), bnet_strerror(dir)); return 0; } if (ff_pkt->type != FT_LNKSAVED && (S_ISREG(ff_pkt->statp.st_mode) && ff_pkt->flags & (FO_MD5 | FO_SHA1 | FO_SHA256 | FO_SHA512))) { int digest_stream = STREAM_NONE; DIGEST *digest = NULL; char *digest_buf = NULL; const char *digest_name = NULL; if (calculate_file_chksum(jcr, ff_pkt, &digest, &digest_stream, &digest_buf, &digest_name)) { /* * Did digest initialization fail? */ if (digest_stream != STREAM_NONE && digest == NULL) { Jmsg(jcr, M_WARNING, 0, _("%s digest initialization failed\n"), stream_to_ascii(digest_stream)); } else if (digest && digest_buf) { Dmsg3(400, "send inx=%d %s=%s\n", jcr->JobFiles, digest_name, digest_buf); dir->fsend("%d %d %s *%s-%d*", jcr->JobFiles, digest_stream, digest_buf, digest_name, jcr->JobFiles); Dmsg3(20, "filed>dir: %s len=%d: msg=%s\n", digest_name, dir->msglen, dir->msg); } } /* * Cleanup. */ if (digest_buf) { free(digest_buf); } if (digest) { crypto_digest_free(digest); } } return 1; }
static inline DEVICE *m_init_dev(JCR *jcr, DEVRES *device, bool new_init) { struct stat statp; int errstat; DCR *dcr = NULL; DEVICE *dev = NULL; uint32_t max_bs; Dmsg1(400, "max_block_size in device res is %u\n", device->max_block_size); /* * If no device type specified, try to guess */ if (!device->dev_type) { /* * Check that device is available */ if (stat(device->device_name, &statp) < 0) { berrno be; Jmsg2(jcr, M_ERROR, 0, _("Unable to stat device %s: ERR=%s\n"), device->device_name, be.bstrerror()); return NULL; } if (S_ISDIR(statp.st_mode)) { device->dev_type = B_FILE_DEV; } else if (S_ISCHR(statp.st_mode)) { device->dev_type = B_TAPE_DEV; } else if (S_ISFIFO(statp.st_mode)) { device->dev_type = B_FIFO_DEV; } else if (!bit_is_set(CAP_REQMOUNT, device->cap_bits)) { Jmsg2(jcr, M_ERROR, 0, _("%s is an unknown device type. Must be tape or directory, st_mode=%x\n"), device->device_name, statp.st_mode); return NULL; } } /* * See what type of device is wanted. */ switch (device->dev_type) { /* * When using dynamic loading use the init_backend_dev() function * for any type of device not being of the type file. */ #ifndef HAVE_DYNAMIC_SD_BACKENDS #ifdef HAVE_GFAPI case B_GFAPI_DEV: dev = New(gfapi_device); break; #endif #ifdef HAVE_OBJECTSTORE case B_OBJECT_STORE_DEV: dev = New(object_store_device); break; #endif #ifdef HAVE_RADOS case B_RADOS_DEV: dev = New(rados_device); break; #endif #ifdef HAVE_CEPHFS case B_CEPHFS_DEV: dev = New(cephfs_device); break; #endif #ifdef HAVE_ELASTO case B_ELASTO_DEV: dev = New(elasto_device); break; #endif #ifdef HAVE_WIN32 case B_TAPE_DEV: dev = New(win32_tape_device); break; case B_FIFO_DEV: dev = New(win32_fifo_device); break; #else case B_TAPE_DEV: dev = New(unix_tape_device); break; case B_FIFO_DEV: dev = New(unix_fifo_device); break; #endif #endif /* HAVE_DYNAMIC_SD_BACKENDS */ #ifdef HAVE_WIN32 case B_FILE_DEV: dev = New(win32_file_device); break; #else case B_FILE_DEV: dev = New(unix_file_device); break; #endif default: #ifdef HAVE_DYNAMIC_SD_BACKENDS dev = init_backend_dev(jcr, device->dev_type); #endif break; } if (!dev) { Jmsg2(jcr, M_ERROR, 0, _("%s has an unknown device type %d\n"), device->device_name, device->dev_type); return NULL; } dev->clear_slot(); /* unknown */ /* * Copy user supplied device parameters from Resource */ dev->dev_name = get_memory(strlen(device->device_name) + 1); pm_strcpy(dev->dev_name, device->device_name); if (device->device_options) { dev->dev_options = get_memory(strlen(device->device_options) + 1); pm_strcpy(dev->dev_options, device->device_options); } dev->prt_name = get_memory(strlen(device->device_name) + strlen(device->name()) + 20); /* * We edit "Resource-name" (physical-name) */ Mmsg(dev->prt_name, "\"%s\" (%s)", device->name(), device->device_name); Dmsg1(400, "Allocate dev=%s\n", dev->print_name()); copy_bits(CAP_MAX, device->cap_bits, dev->capabilities); /* * current block sizes */ dev->min_block_size = device->min_block_size; dev->max_block_size = device->max_block_size; dev->max_volume_size = device->max_volume_size; dev->max_file_size = device->max_file_size; dev->max_concurrent_jobs = device->max_concurrent_jobs; dev->volume_capacity = device->volume_capacity; dev->max_rewind_wait = device->max_rewind_wait; dev->max_open_wait = device->max_open_wait; dev->max_open_vols = device->max_open_vols; dev->vol_poll_interval = device->vol_poll_interval; dev->max_spool_size = device->max_spool_size; dev->drive_index = device->drive_index; dev->autoselect = device->autoselect; dev->norewindonclose = device->norewindonclose; dev->dev_type = device->dev_type; dev->device = device; /* * Sanity check */ if (dev->vol_poll_interval && dev->vol_poll_interval < 60) { dev->vol_poll_interval = 60; } device->dev = dev; if (dev->is_fifo()) { dev->set_cap(CAP_STREAM); /* set stream device */ } /* * If the device requires mount : * - Check that the mount point is available * - Check that (un)mount commands are defined */ if (dev->is_file() && dev->requires_mount()) { if (!device->mount_point || stat(device->mount_point, &statp) < 0) { berrno be; dev->dev_errno = errno; Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"), device->mount_point, be.bstrerror()); } if (!device->mount_command || !device->unmount_command) { Jmsg0(jcr, M_ERROR_TERM, 0, _("Mount and unmount commands must defined for a device which requires mount.\n")); } } /* * Sanity check */ if (dev->max_block_size == 0) { max_bs = DEFAULT_BLOCK_SIZE; } else { max_bs = dev->max_block_size; } if (dev->min_block_size > max_bs) { Jmsg(jcr, M_ERROR_TERM, 0, _("Min block size > max on device %s\n"), dev->print_name()); } if (dev->max_block_size > MAX_BLOCK_LENGTH) { Jmsg3(jcr, M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"), dev->max_block_size, dev->print_name(), DEFAULT_BLOCK_SIZE); dev->max_block_size = 0; } if (dev->max_block_size % TAPE_BSIZE != 0) { Jmsg3(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size=%d.\n"), dev->max_block_size, dev->print_name(), TAPE_BSIZE); } if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) { Jmsg(jcr, M_ERROR_TERM, 0, _("Max Vol Size < 8 * Max Block Size for device %s\n"), dev->print_name()); } dev->errmsg = get_pool_memory(PM_EMSG); *dev->errmsg = 0; if ((errstat = dev->init_mutex()) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } if ((errstat = pthread_cond_init(&dev->wait, NULL)) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } if ((errstat = pthread_cond_init(&dev->wait_next_vol, NULL)) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } if ((errstat = pthread_mutex_init(&dev->spool_mutex, NULL)) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init spool mutex: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } if ((errstat = dev->init_acquire_mutex()) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init acquire mutex: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } if ((errstat = dev->init_read_acquire_mutex()) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init read acquire mutex: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } dev->set_mutex_priorities(); #ifdef xxx if ((errstat = rwl_init(&dev->lock)) != 0) { berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat)); Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } #endif dev->clear_opened(); dev->attached_dcrs = New(dlist(dcr, &dcr->dev_link)); Dmsg2(100, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name); dev->initiated = true; Dmsg3(100, "dev=%s dev_max_bs=%u max_bs=%u\n", dev->dev_name, dev->device->max_block_size, dev->max_block_size); return dev; }
/* * Send data read from an already open file descriptor. * * We return 1 on sucess and 0 on errors. * * ***FIXME*** * We use ff_pkt->statp.st_size when FO_SPARSE to know when to stop * reading. * Currently this is not a problem as the only other stream, resource forks, * are not handled as sparse files. */ static int send_data(JCR *jcr, int stream, FF_PKT *ff_pkt, DIGEST *digest, DIGEST *signing_digest) { BSOCK *sd = jcr->store_bsock; uint64_t fileAddr = 0; /* file address */ char *rbuf, *wbuf; int32_t rsize = jcr->buf_size; /* read buffer size */ POOLMEM *msgsave; CIPHER_CONTEXT *cipher_ctx = NULL; /* Quell bogus uninitialized warnings */ const uint8_t *cipher_input; uint32_t cipher_input_len; uint32_t cipher_block_size; uint32_t encrypted_len; #ifdef FD_NO_SEND_TEST return 1; #endif msgsave = sd->msg; rbuf = sd->msg; /* read buffer */ wbuf = sd->msg; /* write buffer */ cipher_input = (uint8_t *)rbuf; /* encrypt uncompressed data */ Dmsg1(300, "Saving data, type=%d\n", ff_pkt->type); #ifdef HAVE_LIBZ uLong compress_len = 0; uLong max_compress_len = 0; const Bytef *cbuf = NULL; int zstat; if (ff_pkt->flags & FO_GZIP) { if (ff_pkt->flags & FO_SPARSE) { cbuf = (Bytef *)jcr->compress_buf + SPARSE_FADDR_SIZE; max_compress_len = jcr->compress_buf_size - SPARSE_FADDR_SIZE; } else { cbuf = (Bytef *)jcr->compress_buf; max_compress_len = jcr->compress_buf_size; /* set max length */ } wbuf = jcr->compress_buf; /* compressed output here */ cipher_input = (uint8_t *)jcr->compress_buf; /* encrypt compressed data */ /* * Only change zlib parameters if there is no pending operation. * This should never happen as deflatereset is called after each * deflate. */ if (((z_stream*)jcr->pZLIB_compress_workset)->total_in == 0) { /* set gzip compression level - must be done per file */ if ((zstat=deflateParams((z_stream*)jcr->pZLIB_compress_workset, ff_pkt->GZIP_level, Z_DEFAULT_STRATEGY)) != Z_OK) { Jmsg(jcr, M_FATAL, 0, _("Compression deflateParams error: %d\n"), zstat); set_jcr_job_status(jcr, JS_ErrorTerminated); goto err; } } } #else const uint32_t max_compress_len = 0; #endif if (ff_pkt->flags & FO_ENCRYPT) { if (ff_pkt->flags & FO_SPARSE) { Jmsg0(jcr, M_FATAL, 0, _("Encrypting sparse data not supported.\n")); goto err; } /* Allocate the cipher context */ if ((cipher_ctx = crypto_cipher_new(jcr->crypto.pki_session, true, &cipher_block_size)) == NULL) { /* Shouldn't happen! */ Jmsg0(jcr, M_FATAL, 0, _("Failed to initialize encryption context.\n")); goto err; } /* * Grow the crypto buffer, if necessary. * crypto_cipher_update() will buffer up to (cipher_block_size - 1). * We grow crypto_buf to the maximum number of blocks that * could be returned for the given read buffer size. * (Using the larger of either rsize or max_compress_len) */ jcr->crypto.crypto_buf = check_pool_memory_size(jcr->crypto.crypto_buf, (MAX(rsize + (int)sizeof(uint32_t), (int32_t)max_compress_len) + cipher_block_size - 1) / cipher_block_size * cipher_block_size); wbuf = jcr->crypto.crypto_buf; /* Encrypted, possibly compressed output here. */ } /* * Send Data header to Storage daemon * <file-index> <stream> <info> */ if (!sd->fsend("%ld %d 0", jcr->JobFiles, stream)) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); goto err; } Dmsg1(300, ">stored: datahdr %s\n", sd->msg); /* * Make space at beginning of buffer for fileAddr because this * same buffer will be used for writing if compression is off. */ if (ff_pkt->flags & FO_SPARSE) { rbuf += SPARSE_FADDR_SIZE; rsize -= SPARSE_FADDR_SIZE; #ifdef HAVE_FREEBSD_OS /* * To read FreeBSD partitions, the read size must be * a multiple of 512. */ rsize = (rsize/512) * 512; #endif } /* a RAW device read on win32 only works if the buffer is a multiple of 512 */ #ifdef HAVE_WIN32 if (S_ISBLK(ff_pkt->statp.st_mode)) rsize = (rsize/512) * 512; #endif /* * Read the file data */ while ((sd->msglen=(uint32_t)bread(&ff_pkt->bfd, rbuf, rsize)) > 0) { /* Check for sparse blocks */ if (ff_pkt->flags & FO_SPARSE) { ser_declare; bool allZeros = false; if ((sd->msglen == rsize && fileAddr+sd->msglen < (uint64_t)ff_pkt->statp.st_size) || ((ff_pkt->type == FT_RAW || ff_pkt->type == FT_FIFO) && (uint64_t)ff_pkt->statp.st_size == 0)) { allZeros = is_buf_zero(rbuf, rsize); } if (!allZeros) { /* Put file address as first data in buffer */ ser_begin(wbuf, SPARSE_FADDR_SIZE); ser_uint64(fileAddr); /* store fileAddr in begin of buffer */ } fileAddr += sd->msglen; /* update file address */ /* Skip block of all zeros */ if (allZeros) { continue; /* skip block of zeros */ } } jcr->ReadBytes += sd->msglen; /* count bytes read */ /* Uncompressed cipher input length */ cipher_input_len = sd->msglen; /* Update checksum if requested */ if (digest) { crypto_digest_update(digest, (uint8_t *)rbuf, sd->msglen); } /* Update signing digest if requested */ if (signing_digest) { crypto_digest_update(signing_digest, (uint8_t *)rbuf, sd->msglen); } #ifdef HAVE_LIBZ /* Do compression if turned on */ if (ff_pkt->flags & FO_GZIP && jcr->pZLIB_compress_workset) { Dmsg3(400, "cbuf=0x%x rbuf=0x%x len=%u\n", cbuf, rbuf, sd->msglen); ((z_stream*)jcr->pZLIB_compress_workset)->next_in = (Bytef *)rbuf; ((z_stream*)jcr->pZLIB_compress_workset)->avail_in = sd->msglen; ((z_stream*)jcr->pZLIB_compress_workset)->next_out = (Bytef *)cbuf; ((z_stream*)jcr->pZLIB_compress_workset)->avail_out = max_compress_len; if ((zstat=deflate((z_stream*)jcr->pZLIB_compress_workset, Z_FINISH)) != Z_STREAM_END) { Jmsg(jcr, M_FATAL, 0, _("Compression deflate error: %d\n"), zstat); set_jcr_job_status(jcr, JS_ErrorTerminated); goto err; } compress_len = ((z_stream*)jcr->pZLIB_compress_workset)->total_out; /* reset zlib stream to be able to begin from scratch again */ if ((zstat=deflateReset((z_stream*)jcr->pZLIB_compress_workset)) != Z_OK) { Jmsg(jcr, M_FATAL, 0, _("Compression deflateReset error: %d\n"), zstat); set_jcr_job_status(jcr, JS_ErrorTerminated); goto err; } Dmsg2(400, "compressed len=%d uncompressed len=%d\n", compress_len, sd->msglen); sd->msglen = compress_len; /* set compressed length */ cipher_input_len = compress_len; } #endif /* * Note, here we prepend the current record length to the beginning * of the encrypted data. This is because both sparse and compression * restore handling want records returned to them with exactly the * same number of bytes that were processed in the backup handling. * That is, both are block filters rather than a stream. When doing * compression, the compression routines may buffer data, so that for * any one record compressed, when it is decompressed the same size * will not be obtained. Of course, the buffered data eventually comes * out in subsequent crypto_cipher_update() calls or at least * when crypto_cipher_finalize() is called. Unfortunately, this * "feature" of encryption enormously complicates the restore code. */ if (ff_pkt->flags & FO_ENCRYPT) { uint32_t initial_len = 0; ser_declare; if (ff_pkt->flags & FO_SPARSE) { cipher_input_len += SPARSE_FADDR_SIZE; } /* Encrypt the length of the input block */ uint8_t packet_len[sizeof(uint32_t)]; ser_begin(packet_len, sizeof(uint32_t)); ser_uint32(cipher_input_len); /* store data len in begin of buffer */ Dmsg1(20, "Encrypt len=%d\n", cipher_input_len); if (!crypto_cipher_update(cipher_ctx, packet_len, sizeof(packet_len), (uint8_t *)jcr->crypto.crypto_buf, &initial_len)) { /* Encryption failed. Shouldn't happen. */ Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); goto err; } /* Encrypt the input block */ if (crypto_cipher_update(cipher_ctx, cipher_input, cipher_input_len, (uint8_t *)&jcr->crypto.crypto_buf[initial_len], &encrypted_len)) { if ((initial_len + encrypted_len) == 0) { /* No full block of data available, read more data */ continue; } Dmsg2(400, "encrypted len=%d unencrypted len=%d\n", encrypted_len, sd->msglen); sd->msglen = initial_len + encrypted_len; /* set encrypted length */ } else { /* Encryption failed. Shouldn't happen. */ Jmsg(jcr, M_FATAL, 0, _("Encryption error\n")); goto err; } } /* Send the buffer to the Storage daemon */ if (ff_pkt->flags & FO_SPARSE) { sd->msglen += SPARSE_FADDR_SIZE; /* include fileAddr in size */ } sd->msg = wbuf; /* set correct write buffer */ if (!sd->send()) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); goto err; } Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); /* #endif */ jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ sd->msg = msgsave; /* restore read buffer */ } /* end while read file data */ if (sd->msglen < 0) { /* error */ berrno be; Jmsg(jcr, M_ERROR, 0, _("Read error on file %s. ERR=%s\n"), ff_pkt->fname, be.bstrerror(ff_pkt->bfd.berrno)); if (jcr->JobErrors++ > 1000) { /* insanity check */ Jmsg(jcr, M_FATAL, 0, _("Too many errors.\n")); } } else if (ff_pkt->flags & FO_ENCRYPT) { /* * For encryption, we must call finalize to push out any * buffered data. */ if (!crypto_cipher_finalize(cipher_ctx, (uint8_t *)jcr->crypto.crypto_buf, &encrypted_len)) { /* Padding failed. Shouldn't happen. */ Jmsg(jcr, M_FATAL, 0, _("Encryption padding error\n")); goto err; } /* Note, on SSL pre-0.9.7, there is always some output */ if (encrypted_len > 0) { sd->msglen = encrypted_len; /* set encrypted length */ sd->msg = jcr->crypto.crypto_buf; /* set correct write buffer */ if (!sd->send()) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); goto err; } Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ sd->msg = msgsave; /* restore bnet buffer */ } } if (!sd->signal(BNET_EOD)) { /* indicate end of file data */ Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); goto err; } /* Free the cipher context */ if (cipher_ctx) { crypto_cipher_free(cipher_ctx); } return 1; err: /* Free the cipher context */ if (cipher_ctx) { crypto_cipher_free(cipher_ctx); } sd->msg = msgsave; /* restore bnet buffer */ sd->msglen = 0; return 0; }
/* * Clear volume header. */ void DEVICE::clear_volhdr() { Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName); memset(&VolHdr, 0, sizeof(VolHdr)); setVolCatInfo(false); }
/* Redefine DEVICE virtual function */ int vtape::d_open(const char *pathname, int uflags) { Dmsg2(dbglevel, "vtape::d_open(%s, %i)\n", pathname, uflags); online = true; /* assume that drive contains a tape */ struct flock lock; struct stat statp; if (stat(pathname, &statp) != 0) { fd = -1; Dmsg1(dbglevel, "Can't stat on %s\n", pathname); if (uflags & O_NONBLOCK) { online = false; fd = ::open("/dev/null", O_RDWR | O_LARGEFILE, 0600); } } else { fd = ::open(pathname, O_RDWR | O_LARGEFILE, 0600); } if (fd < 0) { berrno be; Dmsg2(0, "Unable to open vtape device %s ERR=%s\n", pathname, be.bstrerror()); errno = ENOMEDIUM; return -1; } lockfile = (char *)malloc(strlen(pathname) + 3); strcpy(lockfile, pathname); strcat(lockfile, ".l"); lockfd = ::open(lockfile, O_CREAT | O_RDWR | O_LARGEFILE, 0600); if (lockfd < 0) { berrno be; Dmsg2(0, "Unable to open vtape device lock %s ERR=%s\n", lockfile, be.bstrerror()); } else { lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; lock.l_pid = getpid(); ASSERT(fcntl(lockfd, F_SETLK, &lock) != -1); } file_block = 0; current_block = 0; current_file = 0; cur_FM = next_FM = last_FM = 0; needEOF = false; atBOT = true; atEOT = atEOD = false; /* If the vtape is empty, start by writing a EOF */ if (online && !read_fm(VT_READ_EOF)) { lseek(fd, 0, SEEK_SET); /* rewind */ cur_FM = next_FM = last_FM = 0; /* reset */ weof(); /* write the first EOF */ last_file = current_file=0; } return fd; }
int unpack_attributes_record(JCR *jcr, int32_t stream, char *rec, int32_t reclen, ATTR *attr) { char *p; int object_len; /* * An Attributes record consists of: * File_index * Type (FT_types) * Filename * Attributes * Link name (if file linked i.e. FT_LNK) * Extended attributes (Win32) * plus optional values determined by AR_ flags in upper bits of Type * Data_stream * */ attr->stream = stream; Dmsg1(dbglvl, "Attr: %s\n", rec); if (sscanf(rec, "%d %d", &attr->file_index, &attr->type) != 2) { Jmsg(jcr, M_FATAL, 0, _("Error scanning attributes: %s\n"), rec); Dmsg1(dbglvl, "\nError scanning attributes. %s\n", rec); return 0; } Dmsg2(dbglvl, "Got Attr: FilInx=%d type=%d\n", attr->file_index, attr->type); /* * Note AR_DATA_STREAM should never be set since it is encoded * at the end of the attributes. */ if (attr->type & AR_DATA_STREAM) { attr->data_stream = 1; } else { attr->data_stream = 0; } attr->type &= FT_MASK; /* keep only type bits */ p = rec; while (*p++ != ' ') /* skip record file index */ { } while (*p++ != ' ') /* skip type */ { } attr->fname = p; /* set filname position */ while (*p++ != 0) /* skip filename */ { } attr->attr = p; /* set attributes position */ while (*p++ != 0) /* skip attributes */ { } attr->lname = p; /* set link position */ while (*p++ != 0) /* skip link */ { } attr->delta_seq = 0; if (attr->type == FT_RESTORE_FIRST) { /* We have an object, so do a binary copy */ object_len = reclen + rec - p; attr->attrEx = check_pool_memory_size(attr->attrEx, object_len + 1); memcpy(attr->attrEx, p, object_len); /* Add a EOS for those who attempt to print the object */ p = attr->attrEx + object_len; *p = 0; } else { pm_strcpy(attr->attrEx, p); /* copy extended attributes, if any */ if (attr->data_stream) { int64_t val; while (*p++ != 0) /* skip extended attributes */ { } from_base64(&val, p); attr->data_stream = (int32_t)val; } else { while (*p++ != 0) /* skip extended attributes */ { } if (p - rec < reclen) { attr->delta_seq = str_to_int32(p); /* delta_seq */ } } } Dmsg8(dbglvl, "unpack_attr FI=%d Type=%d fname=%s attr=%s lname=%s attrEx=%s datastr=%d delta_seq=%d\n", attr->file_index, attr->type, attr->fname, attr->attr, attr->lname, attr->attrEx, attr->data_stream, attr->delta_seq); *attr->ofname = 0; *attr->olname = 0; return 1; }
/* * Close both pipes and free resources * * Returns: 0 on success * berrno on failure */ int close_bpipe(BPIPE *bpipe) { int chldstatus = 0; int status = 0; int wait_option; int remaining_wait; pid_t wpid = 0; /* Close pipes */ if (bpipe->rfd) { fclose(bpipe->rfd); bpipe->rfd = NULL; } if (bpipe->wfd) { fclose(bpipe->wfd); bpipe->wfd = NULL; } if (bpipe->wait == 0) { wait_option = 0; /* wait indefinitely */ } else { wait_option = WNOHANG; /* don't hang */ } remaining_wait = bpipe->wait; /* wait for worker child to exit */ for ( ;; ) { Dmsg2(800, "Wait for %d opt=%d\n", bpipe->worker_pid, wait_option); do { wpid = waitpid(bpipe->worker_pid, &chldstatus, wait_option); } while (wpid == -1 && (errno == EINTR || errno == EAGAIN)); if (wpid == bpipe->worker_pid || wpid == -1) { berrno be; status = errno; Dmsg3(800, "Got break wpid=%d status=%d ERR=%s\n", wpid, chldstatus, wpid==-1?be.bstrerror():"none"); break; } Dmsg3(800, "Got wpid=%d status=%d ERR=%s\n", wpid, chldstatus, wpid==-1?strerror(errno):"none"); if (remaining_wait > 0) { bmicrosleep(1, 0); /* wait one second */ remaining_wait--; } else { status = ETIME; /* set error status */ wpid = -1; break; /* don't wait any longer */ } } if (wpid > 0) { if (WIFEXITED(chldstatus)) { /* process exit()ed */ status = WEXITSTATUS(chldstatus); if (status != 0) { Dmsg1(800, "Non-zero status %d returned from child.\n", status); status |= b_errno_exit; /* exit status returned */ } Dmsg1(800, "child status=%d\n", status & ~b_errno_exit); } else if (WIFSIGNALED(chldstatus)) { /* process died */ #ifndef HAVE_WIN32 status = WTERMSIG(chldstatus); #else status = 1; /* fake child status */ #endif Dmsg1(800, "Child died from signal %d\n", status); status |= b_errno_signal; /* exit signal returned */ } } if (bpipe->timer_id) { stop_child_timer(bpipe->timer_id); } free(bpipe); Dmsg2(800, "returning status=%d,%d\n", status & ~(b_errno_exit|b_errno_signal), status); return status; }
/* * Authenticate Storage daemon connection */ bool authenticate_storage_daemon(JCR *jcr, STORE *store) { BSOCK *sd = jcr->store_bsock; char dirname[MAX_NAME_LENGTH]; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; int compatible = true; bool auth_success = false; /* * Send my name to the Storage daemon then do authentication */ bstrncpy(dirname, director->hdr.name, sizeof(dirname)); bash_spaces(dirname); /* Timeout Hello after 1 min */ btimer_t *tid = start_bsock_timer(sd, AUTH_TIMEOUT); if (!sd->fsend(hello, dirname)) { stop_bsock_timer(tid); Dmsg1(dbglvl, _("Error sending Hello to Storage daemon. ERR=%s\n"), bnet_strerror(sd)); Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to Storage daemon. ERR=%s\n"), bnet_strerror(sd)); return 0; } /* TLS Requirement */ if (store->tls_enable) { if (store->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (store->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } auth_success = cram_md5_respond(sd, store->password, &tls_remote_need, &compatible); if (auth_success) { auth_success = cram_md5_challenge(sd, store->password, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_challenge failed for %s\n", sd->who()); } } else { Dmsg1(dbglvl, "cram_respond failed for %s\n", sd->who()); } if (!auth_success) { stop_bsock_timer(tid); Dmsg0(dbglvl, _("Director and Storage daemon passwords or names not the same.\n")); Jmsg2(jcr, M_FATAL, 0, _("Director unable to authenticate with Storage daemon at \"%s:%d\". Possible causes:\n" "Passwords or names not the same or\n" "Maximum Concurrent Jobs exceeded on the SD or\n" "SD networking messed up (restart daemon).\n" "Please see " MANUAL_AUTH_URL " for help.\n"), sd->host(), sd->port()); return 0; } /* Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not advertise required TLS support.\n")); return 0; } /* Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); return 0; } /* Is TLS Enabled? */ if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* Engage TLS! Full Speed Ahead! */ if (!bnet_tls_client(store->tls_ctx, sd, NULL)) { stop_bsock_timer(tid); Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed with SD at \"%s:%d\"\n"), sd->host(), sd->port()); return 0; } if (store->tls_authenticate) { /* authentication only? */ sd->free_tls(); /* yes, stop tls */ } } Dmsg1(116, ">stored: %s", sd->msg); if (sd->recv() <= 0) { stop_bsock_timer(tid); Jmsg3(jcr, M_FATAL, 0, _("bdird<stored: \"%s:%s\" bad response to Hello command: ERR=%s\n"), sd->who(), sd->host(), sd->bstrerror()); return 0; } Dmsg1(110, "<stored: %s", sd->msg); stop_bsock_timer(tid); if (strncmp(sd->msg, OKhello, sizeof(OKhello)) != 0) { Dmsg0(dbglvl, _("Storage daemon rejected Hello command\n")); Jmsg2(jcr, M_FATAL, 0, _("Storage daemon at \"%s:%d\" rejected Hello command\n"), sd->host(), sd->port()); return 0; } return 1; }
/* * Run an external program. Optionally wait a specified number * of seconds. Program killed if wait exceeded (it is done by the * watchdog, as fgets is a blocking function). * * If the watchdog kills the program, fgets returns, and ferror is set * to 1 (=>SUCCESS), so we check if the watchdog killed the program. * * Return the full output from the program (not only the first line). * * Contrary to my normal calling conventions, this program * * Returns: 0 on success * non-zero on error == berrno status * */ int run_program_full_output(char *prog, int wait, POOLMEM *&results) { BPIPE *bpipe; int stat1, stat2; char *mode; POOLMEM* tmp; char *buf; const int bufsize = 32000; Dsm_check(200); tmp = get_pool_memory(PM_MESSAGE); buf = (char *)malloc(bufsize+1); results[0] = 0; mode = (char *)"r"; bpipe = open_bpipe(prog, wait, mode); if (!bpipe) { stat1 = ENOENT; goto bail_out; } Dsm_check(200); tmp[0] = 0; while (1) { buf[0] = 0; fgets(buf, bufsize, bpipe->rfd); buf[bufsize] = 0; pm_strcat(tmp, buf); if (feof(bpipe->rfd)) { stat1 = 0; Dmsg1(900, "Run program fgets stat=%d\n", stat1); break; } else { stat1 = ferror(bpipe->rfd); } if (stat1 < 0) { berrno be; Dmsg2(200, "Run program fgets stat=%d ERR=%s\n", stat1, be.bstrerror()); break; } else if (stat1 != 0) { Dmsg1(900, "Run program fgets stat=%d\n", stat1); if (bpipe->timer_id && bpipe->timer_id->killed) { Dmsg1(250, "Run program saw fgets killed=%d\n", bpipe->timer_id->killed); break; } } } /* * We always check whether the timer killed the program. We would see * an eof even when it does so we just have to trust the killed flag * and set the timer values to avoid edge cases where the program ends * just as the timer kills it. */ if (bpipe->timer_id && bpipe->timer_id->killed) { Dmsg1(150, "Run program fgets killed=%d\n", bpipe->timer_id->killed); pm_strcpy(tmp, _("Program killed by BAREOS (timeout)\n")); stat1 = ETIME; } pm_strcpy(results, tmp); Dmsg3(1900, "resadr=0x%x reslen=%d res=%s\n", results, strlen(results), results); stat2 = close_bpipe(bpipe); stat1 = stat2 != 0 ? stat2 : stat1; Dmsg1(900, "Run program returning %d\n", stat1); bail_out: free_pool_memory(tmp); free(buf); return stat1; }
/* * The first step in the restore process is for the user to * select a list of JobIds from which he will subsequently * select which files are to be restored. * * Returns: 2 if filename list made * 1 if jobid list made * 0 on error */ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; bool have_date = false; /* Include current second if using current time */ utime_t now = time(NULL) + 1; JobId_t JobId; JOB_DBR jr = { (JobId_t)-1 }; bool done = false; int i, j; const char *list[] = { _("List last 20 Jobs run"), _("List Jobs where a given File is saved"), _("Enter list of comma separated JobIds to select"), _("Enter SQL list command"), _("Select the most recent backup for a client"), _("Select backup for a client before a specified time"), _("Enter a list of files to restore"), _("Enter a list of files to restore before a specified time"), _("Find the JobIds of the most recent backup for a client"), _("Find the JobIds for a backup for a client before a specified time"), _("Enter a list of directories to restore for found JobIds"), _("Select full restore to a specified Job date"), _("Cancel"), NULL }; const char *kw[] = { /* These keywords are handled in a for loop */ "jobid", /* 0 */ "current", /* 1 */ "before", /* 2 */ "file", /* 3 */ "directory", /* 4 */ "select", /* 5 */ "pool", /* 6 */ "all", /* 7 */ /* The keyword below are handled by individual arg lookups */ "client", /* 8 */ "storage", /* 9 */ "fileset", /* 10 */ "where", /* 11 */ "yes", /* 12 */ "bootstrap", /* 13 */ "done", /* 14 */ "strip_prefix", /* 15 */ "add_prefix", /* 16 */ "add_suffix", /* 17 */ "regexwhere", /* 18 */ "restoreclient", /* 19 */ "copies", /* 20 */ "comment", /* 21 */ "restorejob", /* 22 */ "replace", /* 23 */ NULL }; rx->JobIds[0] = 0; for (i=1; i<ua->argc; i++) { /* loop through arguments */ bool found_kw = false; for (j=0; kw[j]; j++) { /* loop through keywords */ if (strcasecmp(kw[j], ua->argk[i]) == 0) { found_kw = true; break; } } if (!found_kw) { ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]); return 0; } /* Found keyword in kw[] list, process it */ switch (j) { case 0: /* jobid */ if (!has_value(ua, i)) { return 0; } if (*rx->JobIds != 0) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->argv[i]); done = true; break; case 1: /* current */ /* * Note, we add one second here just to include any job * that may have finished within the current second, * which happens a lot in scripting small jobs. */ bstrutime(date, sizeof(date), now); have_date = true; break; case 2: /* before */ if (have_date || !has_value(ua, i)) { return 0; } if (str_to_utime(ua->argv[i]) == 0) { ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]); return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); have_date = true; break; case 3: /* file */ case 4: /* dir */ if (!has_value(ua, i)) { return 0; } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } pm_strcpy(ua->cmd, ua->argv[i]); insert_one_file_or_dir(ua, rx, date, j==4); return 2; case 5: /* select */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; break; case 6: /* pool specified */ if (!has_value(ua, i)) { return 0; } rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]); if (!rx->pool) { ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]); return 0; } if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) { rx->pool = NULL; ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]); return 0; } break; case 7: /* all specified */ rx->all = true; break; /* * All keywords 7 or greater are ignored or handled by a select prompt */ default: break; } } if (!done) { ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n" "to be restored. You will be presented several methods\n" "of specifying the JobIds. Then you will be allowed to\n" "select which files from those JobIds are to be restored.\n\n")); } /* If choice not already made above, prompt */ for ( ; !done; ) { char *fname; int len; bool gui_save; db_list_ctx jobids; start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { add_prompt(ua, list[i]); } done = true; switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) { case -1: /* error or cancel */ return 0; case 0: /* list last 20 Jobs run */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 1: /* list where a file is saved */ if (!get_client_name(ua, rx)) { return 0; } if (!get_cmd(ua, _("Enter Filename (no path):"))) { return 0; } len = strlen(ua->cmd); fname = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len); Mmsg(rx->query, uar_file[db_get_type_index(ua->db)], rx->ClientName, fname); free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 2: /* enter a list of JobIds */ if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } pm_strcpy(rx->JobIds, ua->cmd); break; case 3: /* Enter an SQL list command */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } if (!get_cmd(ua, _("Enter SQL list command: "))) { return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 4: /* Select the most recent backups */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 5: /* select backup at specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 6: /* Enter files */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 7: /* enter files backed up before specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 8: /* Find JobIds for current backup */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 9: /* Find JobIds for give date */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 10: /* Enter directories */ if (*rx->JobIds != 0) { ua->send_msg(_("You have already selected the following JobIds: %s\n"), rx->JobIds); } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { if (*rx->JobIds != 0 && *ua->cmd) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->cmd); } if (*rx->JobIds == 0 || *rx->JobIds == '.') { *rx->JobIds = 0; return 0; /* nothing entered, return */ } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter full directory names or start the name\n" "with a < to indicate it is a filename containing a list\n" "of directories and terminate them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter directory name: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } /* Add trailing slash to end of directory names */ if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) { strcat(ua->cmd, "/"); } insert_one_file_or_dir(ua, rx, date, true); } return 2; case 11: /* Choose a jobid and select jobs */ if (!get_cmd(ua, _("Enter JobId to get the state to restore: ")) || !is_an_integer(ua->cmd)) { return 0; } memset(&jr, 0, sizeof(JOB_DBR)); jr.JobId = str_to_int64(ua->cmd); if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), ua->cmd, db_strerror(ua->db)); return 0; } ua->send_msg(_("Selecting jobs to build the Full state at %s\n"), jr.cStartTime); jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) { return 0; } pm_strcpy(rx->JobIds, jobids.list); Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds); break; case 12: /* Cancel or quit */ return 0; } } memset(&jr, 0, sizeof(JOB_DBR)); POOLMEM *JobIds = get_pool_memory(PM_FNAME); *JobIds = 0; rx->TotalFiles = 0; /* * Find total number of files to be restored, and filter the JobId * list to contain only ones permitted by the ACL conditions. */ for (p=rx->JobIds; ; ) { char ed1[50]; int stat = get_next_jobid_from_list(&p, &JobId); if (stat < 0) { ua->error_msg(_("Invalid JobId in list.\n")); free_pool_memory(JobIds); return 0; } if (stat == 0) { break; } if (jr.JobId == JobId) { continue; /* duplicate of last JobId */ } memset(&jr, 0, sizeof(JOB_DBR)); jr.JobId = JobId; if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), edit_int64(JobId, ed1), db_strerror(ua->db)); free_pool_memory(JobIds); return 0; } if (!acl_access_ok(ua, Job_ACL, jr.Name)) { ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"), edit_int64(JobId, ed1), jr.Name); continue; } if (*JobIds != 0) { pm_strcat(JobIds, ","); } pm_strcat(JobIds, edit_int64(JobId, ed1)); rx->TotalFiles += jr.JobFiles; } free_pool_memory(rx->JobIds); rx->JobIds = JobIds; /* Set ACL filtered list */ if (*rx->JobIds == 0) { ua->warning_msg(_("No Jobs selected.\n")); return 0; } if (strchr(rx->JobIds,',')) { ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds); } else { ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds); } return 1; }
/* * First prove our identity to the Remote daemon and then make him prove his identity. */ static inline bool two_way_authenticate(BSOCK *bs, JCR *jcr, bool initiate, const char *what) { int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; bool auth_success = false; btimer_t *tid = NULL; /* * TLS Requirement */ if (have_tls && me->tls_enable) { if (me->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (me->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(bs, AUTH_TIMEOUT); /* * See if we initiate the challenge or respond to a challenge. */ if (initiate) { /* * Challenge SD */ auth_success = cram_md5_challenge(bs, jcr->sd_auth_key, tls_local_need, compatible); if (auth_success) { /* * Respond to his challenge */ auth_success = cram_md5_respond(bs, jcr->sd_auth_key, &tls_remote_need, &compatible); if (!auth_success) { Dmsg1(dbglvl, "Respond cram-get-auth failed with %s\n", bs->who()); } } else { Dmsg1(dbglvl, "Challenge cram-auth failed with %s\n", bs->who()); } } else { /* * Respond to challenge */ auth_success = cram_md5_respond(bs, jcr->sd_auth_key, &tls_remote_need, &compatible); if (job_canceled(jcr)) { auth_success = false; /* force quick exit */ goto auth_fatal; } if (!auth_success) { Dmsg1(dbglvl, "cram_respond failed for %s\n", bs->who()); } else { /* * Challenge SD. */ auth_success = cram_md5_challenge(bs, jcr->sd_auth_key, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_challenge failed for %s\n", bs->who()); } } } if (!auth_success) { Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by %s daemon.\n" "Please see " MANUAL_AUTH_URL " for help.\n"), what); goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_client(me->tls_ctx, bs, NULL)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (me->tls_authenticate) { /* tls authentication only? */ bs->free_tls(); /* yes, shutdown tls */ } } auth_fatal: /* * Destroy session key */ memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); stop_bsock_timer(tid); /* * Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
/* * Depending on the initiate parameter perform one of the following: * * - First make him prove his identity and then prove our identity to the Remote. * - First prove our identity to the Remote and then make him prove his identity. */ bool BSOCK::two_way_authenticate(JCR *jcr, const char *what, const char *name, s_password &password, tls_t &tls, bool initiated_by_remote) { btimer_t *tid = NULL; const int dbglvl = 50; bool compatible = true; bool auth_success = false; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; ASSERT(password.encoding == p_encoding_md5); /* * TLS Requirement */ if (get_tls_enable(tls.ctx)) { tls_local_need = get_tls_require(tls.ctx) ? BNET_TLS_REQUIRED : BNET_TLS_OK; } if (jcr && job_canceled(jcr)) { Dmsg0(dbglvl, "Failed, because job is canceled.\n"); auth_success = false; /* force quick exit */ goto auth_fatal; } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(this, AUTH_TIMEOUT); /* * See if we initiate the challenge or respond to a challenge. */ if (initiated_by_remote) { /* * Challenge Remote. */ auth_success = cram_md5_challenge(this, password.value, tls_local_need, compatible); if (auth_success) { /* * Respond to remote challenge */ auth_success = cram_md5_respond(this, password.value, &tls_remote_need, &compatible); if (!auth_success) { Dmsg1(dbglvl, "Respond cram-get-auth failed with %s\n", who()); } } else { Dmsg1(dbglvl, "Challenge cram-auth failed with %s\n", who()); } } else { /* * Respond to remote challenge */ auth_success = cram_md5_respond(this, password.value, &tls_remote_need, &compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_respond failed for %s\n", who()); } else { /* * Challenge Remote. */ auth_success = cram_md5_challenge(this, password.value, tls_local_need, compatible); if (!auth_success) { Dmsg1(dbglvl, "cram_challenge failed for %s\n", who()); } } } if (!auth_success) { Jmsg(jcr, M_FATAL, 0, _("Authorization key rejected by %s %s.\n" "Please see %s for help.\n"), what, name, MANUAL_AUTH_URL); goto auth_fatal; } if (jcr && job_canceled(jcr)) { Dmsg0(dbglvl, "Failed, because job is canceled.\n"); auth_success = false; /* force quick exit */ goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { alist *verify_list = NULL; if (tls.verify_peer) { verify_list = tls.allowed_cns; } /* * See if we are handshaking a passive client connection. */ if (initiated_by_remote) { if (!bnet_tls_server(tls.ctx, this, verify_list)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); Dmsg0(dbglvl, "TLS negotiation failed.\n"); auth_success = false; goto auth_fatal; } } else { if (!bnet_tls_client(tls.ctx, this, tls.verify_peer, verify_list)) { Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); Dmsg0(dbglvl, "TLS negotiation failed.\n"); auth_success = false; goto auth_fatal; } } if (tls.authenticate) { /* tls authentication only? */ free_tls(); /* yes, shutdown tls */ } } auth_fatal: if (tid) { stop_bsock_timer(tid); tid = NULL; } if (jcr) { jcr->authenticated = auth_success; } return auth_success; }
/* * See who is connecting and lookup the authentication information. * First make him prove his identity and then prove our identity to the Remote daemon. */ static inline bool two_way_authenticate(int rcode, BSOCK *bs, JCR* jcr) { POOLMEM *dirname = get_pool_memory(PM_MESSAGE); DIRRES *director = NULL; int tls_local_need = BNET_TLS_NONE; int tls_remote_need = BNET_TLS_NONE; bool compatible = true; /* Want md5 compatible DIR */ bool auth_success = false; alist *verify_list = NULL; btimer_t *tid = NULL; if (rcode != R_DIRECTOR) { Dmsg1(dbglvl, "I only authenticate directors, not %d\n", rcode); Jmsg1(jcr, M_FATAL, 0, _("I only authenticate directors, not %d\n"), rcode); goto auth_fatal; } if (bs->msglen < 25 || bs->msglen > 500) { Dmsg2(dbglvl, "Bad Hello command from Director at %s. Len=%d.\n", bs->who(), bs->msglen); char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s. Len=%d.\n"), who, bs->msglen); goto auth_fatal; } dirname = check_pool_memory_size(dirname, bs->msglen); if (sscanf(bs->msg, "Hello Director %s calling", dirname) != 1) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; bs->msg[100] = 0; Dmsg2(dbglvl, "Bad Hello command from Director at %s: %s\n", bs->who(), bs->msg); Jmsg2(jcr, M_FATAL, 0, _("Bad Hello command from Director at %s: %s\n"), who, bs->msg); goto auth_fatal; } unbash_spaces(dirname); foreach_res(director, R_DIRECTOR) { if (bstrcmp(director->hdr.name, dirname)) break; } if (!director) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Jmsg2(jcr, M_FATAL, 0, _("Connection from unknown Director %s at %s rejected.\n"), dirname, who); goto auth_fatal; } if (have_tls) { /* * TLS Requirement */ if (director->tls_enable) { if (director->tls_require) { tls_local_need = BNET_TLS_REQUIRED; } else { tls_local_need = BNET_TLS_OK; } } if (director->tls_authenticate) { tls_local_need = BNET_TLS_REQUIRED; } if (director->tls_verify_peer) { verify_list = director->tls_allowed_cns; } } /* * Timeout Hello after 10 min */ tid = start_bsock_timer(bs, AUTH_TIMEOUT); /* * Challenge the director */ auth_success = cram_md5_challenge(bs, director->password, tls_local_need, compatible); if (job_canceled(jcr)) { auth_success = false; goto auth_fatal; /* quick exit */ } if (auth_success) { auth_success = cram_md5_respond(bs, director->password, &tls_remote_need, &compatible); if (!auth_success) { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_get_auth failed for %s\n", who); } } else { char addr[64]; char *who = bnet_get_peer(bs, addr, sizeof(addr)) ? bs->who() : addr; Dmsg1(dbglvl, "cram_auth failed for %s\n", who); } if (!auth_success) { Emsg1(M_FATAL, 0, _("Incorrect password given by Director at %s.\n"), bs->who()); goto auth_fatal; } /* * Verify that the remote host is willing to meet our TLS requirements */ if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not" " advertize required TLS support.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } /* * Verify that we are willing to meet the remote host's requirements */ if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) { Jmsg0(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n")); Dmsg2(dbglvl, "remote_need=%d local_need=%d\n", tls_remote_need, tls_local_need); auth_success = false; goto auth_fatal; } if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) { /* * Engage TLS! Full Speed Ahead! */ if (!bnet_tls_server(director->tls_ctx, bs, verify_list)) { Jmsg0(jcr, M_FATAL, 0, _("TLS negotiation failed.\n")); auth_success = false; goto auth_fatal; } if (director->tls_authenticate) { /* authentication only? */ bs->free_tls(); /* shutodown tls */ } } auth_fatal: if (tid) { stop_bsock_timer(tid); tid = NULL; } free_pool_memory(dirname); jcr->director = director; /* * Single thread all failures to avoid DOS */ if (!auth_success) { P(mutex); bmicrosleep(6, 0); V(mutex); } return auth_success; }
bool run_cmd(JCR *jcr) { struct timeval tv; struct timezone tz; struct timespec timeout; int errstat = 0; BSOCK *cl; int fd_version = 0; int sd_version = 0; char job_name[500]; int i; int stat; Dsm_check(200); Dmsg1(200, "Run_cmd: %s\n", jcr->dir_bsock->msg); /* If we do not need the FD, we are doing a virtual backup. */ if (jcr->no_client_used()) { do_vbackup(jcr); return false; } jcr->sendJobStatus(JS_WaitFD); /* wait for FD to connect */ Dmsg2(050, "sd_calls_client=%d sd_client=%d\n", jcr->sd_calls_client, jcr->sd_client); if (jcr->sd_calls_client) { /* We connected to Client, so finish work */ cl = jcr->file_bsock; if (!cl) { Jmsg0(jcr, M_FATAL, 0, _("Client socket not open. Could not connect to Client.\n")); Dmsg0(050, "Client socket not open. Could not connect to Client.\n"); return false; } /* Get response to Hello command sent earlier */ Dmsg0(050, "Read Hello command from Client\n"); for (i=0; i<60; i++) { stat = cl->recv(); if (stat <= 0) { bmicrosleep(1, 0); } else { break; } } if (stat <= 0) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); Dmsg1(050, _("Recv request to Client failed. ERR=%s\n"), be.bstrerror()); return false; } Dmsg1(050, "Got from FD: %s\n", cl->msg); if (sscanf(cl->msg, "Hello Bacula SD: Start Job %127s %d %d", job_name, &fd_version, &sd_version) != 3) { Jmsg1(jcr, M_FATAL, 0, _("Bad Hello from Client: %s.\n"), cl->msg); Dmsg1(050, _("Bad Hello from Client: %s.\n"), cl->msg); return false; } unbash_spaces(job_name); jcr->FDVersion = fd_version; jcr->SDVersion = sd_version; Dmsg1(050, "FDVersion=%d\n", fd_version); /* * Authenticate the File daemon */ Dmsg0(050, "=== Authenticate FD\n"); if (jcr->authenticated || !authenticate_filed(jcr)) { Dmsg1(050, "Authentication failed Job %s\n", jcr->Job); Jmsg(jcr, M_FATAL, 0, _("Unable to authenticate File daemon\n")); } else { jcr->authenticated = true; } } else if (!jcr->sd_client) { /* We wait to receive connection from Client */ gettimeofday(&tv, &tz); timeout.tv_nsec = tv.tv_usec * 1000; timeout.tv_sec = tv.tv_sec + me->client_wait; Dmsg3(050, "%s waiting %d sec for FD to contact SD key=%s\n", jcr->Job, (int)(timeout.tv_sec-time(NULL)), jcr->sd_auth_key); Dmsg3(800, "=== Block Job=%s jid=%d %p\n", jcr->Job, jcr->JobId, jcr); /* * Wait for the File daemon to contact us to start the Job, * when he does, we will be released, unless the 30 minutes * expires. */ P(mutex); while ( !jcr->authenticated && !job_canceled(jcr) ) { errstat = pthread_cond_timedwait(&jcr->job_start_wait, &mutex, &timeout); if (errstat == ETIMEDOUT || errstat == EINVAL || errstat == EPERM) { break; } Dmsg1(800, "=== Auth cond errstat=%d\n", errstat); } Dmsg4(050, "=== Auth=%d jid=%d canceled=%d errstat=%d\n", jcr->JobId, jcr->authenticated, job_canceled(jcr), errstat); V(mutex); Dmsg2(800, "Auth fail or cancel for jid=%d %p\n", jcr->JobId, jcr); } memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key)); if (jcr->authenticated && !job_canceled(jcr)) { Dmsg2(800, "Running jid=%d %p\n", jcr->JobId, jcr); run_job(jcr); /* Run the job */ } Dmsg2(800, "Done jid=%d %p\n", jcr->JobId, jcr); return false; }
/* * Do a virtual backup, which consolidates all previous backups into * a sort of synthetic Full. * * Returns: false on failure * true on success */ bool do_native_vbackup(JCR *jcr) { char ed1[100]; BSOCK *sd; char *p; db_list_ctx jobids; if (!jcr->rstorage) { Jmsg(jcr, M_FATAL, 0, _("No storage for reading given.\n")); return false; } if (!jcr->wstorage) { Jmsg(jcr, M_FATAL, 0, _("No storage for writing given.\n")); return false; } Dmsg2(100, "rstorage=%p wstorage=%p\n", jcr->rstorage, jcr->wstorage); Dmsg2(100, "Read store=%s, write store=%s\n", ((STORERES *)jcr->rstorage->first())->name(), ((STORERES *)jcr->wstorage->first())->name()); /* * Print Job Start message */ Jmsg(jcr, M_INFO, 0, _("Start Virtual Backup JobId %s, Job=%s\n"), edit_uint64(jcr->JobId, ed1), jcr->Job); if (!jcr->accurate) { Jmsg(jcr, M_WARNING, 0, _("This Job is not an Accurate backup so is not equivalent to a Full backup.\n")); } db_accurate_get_jobids(jcr, jcr->db, &jcr->jr, &jobids); Dmsg1(10, "Accurate jobids=%s\n", jobids.list); if (jobids.count == 0) { Jmsg(jcr, M_FATAL, 0, _("No previous Jobs found.\n")); return false; } /* * Now we find the last job that ran and store it's info in * the previous_jr record. We will set our times to the * values from that job so that anything changed after that * time will be picked up on the next backup. */ p = strrchr(jobids.list, ','); /* find last jobid */ if (p != NULL) { p++; } else { p = jobids.list; } memset(&jcr->previous_jr, 0, sizeof(jcr->previous_jr)); jcr->previous_jr.JobId = str_to_int64(p); Dmsg1(10, "Previous JobId=%s\n", p); if (!db_get_job_record(jcr, jcr->db, &jcr->previous_jr)) { Jmsg(jcr, M_FATAL, 0, _("Error getting Job record for previous Job: ERR=%s"), db_strerror(jcr->db)); return false; } if (!create_bootstrap_file(jcr, jobids.list)) { Jmsg(jcr, M_FATAL, 0, _("Could not get or create the FileSet record.\n")); return false; } /* * Open a message channel connection with the Storage * daemon. This is to let him know that our client * will be contacting him for a backup session. * */ Dmsg0(110, "Open connection with storage daemon\n"); jcr->setJobStatus(JS_WaitSD); /* * Start conversation with Storage daemon */ if (!connect_to_storage_daemon(jcr, 10, me->SDConnectTimeout, true)) { return false; } sd = jcr->store_bsock; /* * Now start a job with the Storage daemon */ if (!start_storage_daemon_job(jcr, jcr->rstorage, jcr->wstorage, /* send_bsr */ true)) { return false; } Dmsg0(100, "Storage daemon connection OK\n"); /* * We re-update the job start record so that the start * time is set after the run before job. This avoids * that any files created by the run before job will * be saved twice. They will be backed up in the current * job, but not in the next one unless they are changed. * Without this, they will be backed up in this job and * in the next job run because in that case, their date * is after the start of this run. */ jcr->start_time = time(NULL); jcr->jr.StartTime = jcr->start_time; jcr->jr.JobTDate = jcr->start_time; jcr->setJobStatus(JS_Running); /* * Update job start record */ if (!db_update_job_start_record(jcr, jcr->db, &jcr->jr)) { Jmsg(jcr, M_FATAL, 0, "%s", db_strerror(jcr->db)); return false; } /* * Declare the job started to start the MaxRunTime check */ jcr->setJobStarted(); /* * Start the job prior to starting the message thread below * to avoid two threads from using the BSOCK structure at * the same time. */ if (!sd->fsend("run")) { return false; } /* * Now start a Storage daemon message thread */ if (!start_storage_daemon_message_thread(jcr)) { return false; } jcr->setJobStatus(JS_Running); /* * Pickup Job termination data * Note, the SD stores in jcr->JobFiles/ReadBytes/JobBytes/JobErrors */ wait_for_storage_daemon_termination(jcr); jcr->setJobStatus(jcr->SDJobStatus); db_write_batch_file_records(jcr); /* used by bulk batch file insert */ if (!jcr->is_JobStatus(JS_Terminated)) { return false; } native_vbackup_cleanup(jcr, jcr->JobStatus); return true; }