Пример #1
7
/*
 *  Append Data sent from File daemon
 *
 */
bool do_append_data(JCR *jcr)
{
   int32_t n;
   int32_t file_index, stream, last_file_index;
   BSOCK *fd = jcr->file_bsock;
   bool ok = true;
   DEV_RECORD rec;
   char buf1[100], buf2[100];
   DCR *dcr = jcr->dcr;
   DEVICE *dev;
   char ec[50];


   if (!dcr) { 
      Jmsg0(jcr, M_FATAL, 0, _("DCR is NULL!!!\n"));
      return false;
   }                                              
   dev = dcr->dev;
   if (!dev) { 
      Jmsg0(jcr, M_FATAL, 0, _("DEVICE is NULL!!!\n"));
      return false;
   }                                              

   Dmsg1(100, "Start append data. res=%d\n", dev->num_reserved());

   memset(&rec, 0, sizeof(rec));

   if (!fd->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
      jcr->setJobStatus(JS_ErrorTerminated);
      Jmsg0(jcr, M_FATAL, 0, _("Unable to set network buffer size.\n"));
      return false;
   }

   if (!acquire_device_for_append(dcr)) {
      jcr->setJobStatus(JS_ErrorTerminated);
      return false;
   }

   jcr->setJobStatus(JS_Running);
   dir_send_job_status(jcr);

   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }
   Dmsg1(50, "Begin append device=%s\n", dev->print_name());

   begin_data_spool(dcr);
   begin_attribute_spool(jcr);

   Dmsg0(100, "Just after acquire_device_for_append\n");
   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }
   /*
    * Write Begin Session Record
    */
   if (!write_session_label(dcr, SOS_LABEL)) {
      Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
         dev->bstrerror());
      jcr->setJobStatus(JS_ErrorTerminated);
      ok = false;
   }
   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }

   /* Tell File daemon to send data */
   if (!fd->fsend(OK_data)) {
      berrno be;
      Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
            be.bstrerror(fd->b_errno));
      ok = false;
   }

   /*
    * Get Data from File daemon, write to device.  To clarify what is
    *   going on here.  We expect:
    *     - A stream header
    *     - Multiple records of data
    *     - EOD record
    *
    *    The Stream header is just used to sychronize things, and
    *    none of the stream header is written to tape.
    *    The Multiple records of data, contain first the Attributes,
    *    then after another stream header, the file data, then
    *    after another stream header, the MD5 data if any.
    *
    *   So we get the (stream header, data, EOD) three time for each
    *   file. 1. for the Attributes, 2. for the file data if any,
    *   and 3. for the MD5 if any.
    */
   dcr->VolFirstIndex = dcr->VolLastIndex = 0;
   jcr->run_time = time(NULL);              /* start counting time for rates */
   for (last_file_index = 0; ok && !jcr->is_job_canceled(); ) {

      /* Read Stream header from the File daemon.
       *  The stream header consists of the following:
       *    file_index (sequential Bacula file index, base 1)
       *    stream     (Bacula number to distinguish parts of data)
       *    info       (Info for Storage daemon -- compressed, encrypted, ...)
       *       info is not currently used, so is read, but ignored!
       */
     if ((n=bget_msg(fd)) <= 0) {
         if (n == BNET_SIGNAL && fd->msglen == BNET_EOD) {
            break;                    /* end of data */
         }
         Jmsg1(jcr, M_FATAL, 0, _("Error reading data header from FD. ERR=%s\n"),
               fd->bstrerror());
         possible_incomplete_job(jcr, last_file_index);
         ok = false;
         break;
      }

      if (sscanf(fd->msg, "%ld %ld", &file_index, &stream) != 2) {
         Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), fd->msg);
         ok = false;
         possible_incomplete_job(jcr, last_file_index);
         break;
      }

      Dmsg2(890, "<filed: Header FilInx=%d stream=%d\n", file_index, stream);

      /*
       * We make sure the file_index is advancing sequentially.
       * An incomplete job can start the file_index at any number.
       * otherwise, it must start at 1.
       */
      if (jcr->rerunning && file_index > 0 && last_file_index == 0) {
         goto fi_checked;
      }
      if (file_index > 0 && (file_index == last_file_index ||
          file_index == last_file_index + 1)) {
         goto fi_checked;
      }
      Jmsg2(jcr, M_FATAL, 0, _("FI=%d from FD not positive or sequential=%d\n"),
            file_index, last_file_index);
      possible_incomplete_job(jcr, last_file_index);
      ok = false;
      break;

fi_checked:
      if (file_index != last_file_index) {
         jcr->JobFiles = file_index;
         last_file_index = file_index;
      }

      /* Read data stream from the File daemon.
       *  The data stream is just raw bytes
       */
      while ((n=bget_msg(fd)) > 0 && !jcr->is_job_canceled()) {
         rec.VolSessionId = jcr->VolSessionId;
         rec.VolSessionTime = jcr->VolSessionTime;
         rec.FileIndex = file_index;
         rec.Stream = stream;
         rec.maskedStream = stream & STREAMMASK_TYPE;   /* strip high bits */
         rec.data_len = fd->msglen;
         rec.data = fd->msg;            /* use message buffer */

         Dmsg4(850, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
            rec.FileIndex, rec.VolSessionId, 
            stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
            rec.data_len);

         while (!write_record_to_block(dcr->block, &rec)) {
            Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
                       rec.remainder);
            if (!write_block_to_device(dcr)) {
               Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
                  dev->print_name(), dev->bstrerror());
               ok = false;
               break;
            }
         }
         if (!ok) {
            Dmsg0(400, "Not OK\n");
            break;
         }
         jcr->JobBytes += rec.data_len;   /* increment bytes this job */
         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);

         send_attrs_to_dir(jcr, &rec);
         Dmsg0(650, "Enter bnet_get\n");
      }
      Dmsg1(650, "End read loop with FD. Stat=%d\n", n);

      if (fd->is_error()) {
         if (!jcr->is_job_canceled()) {
            Dmsg1(350, "Network read error from FD. ERR=%s\n", fd->bstrerror());
            Jmsg1(jcr, M_FATAL, 0, _("Network error reading from FD. ERR=%s\n"),
                  fd->bstrerror());
            possible_incomplete_job(jcr, last_file_index);
         }
         ok = false;
         break;
      }
   }

   /* Create Job status for end of session label */
   jcr->setJobStatus(ok?JS_Terminated:JS_ErrorTerminated);

   if (ok) {
      /* Terminate connection with FD */
      fd->fsend(OK_append);
      do_fd_commands(jcr);               /* finish dialog with FD */
   } else {
      fd->fsend("3999 Failed append\n");
   }

   /*
    * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
    *   and the subsequent Jmsg() editing will break
    */
   int32_t job_elapsed = time(NULL) - jcr->run_time;

   if (job_elapsed <= 0) {
      job_elapsed = 1;
   }

   Jmsg(dcr->jcr, M_INFO, 0, _("Job write elapsed time = %02d:%02d:%02d, Transfer rate = %s Bytes/second\n"),
         job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
         edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));


   Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);

   /*
    * Check if we can still write. This may not be the case
    *  if we are at the end of the tape or we got a fatal I/O error.
    */
   if (ok || dev->can_write()) {
      if (!write_session_label(dcr, EOS_LABEL)) {
         /* Print only if ok and not cancelled to avoid spurious messages */
         if (ok && !jcr->is_job_canceled()) {
            Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
                  dev->bstrerror());
            possible_incomplete_job(jcr, last_file_index);
         }
         jcr->setJobStatus(JS_ErrorTerminated);
         ok = false;
      }
      if (dev->VolCatInfo.VolCatName[0] == 0) {
         Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
         Dmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
      }
      Dmsg0(90, "back from write_end_session_label()\n");
      /* Flush out final partial block of this session */
      if (!write_block_to_device(dcr)) {
         /* Print only if ok and not cancelled to avoid spurious messages */
         if (ok && !jcr->is_job_canceled()) {
            Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
                  dev->print_name(), dev->bstrerror());
            Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
            possible_incomplete_job(jcr, last_file_index);
         }
         jcr->setJobStatus(JS_ErrorTerminated);
         ok = false;
      }
   }


   if (!ok && !jcr->is_JobStatus(JS_Incomplete)) {
      discard_data_spool(dcr);
   } else {
      /* Note: if commit is OK, the device will remain blocked */
      commit_data_spool(dcr);
   }

   if (ok) {
      ok = dvd_close_job(dcr);  /* do DVD cleanup if any */
   }
   
   /*
    * Release the device -- and send final Vol info to DIR
    *  and unlock it.
    */
   release_device(dcr);

   if ((!ok || jcr->is_job_canceled()) && !jcr->is_JobStatus(JS_Incomplete)) {
      discard_attribute_spool(jcr);
   } else {
      commit_attribute_spool(jcr);
   }

   dir_send_job_status(jcr);          /* update director */

   Dmsg1(100, "return from do_append_data() ok=%d\n", ok);
   return ok;
}
Пример #2
0
static inline ssize_t write_data_to_block(DEV_BLOCK *block, const DEV_RECORD *rec)
{
   uint32_t len;

   len = MIN(rec->remainder, block_write_navail(block));
   memcpy(block->bufp,
          ((unsigned char *)rec->data) + (rec->data_len - rec->remainder),
          len);
   block->bufp += len;
   block->binbuf += len;

#ifdef xxxxxSMCHECK
   if (!sm_check_rtn(__FILE__, __LINE__, False)) {
      /*
       * We damaged a buffer
       */
      Dmsg7(0, "Damaged block FI=%s SessId=%d Strm=%s datalen=%d\n"
            "len=%d rem=%d remainder=%d\n",
            FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
            stream_to_ascii(buf2, rec->Stream, rec->FileIndex),
            rec->data_len, len, block_write_navail(block),
            rec->remainder);
      Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
            block->bufp, block->binbuf, block->buf_len,
            block_write_navail(block), len);
      Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
            block->buf, block->bufp-block->buf);
      Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
   }
