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; } }
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; }
/* * 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); }
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; }
/* * 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); }
/* * 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; }