/* * The following entry points are accessed through the function pointers we supplied to Bareos. * Each plugin type (dir, fd, sd) has its own set of entry points that the plugin must define. * * Create a new instance of the plugin i.e. allocate our private storage */ static bRC newPlugin(bpContext *ctx) { plugin_ctx *p_ctx; p_ctx = (plugin_ctx *)malloc(sizeof(plugin_ctx)); if (!p_ctx) { return bRC_Error; } memset(p_ctx, 0, sizeof(plugin_ctx)); ctx->pContext = (void *)p_ctx; /* set our context pointer */ /* * Allocate some internal memory for: * - The current working dir. * - The file we are processing * - The link target of a symbolic link. * - The list of xattrs. */ p_ctx->cwd = get_pool_memory(PM_FNAME); p_ctx->next_filename = get_pool_memory(PM_FNAME); p_ctx->link_target = get_pool_memory(PM_FNAME); p_ctx->xattr_list = get_pool_memory(PM_MESSAGE); /* * Resize all buffers for PATH like names to CEPHFS_PATH_MAX. */ p_ctx->cwd = check_pool_memory_size(p_ctx->cwd, CEPHFS_PATH_MAX); p_ctx->next_filename = check_pool_memory_size(p_ctx->next_filename, CEPHFS_PATH_MAX); p_ctx->link_target = check_pool_memory_size(p_ctx->link_target, CEPHFS_PATH_MAX); /* * This is a alist that holds the stack of directories we have open. * We push the current directory onto this stack the moment we start * processing a sub directory and pop it from this list when we are * done processing that sub directory. */ p_ctx->dir_stack = new alist(10, owned_by_alist); /* * Only register the events we are really interested in. */ bfuncs->registerBareosEvents(ctx, 7, bEventLevel, bEventSince, bEventRestoreCommand, bEventBackupCommand, bEventPluginCommand, bEventEndRestoreJob, bEventNewPluginOptions); return bRC_OK; }
/* * Given a full filename, split it into its path * and filename parts. They are returned in pool memory * in the mdb structure. */ void split_path_and_file(JCR *jcr, B_DB *mdb, const char *fname) { const char *p, *f; /* Find path without the filename. * I.e. everything after the last / is a "filename". * OK, maybe it is a directory name, but we treat it like * a filename. If we don't find a / then the whole name * must be a path name (e.g. c:). */ for (p=f=fname; *p; p++) { if (IsPathSeparator(*p)) { f = p; /* set pos of last slash */ } } if (IsPathSeparator(*f)) { /* did we find a slash? */ f++; /* yes, point to filename */ } else { f = p; /* no, whole thing must be path name */ } /* If filename doesn't exist (i.e. root directory), we * simply create a blank name consisting of a single * space. This makes handling zero length filenames * easier. */ mdb->fnl = p - f; if (mdb->fnl > 0) { mdb->fname = check_pool_memory_size(mdb->fname, mdb->fnl+1); memcpy(mdb->fname, f, mdb->fnl); /* copy filename */ mdb->fname[mdb->fnl] = 0; } else { mdb->fname[0] = 0; mdb->fnl = 0; } mdb->pnl = f - fname; if (mdb->pnl > 0) { mdb->path = check_pool_memory_size(mdb->path, mdb->pnl+1); memcpy(mdb->path, fname, mdb->pnl); mdb->path[mdb->pnl] = 0; } else { Mmsg1(&mdb->errmsg, _("Path length is zero. File=%s\n"), fname); Jmsg(jcr, M_ERROR, 0, "%s", mdb->errmsg); mdb->path[0] = 0; mdb->pnl = 0; } Dmsg2(500, "split path=%s file=%s\n", mdb->path, mdb->fname); }
void do_messages(UAContext *ua, const char *cmd) { char msg[2000]; int mlen; bool do_truncate = false; Pw(con_lock); pthread_cleanup_push(con_lock_release, (void *)NULL); rewind(con_fd); while (fgets(msg, sizeof(msg), con_fd)) { mlen = strlen(msg); ua->UA_sock->msg = check_pool_memory_size(ua->UA_sock->msg, mlen+1); strcpy(ua->UA_sock->msg, msg); ua->UA_sock->msglen = mlen; ua->UA_sock->send(); do_truncate = true; } if (do_truncate) { (void)ftruncate(fileno(con_fd), 0L); } console_msg_pending = FALSE; ua->user_notified_msg_pending = FALSE; pthread_cleanup_pop(0); Vw(con_lock); }
/* Match a regexp and add the result to the items list * This function is recursive */ static void match_kw(regex_t *preg, const char *what, int len, POOLMEM **buf) { int rc, size; int nmatch=20; regmatch_t pmatch[20]; if (len <= 0) { return; } rc = regexec(preg, what, nmatch, pmatch, 0); if (rc == 0) { #if 0 Pmsg1(0, "\n\n%s\n0123456789012345678901234567890123456789\n 10 20 30\n", what); Pmsg2(0, "%i-%i\n", pmatch[0].rm_so, pmatch[0].rm_eo); Pmsg2(0, "%i-%i\n", pmatch[1].rm_so, pmatch[1].rm_eo); Pmsg2(0, "%i-%i\n", pmatch[2].rm_so, pmatch[2].rm_eo); Pmsg2(0, "%i-%i\n", pmatch[3].rm_so, pmatch[3].rm_eo); #endif size = pmatch[1].rm_eo - pmatch[1].rm_so; *buf = check_pool_memory_size(*buf, size + 1); memcpy(*buf, what+pmatch[1].rm_so, size); (*buf)[size] = 0; items->list.append(bstrdup(*buf)); /* We search for the next keyword in the line */ match_kw(preg, what + pmatch[1].rm_eo, len - pmatch[1].rm_eo, buf); } }
/** * Update File Attribute data * We do the following: * 1. expand the bsock buffer to be large enough * 2. Write a "header" into the buffer with serialized data * VolSessionId * VolSeesionTime * FileIndex * Stream * data length that follows * start of raw byte data from the Device record. * Note, this is primarily for Attribute data, but can * also handle any device record. The Director must know * the raw byte data format that is defined for each Stream. * Now Restore Objects pass through here STREAM_RESTORE_OBJECT */ bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { JCR *jcr = dcr->jcr; BSOCK *dir = jcr->dir_bsock; ser_declare; #ifdef NO_ATTRIBUTES_TEST return true; #endif dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) + MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1); dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) + MAX_NAME_LENGTH + 1, FileAttributes, jcr->Job); ser_begin(dir->msg + dir->msglen, 0); ser_uint32(rec->VolSessionId); ser_uint32(rec->VolSessionTime); ser_int32(rec->FileIndex); ser_int32(rec->Stream); ser_uint32(rec->data_len); ser_bytes(rec->data, rec->data_len); dir->msglen = ser_length(dir->msg); Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */ return dir->send(); }
int pm_strcpy(POOLMEM *&pm, POOL_MEM &str) { int len = strlen(str.c_str()) + 1; pm = check_pool_memory_size(pm, len); memcpy(pm, str.c_str(), len); return len - 1; }
static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name) { char *p, *f; /* Find path without the filename. * I.e. everything after the last / is a "filename". * OK, maybe it is a directory name, but we treat it like * a filename. If we don't find a / then the whole name * must be a path name (e.g. c:). */ for (p=f=name; *p; p++) { if (IsPathSeparator(*p)) { f = p; /* set pos of last slash */ } } if (IsPathSeparator(*f)) { /* did we find a slash? */ f++; /* yes, point to filename */ } else { /* no, whole thing must be path name */ f = p; } /* If filename doesn't exist (i.e. root directory), we * simply create a blank name consisting of a single * space. This makes handling zero length filenames * easier. */ rx->fnl = p - f; if (rx->fnl > 0) { rx->fname = check_pool_memory_size(rx->fname, 2*(rx->fnl)+1); db_escape_string(ua->jcr, ua->db, rx->fname, f, rx->fnl); } else { rx->fname[0] = 0; rx->fnl = 0; } rx->pnl = f - name; if (rx->pnl > 0) { rx->path = check_pool_memory_size(rx->path, 2*(rx->pnl)+1); db_escape_string(ua->jcr, ua->db, rx->path, name, rx->pnl); } else { rx->path[0] = 0; rx->pnl = 0; } Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname); }
int pm_strcat(POOLMEM *&pm, POOL_MEM &str) { int pmlen = strlen(pm); int len = strlen(str.c_str()) + 1; pm = check_pool_memory_size(pm, pmlen + len); memcpy(pm+pmlen, str.c_str(), len); return pmlen + len - 1; }
/* * create_volume_label_record * Serialize label (from dev->VolHdr structure) into device record. * Assumes that the dev->VolHdr structure is properly * initialized. */ static void create_volume_label_record(DCR *dcr, DEVICE *dev, DEV_RECORD *rec) { ser_declare; struct date_time dt; JCR *jcr = dcr->jcr; char buf[100]; /* Serialize the label into the device record. */ rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label); ser_begin(rec->data, SER_LENGTH_Volume_Label); ser_string(dev->VolHdr.Id); ser_uint32(dev->VolHdr.VerNum); if (dev->VolHdr.VerNum >= 11) { ser_btime(dev->VolHdr.label_btime); dev->VolHdr.write_btime = get_current_btime(); ser_btime(dev->VolHdr.write_btime); dev->VolHdr.write_date = 0; dev->VolHdr.write_time = 0; } else { /* OLD WAY DEPRECATED */ ser_float64(dev->VolHdr.label_date); ser_float64(dev->VolHdr.label_time); get_current_time(&dt); dev->VolHdr.write_date = dt.julian_day_number; dev->VolHdr.write_time = dt.julian_day_fraction; } ser_float64(dev->VolHdr.write_date); /* 0 if VerNum >= 11 */ ser_float64(dev->VolHdr.write_time); /* 0 if VerNum >= 11 */ ser_string(dev->VolHdr.VolumeName); ser_string(dev->VolHdr.PrevVolumeName); ser_string(dev->VolHdr.PoolName); ser_string(dev->VolHdr.PoolType); ser_string(dev->VolHdr.MediaType); ser_string(dev->VolHdr.HostName); ser_string(dev->VolHdr.LabelProg); ser_string(dev->VolHdr.ProgVersion); ser_string(dev->VolHdr.ProgDate); ser_end(rec->data, SER_LENGTH_Volume_Label); bstrncpy(dcr->VolumeName, dev->VolHdr.VolumeName, sizeof(dcr->VolumeName)); rec->data_len = ser_length(rec->data); rec->FileIndex = dev->VolHdr.LabelType; rec->VolSessionId = jcr->VolSessionId; rec->VolSessionTime = jcr->VolSessionTime; rec->Stream = jcr->NumWriteVolumes; rec->maskedStream = jcr->NumWriteVolumes; Dmsg2(150, "Created Vol label rec: FI=%s len=%d\n", FI_to_ascii(buf, rec->FileIndex), rec->data_len); }
int pm_strcpy(POOLMEM *&pm, const char *str) { int len; if (!str) str = ""; len = strlen(str) + 1; pm = check_pool_memory_size(pm, len); memcpy(pm, str, len); return len - 1; }
/* * Create session label * The pool memory must be released by the calling program */ void create_session_label(DCR *dcr, DEV_RECORD *rec, int label) { JCR *jcr = dcr->jcr; ser_declare; rec->VolSessionId = jcr->VolSessionId; rec->VolSessionTime = jcr->VolSessionTime; rec->Stream = jcr->JobId; rec->maskedStream = jcr->JobId; rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label); ser_begin(rec->data, SER_LENGTH_Session_Label); if (me->compatible) { ser_string(OldBaculaId); ser_uint32(OldCompatibleBareosTapeVersion1); } else { ser_string(BareosId); ser_uint32(BareosTapeVersion); } ser_uint32(jcr->JobId); /* Changed in VerNum 11 */ ser_btime(get_current_btime()); ser_float64(0); ser_string(dcr->pool_name); ser_string(dcr->pool_type); ser_string(jcr->job_name); /* base Job name */ ser_string(jcr->client_name); /* Added in VerNum 10 */ ser_string(jcr->Job); /* Unique name of this Job */ ser_string(jcr->fileset_name); ser_uint32(jcr->getJobType()); ser_uint32(jcr->getJobLevel()); /* Added in VerNum 11 */ ser_string(jcr->fileset_md5); if (label == EOS_LABEL) { ser_uint32(jcr->JobFiles); ser_uint64(jcr->JobBytes); ser_uint32(dcr->StartBlock); ser_uint32(dcr->EndBlock); ser_uint32(dcr->StartFile); ser_uint32(dcr->EndFile); ser_uint32(jcr->JobErrors); /* Added in VerNum 11 */ ser_uint32(jcr->JobStatus); } ser_end(rec->data, SER_LENGTH_Session_Label); rec->data_len = ser_length(rec->data); }
int pm_strcat(POOLMEM *&pm, const char *str) { int pmlen = strlen(pm); int len; if (!str) str = ""; len = strlen(str) + 1; pm = check_pool_memory_size(pm, pmlen + len); memcpy(pm+pmlen, str, len); return pmlen + len - 1; }
/* unser_volume_label * * Unserialize the Bareos Volume label into the device Volume_Label * structure. * * Assumes that the record is already read. * * Returns: false on error * true on success */ bool unser_volume_label(DEVICE *dev, DEV_RECORD *rec) { ser_declare; char buf1[100], buf2[100]; if (rec->FileIndex != VOL_LABEL && rec->FileIndex != PRE_LABEL) { Mmsg3(dev->errmsg, _("Expecting Volume Label, got FI=%s Stream=%s len=%d\n"), FI_to_ascii(buf1, rec->FileIndex), stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len); if (!forge_on) { return false; } } dev->VolHdr.LabelType = rec->FileIndex; dev->VolHdr.LabelSize = rec->data_len; /* Unserialize the record into the Volume Header */ rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Volume_Label); ser_begin(rec->data, SER_LENGTH_Volume_Label); unser_string(dev->VolHdr.Id); unser_uint32(dev->VolHdr.VerNum); if (dev->VolHdr.VerNum >= 11) { unser_btime(dev->VolHdr.label_btime); unser_btime(dev->VolHdr.write_btime); } else { /* old way */ unser_float64(dev->VolHdr.label_date); unser_float64(dev->VolHdr.label_time); } unser_float64(dev->VolHdr.write_date); /* Unused with VerNum >= 11 */ unser_float64(dev->VolHdr.write_time); /* Unused with VerNum >= 11 */ unser_string(dev->VolHdr.VolumeName); unser_string(dev->VolHdr.PrevVolumeName); unser_string(dev->VolHdr.PoolName); unser_string(dev->VolHdr.PoolType); unser_string(dev->VolHdr.MediaType); unser_string(dev->VolHdr.HostName); unser_string(dev->VolHdr.LabelProg); unser_string(dev->VolHdr.ProgVersion); unser_string(dev->VolHdr.ProgDate); ser_end(rec->data, SER_LENGTH_Volume_Label); Dmsg0(190, "unser_vol_label\n"); if (debug_level >= 190) { dump_volume_label(dev); } return true; }
accurate_payload *B_ACCURATE_LMDB::lookup_payload(JCR *jcr, char *fname) { int result; int lstat_length; MDB_val key, data; accurate_payload *payload = NULL; key.mv_data = fname; key.mv_size = strlen(fname) + 1; result = mdb_get(m_db_ro_txn, m_db_dbi, &key, &data); switch (result) { case 0: /* * Success. * * We need to make a private copy of the LDMB data as we are not * allowed to change its content and we need to update the lstat * and chksum pointer to point to the actual lstat and chksum that * is stored behind the accurate_payload structure in the LMDB. */ m_pay_load = check_pool_memory_size(m_pay_load, data.mv_size); payload = (accurate_payload *)m_pay_load; memcpy(payload, data.mv_data, data.mv_size); payload->lstat = (char *)payload + sizeof(accurate_payload); lstat_length = strlen(payload->lstat); payload->chksum = (char *)payload->lstat + lstat_length + 1; /* * We keep the transaction as short a possible so after a lookup * and copying the actual data out we reset the read transaction * and do a renew of the read transaction for a new run. */ mdb_txn_reset(m_db_ro_txn); result = mdb_txn_renew(m_db_ro_txn); if (result != 0) { Jmsg1(jcr, M_FATAL, 0, _("Unable to renew read transaction: %s\n"), mdb_strerror(result)); return NULL; } break; case MDB_NOTFOUND: /* * Failed to find the given key. */ break; default: break; } return payload; }
/* * Add a character to the current string */ static void add_str(LEX *lf, int ch) { /* * The default config string is sized to 256 bytes. * If we need longer config strings its increased with 256 bytes each time. */ if ((lf->str_len + 3) >= lf->str_max_len) { lf->str = check_pool_memory_size(lf->str, lf->str_max_len + 256); lf->str_max_len = sizeof_pool_memory(lf->str); } lf->str[lf->str_len++] = ch; lf->str[lf->str_len] = 0; }
/* Update the attributes record by adding the file digest */ bool db_add_digest_to_file_record(JCR *jcr, B_DB *mdb, FileId_t FileId, char *digest, int type) { bool retval; char ed1[50]; int len = strlen(digest); db_lock(mdb); mdb->esc_name = check_pool_memory_size(mdb->esc_name, len*2+1); mdb->db_escape_string(jcr, mdb->esc_name, digest, len); Mmsg(mdb->cmd, "UPDATE File SET MD5='%s' WHERE FileId=%s", mdb->esc_name, edit_int64(FileId, ed1)); retval = UPDATE_DB(jcr, mdb, mdb->cmd); db_unlock(mdb); return retval; }
bool unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec) { ser_declare; rec->data = check_pool_memory_size(rec->data, SER_LENGTH_Session_Label); unser_begin(rec->data, SER_LENGTH_Session_Label); unser_string(label->Id); unser_uint32(label->VerNum); unser_uint32(label->JobId); if (label->VerNum >= 11) { unser_btime(label->write_btime); } else { unser_float64(label->write_date); } unser_float64(label->write_time); unser_string(label->PoolName); unser_string(label->PoolType); unser_string(label->JobName); unser_string(label->ClientName); if (label->VerNum >= 10) { unser_string(label->Job); /* Unique name of this Job */ unser_string(label->FileSetName); unser_uint32(label->JobType); unser_uint32(label->JobLevel); } if (label->VerNum >= 11) { unser_string(label->FileSetMD5); } else { label->FileSetMD5[0] = 0; } if (rec->FileIndex == EOS_LABEL) { unser_uint32(label->JobFiles); unser_uint64(label->JobBytes); unser_uint32(label->StartBlock); unser_uint32(label->EndBlock); unser_uint32(label->StartFile); unser_uint32(label->EndFile); unser_uint32(label->JobErrors); if (label->VerNum >= 11) { unser_uint32(label->JobStatus); } else { label->JobStatus = JS_Terminated; /* kludge */ } } return true; }
/* * BAREOS's implementation of fgets(). The difference is that it handles * being interrupted by a signal (e.g. a SIGCHLD) and it has a * different calling sequence which implements input lines of * up to a million characters. */ char *bfgets(POOLMEM *&s, FILE *fd) { int ch; int soft_max; int i = 0; s[0] = 0; soft_max = sizeof_pool_memory(s) - 10; for ( ;; ) { do { errno = 0; ch = fgetc(fd); } while (ch == EOF && ferror(fd) && (errno == EINTR || errno == EAGAIN)); if (ch == EOF) { if (i == 0) { return NULL; } else { return s; } } if (i > soft_max) { /* Insanity check */ if (soft_max > 1000000) { return s; } s = check_pool_memory_size(s, soft_max+10000); soft_max = sizeof_pool_memory(s) - 10; } s[i++] = ch; s[i] = 0; if (ch == '\r') { /* Support for Mac/Windows file format */ ch = fgetc(fd); if (ch != '\n') { /* Mac (\r only) */ (void)ungetc(ch, fd); /* Push next character back to fd */ } s[i-1] = '\n'; break; } if (ch == '\n') { break; } } return s; }
/** * Update NDMP level mapping * * Returns: false on failure * true on success */ bool db_update_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesystem, int level) { bool retval; char ed1[50], ed2[50], ed3[50]; db_lock(mdb); mdb->esc_name = check_pool_memory_size(mdb->esc_name, strlen(filesystem) * 2 + 1); db_escape_string(jcr, mdb, mdb->esc_name, filesystem, strlen(filesystem)); Mmsg(mdb->cmd, "UPDATE NDMPLevelMap SET DumpLevel='%s' WHERE " "ClientId='%s' AND FileSetId='%s' AND FileSystem='%s'", edit_uint64(level, ed1), edit_uint64(jr->ClientId, ed2), edit_uint64(jr->FileSetId, ed3), mdb->esc_name); retval = UPDATE_DB(jcr, mdb, mdb->cmd); db_unlock(mdb); return retval; }
/* * Open the bootstrap file and find the first Storage= * Returns ok if able to open * * It fills the storage name (should be the first line) * and the file descriptor to the bootstrap file, * it should be used for next operations, and need to be * closed at the end. */ bool open_bootstrap_file(JCR *jcr, bootstrap_info &info) { FILE *bs; UAContext *ua; info.bs = NULL; info.ua = NULL; if (!jcr->RestoreBootstrap) { return false; } bstrncpy(info.storage, jcr->res.rstore->name(), MAX_NAME_LENGTH); bs = fopen(jcr->RestoreBootstrap, "rb"); if (!bs) { berrno be; Jmsg(jcr, M_FATAL, 0, _("Could not open bootstrap file %s: ERR=%s\n"), jcr->RestoreBootstrap, be.bstrerror()); jcr->setJobStatus(JS_ErrorTerminated); return false; } ua = new_ua_context(jcr); ua->cmd = check_pool_memory_size(ua->cmd, UA_CMD_SIZE + 1); while (!fgets(ua->cmd, UA_CMD_SIZE, bs)) { parse_ua_args(ua); if (ua->argc != 1) { continue; } if (bstrcasecmp(ua->argk[0], "Storage")) { bstrncpy(info.storage, ua->argv[0], MAX_NAME_LENGTH); break; } } info.bs = bs; info.ua = ua; fseek(bs, 0, SEEK_SET); /* return to the top of the file */ return true; }
static bool decompress_with_zlib(JCR *jcr, const char *last_fname, char **data, uint32_t *length, bool sparse, bool with_header, bool want_data_stream) { char ec1[50]; /* Buffer printing huge values */ uLong compress_len; const unsigned char *cbuf; char *wbuf; int status, real_compress_len; /* * NOTE! We only use uLong and Byte because they are * needed by the zlib routines, they should not otherwise * be used in Bareos. */ if (sparse && want_data_stream) { wbuf = jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; compress_len = jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; } else { wbuf = jcr->compress.inflate_buffer; compress_len = jcr->compress.inflate_buffer_size; } /* * See if this is a compressed stream with the new compression header or an old one. */ if (with_header) { cbuf = (const unsigned char*)*data + sizeof(comp_stream_header); real_compress_len = *length - sizeof(comp_stream_header); } else { cbuf = (const unsigned char*)*data; real_compress_len = *length; } Dmsg2(400, "Comp_len=%d msglen=%d\n", compress_len, *length); while ((status = uncompress((Byte *)wbuf, &compress_len, (const Byte *)cbuf, (uLong)real_compress_len)) == Z_BUF_ERROR) { /* * The buffer size is too small, try with a bigger one */ jcr->compress.inflate_buffer_size = jcr->compress.inflate_buffer_size + (jcr->compress.inflate_buffer_size >> 1); jcr->compress.inflate_buffer = check_pool_memory_size(jcr->compress.inflate_buffer, jcr->compress.inflate_buffer_size); if (sparse && want_data_stream) { wbuf = jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; compress_len = jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; } else { wbuf = jcr->compress.inflate_buffer; compress_len = jcr->compress.inflate_buffer_size; } Dmsg2(400, "Comp_len=%d msglen=%d\n", compress_len, *length); } if (status != Z_OK) { Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"), last_fname, zlib_strerror(status)); return false; } /* * We return a decompressed data stream with the fileoffset encoded when this was a sparse stream. */ if (sparse && want_data_stream) { memcpy(jcr->compress.inflate_buffer, *data, OFFSET_FADDR_SIZE); } *data = jcr->compress.inflate_buffer; *length = compress_len; Dmsg2(400, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1)); return true; }
/* * Note, we receive the whole attribute record, but we select out only the stat * packet, VolSessionId, VolSessionTime, FileIndex, file type, and file name to * store in the catalog. */ static void update_attribute(JCR *jcr, char *msg, int32_t msglen) { unser_declare; uint32_t VolSessionId, VolSessionTime; int32_t Stream; uint32_t FileIndex; char *p; int len; char *fname, *attr; ATTR_DBR *ar = NULL; uint32_t reclen; /* * Start transaction allocates jcr->attr and jcr->ar if needed */ db_start_transaction(jcr, jcr->db); /* start transaction if not already open */ ar = jcr->ar; /* * Start by scanning directly in the message buffer to get Stream * there may be a cached attr so we cannot yet write into * jcr->attr or jcr->ar */ p = msg; skip_nonspaces(&p); /* UpdCat */ skip_spaces(&p); skip_nonspaces(&p); /* Job=nnn */ skip_spaces(&p); skip_nonspaces(&p); /* "FileAttributes" */ p += 1; /* * The following "SD header" fields are serialized */ unser_begin(p, 0); unser_uint32(VolSessionId); /* VolSessionId */ unser_uint32(VolSessionTime); /* VolSessionTime */ unser_int32(FileIndex); /* FileIndex */ unser_int32(Stream); /* Stream */ unser_uint32(reclen); /* Record length */ p += unser_length(p); /* Raw record follows */ /** * At this point p points to the raw record, which varies according * to what kind of a record (Stream) was sent. Note, the integer * fields at the beginning of these "raw" records are in ASCII with * spaces between them so one can use scanf or manual scanning to * extract the fields. * * File Attributes * File_index * File type * Filename (full path) * Encoded attributes * Link name (if type==FT_LNK or FT_LNKSAVED) * Encoded extended-attributes (for Win32) * Delta sequence number (32 bit int) * * Restore Object * File_index * File_type * Object_index * Object_len (possibly compressed) * Object_full_len (not compressed) * Object_compression * Plugin_name * Object_name * Binary Object data */ Dmsg1(400, "UpdCat msg=%s\n", msg); Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d reclen=%d\n", VolSessionId, VolSessionTime, FileIndex, Stream, reclen); jcr->SDJobBytes += reclen; /* update number of bytes transferred for quotas */ /* * Depending on the stream we are handling dispatch. */ switch (Stream) { case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: if (jcr->cached_attribute) { Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname); if (!db_create_attributes_record(jcr, jcr->db, ar)) { Jmsg1(jcr, M_FATAL, 0, _("Attribute create error: ERR=%s"), db_strerror(jcr->db)); } jcr->cached_attribute = false; } /* * Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */ jcr->attr = check_pool_memory_size(jcr->attr, msglen); memcpy(jcr->attr, msg, msglen); p = jcr->attr - msg + p; /* point p into jcr->attr */ skip_nonspaces(&p); /* skip FileIndex */ skip_spaces(&p); ar->FileType = str_to_int32(p); skip_nonspaces(&p); /* skip FileType */ skip_spaces(&p); fname = p; len = strlen(fname); /* length before attributes */ attr = &fname[len+1]; ar->DeltaSeq = 0; if (ar->FileType == FT_REG) { p = attr + strlen(attr) + 1; /* point to link */ p = p + strlen(p) + 1; /* point to extended attributes */ p = p + strlen(p) + 1; /* point to delta sequence */ /* * Older FDs don't have a delta sequence, so check if it is there */ if (p - jcr->attr < msglen) { ar->DeltaSeq = str_to_int32(p); /* delta_seq */ } } Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname); Dmsg1(400, "dird<stored: attr=%s\n", attr); ar->attr = attr; ar->fname = fname; if (ar->FileType == FT_DELETED) { ar->FileIndex = 0; /* special value */ } else { ar->FileIndex = FileIndex; } ar->Stream = Stream; ar->link = NULL; if (jcr->mig_jcr) { ar->JobId = jcr->mig_jcr->JobId; } else { ar->JobId = jcr->JobId; } ar->Digest = NULL; ar->DigestType = CRYPTO_DIGEST_NONE; jcr->cached_attribute = true; Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname); Dmsg1(400, "dird<filed: attr=%s\n", attr); break; case STREAM_RESTORE_OBJECT: { ROBJECT_DBR ro; memset(&ro, 0, sizeof(ro)); ro.Stream = Stream; ro.FileIndex = FileIndex; if (jcr->mig_jcr) { ro.JobId = jcr->mig_jcr->JobId; } else { ro.JobId = jcr->JobId; } Dmsg1(100, "Robj=%s\n", p); skip_nonspaces(&p); /* skip FileIndex */ skip_spaces(&p); ro.FileType = str_to_int32(p); /* FileType */ skip_nonspaces(&p); skip_spaces(&p); ro.object_index = str_to_int32(p); /* Object Index */ skip_nonspaces(&p); skip_spaces(&p); ro.object_len = str_to_int32(p); /* object length possibly compressed */ skip_nonspaces(&p); skip_spaces(&p); ro.object_full_len = str_to_int32(p); /* uncompressed object length */ skip_nonspaces(&p); skip_spaces(&p); ro.object_compression = str_to_int32(p); /* compression */ skip_nonspaces(&p); skip_spaces(&p); ro.plugin_name = p; /* point to plugin name */ len = strlen(ro.plugin_name); ro.object_name = &ro.plugin_name[len+1]; /* point to object name */ len = strlen(ro.object_name); ro.object = &ro.object_name[len+1]; /* point to object */ ro.object[ro.object_len] = 0; /* add zero for those who attempt printing */ Dmsg7(100, "oname=%s stream=%d FT=%d FI=%d JobId=%d, obj_len=%d\nobj=\"%s\"\n", ro.object_name, ro.Stream, ro.FileType, ro.FileIndex, ro.JobId, ro.object_len, ro.object); /* * Store it. */ if (!db_create_restore_object_record(jcr, jcr->db, &ro)) { Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db)); } break; } default: if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) { fname = p; if (ar->FileIndex != FileIndex) { Jmsg3(jcr, M_WARNING, 0, _("%s not same File=%d as attributes=%d\n"), stream_to_ascii(Stream), FileIndex, ar->FileIndex); } else { /* * Update digest in catalog */ char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)]; int len = 0; int type = CRYPTO_DIGEST_NONE; switch(Stream) { case STREAM_MD5_DIGEST: len = CRYPTO_DIGEST_MD5_SIZE; type = CRYPTO_DIGEST_MD5; break; case STREAM_SHA1_DIGEST: len = CRYPTO_DIGEST_SHA1_SIZE; type = CRYPTO_DIGEST_SHA1; break; case STREAM_SHA256_DIGEST: len = CRYPTO_DIGEST_SHA256_SIZE; type = CRYPTO_DIGEST_SHA256; break; case STREAM_SHA512_DIGEST: len = CRYPTO_DIGEST_SHA512_SIZE; type = CRYPTO_DIGEST_SHA512; break; default: /* * Never reached ... */ Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"), Stream); } bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true); Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf), digestbuf, Stream); if (jcr->cached_attribute) { ar->Digest = digestbuf; ar->DigestType = type; Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname); /* * Update BaseFile table */ if (!db_create_attributes_record(jcr, jcr->db, ar)) { Jmsg1(jcr, M_FATAL, 0, _("attribute create error. %s"), db_strerror(jcr->db)); } jcr->cached_attribute = false; } else { if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) { Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"), db_strerror(jcr->db)); } } } } break; } }
bool B_ACCURATE_LMDB::update_payload(JCR *jcr, char *fname, accurate_payload *payload) { int result, total_length, lstat_length, chksum_length; MDB_val key, data; bool retval = false; accurate_payload *new_payload; lstat_length = strlen(payload->lstat); chksum_length = strlen(payload->chksum); total_length = sizeof(accurate_payload) + lstat_length + chksum_length + 2; /* * Make sure m_pay_load is large enough. */ m_pay_load = check_pool_memory_size(m_pay_load, total_length); /* * We store the total pay load as: * * accurate_payload structure\0lstat\0chksum\0 */ new_payload = (accurate_payload *)m_pay_load; new_payload->lstat = (char *)payload + sizeof(accurate_payload); memcpy(new_payload->lstat, payload->lstat, lstat_length); new_payload->lstat[lstat_length] = '\0'; new_payload->chksum = (char *)new_payload->lstat + lstat_length + 1; if (chksum_length) { memcpy(new_payload->chksum, payload->chksum, chksum_length); } new_payload->chksum[chksum_length] = '\0'; new_payload->delta_seq = payload->delta_seq; new_payload->filenr = payload->filenr; key.mv_data = fname; key.mv_size = strlen(fname) + 1; data.mv_data = new_payload; data.mv_size = total_length; retry: result = mdb_put(m_db_rw_txn, m_db_dbi, &key, &data, 0); switch (result) { case 0: result = mdb_txn_commit(m_db_rw_txn); if (result == 0) { result = mdb_txn_begin(m_db_env, NULL, 0, &m_db_rw_txn); if (result != 0) { Jmsg1(jcr, M_FATAL, 0, _("Unable to create write transaction: %s\n"), mdb_strerror(result)); } else { retval = true; } } else { Jmsg1(jcr, M_FATAL, 0, _("Unable close write transaction: %s\n"), mdb_strerror(result)); } break; case MDB_TXN_FULL: /* * Seems we filled the transaction. * Flush the current transaction start a new one and retry the put. */ result = mdb_txn_commit(m_db_rw_txn); if (result == 0) { result = mdb_txn_begin(m_db_env, NULL, 0, &m_db_rw_txn); if (result == 0) { goto retry; } } /* * Fallthrough wanted. */ default: Jmsg1(jcr, M_FATAL, 0, _("Unable insert new data: %s\n"), mdb_strerror(result)); break; } return retval; }
/* * 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); /* * Sanity check. */ ASSERT(director->password.encoding == p_encoding_md5); /* * Challenge the director */ auth_success = cram_md5_challenge(bs, director->password.value, 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.value, &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 B_ACCURATE_LMDB::add_file(JCR *jcr, char *fname, int fname_length, char *lstat, int lstat_length, char *chksum, int chksum_length, int32_t delta_seq) { accurate_payload *payload; int result; int total_length; MDB_val key, data; bool retval = false; total_length = sizeof(accurate_payload) + lstat_length + chksum_length + 2; /* * Make sure m_pay_load is large enough. */ m_pay_load = check_pool_memory_size(m_pay_load, total_length); /* * We store the total pay load as: * * accurate_payload structure\0lstat\0chksum\0 */ payload = (accurate_payload *)m_pay_load; payload->lstat = (char *)payload + sizeof(accurate_payload); memcpy(payload->lstat, lstat, lstat_length); payload->lstat[lstat_length] = '\0'; payload->chksum = (char *)payload->lstat + lstat_length + 1; if (chksum_length) { memcpy(payload->chksum, chksum, chksum_length); } payload->chksum[chksum_length] = '\0'; payload->delta_seq = delta_seq; payload->filenr = m_filenr++; key.mv_data = fname; key.mv_size = strlen(fname) + 1; data.mv_data = payload; data.mv_size = total_length; retry: result = mdb_put(m_db_rw_txn, m_db_dbi, &key, &data, MDB_NOOVERWRITE); switch (result) { case 0: if (chksum) { Dmsg4(dbglvl, "add fname=<%s> lstat=%s delta_seq=%i chksum=%s\n", fname, lstat, delta_seq, chksum); } else { Dmsg2(dbglvl, "add fname=<%s> lstat=%s\n", fname, lstat); } retval = true; break; case MDB_TXN_FULL: /* * Seems we filled the transaction. * Flush the current transaction start a new one and retry the put. */ result = mdb_txn_commit(m_db_rw_txn); if (result == 0) { result = mdb_txn_begin(m_db_env, NULL, 0, &m_db_rw_txn); if (result == 0) { goto retry; } } /* * Fallthrough wanted. */ default: Jmsg1(jcr, M_FATAL, 0, _("Unable insert new data: %s\n"), mdb_strerror(result)); break; } return retval; }
/** * 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; bool plugin_started = false; bool do_plugin_set = false; int status, data_stream; int rtnstat = 0; b_save_ctx bsctx; bool has_file_data = false; struct save_pkt sp; /* use by option plugin */ BSOCK *sd = jcr->store_bsock; if (jcr->is_canceled() || jcr->is_incomplete()) { 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_RESTORE_FIRST: Dmsg1(100, "FT_RESTORE_FIRST saving: %s\n", ff_pkt->fname); break; case FT_PLUGIN_CONFIG: Dmsg1(100, "FT_PLUGIN_CONFIG saving: %s\n", ff_pkt->fname); 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 it.\n"), ff_pkt->fname, ff_pkt->top_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_JUNCTION: 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; } case FT_DELETED: Dmsg1(130, "FT_DELETED: %s\n", ff_pkt->fname); break; 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, "filed: sending %s to stored\n", ff_pkt->fname); /* * Setup backup signing context. */ memset(&bsctx, 0, sizeof(b_save_ctx)); bsctx.digest_stream = STREAM_NONE; bsctx.jcr = jcr; bsctx.ff_pkt = ff_pkt; /* * Digests and encryption are only useful if there's file data */ if (has_file_data) { if (!setup_encryption_digests(bsctx)) { goto good_rtn; } } /* * Initialize the file descriptor we use for data and other streams. */ binit(&ff_pkt->bfd); if (bit_is_set(FO_PORTABLE, ff_pkt->flags)) { set_portable_backup(&ff_pkt->bfd); /* disable Win32 BackupRead() */ } /* * Option and cmd plugin are not compatible together */ if (ff_pkt->cmd_plugin) { do_plugin_set = true; } else if (ff_pkt->opt_plugin) { /* * Ask the option plugin what to do with this file */ switch (plugin_option_handle_file(jcr, ff_pkt, &sp)) { case bRC_OK: Dmsg2(10, "Option plugin %s will be used to backup %s\n", ff_pkt->plugin, ff_pkt->fname); jcr->opt_plugin = true; jcr->plugin_sp = &sp; plugin_update_ff_pkt(ff_pkt, &sp); do_plugin_set = true; break; case bRC_Skip: Dmsg2(10, "Option plugin %s decided to skip %s\n", ff_pkt->plugin, ff_pkt->fname); goto good_rtn; case bRC_Core: Dmsg2(10, "Option plugin %s decided to let bareos handle %s\n", ff_pkt->plugin, ff_pkt->fname); break; default: goto bail_out; } } if (do_plugin_set) { /* * Tell bfile that it needs to call plugin */ if (!set_cmd_plugin(&ff_pkt->bfd, jcr)) { goto bail_out; } send_plugin_name(jcr, sd, true); /* signal start of plugin data */ plugin_started = true; } /* * Send attributes -- must be done after binit() */ if (!encode_and_send_attributes(jcr, ff_pkt, data_stream)) { goto bail_out; } /* * Meta data only for restore object */ if (IS_FT_OBJECT(ff_pkt->type)) { goto good_rtn; } /* * Meta data only for deleted files */ if (ff_pkt->type == FT_DELETED) { goto good_rtn; } /* * 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; } } /* * For a command plugin use the setting from the plugins savepkt no_read field * which is saved in the ff_pkt->no_read variable. do_read is the inverted * value of this variable as no_read == TRUE means do_read == FALSE */ if (ff_pkt->cmd_plugin) { do_read = !ff_pkt->no_read; } else { /* * 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 || ff_pkt->type == FT_JUNCTION || (!is_portable_backup(&ff_pkt->bfd) && ff_pkt->type == FT_DIREND)) { do_read = true; } } Dmsg2(150, "type=%d do_read=%d\n", ff_pkt->type, do_read); if (do_read) { btimer_t *tid; int noatime; if (ff_pkt->type == FT_FIFO) { tid = start_thread_timer(jcr, pthread_self(), 60); } else { tid = NULL; } noatime = bit_is_set(FO_NOATIME, ff_pkt->flags) ? O_NOATIME : 0; ff_pkt->bfd.reparse_point = (ff_pkt->type == FT_REPARSE || ff_pkt->type == FT_JUNCTION); if (bopen(&ff_pkt->bfd, ff_pkt->fname, O_RDONLY | O_BINARY | noatime, 0, ff_pkt->statp.st_rdev) < 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; } status = send_data(jcr, data_stream, ff_pkt, bsctx.digest, bsctx.signing_digest); if (bit_is_set(FO_CHKCHANGES, ff_pkt->flags)) { has_file_changed(jcr, ff_pkt); } bclose(&ff_pkt->bfd); if (!status) { goto bail_out; } } if (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) && bit_is_set(FO_HFSPLUS, ff_pkt->flags))) { if (!save_rsrc_and_finder(bsctx)) { goto bail_out; } } } /* * Save ACLs when requested and available for anything not being a symlink. */ if (have_acl) { if (bit_is_set(FO_ACL, ff_pkt->flags) && ff_pkt->type != FT_LNK) { if (!do_backup_acl(jcr, ff_pkt)) { goto bail_out; } } } /* * Save Extended Attributes when requested and available for all files. */ if (have_xattr) { if (bit_is_set(FO_XATTR, ff_pkt->flags)) { if (!do_backup_xattr(jcr, ff_pkt)) { goto bail_out; } } } /* * Terminate the signing digest and send it to the Storage daemon */ if (bsctx.signing_digest) { if (!terminate_signing_digest(bsctx)) { goto bail_out; } } /* * Terminate any digest and send it to Storage daemon */ if (bsctx.digest) { if (!terminate_digest(bsctx)) { goto bail_out; } } /* * Check if original file has a digest, and send it */ if (ff_pkt->type == FT_LNKSAVED && ff_pkt->digest) { Dmsg2(300, "Link %s digest %d\n", ff_pkt->fname, ff_pkt->digest_len); sd->fsend("%ld %d 0", jcr->JobFiles, ff_pkt->digest_stream); sd->msg = check_pool_memory_size(sd->msg, ff_pkt->digest_len); memcpy(sd->msg, ff_pkt->digest, ff_pkt->digest_len); sd->msglen = ff_pkt->digest_len; sd->send(); sd->signal(BNET_EOD); /* end of hardlink record */ } good_rtn: rtnstat = jcr->is_canceled() ? 0 : 1; /* good return if not canceled */ bail_out: if (jcr->is_incomplete() || jcr->is_canceled()) { rtnstat = 0; } if (plugin_started) { send_plugin_name(jcr, sd, false); /* signal end of plugin data */ } if (ff_pkt->opt_plugin) { jcr->plugin_sp = NULL; /* sp is local to this function */ jcr->opt_plugin = false; } if (bsctx.digest) { crypto_digest_free(bsctx.digest); } if (bsctx.signing_digest) { crypto_digest_free(bsctx.signing_digest); } return rtnstat; }
bool encode_and_send_attributes(JCR *jcr, FF_PKT *ff_pkt, int &data_stream) { BSOCK *sd = jcr->store_bsock; POOL_MEM attribs(PM_NAME), attribsExBuf(PM_NAME); char *attribsEx = NULL; int attr_stream; int comp_len; bool status; int hangup = get_hangup(); #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, me->compatible)) == 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.c_str(), &ff_pkt->statp, sizeof(ff_pkt->statp), ff_pkt->LinkFI, data_stream); /** Now possibly extend the attributes */ if (IS_FT_OBJECT(ff_pkt->type)) { attr_stream = STREAM_RESTORE_OBJECT; } else { attribsEx = attribsExBuf.c_str(); attr_stream = encode_attribsEx(jcr, attribsEx, ff_pkt); } Dmsg3(300, "File %s\nattribs=%s\nattribsEx=%s\n", ff_pkt->fname, attribs.c_str(), 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(); /* * Debug code: check if we must hangup */ if (hangup && (jcr->JobFiles > (uint32_t)hangup)) { jcr->setJobStatus(JS_Incomplete); Jmsg1(jcr, M_FATAL, 0, "Debug hangup requested after %d files.\n", hangup); set_hangup(0); return false; } /** * Send Attributes header to Storage daemon * <file-index> <stream> <info> */ if (!sd->fsend("%ld %d 0", jcr->JobFiles, attr_stream)) { if (!jcr->is_canceled() && !jcr->is_incomplete()) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); } return false; } Dmsg1(300, ">stored: attrhdr %s", 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) * Delta Sequence Number * * or send Restore Object to Storage daemon * File_index * File_type * Object_index * Object_len (possibly compressed) * Object_full_len (not compressed) * Object_compression * Plugin_name * Object_name * Binary Object data * * For a directory, link is the same as fname, but with trailing * slash. For a linked file, link is the link. */ if (!IS_FT_OBJECT(ff_pkt->type) && ff_pkt->type != FT_DELETED) { /* already stripped */ strip_path(ff_pkt); } switch (ff_pkt->type) { case FT_JUNCTION: case FT_LNK: case FT_LNKSAVED: Dmsg3(300, "Link %d %s to %s\n", jcr->JobFiles, ff_pkt->fname, ff_pkt->link); status = sd->fsend("%ld %d %s%c%s%c%s%c%s%c%u%c", jcr->JobFiles, ff_pkt->type, ff_pkt->fname, 0, attribs.c_str(), 0, ff_pkt->link, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_DIREND: case FT_REPARSE: /* Here link is the canonical filename (i.e. with trailing slash) */ status = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, ff_pkt->type, ff_pkt->link, 0, attribs.c_str(), 0, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; case FT_PLUGIN_CONFIG: case FT_RESTORE_FIRST: comp_len = ff_pkt->object_len; ff_pkt->object_compression = 0; if (ff_pkt->object_len > 1000) { /* * Big object, compress it */ comp_len = compressBound(ff_pkt->object_len); POOLMEM *comp_obj = get_memory(comp_len); /* * FIXME: check Zdeflate error */ Zdeflate(ff_pkt->object, ff_pkt->object_len, comp_obj, comp_len); if (comp_len < ff_pkt->object_len) { ff_pkt->object = comp_obj; ff_pkt->object_compression = 1; /* zlib level 9 compression */ } else { /* * Uncompressed object smaller, use it */ comp_len = ff_pkt->object_len; } Dmsg2(100, "Object compressed from %d to %d bytes\n", ff_pkt->object_len, comp_len); } sd->msglen = Mmsg(sd->msg, "%d %d %d %d %d %d %s%c%s%c", jcr->JobFiles, ff_pkt->type, ff_pkt->object_index, comp_len, ff_pkt->object_len, ff_pkt->object_compression, ff_pkt->fname, 0, ff_pkt->object_name, 0); sd->msg = check_pool_memory_size(sd->msg, sd->msglen + comp_len + 2); memcpy(sd->msg + sd->msglen, ff_pkt->object, comp_len); /* * Note we send one extra byte so Dir can store zero after object */ sd->msglen += comp_len + 1; status = sd->send(); if (ff_pkt->object_compression) { free_and_null_pool_memory(ff_pkt->object); } break; case FT_REG: status = sd->fsend("%ld %d %s%c%s%c%c%s%c%d%c", jcr->JobFiles, ff_pkt->type, ff_pkt->fname, 0, attribs.c_str(), 0, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; default: status = sd->fsend("%ld %d %s%c%s%c%c%s%c%u%c", jcr->JobFiles, ff_pkt->type, ff_pkt->fname, 0, attribs.c_str(), 0, 0, attribsEx, 0, ff_pkt->delta_seq, 0); break; } if (!IS_FT_OBJECT(ff_pkt->type) && ff_pkt->type != FT_DELETED) { unstrip_path(ff_pkt); } Dmsg2(300, ">stored: attr len=%d: %s\n", sd->msglen, sd->msg); if (!status && !jcr->is_job_canceled()) { Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); } sd->signal(BNET_EOD); /* indicate end of attributes data */ return status; }
static bool decompress_with_fastlz(JCR *jcr, const char *last_fname, char **data, uint32_t *length, uint32_t comp_magic, bool sparse, bool want_data_stream) { int zstat; zfast_stream stream; zfast_stream_compressor compressor = COMPRESSOR_FASTLZ; char ec1[50]; /* Buffer printing huge values */ switch (comp_magic) { case COMPRESS_FZ4L: case COMPRESS_FZ4H: compressor = COMPRESSOR_LZ4; break; } /* * NOTE! We only use uInt and Bytef because they are * needed by the fastlz routines, they should not otherwise * be used in Bareos. */ memset(&stream, 0, sizeof(stream)); stream.next_in = (Bytef *)*data + sizeof(comp_stream_header); stream.avail_in = (uInt)*length - sizeof(comp_stream_header); if (sparse && want_data_stream) { stream.next_out = (Bytef *)jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; stream.avail_out = (uInt)jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; } else { stream.next_out = (Bytef *)jcr->compress.inflate_buffer; stream.avail_out = (uInt)jcr->compress.inflate_buffer_size; } Dmsg2(400, "Comp_len=%d msglen=%d\n", stream.avail_in, *length); if ((zstat = fastlzlibDecompressInit(&stream)) != Z_OK) { goto cleanup; } if ((zstat = fastlzlibSetCompressor(&stream, compressor)) != Z_OK) { goto cleanup; } while (1) { zstat = fastlzlibDecompress(&stream); switch (zstat) { case Z_BUF_ERROR: /* * The buffer size is too small, try with a bigger one */ jcr->compress.inflate_buffer_size = jcr->compress.inflate_buffer_size + (jcr->compress.inflate_buffer_size >> 1); jcr->compress.inflate_buffer = check_pool_memory_size(jcr->compress.inflate_buffer, jcr->compress.inflate_buffer_size); if (sparse && want_data_stream) { stream.next_out = (Bytef *)jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; stream.avail_out = (uInt)jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; } else { stream.next_out = (Bytef *)jcr->compress.inflate_buffer; stream.avail_out = (uInt)jcr->compress.inflate_buffer_size; } continue; case Z_OK: case Z_STREAM_END: break; default: goto cleanup; } break; } /* * We return a decompressed data stream with the fileoffset encoded when this was a sparse stream. */ if (sparse && want_data_stream) { memcpy(jcr->compress.inflate_buffer, *data, OFFSET_FADDR_SIZE); } *data = jcr->compress.inflate_buffer; *length = stream.total_out; Dmsg2(400, "Write uncompressed %d bytes, total before write=%s\n", *length, edit_uint64(jcr->JobBytes, ec1)); fastlzlibDecompressEnd(&stream); return true; cleanup: Qmsg(jcr, M_ERROR, 0, _("Uncompression error on file %s. ERR=%s\n"), last_fname, zlib_strerror(zstat)); fastlzlibDecompressEnd(&stream); return false; }
static bool decompress_with_lzo(JCR *jcr, const char *last_fname, char **data, uint32_t *length, bool sparse, bool want_data_stream) { char ec1[50]; /* Buffer printing huge values */ lzo_uint compress_len; const unsigned char *cbuf; unsigned char *wbuf; int status, real_compress_len; if (sparse && want_data_stream) { compress_len = jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; cbuf = (const unsigned char *)*data + OFFSET_FADDR_SIZE + sizeof(comp_stream_header); wbuf = (unsigned char *)jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; } else { compress_len = jcr->compress.inflate_buffer_size; cbuf = (const unsigned char *)*data + sizeof(comp_stream_header); wbuf = (unsigned char *)jcr->compress.inflate_buffer; } real_compress_len = *length - sizeof(comp_stream_header); Dmsg2(400, "Comp_len=%d msglen=%d\n", compress_len, *length); while ((status = lzo1x_decompress_safe(cbuf, real_compress_len, wbuf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN) { /* * The buffer size is too small, try with a bigger one */ jcr->compress.inflate_buffer_size = jcr->compress.inflate_buffer_size + (jcr->compress.inflate_buffer_size >> 1); jcr->compress.inflate_buffer = check_pool_memory_size(jcr->compress.inflate_buffer, jcr->compress.inflate_buffer_size); if (sparse && want_data_stream) { compress_len = jcr->compress.inflate_buffer_size - OFFSET_FADDR_SIZE; wbuf = (unsigned char *)jcr->compress.inflate_buffer + OFFSET_FADDR_SIZE; } else { compress_len = jcr->compress.inflate_buffer_size; wbuf = (unsigned char *)jcr->compress.inflate_buffer; } Dmsg2(400, "Comp_len=%d msglen=%d\n", compress_len, *length); } if (status != LZO_E_OK) { Qmsg(jcr, M_ERROR, 0, _("LZO uncompression error on file %s. ERR=%d\n"), last_fname, status); return false; } /* * We return a decompressed data stream with the fileoffset encoded when this was a sparse stream. */ if (sparse && want_data_stream) { memcpy(jcr->compress.inflate_buffer, *data, OFFSET_FADDR_SIZE); } *data = jcr->compress.inflate_buffer; *length = compress_len; Dmsg2(400, "Write uncompressed %d bytes, total before write=%s\n", compress_len, edit_uint64(jcr->JobBytes, ec1)); return true; }
/* * Read the header record */ static bool read_header(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec) { ser_declare; uint32_t VolSessionId; uint32_t VolSessionTime; int32_t FileIndex; int32_t Stream; uint32_t rhl; char buf1[100], buf2[100]; Dmsg0(dbgep, "=== rpath 1 read_header\n"); /* Clear state flags */ rec->state_bits = 0; if (block->dev->is_tape()) { rec->state_bits |= REC_ISTAPE; } rec->Block = ((DEVICE *)block->dev)->EndBlock; rec->File = ((DEVICE *)block->dev)->EndFile; /* * Get the header. There is always a full header, * otherwise we find it in the next block. */ Dmsg3(read_dbglvl, "Block=%d Ver=%d block_len=%u\n", block->BlockNumber, block->BlockVer, block->block_len); if (block->BlockVer == 1) { rhl = RECHDR1_LENGTH; } else { rhl = RECHDR2_LENGTH; } if (rec->remlen >= rhl) { Dmsg0(dbgep, "=== rpath 2 begin unserial header\n"); Dmsg4(read_dbglvl, "read_header: remlen=%d data_len=%d rem=%d blkver=%d\n", rec->remlen, rec->data_len, rec->remainder, block->BlockVer); unser_begin(block->bufp, WRITE_RECHDR_LENGTH); if (block->BlockVer == 1) { unser_uint32(VolSessionId); unser_uint32(VolSessionTime); } else { VolSessionId = block->VolSessionId; VolSessionTime = block->VolSessionTime; } unser_int32(FileIndex); unser_int32(Stream); unser_uint32(rec->data_bytes); block->bufp += rhl; block->binbuf -= rhl; rec->remlen -= rhl; /* If we are looking for more (remainder!=0), we reject anything * where the VolSessionId and VolSessionTime don't agree */ if (rec->remainder && (rec->VolSessionId != VolSessionId || rec->VolSessionTime != VolSessionTime)) { rec->state_bits |= REC_NO_MATCH; Dmsg0(read_dbglvl, "remainder and VolSession doesn't match\n"); Dmsg0(dbgep, "=== rpath 4 VolSession no match\n"); return false; /* This is from some other Session */ } /* if Stream is negative, it means that this is a continuation * of a previous partially written record. */ if (Stream < 0) { /* continuation record? */ Dmsg0(dbgep, "=== rpath 5 negative stream\n"); Dmsg1(read_dbglvl, "Got negative Stream => continuation. remainder=%d\n", rec->remainder); rec->state_bits |= REC_CONTINUATION; if (!rec->remainder) { /* if we didn't read previously */ Dmsg0(dbgep, "=== rpath 6 no remainder\n"); rec->data_len = 0; /* return data as if no continuation */ } else if (rec->Stream != -Stream) { Dmsg0(dbgep, "=== rpath 7 wrong cont stream\n"); rec->state_bits |= REC_NO_MATCH; return false; /* This is from some other Session */ } rec->Stream = -Stream; /* set correct Stream */ rec->maskedStream = rec->Stream & STREAMMASK_TYPE; } else { /* Regular record */ Dmsg0(dbgep, "=== rpath 8 normal stream\n"); rec->Stream = Stream; rec->maskedStream = rec->Stream & STREAMMASK_TYPE; rec->data_len = 0; /* transfer to beginning of data */ } rec->VolSessionId = VolSessionId; rec->VolSessionTime = VolSessionTime; rec->FileIndex = FileIndex; if (FileIndex > 0) { Dmsg0(dbgep, "=== rpath 9 FileIndex>0\n"); if (block->FirstIndex == 0) { Dmsg0(dbgep, "=== rpath 10 FirstIndex\n"); block->FirstIndex = FileIndex; } block->LastIndex = rec->FileIndex; } Dmsg6(read_dbglvl, "read_header: FI=%s SessId=%d Strm=%s len=%u rec->remlen=%d data_len=%d\n", FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen, rec->data_len); } else { Dmsg0(dbgep, "=== rpath 11a block out of records\n"); /* * No more records in this block because the number * of remaining bytes are less than a record header * length, so return empty handed, but indicate that * he must read again. By returning, we allow the * higher level routine to fetch the next block and * then reread. */ Dmsg0(read_dbglvl, "read_header: End of block\n"); rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY); empty_block(block); /* mark block empty */ return false; } /* Sanity check */ if (rec->data_bytes >= MAX_BLOCK_LENGTH) { Dmsg0(dbgep, "=== rpath 11b maxlen too big\n"); /* * Something is wrong, force read of next block, abort * continuing with this block. */ rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY); empty_block(block); Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"), MAX_BLOCK_LENGTH, rec->data_bytes); return false; } rec->data = check_pool_memory_size(rec->data, rec->data_len+rec->data_bytes); rec->rstate = st_data; return true; }