#endif

   return len;
}
Пример #3
0
/*
 * Write a Record to the block
 *
 * Returns: false means the block could not be written to tape/disk.
 *          true on success (all bytes written to the block).
 */
bool DCR::write_record()
{
   bool retval = false;
   bool translated_record = false;
   char buf1[100], buf2[100];

   /*
    * Perform record translations.
    */
   before_rec = rec;
   after_rec = NULL;
   if (generate_plugin_event(jcr, bsdEventWriteRecordTranslation, this) != bRC_OK) {
      goto bail_out;
   }

   /*
    * The record got translated when we got an after_rec pointer after calling the
    * bsdEventWriteRecordTranslation plugin event. If no translation has taken place
    * we just point the after_rec pointer to same DEV_RECORD as in the before_rec pointer.
    */
   if (!after_rec) {
      after_rec = before_rec;
   } else {
      translated_record = true;
   }

   while (!write_record_to_block(this, after_rec)) {
      Dmsg2(850, "!write_record_to_block data_len=%d rem=%d\n",
            after_rec->data_len, after_rec->remainder);
      if (!write_block_to_device()) {
         Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
               dev->print_name(), dev->bstrerror());
         goto bail_out;
      }
   }

   jcr->JobBytes += after_rec->data_len;   /* increment bytes this job */
   if (jcr->RemainingQuota && jcr->JobBytes > jcr->RemainingQuota) {
      Jmsg0(jcr, M_FATAL, 0, _("Quota Exceeded. Job Terminated.\n"));
      goto bail_out;
   }

   Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
         FI_to_ascii(buf1, after_rec->FileIndex), after_rec->VolSessionId,
         stream_to_ascii(buf2, after_rec->Stream, after_rec->FileIndex), after_rec->data_len);

   retval = true;

bail_out:
   if (translated_record) {
      copy_record_state(before_rec, after_rec);
      free_record(after_rec);
      after_rec = NULL;
   }

   return retval;
}
Пример #4
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);
}
Пример #5
0
static const char *findex_to_str(int32_t index, char *buf, size_t bufsz)
{
   if (index >= 0) {
      bsnprintf(buf, bufsz, "<User> %d", index);
      return buf;
   }

   FI_to_ascii(buf, index);

   return buf;
}
Пример #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;
}
Пример #7
0
/*
 * Called here for each record from read_records()
 *
 * Returns: true if OK
 *          false if error
 */
static bool record_cb(DCR *dcr, DEV_RECORD *rec)
{
   JCR *jcr = dcr->jcr;
   BSOCK *fd = jcr->file_bsock;
   bool ok = true;
   POOLMEM *save_msg;
   char ec1[50], ec2[50];

   if (rec->FileIndex < 0) {
      return true;
   }

   Dmsg5(400, "Send to FD: SessId=%u SessTim=%u FI=%s Strm=%s, len=%d\n",
         rec->VolSessionId, rec->VolSessionTime,
         FI_to_ascii(ec1, rec->FileIndex),
         stream_to_ascii(ec2, rec->Stream, rec->FileIndex),
         rec->data_len);

   /*
    * Send record header to File daemon
    */
   if (!fd->fsend(rec_header, rec->VolSessionId, rec->VolSessionTime,
                  rec->FileIndex, rec->Stream, rec->data_len)) {
      Pmsg1(000, _(">filed: Error Hdr=%s"), fd->msg);
      Jmsg1(jcr, M_FATAL, 0, _("Error sending to File daemon. ERR=%s\n"),
            fd->bstrerror());
      return false;
   } else {
      Dmsg1(400, ">filed: Hdr=%s", fd->msg);
   }

   /*
    * Send data record to File daemon
    */
   save_msg = fd->msg;          /* save fd message pointer */
   fd->msg = rec->data;         /* pass data directly to the FD */
   fd->msglen = rec->data_len;

   Dmsg1(400, ">filed: send %d bytes data.\n", fd->msglen);
   if (!fd->send()) {
      Pmsg1(000, _("Error sending to FD. ERR=%s\n"), fd->bstrerror());
      Jmsg1(jcr, M_FATAL, 0, _("Error sending to File daemon. ERR=%s\n"),
         fd->bstrerror());

      ok = false;
   }
   fd->msg = save_msg;                /* restore fd message pointer */

   return ok;
}
Пример #8
0
/*
 * Called here for each record from read_records()
 */
static bool record_cb(DCR *dcr, DEV_RECORD *rec)
{
    if (verbose && rec->FileIndex < 0) {
        dump_label_record(dcr->dev, rec, verbose);
        return true;
    }
    if (verbose) {
        char buf1[100], buf2[100];
        Pmsg6(000, "Record: FI=%s SessId=%d Strm=%s len=%u remlen=%d data_len=%d\n",
              FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
              stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen,
              rec->data_len);
    }

    /* File Attributes stream */
    if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
            rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
        if (!unpack_attributes_record(jcr, rec->Stream, rec->data, rec->data_len, attr)) {
            if (!forge_on) {
                Emsg0(M_ERROR_TERM, 0, _("Cannot continue.\n"));
            } else {
                Emsg0(M_ERROR, 0, _("Attrib unpack error!\n"));
            }
            num_files++;
            return true;
        }

        attr->data_stream = decode_stat(attr->attr, &attr->statp, sizeof(attr->statp), &attr->LinkFI);
        build_attr_output_fnames(jcr, attr);

        if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
            if (verbose) {
                Pmsg5(000, _("FileIndex=%d VolSessionId=%d VolSessionTime=%d Stream=%d DataLen=%d\n"),
                      rec->FileIndex, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
            }
            print_ls_output(jcr, attr);
            num_files++;
        }
    } else if (rec->Stream == STREAM_PLUGIN_NAME) {
        char data[100];
        int len = MIN(rec->data_len+1, sizeof(data));
        bstrncpy(data, rec->data, len);
        Dmsg1(100, "Plugin data: %s\n", data);
    } else if (rec->Stream == STREAM_RESTORE_OBJECT) {
        Dmsg0(100, "Restore Object record\n");
    }

    return true;
}
Пример #9
0
/*
 * Read a Record from the block
 *  Returns: false if nothing read or if the continuation record does not match.
 *                 In both of these cases, a block read must be done.
 *           true  if at least the record header was read, this
 *                 routine may have to be called again with a new
 *                 block if the entire record was not read.
 */
