Esempio n. 1
0
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;
}
Esempio n. 2
0
File: askdir.c Progetto: AlD/bareos
/**
 * 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();
}
Esempio n. 3
0
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;
   }
}
Esempio n. 4
0
/*
 * 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);
}
Esempio n. 5
0
/*
 *  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);
}
Esempio n. 6
0
/*  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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
/*
 * 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;
}
Esempio n. 10
0
/*
 * 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 */
   }
}
Esempio n. 11
0
/*
 * 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;
}
Esempio n. 12
0
/*
 * 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);
	}

}
Esempio n. 13
0
/*
 * 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;
}
Esempio n. 14
0
/*
 * 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;
}