+}
+
+static void unser_crypto_packet_len(JCR *jcr)
+{
+ unser_declare;
+ if (jcr->crypto_packet_len == 0 && jcr->crypto_buf_len >= CRYPTO_LEN_SIZE) {
+ unser_begin(&jcr->crypto_buf[0], CRYPTO_LEN_SIZE);
+ unser_uint32(jcr->crypto_packet_len);
+ jcr->crypto_packet_len += CRYPTO_LEN_SIZE;
+ }
+}
+
+bool store_data(JCR *jcr, BFILE *bfd, char *data, const int32_t length, bool win32_decomp)
+{
+ if (win32_decomp) {
+ if (!processWin32BackupAPIBlock(bfd, data, length)) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error in Win32 Block Decomposition on %s: %s\n"),
+ jcr->last_fname, be.strerror(bfd->berrno));
+ return false;
+ }
+ } else if (bwrite(bfd, data, length) != (ssize_t)length) {
+ berrno be;
+ Jmsg2(jcr, M_ERROR, 0, _("Write error on %s: %s\n"),
+ jcr->last_fname, be.strerror(bfd->berrno));
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * In the context of jcr, write data to bfd.
+ * We write buflen bytes in buf at addr. addr is updated in place.
+ * The flags specify whether to use sparse files or compression.
+ * Return value is the number of bytes written, or -1 on errors.
+ */
+int32_t extract_data(JCR *jcr, BFILE *bfd, POOLMEM *buf, int32_t buflen,
+ uint64_t *addr, int flags, CIPHER_CONTEXT *cipher, uint32_t cipher_block_size)
+{
+ char *wbuf; /* write buffer */
+ uint32_t wsize; /* write size */
+ uint32_t rsize; /* read size */
+ uint32_t decrypted_len = 0; /* Decryption output length */
+ char ec1[50]; /* Buffer printing huge values */
+
+ rsize = buflen;
+ jcr->ReadBytes += rsize;
+ wsize = rsize;
+ wbuf = buf;
+
+ if (flags & FO_ENCRYPT) {
+ ASSERT(cipher);
+
+ /* NOTE: We must implement block preserving semantics for the
+ * non-streaming compression and sparse code. */
+
+ /*
+ * Grow the crypto buffer, if necessary.
+ * crypto_cipher_update() will process only whole blocks,
+ * buffering the remaining input.
+ */
+ jcr->crypto_buf = check_pool_memory_size(jcr->crypto_buf,
+ jcr->crypto_buf_len + wsize + cipher_block_size);
+
+ /* Decrypt the input block */
+ if (!crypto_cipher_update(cipher,
+ (const u_int8_t *)wbuf,
+ wsize,
+ (u_int8_t *)&jcr->crypto_buf[jcr->crypto_buf_len],
+ &decrypted_len)) {
+ /* Decryption failed. Shouldn't happen. */
+ Jmsg(jcr, M_FATAL, 0, _("Decryption error\n"));
+ return -1;
+ }
+
+ if (decrypted_len == 0) {
+ /* No full block of encrypted data available, write more data */
+ return 0;
+ }
+
+ Dmsg2(100, "decrypted len=%d encrypted len=%d\n", decrypted_len, wsize);
+
+ jcr->crypto_buf_len += decrypted_len;
+ wbuf = jcr->crypto_buf;
+
+ /* If one full preserved block is available, write it to disk,
+ * and then buffer any remaining data. This should be effecient
+ * as long as Bacula's block size is not significantly smaller than the
+ * encryption block size (extremely unlikely!) */
+ unser_crypto_packet_len(jcr);
+ Dmsg1(500, "Crypto unser block size=%d\n", jcr->crypto_packet_len - CRYPTO_LEN_SIZE);
+
+ if (jcr->crypto_packet_len == 0 || jcr->crypto_buf_len < jcr->crypto_packet_len) {
+ /* No full preserved block is available. */
+ return 0;
+ }
+
+ /* We have one full block, set up the filter input buffers */
+ wsize = jcr->crypto_packet_len - CRYPTO_LEN_SIZE;
+ wbuf = &wbuf[CRYPTO_LEN_SIZE]; /* Skip the block length header */
+ jcr->crypto_buf_len -= jcr->crypto_packet_len;
+ Dmsg2(30, "Encryption writing full block, %u bytes, remaining %u bytes in buffer\n", wsize, jcr->crypto_buf_len);
+ }
+
+ if (flags & FO_SPARSE) {
+ if (!sparse_data(jcr, bfd, addr, &wbuf, &wsize)) {
+ return -1;
+ }
+ }
+
+ if (flags & FO_GZIP) {
+ if (!decompress_data(jcr, &wbuf, &wsize)) {
+ return -1;
+ }
+ } else {
+ Dmsg2(30, "Write %u bytes, total before write=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
+ }
+
+ if (!store_data(jcr, bfd, wbuf, wsize, (flags & FO_WIN32DECOMP) != 0)) {
+ return -1;
+ }
+
+ jcr->JobBytes += wsize;
+ *addr += wsize;
+
+ /* Clean up crypto buffers */
+ if (flags & FO_ENCRYPT) {
+ /* Move any remaining data to start of buffer */
+ if (jcr->crypto_buf_len > 0) {
+ Dmsg1(30, "Moving %u buffered bytes to start of buffer\n", jcr->crypto_buf_len);
+ memmove(jcr->crypto_buf, &jcr->crypto_buf[jcr->crypto_packet_len],
+ jcr->crypto_buf_len);
+ }
+ /* The packet was successfully written, reset the length so that the next
+ * packet length may be re-read by unser_crypto_packet_len() */
+ jcr->crypto_packet_len = 0;
+ }
+
+ return wsize;
+}
+
+/*
+ * In the context of jcr, flush any remaining data from the cipher context,
+ * writing it to bfd.
+ * Return value is true on success, false on failure.
+ */
+bool flush_cipher(JCR *jcr, BFILE *bfd, uint64_t *addr, int flags, CIPHER_CONTEXT *cipher,
+ uint32_t cipher_block_size)
+{
+ uint32_t decrypted_len;
+ char *wbuf; /* write buffer */
+ uint32_t wsize; /* write size */
+ char ec1[50]; /* Buffer printing huge values */
+
+ /* Write out the remaining block and free the cipher context */
+ jcr->crypto_buf = check_pool_memory_size(jcr->crypto_buf, jcr->crypto_buf_len +
+ cipher_block_size);
+
+ if (!crypto_cipher_finalize(cipher, (uint8_t *)&jcr->crypto_buf[jcr->crypto_buf_len],
+ &decrypted_len)) {
+ /* Writing out the final, buffered block failed. Shouldn't happen. */
+ Jmsg1(jcr, M_FATAL, 0, _("Decryption error for %s\n"), jcr->last_fname);
+ }
+
+ /* If nothing new was decrypted, and our output buffer is empty, return */
+ if (decrypted_len == 0 && jcr->crypto_buf_len == 0) {
+ return true;
+ }
+
+ jcr->crypto_buf_len += decrypted_len;
+
+ unser_crypto_packet_len(jcr);
+ Dmsg1(500, "Crypto unser block size=%d\n", jcr->crypto_packet_len - CRYPTO_LEN_SIZE);
+ wsize = jcr->crypto_packet_len - CRYPTO_LEN_SIZE;
+ wbuf = &jcr->crypto_buf[CRYPTO_LEN_SIZE]; /* Decrypted, possibly decompressed output here. */
+
+ if (jcr->crypto_buf_len != jcr->crypto_packet_len) {
+ Jmsg2(jcr, M_FATAL, 0,
+ _("Unexpected number of bytes remaining at end of file, received %u, expected %u\n"),
+ jcr->crypto_packet_len, jcr->crypto_buf_len);
+ return false;
+ }
+
+ jcr->crypto_buf_len = 0;
+ jcr->crypto_packet_len = 0;
+
+ if (flags & FO_SPARSE) {
+ if (!sparse_data(jcr, bfd, addr, &wbuf, &wsize)) {
+ return false;
+ }
+ }
+
+ if (flags & FO_GZIP) {
+ decompress_data(jcr, &wbuf, &wsize);
+ } else {
+ Dmsg2(30, "Write %u bytes, total before write=%s\n", wsize, edit_uint64(jcr->JobBytes, ec1));
+ }
+
+ if (!store_data(jcr, bfd, wbuf, wsize, (flags & FO_WIN32DECOMP) != 0)) {
+ return false;
+ }
+
+ jcr->JobBytes += wsize;
+
+ return true;
+}