bool read_record_from_block(DCR *dcr,  DEV_RECORD *rec)
{
    bool rtn;

    Dmsg0(dbgep, "=== rpath 1 Enter read_record_from block\n");
    for ( ;; ) {
        switch (rec->rstate) {
        case st_none:
            dump_block(dcr->block, "st_none");
        case st_header:
            Dmsg0(dbgep, "=== rpath 33 st_header\n");
            rec->remlen = dcr->block->binbuf;
            /* Note read_header sets rec->rstate on return true */
            if (!read_header(dcr, dcr->block, rec)) {  /* sets state */
                Dmsg0(dbgep, "=== rpath 34 failed read header\n");
                Dmsg0(read_dbglvl, "read_header returned EOF.\n");
                goto fail_out;
            }
            continue;

        case st_data:
            Dmsg0(dbgep, "=== rpath 37 st_data\n");
            read_data(dcr->block, rec);
            rec->rstate = st_header;         /* next pass look for a header */
            goto get_out;

        default:
            Dmsg0(dbgep, "=== rpath 50 default\n");
            Dmsg0(0, "======= In default !!!!!\n");
            Pmsg1(190, "Read: unknown state=%d\n", rec->rstate);
            goto fail_out;
        }
    }
get_out:
    char buf1[100], buf2[100];
    Dmsg5(read_dbglvl, "read_rec return: FI=%s Strm=%s len=%d rem=%d remainder=%d\n",
          FI_to_ascii(buf1, rec->FileIndex),
          stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
          rec->remlen, rec->remainder);
    rtn = true;
    goto out;
fail_out:
    rec->rstate = st_none;
    rtn = false;
out:
    return rtn;
}
Пример #10
0
static inline bool write_data_to_block(DEV_BLOCK *block, DEV_RECORD *rec)
{
   rec->remlen = block->buf_len - block->binbuf;

   /*
    * Write as much of data as possible
    */
   if (rec->remlen >= rec->remainder) {
      memcpy(block->bufp, rec->data + (rec->data_len - rec->remainder), rec->remainder);
      block->bufp += rec->remainder;
      block->binbuf += rec->remainder;
   } else {
      memcpy(block->bufp, rec->data + (rec->data_len - rec->remainder), rec->remlen);
#ifdef xxxxxSMCHECK
      if (!sm_check_rtn(__FILE__, __LINE__, False)) {
         /*
          * We damaged a buffer
          */
         Dmsg6(0, "Damaged block FI=%s SessId=%d Strm=%s len=%d\n"
            "rem=%d remainder=%d\n",
            FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
            stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
            rec->remlen, rec->remainder);
         Dmsg5(0, "Damaged block: bufp=%x binbuf=%d buf_len=%d rem=%d moved=%d\n",
            block->bufp, block->binbuf, block->buf_len, block->buf_len-block->binbuf,
            rec->remlen);
         Dmsg2(0, "Damaged block: buf=%x binbuffrombuf=%d \n",
            block->buf, block->bufp-block->buf);
         Emsg0(M_ABORT, 0, _("Damaged buffer\n"));
      }
#endif

      block->bufp += rec->remlen;
      block->binbuf += rec->remlen;
      rec->remainder -= rec->remlen;

      return false;                /* did partial transfer */
   }

   return true;
}
Пример #11
0
/*
 * We have just read a header, now read the data into the record.
 *  Note, if we do not read the full record, we will return to
 *  read the next header, which will then come back here later
 *  to finish reading the full record.
 */
static void read_data(DEV_BLOCK *block, DEV_RECORD *rec)
{
    char buf1[100], buf2[100];

    Dmsg0(dbgep, "=== rpath 22 read_data\n");
    /*
     * At this point, we have read the header, now we
     * must transfer as much of the data record as
     * possible taking into account: 1. A partial
     * data record may have previously been transferred,
     * 2. The current block may not contain the whole data
     * record.
     */
    if (rec->remlen >= rec->data_bytes) {
        Dmsg0(dbgep, "=== rpath 23 full record\n");
        /* Got whole record */
        memcpy(rec->data+rec->data_len, block->bufp, rec->data_bytes);
        block->bufp += rec->data_bytes;
        block->binbuf -= rec->data_bytes;
        rec->data_len += rec->data_bytes;
        rec->remainder = 0;
        Dmsg5(190, "Rdata full FI=%s SessId=%d Strm=%s len=%d block=%p\n",
              FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
              stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
              block);
    } else {
        Dmsg0(dbgep, "=== rpath 24 partial record\n");
        /* Partial record */
        memcpy(rec->data+rec->data_len, block->bufp, rec->remlen);
        block->bufp += rec->remlen;
        block->binbuf -= rec->remlen;
        rec->data_len += rec->remlen;
        rec->remainder = 1;             /* partial record transferred */
        Dmsg1(read_dbglvl, "read_data: partial xfered=%d\n", rec->data_len);
        rec->state_bits |= (REC_PARTIAL_RECORD | REC_BLOCK_EMPTY);
    }
}
Пример #12
0
Файл: bls.c Проект: AlD/bareos
/* List just block information */
static void do_blocks(char *infname)
{
   DEV_BLOCK *block = dcr->block;
   char buf1[100], buf2[100];
   for ( ;; ) {
      if (!dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK)) {
         Dmsg1(100, "!read_block(): ERR=%s\n", dev->bstrerror());
         if (dev->at_eot()) {
            if (!mount_next_read_volume(dcr)) {
               Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"),
                  dev->file, dev->print_name(), dcr->VolumeName);
               break;
            }
            /* Read and discard Volume label */
            DEV_RECORD *record;
            record = new_record();
            dcr->read_block_from_device(NO_BLOCK_NUMBER_CHECK);
            read_record_from_block(dcr, record);
            get_session_record(dev, record, &sessrec);
            free_record(record);
            Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), dcr->VolumeName);
         } else if (dev->at_eof()) {
            Jmsg(jcr, M_INFO, 0, _("End of file %u on device %s, Volume \"%s\"\n"),
               dev->file, dev->print_name(), dcr->VolumeName);
            Dmsg0(20, "read_record got eof. try again\n");
            continue;
         } else if (dev->is_short_block()) {
            Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
            continue;
         } else {
            /* I/O error */
            display_tape_error_status(jcr, dev);
            break;
         }
      }
      if (!match_bsr_block(bsr, block)) {
         Dmsg5(100, "reject Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
            block->BlockNumber, block->block_len, block->BlockVer,
            block->VolSessionId, block->VolSessionTime);
         continue;
      }
      Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
        block->BlockNumber, block->block_len, block->BlockVer,
        block->VolSessionId, block->VolSessionTime);
      if (verbose == 1) {
         read_record_from_block(dcr, rec);
         Pmsg9(-1, _("File:blk=%u:%u blk_num=%u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n"),
              dev->file, dev->block_num,
              block->BlockNumber, block->block_len,
              FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
              stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
         rec->remainder = 0;
      } else if (verbose > 1) {
         dump_block(block, "");
      } else {
         printf(_("Block: %d size=%d\n"), block->BlockNumber, block->block_len);
      }

   }
   return;
}
Пример #13
0
/*
 * Read the header record
 */
