static inline uint32_t serialize_acl_stream(POOL_MEM *buf, uint32_t expected_serialize_len, uint32_t offset, const char *acl_name, uint32_t acl_name_length, char *xattr_value, uint32_t xattr_value_length) { ser_declare; uint32_t content_length; char *buffer; /* * Make sure the serialized stream fits in the poolmem buffer. * We allocate some more to be sure the stream is gonna fit. */ buf->check_size(offset + expected_serialize_len + 10); buffer = buf->c_str() + offset; ser_begin(buffer, expected_serialize_len + 10); /* * Encode the ACL name including the \0 */ ser_uint32(acl_name_length + 1); ser_bytes(acl_name, acl_name_length + 1); /* * Encode the actual ACL data as stored as XATTR. */ ser_uint32(xattr_value_length); ser_bytes(xattr_value, xattr_value_length); ser_end(buffer, expected_serialize_len + 10); content_length = ser_length(buffer); return offset + content_length; }
/** * 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(); }
static inline void write_continue_header_to_block(DEV_BLOCK *block, DEV_RECORD *rec) { ser_declare; rec->remlen = block->buf_len - block->binbuf; /* * We have unwritten bytes from a previous * time. Presumably we have a new buffer (possibly * containing a volume label), so the new header * should be able to fit in the block -- otherwise we have * an error. Note, we have to continue splitting the * data record if it is longer than the block. * * First, write the header. * * Every time we write a header and it is a continuation * of a previous partially written record, we store the * Stream as -Stream in the record header. */ ser_begin(block->bufp, WRITE_RECHDR_LENGTH); if (BLOCK_VER == 1) { ser_uint32(rec->VolSessionId); ser_uint32(rec->VolSessionTime); } else { block->VolSessionId = rec->VolSessionId; block->VolSessionTime = rec->VolSessionTime; } ser_int32(rec->FileIndex); if (rec->remainder > rec->data_len) { ser_int32(rec->Stream); /* normal full header */ ser_uint32(rec->data_len); rec->remainder = rec->data_len; /* must still do data record */ } else { ser_int32(-rec->Stream); /* mark this as a continuation record */ ser_uint32(rec->remainder); /* bytes to do */ } /* * Require enough room to write a full header */ ASSERT(rec->remlen >= WRITE_RECHDR_LENGTH); block->bufp += WRITE_RECHDR_LENGTH; block->binbuf += WRITE_RECHDR_LENGTH; rec->remlen -= WRITE_RECHDR_LENGTH; if (rec->FileIndex > 0) { /* * If data record, update what we have in this block */ if (block->FirstIndex == 0) { block->FirstIndex = rec->FileIndex; } block->LastIndex = rec->FileIndex; } }
/* * 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); }
/* * 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); }
/* 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; }
static inline bool write_header_to_block(DEV_BLOCK *block, DEV_RECORD *rec) { ser_declare; rec->remlen = block->buf_len - block->binbuf; /* * Require enough room to write a full header */ if (rec->remlen >= WRITE_RECHDR_LENGTH) { ser_begin(block->bufp, WRITE_RECHDR_LENGTH); if (BLOCK_VER == 1) { ser_uint32(rec->VolSessionId); ser_uint32(rec->VolSessionTime); } else { block->VolSessionId = rec->VolSessionId; block->VolSessionTime = rec->VolSessionTime; } ser_int32(rec->FileIndex); ser_int32(rec->Stream); ser_uint32(rec->data_len); block->bufp += WRITE_RECHDR_LENGTH; block->binbuf += WRITE_RECHDR_LENGTH; rec->remlen -= WRITE_RECHDR_LENGTH; rec->remainder = rec->data_len; if (rec->FileIndex > 0) { /* * If data record, update what we have in this block */ if (block->FirstIndex == 0) { block->FirstIndex = rec->FileIndex; } block->LastIndex = rec->FileIndex; } } else { rec->remainder = rec->data_len + WRITE_RECHDR_LENGTH; return false; } return true; }
static inline ssize_t write_header_to_block(DEV_BLOCK *block, const DEV_RECORD *rec, int32_t Stream) { ser_declare; /* * Require enough room to write a full header */ if (block_write_navail(block) < WRITE_RECHDR_LENGTH) return -1; ser_begin(block->bufp, WRITE_RECHDR_LENGTH); if (BLOCK_VER == 1) { ser_uint32(rec->VolSessionId); ser_uint32(rec->VolSessionTime); } else { block->VolSessionId = rec->VolSessionId; block->VolSessionTime = rec->VolSessionTime; } ser_int32(rec->FileIndex); ser_int32(Stream); ser_uint32(rec->remainder); /* each header tracks remaining user bytes to write */ block->bufp += WRITE_RECHDR_LENGTH; block->binbuf += WRITE_RECHDR_LENGTH; if (rec->FileIndex > 0) { /* * If data record, update what we have in this block */ if (block->FirstIndex == 0) { block->FirstIndex = rec->FileIndex; } block->LastIndex = rec->FileIndex; } return WRITE_RECHDR_LENGTH; }
/* * Handle the data just read and send it to the SD after doing any postprocessing needed. */ static inline bool send_data_to_sd(b_ctx *bctx) { BSOCK *sd = bctx->jcr->store_bsock; bool need_more_data; /* * Check for sparse blocks */ if (bit_is_set(FO_SPARSE, bctx->ff_pkt->flags)) { bool allZeros; ser_declare; allZeros = false; if ((sd->msglen == bctx->rsize && (bctx->fileAddr + sd->msglen < (uint64_t)bctx->ff_pkt->statp.st_size)) || ((bctx->ff_pkt->type == FT_RAW || bctx->ff_pkt->type == FT_FIFO) && ((uint64_t)bctx->ff_pkt->statp.st_size == 0))) { allZeros = is_buf_zero(bctx->rbuf, bctx->rsize); } if (!allZeros) { /* * Put file address as first data in buffer */ ser_begin(bctx->wbuf, OFFSET_FADDR_SIZE); ser_uint64(bctx->fileAddr); /* store fileAddr in begin of buffer */ } bctx->fileAddr += sd->msglen; /* update file address */ /* * Skip block of all zeros */ if (allZeros) { return true; } } else if (bit_is_set(FO_OFFSETS, bctx->ff_pkt->flags)) { ser_declare; ser_begin(bctx->wbuf, OFFSET_FADDR_SIZE); ser_uint64(bctx->ff_pkt->bfd.offset); /* store offset in begin of buffer */ } bctx->jcr->ReadBytes += sd->msglen; /* count bytes read */ /* * Uncompressed cipher input length */ bctx->cipher_input_len = sd->msglen; /* * Update checksum if requested */ if (bctx->digest) { crypto_digest_update(bctx->digest, (uint8_t *)bctx->rbuf, sd->msglen); } /* * Update signing digest if requested */ if (bctx->signing_digest) { crypto_digest_update(bctx->signing_digest, (uint8_t *)bctx->rbuf, sd->msglen); } /* * Compress the data. */ if (bit_is_set(FO_COMPRESS, bctx->ff_pkt->flags)) { if (!compress_data(bctx->jcr, bctx->ff_pkt->Compress_algo, bctx->rbuf, bctx->jcr->store_bsock->msglen, bctx->cbuf, bctx->max_compress_len, &bctx->compress_len)) { return false; } /* * See if we need to generate a compression header. */ if (bctx->chead) { ser_declare; /* * Complete header */ ser_begin(bctx->chead, sizeof(comp_stream_header)); ser_uint32(bctx->ch.magic); ser_uint32(bctx->compress_len); ser_uint16(bctx->ch.level); ser_uint16(bctx->ch.version); ser_end(bctx->chead, sizeof(comp_stream_header)); bctx->compress_len += sizeof(comp_stream_header); /* add size of header */ } bctx->jcr->store_bsock->msglen = bctx->compress_len; /* set compressed length */ bctx->cipher_input_len = bctx->compress_len; } /* * Encrypt the data. */ need_more_data = false; if (bit_is_set(FO_ENCRYPT, bctx->ff_pkt->flags) && !encrypt_data(bctx, &need_more_data)) { if (need_more_data) { return true; } return false; } /* * Send the buffer to the Storage daemon */ if (bit_is_set(FO_SPARSE, bctx->ff_pkt->flags) || bit_is_set(FO_OFFSETS, bctx->ff_pkt->flags)) { sd->msglen += OFFSET_FADDR_SIZE; /* include fileAddr in size */ } sd->msg = bctx->wbuf; /* set correct write buffer */ if (!sd->send()) { if (!bctx->jcr->is_job_canceled()) { Jmsg1(bctx->jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"), sd->bstrerror()); } return false; } Dmsg1(130, "Send data to SD len=%d\n", sd->msglen); bctx->jcr->JobBytes += sd->msglen; /* count bytes saved possibly compressed/encrypted */ sd->msg = bctx->msgsave; /* restore read buffer */ return true; }
/* * Write an ANSI or IBM 80 character tape label * Type determines whether we are writing HDR, EOF, or EOV labels * Assume we are positioned to write the labels * Returns: true of OK * false if error */ bool write_ansi_ibm_labels(DCR *dcr, int type, const char *VolName) { DEVICE *dev = dcr->dev; JCR *jcr = dcr->jcr; char ansi_volname[7]; /* 6 char + \0 */ char label[80]; /* tape label */ char date[20]; /* ansi date buffer */ time_t now; int len, stat, label_type; /* * If the Device requires a specific label type use it, * otherwise, use the type requested by the Director */ if (dcr->device->label_type != B_BACULA_LABEL) { label_type = dcr->device->label_type; /* force label type */ } else { label_type = dcr->VolCatInfo.LabelType; /* accept Dir type */ } switch (label_type) { case B_BACULA_LABEL: return true; case B_ANSI_LABEL: case B_IBM_LABEL: ser_declare; Dmsg1(100, "Write ANSI label type=%d\n", label_type); len = strlen(VolName); if (len > 6) { Jmsg1(jcr, M_FATAL, 0, _("ANSI Volume label name \"%s\" longer than 6 chars.\n"), VolName); return false; } /* ANSI labels have 6 characters, and are padded with spaces * 'vol1\0' => 'vol1 \0' */ strcpy(ansi_volname, VolName); for(int i=len; i < 6; i++) { ansi_volname[i]=' '; } ansi_volname[6]='\0'; /* only for debug */ if (type == ANSI_VOL_LABEL) { ser_begin(label, sizeof(label)); ser_bytes("VOL1", 4); ser_bytes(ansi_volname, 6); /* Write VOL1 label */ if (label_type == B_IBM_LABEL) { ascii_to_ebcdic(label, label, sizeof(label)); } else { label[79] = '3'; /* ANSI label flag */ } stat = dev->write(label, sizeof(label)); if (stat != sizeof(label)) { berrno be; Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI VOL1 label. ERR=%s\n"), be.bstrerror()); return false; } } /* Now construct HDR1 label */ memset(label, ' ', sizeof(label)); ser_begin(label, sizeof(label)); ser_bytes(labels[type], 3); ser_bytes("1", 1); ser_bytes("BACULA.DATA", 11); /* Filename field */ ser_begin(&label[21], sizeof(label)-21); /* fileset field */ ser_bytes(ansi_volname, 6); /* write Vol Ser No. */ ser_begin(&label[27], sizeof(label)-27); ser_bytes("00010001000100", 14); /* File section, File seq no, Generation no */ now = time(NULL); ser_bytes(ansi_date(now, date), 6); /* current date */ ser_bytes(ansi_date(now - 24 * 3600, date), 6); /* created yesterday */ ser_bytes(" 000000Bacula ", 27); /* Write HDR1 label */ if (label_type == B_IBM_LABEL) { ascii_to_ebcdic(label, label, sizeof(label)); } /* * This could come at the end of a tape, ignore * EOT errors. */ stat = dev->write(label, sizeof(label)); if (stat != sizeof(label)) { berrno be; if (stat == -1) { dev->clrerror(-1); if (dev->dev_errno == 0) { dev->dev_errno = ENOSPC; /* out of space */ } if (dev->dev_errno != ENOSPC) { Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), be.bstrerror()); return false; } } else { Jmsg(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label.\n")); return false; } } /* Now construct HDR2 label */ memset(label, ' ', sizeof(label)); ser_begin(label, sizeof(label)); ser_bytes(labels[type], 3); ser_bytes("2D3200032000", 12); /* Write HDR2 label */ if (label_type == B_IBM_LABEL) { label[4] = 'V'; ascii_to_ebcdic(label, label, sizeof(label)); } stat = dev->write(label, sizeof(label)); if (stat != sizeof(label)) { berrno be; if (stat == -1) { dev->clrerror(-1); if (dev->dev_errno == 0) { dev->dev_errno = ENOSPC; /* out of space */ } if (dev->dev_errno != ENOSPC) { Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"), be.bstrerror()); return false; } dev->weof(1); return true; } else { Jmsg(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label.\n")); return false; } } if (!dev->weof(1)) { Jmsg(jcr, M_FATAL, 0, _("Error writing EOF to tape. ERR=%s"), dev->errmsg); return false; } return true; default: Jmsg0(jcr, M_ABORT, 0, _("write_ansi_ibm_label called for non-ANSI/IBM type\n")); return false; /* should not get here */ } }
/* * Called here for each record from read_records() */ static bool record_cb(DCR *dcr, DEV_RECORD *rec) { int status; JCR *jcr = dcr->jcr; if (rec->FileIndex < 0) { return true; /* we don't want labels */ } /* File Attributes stream */ switch (rec->maskedStream) { case STREAM_UNIX_ATTRIBUTES: case STREAM_UNIX_ATTRIBUTES_EX: /* If extracting, it was from previous stream, so * close the output file. */ if (extract) { if (!is_bopen(&bfd)) { Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n")); } set_attributes(jcr, attr, &bfd); extract = false; } if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) { Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n")); } if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) { attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI); if (!is_restore_stream_supported(attr->data_stream)) { if (!non_support_data++) { Jmsg(jcr, M_ERROR, 0, _("%s stream not supported on this Client.\n"), stream_to_ascii(attr->data_stream)); } extract = false; return true; } build_attr_output_fnames(jcr, attr); if (attr->type == FT_DELETED) { /* TODO: choose the right fname/ofname */ Jmsg(jcr, M_INFO, 0, _("%s was deleted.\n"), attr->fname); extract = false; return true; } extract = false; status = create_file(jcr, attr, &bfd, REPLACE_ALWAYS); switch (status) { case CF_ERROR: case CF_SKIP: break; case CF_EXTRACT: extract = true; print_ls_output(jcr, attr); num_files++; fileAddr = 0; break; case CF_CREATED: set_attributes(jcr, attr, &bfd); print_ls_output(jcr, attr); num_files++; fileAddr = 0; break; } } break; case STREAM_RESTORE_OBJECT: /* nothing to do */ break; /* Data stream and extracting */ case STREAM_FILE_DATA: case STREAM_SPARSE_DATA: case STREAM_WIN32_DATA: if (extract) { if (rec->maskedStream == STREAM_SPARSE_DATA) { ser_declare; uint64_t faddr; wbuf = rec->data + OFFSET_FADDR_SIZE; wsize = rec->data_len - OFFSET_FADDR_SIZE; ser_begin(rec->data, OFFSET_FADDR_SIZE); unser_uint64(faddr); if (fileAddr != faddr) { fileAddr = faddr; if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) { berrno be; Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), attr->ofname, be.bstrerror()); } } } else { wbuf = rec->data; wsize = rec->data_len; } total += wsize; Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total); store_data(&bfd, wbuf, wsize); fileAddr += wsize; } break; /* GZIP data stream */ case STREAM_GZIP_DATA: case STREAM_SPARSE_GZIP_DATA: case STREAM_WIN32_GZIP_DATA: #ifdef HAVE_LIBZ if (extract) { uLong compress_len = compress_buf_size; int status = Z_BUF_ERROR; if (rec->maskedStream == STREAM_SPARSE_GZIP_DATA) { ser_declare; uint64_t faddr; char ec1[50]; wbuf = rec->data + OFFSET_FADDR_SIZE; wsize = rec->data_len - OFFSET_FADDR_SIZE; ser_begin(rec->data, OFFSET_FADDR_SIZE); unser_uint64(faddr); if (fileAddr != faddr) { fileAddr = faddr; if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) { berrno be; Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"), edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror()); extract = false; return true; } } } else { wbuf = rec->data; wsize = rec->data_len; } while (compress_len < 10000000 && (status = uncompress((Byte *)compress_buf, &compress_len, (const Byte *)wbuf, (uLong)wsize)) == Z_BUF_ERROR) { /* The buffer size is too small, try with a bigger one */ compress_len = 2 * compress_len; compress_buf = check_pool_memory_size(compress_buf, compress_len); } if (status != Z_OK) { Emsg1(M_ERROR, 0, _("Uncompression error. ERR=%d\n"), status); extract = false; return true; } Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total); store_data(&bfd, compress_buf, compress_len); total += compress_len; fileAddr += compress_len; Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len); } #else if (extract) { Emsg0(M_ERROR, 0, _("GZIP data stream found, but GZIP not configured!\n")); extract = false; return true; } #endif break; /* Compressed data stream */ case STREAM_COMPRESSED_DATA: case STREAM_SPARSE_COMPRESSED_DATA: case STREAM_WIN32_COMPRESSED_DATA: if (extract) { uint32_t comp_magic, comp_len; uint16_t comp_level, comp_version; #ifdef HAVE_LZO lzo_uint compress_len; const unsigned char *cbuf; int r, real_compress_len; #endif if (rec->maskedStream == STREAM_SPARSE_COMPRESSED_DATA) { ser_declare; uint64_t faddr; char ec1[50]; wbuf = rec->data + OFFSET_FADDR_SIZE; wsize = rec->data_len - OFFSET_FADDR_SIZE; ser_begin(rec->data, OFFSET_FADDR_SIZE); unser_uint64(faddr); if (fileAddr != faddr) { fileAddr = faddr; if (blseek(&bfd, (boffset_t)fileAddr, SEEK_SET) < 0) { berrno be; Emsg3(M_ERROR, 0, _("Seek to %s error on %s: ERR=%s\n"), edit_uint64(fileAddr, ec1), attr->ofname, be.bstrerror()); extract = false; return true; } } } else { wbuf = rec->data; wsize = rec->data_len; } /* read compress header */ unser_declare; unser_begin(wbuf, sizeof(comp_stream_header)); unser_uint32(comp_magic); unser_uint32(comp_len); unser_uint16(comp_level); unser_uint16(comp_version); Dmsg4(200, "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) { Emsg1(M_ERROR, 0, _("Compressed header version error. version=0x%x\n"), comp_version); return false; } /* size check */ if (comp_len + sizeof(comp_stream_header) != wsize) { Emsg2(M_ERROR, 0, _("Compressed header size error. comp_len=%d, msglen=%d\n"), comp_len, wsize); return false; } switch(comp_magic) { #ifdef HAVE_LZO case COMPRESS_LZO1X: compress_len = compress_buf_size; cbuf = (const unsigned char*) wbuf + sizeof(comp_stream_header); real_compress_len = wsize - sizeof(comp_stream_header); Dmsg2(200, "Comp_len=%d msglen=%d\n", compress_len, wsize); while ((r=lzo1x_decompress_safe(cbuf, real_compress_len, (unsigned char *)compress_buf, &compress_len, NULL)) == LZO_E_OUTPUT_OVERRUN) { /* The buffer size is too small, try with a bigger one */ compress_len = 2 * compress_len; compress_buf = check_pool_memory_size(compress_buf, compress_len); } if (r != LZO_E_OK) { Emsg1(M_ERROR, 0, _("LZO uncompression error. ERR=%d\n"), r); extract = false; return true; } Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total); store_data(&bfd, compress_buf, compress_len); total += compress_len; fileAddr += compress_len; Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len, compress_len); break; #endif default: Emsg1(M_ERROR, 0, _("Compression algorithm 0x%x found, but not supported!\n"), comp_magic); extract = false; return true; } } break; case STREAM_MD5_DIGEST: case STREAM_SHA1_DIGEST: case STREAM_SHA256_DIGEST: case STREAM_SHA512_DIGEST: break; case STREAM_SIGNED_DIGEST: case STREAM_ENCRYPTED_SESSION_DATA: // TODO landonf: Investigate crypto support in the storage daemon break; case STREAM_PROGRAM_NAMES: case STREAM_PROGRAM_DATA: if (!prog_name_msg) { Pmsg0(000, _("Got Program Name or Data Stream. Ignored.\n")); prog_name_msg++; } break; case STREAM_UNIX_ACCESS_ACL: /* Deprecated Standard ACL attributes on UNIX */ case STREAM_UNIX_DEFAULT_ACL: /* Deprecated Default ACL attributes on UNIX */ case STREAM_ACL_AIX_TEXT: case STREAM_ACL_DARWIN_ACCESS_ACL: case STREAM_ACL_FREEBSD_DEFAULT_ACL: case STREAM_ACL_FREEBSD_ACCESS_ACL: case STREAM_ACL_HPUX_ACL_ENTRY: case STREAM_ACL_IRIX_DEFAULT_ACL: case STREAM_ACL_IRIX_ACCESS_ACL: case STREAM_ACL_LINUX_DEFAULT_ACL: case STREAM_ACL_LINUX_ACCESS_ACL: case STREAM_ACL_TRU64_DEFAULT_ACL: case STREAM_ACL_TRU64_DEFAULT_DIR_ACL: case STREAM_ACL_TRU64_ACCESS_ACL: case STREAM_ACL_SOLARIS_ACLENT: case STREAM_ACL_SOLARIS_ACE: case STREAM_ACL_AFS_TEXT: case STREAM_ACL_AIX_AIXC: case STREAM_ACL_AIX_NFS4: case STREAM_ACL_FREEBSD_NFS4_ACL: case STREAM_ACL_HURD_DEFAULT_ACL: case STREAM_ACL_HURD_ACCESS_ACL: if (extract) { wbuf = rec->data; wsize = rec->data_len; pm_strcpy(acl_data.last_fname, attr->fname); parse_acl_streams(jcr, &acl_data, rec->maskedStream, wbuf, wsize); } break; case STREAM_XATTR_HURD: case STREAM_XATTR_IRIX: case STREAM_XATTR_TRU64: case STREAM_XATTR_AIX: case STREAM_XATTR_OPENBSD: case STREAM_XATTR_SOLARIS_SYS: case STREAM_XATTR_SOLARIS: case STREAM_XATTR_DARWIN: case STREAM_XATTR_FREEBSD: case STREAM_XATTR_LINUX: case STREAM_XATTR_NETBSD: if (extract) { wbuf = rec->data; wsize = rec->data_len; pm_strcpy(xattr_data.last_fname, attr->fname); parse_xattr_streams(jcr, &xattr_data, rec->maskedStream, wbuf, wsize); } break; case STREAM_NDMP_SEPERATOR: break; default: /* If extracting, weird stream (not 1 or 2), close output file anyway */ if (extract) { if (!is_bopen(&bfd)) { Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n")); } set_attributes(jcr, attr, &bfd); extract = false; } Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), rec->Stream); break; } /* end switch */ return true; }
/* * Called by Append phase */ void write_container(struct container* c) { assert(c->meta.chunk_num == g_hash_table_size(c->meta.map)); if (container_empty(c)) { /* An empty container * It possibly occurs in the end of backup */ container_count--; VERBOSE("Append phase: Deny writing an empty container %lld", c->meta.id); return; } VERBOSE("Append phase: Writing container %lld of %d chunks", c->meta.id, c->meta.chunk_num); if (destor.simulation_level < SIMULATION_APPEND) { unsigned char * cur = &c->data[CONTAINER_SIZE - CONTAINER_META_SIZE]; ser_declare; ser_begin(cur, CONTAINER_META_SIZE); ser_int64(c->meta.id); ser_int32(c->meta.chunk_num); ser_int32(c->meta.data_size); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, c->meta.map); while (g_hash_table_iter_next(&iter, &key, &value)) { struct metaEntry *me = (struct metaEntry *) value; ser_bytes(&me->fp, sizeof(fingerprint)); ser_bytes(&me->len, sizeof(int32_t)); ser_bytes(&me->off, sizeof(int32_t)); } ser_end(cur, CONTAINER_META_SIZE); pthread_mutex_lock(&mutex); if (fseek(fp, c->meta.id * CONTAINER_SIZE + 8, SEEK_SET) != 0) { perror("Fail seek in container store."); exit(1); } if(fwrite(c->data, CONTAINER_SIZE, 1, fp) != 1){ perror("Fail to write a container in container store."); exit(1); } pthread_mutex_unlock(&mutex); } else { char buf[CONTAINER_META_SIZE]; memset(buf, 0, CONTAINER_META_SIZE); ser_declare; ser_begin(buf, CONTAINER_META_SIZE); ser_int64(c->meta.id); ser_int32(c->meta.chunk_num); ser_int32(c->meta.data_size); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, c->meta.map); while (g_hash_table_iter_next(&iter, &key, &value)) { struct metaEntry *me = (struct metaEntry *) value; ser_bytes(&me->fp, sizeof(fingerprint)); ser_bytes(&me->len, sizeof(int32_t)); ser_bytes(&me->off, sizeof(int32_t)); } ser_end(buf, CONTAINER_META_SIZE); pthread_mutex_lock(&mutex); if(fseek(fp, c->meta.id * CONTAINER_META_SIZE + 8, SEEK_SET) != 0){ perror("Fail seek in container store."); exit(1); } if(fwrite(buf, CONTAINER_META_SIZE, 1, fp) != 1){ perror("Fail to write a container in container store."); exit(1); } pthread_mutex_unlock(&mutex); } }
/* * Perform automatic compression of certain stream types when enabled in the config. */ static bool auto_deflate_record(DCR *dcr) { ser_declare; comp_stream_header ch; DEV_RECORD *rec, *nrec; bool retval = false; bool intermediate_value = false; unsigned int max_compression_length = 0; unsigned char *data = NULL; /* * See what our starting point is. When dcr->after_rec is set we already have * a translated record by an other SD plugin. Then we use that translated record * as the starting point otherwise we start at dcr->before_rec. When an earlier * translation already happened we can free that record when we have a success * full translation here as that record is of no use anymore. */ if (dcr->after_rec) { rec = dcr->after_rec; intermediate_value = true; } else { rec = dcr->before_rec; } /* * We only do autocompression for the following stream types: * * - STREAM_FILE_DATA * - STREAM_WIN32_DATA * - STREAM_SPARSE_DATA */ switch (rec->maskedStream) { case STREAM_FILE_DATA: case STREAM_WIN32_DATA: case STREAM_SPARSE_DATA: break; default: goto bail_out; } /* * Clone the data from the original DEV_RECORD to the converted one. * As we use the compression buffers for the data we need a new * DEV_RECORD without a new memory buffer so we call new_record here * with the with_data boolean set explicitly to false. */ nrec = bfuncs->new_record(false); bfuncs->copy_record_state(nrec, rec); /* * Setup the converted DEV_RECORD to point with its data buffer to the compression buffer. */ nrec->data = dcr->jcr->compress.deflate_buffer; switch (rec->maskedStream) { case STREAM_FILE_DATA: case STREAM_WIN32_DATA: data = (unsigned char *)nrec->data + sizeof(comp_stream_header); max_compression_length = dcr->jcr->compress.deflate_buffer_size - sizeof(comp_stream_header); break; case STREAM_SPARSE_DATA: data = (unsigned char *)nrec->data + OFFSET_FADDR_SIZE + sizeof(comp_stream_header); max_compression_length = dcr->jcr->compress.deflate_buffer_size - OFFSET_FADDR_SIZE - sizeof(comp_stream_header); break; } /* * Compress the data using the configured compression algorithm. */ if (!compress_data(dcr->jcr, dcr->device->autodeflate_algorithm, rec->data, rec->data_len, data, max_compression_length, &nrec->data_len)) { bfuncs->free_record(nrec); goto bail_out; } /* * Map the streams. */ switch (rec->maskedStream) { case STREAM_FILE_DATA: nrec->Stream = STREAM_COMPRESSED_DATA; nrec->maskedStream = STREAM_COMPRESSED_DATA; break; case STREAM_WIN32_DATA: nrec->Stream = STREAM_WIN32_COMPRESSED_DATA; nrec->maskedStream = STREAM_WIN32_COMPRESSED_DATA; break; case STREAM_SPARSE_DATA: nrec->Stream = STREAM_SPARSE_COMPRESSED_DATA; nrec->maskedStream = STREAM_SPARSE_COMPRESSED_DATA; break; default: break; } /* * Generate a compression header. */ ch.magic = dcr->device->autodeflate_algorithm; ch.level = dcr->device->autodeflate_level; ch.version = COMP_HEAD_VERSION; ch.size = nrec->data_len; switch (nrec->maskedStream) { case STREAM_COMPRESSED_DATA: case STREAM_WIN32_COMPRESSED_DATA: ser_begin(nrec->data, sizeof(comp_stream_header)); ser_uint32(ch.magic); ser_uint32(ch.size); ser_uint16(ch.level); ser_uint16(ch.version); ser_end(nrec->data, sizeof(comp_stream_header)); nrec->data_len += sizeof(comp_stream_header); break; case STREAM_SPARSE_COMPRESSED_DATA: /* * Copy the sparse offset from the original. */ memcpy(nrec->data, rec->data, OFFSET_FADDR_SIZE); ser_begin(nrec->data + OFFSET_FADDR_SIZE, sizeof(comp_stream_header)); ser_uint32(ch.magic); ser_uint32(ch.size); ser_uint16(ch.level); ser_uint16(ch.version); ser_end(nrec->data + OFFSET_FADDR_SIZE, sizeof(comp_stream_header)); nrec->data_len += OFFSET_FADDR_SIZE + sizeof(comp_stream_header); break; } Dmsg4(400, "auto_deflate_record: From datastream %d to %d from original size %ld to %ld\n", rec->maskedStream, nrec->maskedStream, rec->data_len, nrec->data_len); /* * If the input is just an intermediate value free it now. */ if (intermediate_value) { bfuncs->free_record(dcr->after_rec); } dcr->after_rec = nrec; retval = true; bail_out: return retval; }
/* * 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; }