static bool read_header(DCR *dcr, DEV_BLOCK *block, DEV_RECORD *rec)
{
    ser_declare;
    uint32_t VolSessionId;
    uint32_t VolSessionTime;
    int32_t  FileIndex;
    int32_t  Stream;
    uint32_t rhl;
    char buf1[100], buf2[100];

    Dmsg0(dbgep, "=== rpath 1 read_header\n");
    /* Clear state flags */
    rec->state_bits = 0;
    if (block->dev->is_tape()) {
        rec->state_bits |= REC_ISTAPE;
    }
    rec->Block = ((DEVICE *)block->dev)->EndBlock;
    rec->File = ((DEVICE *)block->dev)->EndFile;

    /*
     * Get the header. There is always a full header,
     * otherwise we find it in the next block.
     */
    Dmsg3(read_dbglvl, "Block=%d Ver=%d block_len=%u\n",
          block->BlockNumber, block->BlockVer, block->block_len);
    if (block->BlockVer == 1) {
        rhl = RECHDR1_LENGTH;
    } else {
        rhl = RECHDR2_LENGTH;
    }
    if (rec->remlen >= rhl) {
        Dmsg0(dbgep, "=== rpath 2 begin unserial header\n");
        Dmsg4(read_dbglvl, "read_header: remlen=%d data_len=%d rem=%d blkver=%d\n",
              rec->remlen, rec->data_len, rec->remainder, block->BlockVer);

        unser_begin(block->bufp, WRITE_RECHDR_LENGTH);
        if (block->BlockVer == 1) {
            unser_uint32(VolSessionId);
            unser_uint32(VolSessionTime);
        } else {
            VolSessionId = block->VolSessionId;
            VolSessionTime = block->VolSessionTime;
        }
        unser_int32(FileIndex);
        unser_int32(Stream);
        unser_uint32(rec->data_bytes);

        block->bufp += rhl;
        block->binbuf -= rhl;
        rec->remlen -= rhl;

        /* If we are looking for more (remainder!=0), we reject anything
         *  where the VolSessionId and VolSessionTime don't agree
         */
        if (rec->remainder && (rec->VolSessionId != VolSessionId ||
                               rec->VolSessionTime != VolSessionTime)) {
            rec->state_bits |= REC_NO_MATCH;
            Dmsg0(read_dbglvl, "remainder and VolSession doesn't match\n");
            Dmsg0(dbgep, "=== rpath 4 VolSession no match\n");
            return false;             /* This is from some other Session */
        }

        /* if Stream is negative, it means that this is a continuation
         * of a previous partially written record.
         */
        if (Stream < 0) {               /* continuation record? */
            Dmsg0(dbgep, "=== rpath 5 negative stream\n");
            Dmsg1(read_dbglvl, "Got negative Stream => continuation. remainder=%d\n",
                  rec->remainder);
            rec->state_bits |= REC_CONTINUATION;
            if (!rec->remainder) {       /* if we didn't read previously */
                Dmsg0(dbgep, "=== rpath 6 no remainder\n");
                rec->data_len = 0;        /* return data as if no continuation */
            } else if (rec->Stream != -Stream) {
                Dmsg0(dbgep, "=== rpath 7 wrong cont stream\n");
                rec->state_bits |= REC_NO_MATCH;
                return false;             /* This is from some other Session */
            }
            rec->Stream = -Stream;       /* set correct Stream */
            rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
        } else {                        /* Regular record */
            Dmsg0(dbgep, "=== rpath 8 normal stream\n");
            rec->Stream = Stream;
            rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
            rec->data_len = 0;           /* transfer to beginning of data */
        }
        rec->VolSessionId = VolSessionId;
        rec->VolSessionTime = VolSessionTime;
        rec->FileIndex = FileIndex;
        if (FileIndex > 0) {
            Dmsg0(dbgep, "=== rpath 9 FileIndex>0\n");
            if (block->FirstIndex == 0) {
                Dmsg0(dbgep, "=== rpath 10 FirstIndex\n");
                block->FirstIndex = FileIndex;
            }
            block->LastIndex = rec->FileIndex;
        }

        Dmsg6(read_dbglvl, "read_header: FI=%s SessId=%d Strm=%s len=%u rec->remlen=%d data_len=%d\n",
              FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
              stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_bytes, rec->remlen,
              rec->data_len);
    } else {
        Dmsg0(dbgep, "=== rpath 11a block out of records\n");
        /*
         * No more records in this block because the number
         * of remaining bytes are less than a record header
         * length, so return empty handed, but indicate that
         * he must read again. By returning, we allow the
         * higher level routine to fetch the next block and
         * then reread.
         */
        Dmsg0(read_dbglvl, "read_header: End of block\n");
        rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
        empty_block(block);                      /* mark block empty */
        return false;
    }

    /* Sanity check */
    if (rec->data_bytes >= MAX_BLOCK_LENGTH) {
        Dmsg0(dbgep, "=== rpath 11b maxlen too big\n");
        /*
         * Something is wrong, force read of next block, abort
         *   continuing with this block.
         */
        rec->state_bits |= (REC_NO_HEADER | REC_BLOCK_EMPTY);
        empty_block(block);
        Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
              MAX_BLOCK_LENGTH, rec->data_bytes);
        return false;
    }

    rec->data = check_pool_memory_size(rec->data, rec->data_len+rec->data_bytes);
    rec->rstate = st_data;
    return true;
}
Пример #14
0
/*
 * Read a Record from the block
 *
 * Returns: false if nothing read or if the continuation record does not match.
 *                In both of these cases, a block read must be done.
 *          true  if at least the record header was read, this
 *                routine may have to be called again with a new
 *                block if the entire record was not read.
 */
bool read_record_from_block(DCR *dcr, DEV_RECORD *rec)
{
   ser_declare;
   uint32_t remlen;
   uint32_t VolSessionId;
   uint32_t VolSessionTime;
   int32_t  FileIndex;
   int32_t  Stream;
   uint32_t data_bytes;
   uint32_t rhl;
   char buf1[100], buf2[100];

   remlen = dcr->block->binbuf;

   /*
    * Clear state flags
    */
   clear_all_bits(REC_STATE_MAX, rec->state_bits);
   if (dcr->block->dev->is_tape()) {
      set_bit(REC_ISTAPE, rec->state_bits);
   }
   rec->Block = ((DEVICE *)(dcr->block->dev))->EndBlock;
   rec->File = ((DEVICE *)(dcr->block->dev))->EndFile;

   /*
    * Get the header. There is always a full header, otherwise we find it in the next block.
    */
   Dmsg3(450, "Block=%d Ver=%d size=%u\n", dcr->block->BlockNumber, dcr->block->BlockVer,
         dcr->block->block_len);
   if (dcr->block->BlockVer == 1) {
      rhl = RECHDR1_LENGTH;
   } else {
      rhl = RECHDR2_LENGTH;
   }
   if (remlen >= rhl) {
      Dmsg4(450, "Enter read_record_block: remlen=%d data_len=%d rem=%d blkver=%d\n",
            remlen, rec->data_len, rec->remainder, dcr->block->BlockVer);

      unser_begin(dcr->block->bufp, WRITE_RECHDR_LENGTH);
      if (dcr->block->BlockVer == 1) {
         unser_uint32(VolSessionId);
         unser_uint32(VolSessionTime);
      } else {
         VolSessionId = dcr->block->VolSessionId;
         VolSessionTime = dcr->block->VolSessionTime;
      }
      unser_int32(FileIndex);
      unser_int32(Stream);
      unser_uint32(data_bytes);

      dcr->block->bufp += rhl;
      dcr->block->binbuf -= rhl;
      remlen -= rhl;

      /*
       * If we are looking for more (remainder!=0), we reject anything
       * where the VolSessionId and VolSessionTime don't agree
       */
      if (rec->remainder && (rec->VolSessionId != VolSessionId ||
                             rec->VolSessionTime != VolSessionTime)) {
         set_bit(REC_NO_MATCH, rec->state_bits);
         Dmsg0(450, "remainder and VolSession doesn't match\n");
         return false;             /* This is from some other Session */
      }

      /*
       * If Stream is negative, it means that this is a continuation
       * of a previous partially written record.
       */
      if (Stream < 0) {               /* continuation record? */
         Dmsg1(500, "Got negative Stream => continuation. remainder=%d\n", rec->remainder);
         set_bit(REC_CONTINUATION, rec->state_bits);
         if (!rec->remainder) {       /* if we didn't read previously */
            rec->data_len = 0;        /* return data as if no continuation */
         } else if (rec->Stream != -Stream) {
            set_bit(REC_NO_MATCH, rec->state_bits);
            return false;             /* This is from some other Session */
         }
         rec->Stream = -Stream;       /* set correct Stream */
         rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
      } else {                        /* Regular record */
         rec->Stream = Stream;
         rec->maskedStream = rec->Stream & STREAMMASK_TYPE;
         rec->data_len = 0;           /* transfer to beginning of data */
      }
      rec->VolSessionId = VolSessionId;
      rec->VolSessionTime = VolSessionTime;
      rec->FileIndex = FileIndex;
      if (FileIndex > 0) {
         if (dcr->block->FirstIndex == 0) {
            dcr->block->FirstIndex = FileIndex;
         }
         dcr->block->LastIndex = FileIndex;
      }

      Dmsg6(450, "rd_rec_blk() got FI=%s SessId=%d Strm=%s len=%u\n"
                 "remlen=%d data_len=%d\n",
         FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), data_bytes, remlen,
         rec->data_len);
   } else {
      /*
       * No more records in this block because the number
       * of remaining bytes are less than a record header
       * length, so return empty handed, but indicate that
       * he must read again. By returning, we allow the
       * higher level routine to fetch the next block and
       * then reread.
       */
      Dmsg0(450, "read_record_block: nothing\n");
      set_bit(REC_NO_HEADER, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      empty_block(dcr->block);                 /* mark block empty */
      return false;
   }

   /* Sanity check */
   if (data_bytes >= MAX_BLOCK_LENGTH) {
      /*
       * Something is wrong, force read of next block, abort
       *   continuing with this block.
       */
      set_bit(REC_NO_HEADER, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      empty_block(dcr->block);
      Jmsg2(dcr->jcr, M_WARNING, 0, _("Sanity check failed. maxlen=%d datalen=%d. Block discarded.\n"),
         MAX_BLOCK_LENGTH, data_bytes);
      return false;
   }

   rec->data = check_pool_memory_size(rec->data, rec->data_len + data_bytes);

   /*
    * At this point, we have read the header, now we
    * must transfer as much of the data record as
    * possible taking into account: 1. A partial
    * data record may have previously been transferred,
    * 2. The current block may not contain the whole data
    * record.
    */
   if (remlen >= data_bytes) {
      /*
       * Got whole record
       */
      memcpy(rec->data+rec->data_len, dcr->block->bufp, data_bytes);
      dcr->block->bufp += data_bytes;
      dcr->block->binbuf -= data_bytes;
      rec->data_len += data_bytes;
   } else {
      /*
       * Partial record
       */
      memcpy(rec->data+rec->data_len, dcr->block->bufp, remlen);
      dcr->block->bufp += remlen;
      dcr->block->binbuf -= remlen;
      rec->data_len += remlen;
      rec->remainder = 1;             /* partial record transferred */
      Dmsg1(450, "read_record_block: partial xfered=%d\n", rec->data_len);
      set_bit(REC_PARTIAL_RECORD, rec->state_bits);
      set_bit(REC_BLOCK_EMPTY, rec->state_bits);
      return true;
   }
   rec->remainder = 0;

   Dmsg4(450, "Rtn full rd_rec_blk FI=%s SessId=%d Strm=%s len=%d\n",
         FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);

   return true;                       /* transferred full record */
}
Пример #15
0
/*
 * Write a Record to the block
 *
 *  Returns: false on failure (none or partially written)
 *           true  on success (all bytes written)
 *
 *  and remainder returned in packet.
 *
 *  We require enough room for the header, and we deal with
 *  two special cases. 1. Only part of the record may have
 *  been transferred the last time (when remainder is
 *  non-zero), and 2. The remaining bytes to write may not
 *  all fit into the block.
 */
bool write_record_to_block(DCR *dcr, DEV_RECORD *rec)
{
   ssize_t n;
   bool retval = false;
   char buf1[100], buf2[100];
   DEV_BLOCK *block = dcr->block;

   /*
    * After this point the record is in nrec not rec e.g. its either converted
    * or is just a pointer to the same as the rec pointer being passed in.
    */

   while (1) {
      ASSERT(block->binbuf == (uint32_t)(block->bufp - block->buf));
      ASSERT(block->buf_len >= block->binbuf);

      Dmsg9(890, "%s() state=%d (%s) FI=%s SessId=%d Strm=%s len=%d "
            "block_navail=%d remainder=%d\n",
            __func__, rec->state, record_state_to_ascii(rec->state),
            FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
            stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
            block_write_navail(block), rec->remainder);

      switch (rec->state) {
      case st_none:
         /*
          * Figure out what to do
          */
         rec->state = st_header;
         rec->remainder = rec->data_len;   /* length of data remaining to write */
         continue;                         /* goto st_header */

      case st_header:
         /*
          * Write header
          */
         n = write_header_to_block(block, rec, rec->Stream);
         if (n < 0) {
            /*
             * the header did not fit into the block, so flush the current
             * block and come back to st_header and try again on the next block.
             */
            goto bail_out;
         }

         if (block_write_navail(block) == 0) {
            /*
             * The header fit, but no bytes of data will fit,
             * so flush this block and start the next block with a
             * continuation header.
             */
            rec->state = st_header_cont;
            goto bail_out;
         }

         /*
          * The header fit, and at least one byte of data will fit,
          * so move to the st_data state and start filling the block
          * with data bytes
          */
         rec->state = st_data;
         continue;

      case st_header_cont:
         /*
          * Write continuation header
          */
         n = write_header_to_block(block, rec, -rec->Stream);
         if (n < 0) {
            /*
             * The continuation header wouldn't fit, which is impossible
             * unless something is broken
             */
            Emsg0(M_ABORT, 0, _("couldn't write continuation header\n"));
         }

         /*
          * After successfully writing a continuation header, we always start writing
          * data, even if none will fit into this block.
          */
         rec->state = st_data;

         if (block_write_navail(block) == 0) {
            /*
             * The header fit, but no bytes of data will fit,
             * so flush the block and start the next block with
             * data bytes
             */
            goto bail_out;       /* Partial transfer */
         }

         continue;

      case st_data:
         /*
          * Write normal data
          *
          * Part of it may have already been transferred, and we
          * may not have enough room to transfer the whole this time.
          */
         if (rec->remainder > 0) {
            n = write_data_to_block(block, rec);
            if (n < 0) {
               /*
                * error appending data to block should be impossible
                * unless something is broken
                */
               Emsg0(M_ABORT, 0, _("data write error\n"));
            }

            rec->remainder -= n;

            if (rec->remainder > 0) {
               /*
                * Could not fit all of the data bytes into this block, so
                * flush the current block, and start the next block with a
                * continuation header
                */
               rec->state = st_header_cont;
               goto bail_out;
            }
         }

         rec->remainder = 0;               /* did whole transfer */
         rec->state = st_none;
         retval = true;
         goto bail_out;

      default:
         Emsg1(M_ABORT, 0, _("Something went wrong. Unknown state %d.\n"), rec->state);
         rec->state = st_none;
         retval = true;
         goto bail_out;
      }
   }

bail_out:
   return retval;
}
Пример #16
0
/* Write session label
 *  Returns: false on failure
 *           true  on success
 */
bool write_session_label(DCR *dcr, int label)
{
   JCR *jcr = dcr->jcr;
   DEVICE *dev = dcr->dev;
   DEV_RECORD *rec;
   DEV_BLOCK *block = dcr->block;
   char buf1[100], buf2[100];

   rec = new_record();
   Dmsg1(130, "session_label record=%x\n", rec);
   switch (label) {
   case SOS_LABEL:
      set_start_vol_position(dcr);
      break;
   case EOS_LABEL:
      if (dev->is_tape()) {
         dcr->EndBlock = dev->EndBlock;
         dcr->EndFile  = dev->EndFile;
      } else {
         dcr->EndBlock = (uint32_t)dev->file_addr;
         dcr->EndFile = (uint32_t)(dev->file_addr >> 32);
      }
      break;
   default:
      Jmsg1(jcr, M_ABORT, 0, _("Bad Volume session label = %d\n"), label);
      break;
   }
   create_session_label(dcr, rec, label);
   rec->FileIndex = label;

   /*
    * We guarantee that the session record can totally fit
    *  into a block. If not, write the block, and put it in
    *  the next block. Having the sesssion record totally in
    *  one block makes reading them much easier (no need to
    *  read the next block).
    */
   if (!can_write_record_to_block(block, rec)) {
      Dmsg0(150, "Cannot write session label to block.\n");
      if (!dcr->write_block_to_device()) {
         Dmsg0(130, "Got session label write_block_to_dev error.\n");
         free_record(rec);
         return false;
      }
   }
   if (!write_record_to_block(dcr, rec)) {
      free_record(rec);
      return false;
   }

   Dmsg6(150, "Write sesson_label record JobId=%d FI=%s SessId=%d Strm=%s len=%d "
             "remainder=%d\n", jcr->JobId,
      FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
      stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
      rec->remainder);

   free_record(rec);
   Dmsg2(150, "Leave write_session_label Block=%ud File=%ud\n",
      dev->get_block_num(), dev->get_file());
   return true;
}
Пример #17
0
/*
 * Write a Record to the block
 *
 *  Returns: false on failure (none or partially written)
 *           true  on success (all bytes written)
 *
 *  and remainder returned in packet.
 *
 *  We require enough room for the header, and we deal with
 *  two special cases. 1. Only part of the record may have
 *  been transferred the last time (when remainder is
 *  non-zero), and 2. The remaining bytes to write may not
 *  all fit into the block.
 */
bool write_record_to_block(DCR *dcr, DEV_RECORD *rec)
{
   bool retval = false;
   char buf1[100], buf2[100];
   DEV_BLOCK *block = dcr->block;

   /*
    * After this point the record is in nrec not rec e.g. its either converted
    * or is just a pointer to the same as the rec pointer being passed in.
    */

   while (1) {
      ASSERT(block->binbuf == (uint32_t)(block->bufp - block->buf));
      ASSERT(block->buf_len >= block->binbuf);

      Dmsg6(890, "write_record_to_block() FI=%s SessId=%d Strm=%s len=%d\n"
            "rem=%d remainder=%d\n",
            FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
            stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len,
            rec->remlen, rec->remainder);

      switch (rec->state) {
      case st_none:
         /*
          * Figure out what to do
          */
         rec->state = st_header;
         continue;              /* go to next state */

      case st_header:
         /*
          * Write header
          *
          * If rec->remainder is non-zero, we have been called a
          * second (or subsequent) time to finish writing a record
          * that did not previously fit into the block.
          */
          if (!write_header_to_block(block, rec)) {
             goto bail_out;      /* write block then come back here */
          }
          rec->state = st_data;  /* after header, now write data */
          continue;

      /*
       * Write continuation header
       */
      case st_header_cont:
         write_continue_header_to_block(block, rec);
         rec->state = st_data;
         if (rec->remlen == 0) {
            goto bail_out;       /* Partial transfer */
         }
         continue;

      /*
       * Write normal data
       */
      case st_data:
         /*
          * Write data
          *
          * Part of it may have already been transferred, and we
          * may not have enough room to transfer the whole this time.
          */
         if (rec->remainder > 0) {
            if (!write_data_to_block(block, rec)) {
               rec->state = st_header_cont;
               goto bail_out;
            }
         }
         rec->remainder = 0;               /* did whole transfer */
         rec->state = st_none;
         retval = true;
         goto bail_out;

      default:
         Dmsg0(000, "Something went wrong. Default state.\n");
         rec->state = st_none;
         retval = true;
         goto bail_out;
      }
   }

bail_out:
   return retval;
}
Пример #18
0
/*
 *  Append Data sent from Client (FD/SD)
 *
 */
bool do_append_data(JCR *jcr)
{
   int32_t n;
   int32_t file_index, stream, last_file_index;
   uint64_t stream_len;
   BSOCK *fd = jcr->file_bsock;
   bool ok = true;
   DEV_RECORD rec;
   char buf1[100], buf2[100];
   DCR *dcr = jcr->dcr;
   DEVICE *dev;
   char ec[50];
   POOLMEM *eblock = NULL;
   POOL_MEM errmsg(PM_EMSG);

   if (!dcr) {
      pm_strcpy(jcr->errmsg, _("DCR is NULL!!!\n"));
      Jmsg0(jcr, M_FATAL, 0, jcr->errmsg);
      return false;
   }
   dev = dcr->dev;
   if (!dev) {
      pm_strcpy(jcr->errmsg, _("DEVICE is NULL!!!\n"));
      Jmsg0(jcr, M_FATAL, 0, jcr->errmsg);
      return false;
   }

   Dmsg1(100, "Start append data. res=%d\n", dev->num_reserved());

   memset(&rec, 0, sizeof(rec));

   if (!fd->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
      jcr->setJobStatus(JS_ErrorTerminated);
      pm_strcpy(jcr->errmsg, _("Unable to set network buffer size.\n"));
      Jmsg0(jcr, M_FATAL, 0, jcr->errmsg);
      return false;
   }

   if (!acquire_device_for_append(dcr)) {
      jcr->setJobStatus(JS_ErrorTerminated);
      return false;
   }

   jcr->sendJobStatus(JS_Running);

   //ASSERT(dev->VolCatInfo.VolCatName[0]);
   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }
   Dmsg1(50, "Begin append device=%s\n", dev->print_name());

   begin_data_spool(dcr);
   begin_attribute_spool(jcr);

   Dmsg0(100, "Just after acquire_device_for_append\n");
   //ASSERT(dev->VolCatInfo.VolCatName[0]);
   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }
   /*
    * Write Begin Session Record
    */
   if (!write_session_label(dcr, SOS_LABEL)) {
      Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
         dev->bstrerror());
      jcr->setJobStatus(JS_ErrorTerminated);
      ok = false;
   }

   //ASSERT(dev->VolCatInfo.VolCatName[0]);
   if (dev->VolCatInfo.VolCatName[0] == 0) {
      Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
   }

   /* Tell File daemon to send data */
   if (!fd->fsend(OK_data)) {
      berrno be;
      Jmsg1(jcr, M_FATAL, 0, _("Network send error to FD. ERR=%s\n"),
            be.bstrerror(fd->b_errno));
      ok = false;
   }

   /*
    * Get Data from File daemon, write to device.  To clarify what is
    *   going on here.  We expect:
    *     - A stream header
    *     - Multiple records of data
    *     - EOD record
    *
    *    The Stream header is just used to synchronize things, and
    *    none of the stream header is written to tape.
    *    The Multiple records of data, contain first the Attributes,
    *    then after another stream header, the file data, then
    *    after another stream header, the MD5 data if any.
    *
    *   So we get the (stream header, data, EOD) three time for each
    *   file. 1. for the Attributes, 2. for the file data if any,
    *   and 3. for the MD5 if any.
    */
   dcr->VolFirstIndex = dcr->VolLastIndex = 0;
   jcr->run_time = time(NULL);              /* start counting time for rates */

   GetMsg *qfd;

   qfd = New(GetMsg(jcr, fd, NULL, GETMSG_MAX_MSG_SIZE));
   qfd->start_read_sock();

   for (last_file_index = 0; ok && !jcr->is_job_canceled(); ) {

      /* Read Stream header from the File daemon.
       *  The stream header consists of the following:
       *    file_index (sequential Bacula file index, base 1)
       *    stream     (Bacula number to distinguish parts of data)
       *    stream_len (Expected length of this stream. This
       *       will be the size backed up if the file does not
       *       grow during the backup.
       */
      n = qfd->bget_msg(NULL);
      if (n <= 0) {
         if (n == BNET_SIGNAL && qfd->msglen == BNET_EOD) {
            Dmsg0(200, "Got EOD on reading header.\n");
            break;                    /* end of data */
         }
         Jmsg3(jcr, M_FATAL, 0, _("Error reading data header from FD. n=%d msglen=%d ERR=%s\n"),
               n, qfd->msglen, fd->bstrerror());
         // ASX TODO the fd->bstrerror() can be related to the wrong error, I should Queue the error too
         possible_incomplete_job(jcr, last_file_index);
         ok = false;
         break;
      }

      if (sscanf(qfd->msg, "%ld %ld %lld", &file_index, &stream, &stream_len) != 3) {
         // TODO ASX already done in bufmsg, should reuse the values
         char buf[256];
         Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), asciidump(qfd->msg, qfd->msglen, buf, sizeof(buf)));
         ok = false;
         possible_incomplete_job(jcr, last_file_index);
         break;
      }

      Dmsg3(890, "<filed: Header FilInx=%d stream=%d stream_len=%lld\n",
         file_index, stream, stream_len);

      /*
       * We make sure the file_index is advancing sequentially.
       * An incomplete job can start the file_index at any number.
       * otherwise, it must start at 1.
       */
      if (jcr->rerunning && file_index > 0 && last_file_index == 0) {
         goto fi_checked;
      }
      Dmsg2(400, "file_index=%d last_file_index=%d\n", file_index, last_file_index);
      if (file_index > 0 && (file_index == last_file_index ||
          file_index == last_file_index + 1)) {
         goto fi_checked;
      }
      Jmsg2(jcr, M_FATAL, 0, _("FI=%d from FD not positive or last_FI=%d\n"),
            file_index, last_file_index);
      possible_incomplete_job(jcr, last_file_index);
      ok = false;
      break;

fi_checked:
      if (file_index != last_file_index) {
         jcr->JobFiles = file_index;
         last_file_index = file_index;
      }

      /* Read data stream from the File daemon.
       *  The data stream is just raw bytes
       */
      while ((n=qfd->bget_msg(NULL)) > 0 && !jcr->is_job_canceled()) {

         rec.VolSessionId = jcr->VolSessionId;
         rec.VolSessionTime = jcr->VolSessionTime;
         rec.FileIndex = file_index;
         rec.Stream = stream;
         rec.StreamLen = stream_len;
         rec.maskedStream = stream & STREAMMASK_TYPE;   /* strip high bits */
         rec.data_len = qfd->msglen;
         rec.data = qfd->msg;            /* use message buffer */

         Dmsg4(850, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
            rec.FileIndex, rec.VolSessionId,
            stream_to_ascii(buf1, rec.Stream,rec.FileIndex),
            rec.data_len);
         ok = dcr->write_record(&rec);
         if (!ok) {
            Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
                  dcr->dev->print_name(), dcr->dev->bstrerror());
            break;
         }
         jcr->JobBytes += rec.data_len;   /* increment bytes this job */
         jcr->JobBytes += qfd->bmsg->jobbytes; // if the block as been downloaded, count it
         Dmsg4(850, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
            FI_to_ascii(buf1, rec.FileIndex), rec.VolSessionId,
            stream_to_ascii(buf2, rec.Stream, rec.FileIndex), rec.data_len);

         send_attrs_to_dir(jcr, &rec);
         Dmsg0(650, "Enter bnet_get\n");
      }
      Dmsg2(650, "End read loop with FD. JobFiles=%d Stat=%d\n", jcr->JobFiles, n);

      if (fd->is_error()) {
         if (!jcr->is_job_canceled()) {
            Dmsg1(350, "Network read error from FD. ERR=%s\n", fd->bstrerror());
            Jmsg1(jcr, M_FATAL, 0, _("Network error reading from FD. ERR=%s\n"),
                  fd->bstrerror());
            possible_incomplete_job(jcr, last_file_index);
         }
         ok = false;
         break;
      }
   }

   qfd->wait_read_sock();
   free_GetMsg(qfd);

   if (eblock != NULL) {
      free_pool_memory(eblock);
   }

   /* Create Job status for end of session label */
   jcr->setJobStatus(ok?JS_Terminated:JS_ErrorTerminated);

   if (ok) {
      /* Terminate connection with Client */
      fd->fsend(OK_append);
      do_client_commands(jcr);            /* finish dialog with Client */
   } else {
      fd->fsend("3999 Failed append\n");
   }

   Dmsg1(200, "Write EOS label JobStatus=%c\n", jcr->JobStatus);

   /*
    * Check if we can still write. This may not be the case
    *  if we are at the end of the tape or we got a fatal I/O error.
    */
   if (ok || dev->can_write()) {
      if (!write_session_label(dcr, EOS_LABEL)) {
         /* Print only if ok and not cancelled to avoid spurious messages */
         if (ok && !jcr->is_job_canceled()) {
            Jmsg1(jcr, M_FATAL, 0, _("Error writing end session label. ERR=%s\n"),
                  dev->bstrerror());
            possible_incomplete_job(jcr, last_file_index);
         }
         jcr->setJobStatus(JS_ErrorTerminated);
         ok = false;
      }
      /* Flush out final partial block of this session */
      if (!dcr->write_final_block_to_device()) {
         /* Print only if ok and not cancelled to avoid spurious messages */
         if (ok && !jcr->is_job_canceled()) {
            Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
                  dev->print_name(), dev->bstrerror());
            Dmsg0(100, _("Set ok=FALSE after write_final_block_to_device.\n"));
            possible_incomplete_job(jcr, last_file_index);
         }
         jcr->setJobStatus(JS_ErrorTerminated);
         ok = false;
      }
   }
   flush_jobmedia_queue(jcr);
   if (!ok && !jcr->is_JobStatus(JS_Incomplete)) {
      discard_data_spool(dcr);
   } else {
      /* Note: if commit is OK, the device will remain blocked */
      commit_data_spool(dcr);
   }

   /*
    * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
    *   and the subsequent Jmsg() editing will break
    */
   int32_t job_elapsed = time(NULL) - jcr->run_time;

   if (job_elapsed <= 0) {
      job_elapsed = 1;
   }

   Jmsg(dcr->jcr, M_INFO, 0, _("Elapsed time=%02d:%02d:%02d, Transfer rate=%s Bytes/second\n"),
         job_elapsed / 3600, job_elapsed % 3600 / 60, job_elapsed % 60,
         edit_uint64_with_suffix(jcr->JobBytes / job_elapsed, ec));

   /*
    * Release the device -- and send final Vol info to DIR
    *  and unlock it.
    */
   release_device(dcr);

   if ((!ok || jcr->is_job_canceled()) && !jcr->is_JobStatus(JS_Incomplete)) {
      discard_attribute_spool(jcr);
   } else {
      commit_attribute_spool(jcr);
   }

   jcr->sendJobStatus();          /* update director */

   Dmsg1(100, "return from do_append_data() ok=%d\n", ok);
   return ok;
}
Пример #19
0
Файл: mac.c Проект: AlD/bareos
/*
 * Called here for each record from read_records()
 * This function is used when we do a external clone of a Job e.g.
 * this SD is the reading SD. And a remote SD is the writing SD.
 *
 * Returns: true if OK
 *           false if error
 */
static bool clone_record_to_remote_sd(DCR *dcr, DEV_RECORD *rec)
{
   POOLMEM *msgsave;
   JCR *jcr = dcr->jcr;
   char buf1[100], buf2[100];
   BSOCK *sd = jcr->store_bsock;
   bool send_eod, send_header;

#ifdef xxx
   Dmsg5(000, "on entry     JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
#endif

   /*
    * If label discard it
    */
   if (rec->FileIndex < 0) {
      return true;
   }

   /*
    * See if this is the first record being processed.
    */
   if (rec->last_FileIndex == 0) {
      /*
       * Initialize the last counters so we can compare
       * things in the next run through here.
       */
      rec->last_VolSessionId = rec->VolSessionId;
      rec->last_VolSessionTime = rec->VolSessionTime;
      rec->last_FileIndex = rec->FileIndex;
      rec->last_Stream = rec->Stream;
      jcr->JobFiles = 1;

      /*
       * Need to send both a new header only.
       */
      send_eod = false;
      send_header = true;
   } else {
      /*
       * See if we are changing file or stream type.
       */
      if (rec->VolSessionId != rec->last_VolSessionId ||
          rec->VolSessionTime != rec->last_VolSessionTime ||
          rec->FileIndex != rec->last_FileIndex ||
          rec->Stream != rec->last_Stream) {
         /*
          * See if we are changing the FileIndex e.g.
          * start processing the next file in the backup stream.
          */
         if (rec->FileIndex != rec->last_FileIndex) {
            jcr->JobFiles++;
         }

         /*
          * Keep track of the new state.
          */
         rec->last_VolSessionId = rec->VolSessionId;
         rec->last_VolSessionTime = rec->VolSessionTime;
         rec->last_FileIndex = rec->FileIndex;
         rec->last_Stream = rec->Stream;

         /*
          * Need to send both a EOD and a new header.
          */
         send_eod = true;
         send_header = true;
      } else {
         send_eod = false;
         send_header = false;
      }
   }

   /*
    * Send a EOD when needed.
    */
   if (send_eod) {
      if (!sd->signal(BNET_EOD)) { /* indicate end of file data */
         if (!jcr->is_job_canceled()) {
            Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
                  sd->bstrerror());
         }
         return false;
      }
   }

   /*
    * Send a header when needed.
    */
   if (send_header) {
      if (!sd->fsend("%ld %d 0", rec->FileIndex, rec->Stream)) {
         if (!jcr->is_job_canceled()) {
            Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
                  sd->bstrerror());
         }
         return false;
      }
   }

   /*
    * Send the record data.
    * We don't want to copy the data from the record to the socket structure
    * so we save the original msg pointer and use the record data pointer for
    * sending and restore the original msg pointer when done.
    */
   msgsave = sd->msg;
   sd->msg = rec->data;
   sd->msglen = rec->data_len;

   if (!sd->send()) {
      sd->msg = msgsave;
      sd->msglen = 0;
      if (!jcr->is_job_canceled()) {
         Jmsg1(jcr, M_FATAL, 0, _("Network send error to SD. ERR=%s\n"),
               sd->bstrerror());
      }
      return false;
   }

   jcr->JobBytes += sd->msglen;
   sd->msg = msgsave;

   Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);

   return true;
}
Пример #20
0
Файл: mac.c Проект: AlD/bareos
/*
 * Called here for each record from read_records()
 * This function is used when we do a internal clone of a Job e.g.
 * this SD is both the reading and writing SD.
 *
 * Returns: true if OK
 *           false if error
 */
static bool clone_record_internally(DCR *dcr, DEV_RECORD *rec)
{
   JCR *jcr = dcr->jcr;
   DEVICE *dev = jcr->dcr->dev;
   char buf1[100], buf2[100];

#ifdef xxx
   Dmsg5(000, "on entry     JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);
#endif

   /*
    * If label and not for us, discard it
    */
   if (rec->FileIndex < 0 && rec->match_stat <= 0) {
      return true;
   }

   /*
    * We want to write SOS_LABEL and EOS_LABEL discard all others
    */
   switch (rec->FileIndex) {
   case PRE_LABEL:
   case VOL_LABEL:
   case EOT_LABEL:
   case EOM_LABEL:
      return true;                    /* don't write vol labels */
   }

//   if (jcr->is_JobType(JT_BACKUP)) {
      /*
       * For normal migration jobs, FileIndex values are sequential because
       *  we are dealing with one job.  However, for Vbackup (consolidation),
       *  we will be getting records from multiple jobs and writing them back
       *  out, so we need to ensure that the output FileIndex is sequential.
       *  We do so by detecting a FileIndex change and incrementing the
       *  JobFiles, which we then use as the output FileIndex.
       */
      if (rec->FileIndex >= 0) {
         /*
          * If something changed, increment FileIndex
          */
         if (rec->VolSessionId != rec->last_VolSessionId ||
             rec->VolSessionTime != rec->last_VolSessionTime ||
             rec->FileIndex != rec->last_FileIndex) {
            jcr->JobFiles++;
            rec->last_VolSessionId = rec->VolSessionId;
            rec->last_VolSessionTime = rec->VolSessionTime;
            rec->last_FileIndex = rec->FileIndex;
         }
         rec->FileIndex = jcr->JobFiles;     /* set sequential output FileIndex */
      }
//   }

   /*
    * Modify record SessionId and SessionTime to correspond to output.
    */
   rec->VolSessionId = jcr->VolSessionId;
   rec->VolSessionTime = jcr->VolSessionTime;

   Dmsg5(200, "before write JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);

   while (!write_record_to_block(jcr->dcr, rec)) {
      Dmsg4(200, "!write_record_to_block blkpos=%u:%u len=%d rem=%d\n",
            dev->file, dev->block_num, rec->data_len, rec->remainder);
      if (!jcr->dcr->write_block_to_device()) {
         Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
            dev->print_name(), dev->bstrerror());
         Jmsg2(jcr, M_FATAL, 0, _("Fatal append error on device %s: ERR=%s\n"),
               dev->print_name(), dev->bstrerror());
         return false;
      }
      Dmsg2(200, "===== Wrote block new pos %u:%u\n", dev->file, dev->block_num);
   }

   /*
    * Restore packet
    */
   rec->VolSessionId = rec->last_VolSessionId;
   rec->VolSessionTime = rec->last_VolSessionTime;
   if (rec->FileIndex < 0) {
      return true;                    /* don't send LABELs to Dir */
   }

   jcr->JobBytes += rec->data_len;   /* increment bytes of this job */

   Dmsg5(500, "wrote_record JobId=%d FI=%s SessId=%d Strm=%s len=%d\n",
         jcr->JobId, FI_to_ascii(buf1, rec->FileIndex), rec->VolSessionId,
         stream_to_ascii(buf2, rec->Stream, rec->FileIndex), rec->data_len);

   send_attrs_to_dir(jcr, rec);

   return true;
}