Beispiel #1
0
/*
 * Put a volume label into the block
 *
 *  Returns: false on failure
 *           true  on success
 */
static bool write_volume_label_to_block(DCR *dcr)
{
   DEVICE *dev = dcr->dev;
   DEV_BLOCK *block = dcr->block;
   DEV_RECORD rec;
   JCR *jcr = dcr->jcr;

   Dmsg0(130, "write Label in write_volume_label_to_block()\n");

   memset(&rec, 0, sizeof(rec));
   rec.data = get_memory(SER_LENGTH_Volume_Label);
   empty_block(block);                /* Volume label always at beginning */

   create_volume_label_record(dcr, dev, &rec);

   block->BlockNumber = 0;
   if (!write_record_to_block(dcr, &rec)) {
      free_pool_memory(rec.data);
      Jmsg1(jcr, M_FATAL, 0, _("Cannot write Volume label to block for device %s\n"),
         dev->print_name());
      return false;
   } else {
      Dmsg2(130, "Wrote label of %d bytes to block. Vol=%s\n", rec.data_len,
            dcr->VolumeName);
   }
   free_pool_memory(rec.data);
   return true;
}
Beispiel #2
0
//==============================================================================
void CC_UnitDriver::write_flash_slow(const DataSectionStore &section_store)
{
    const size_t WRITE_BLOCK_SIZE = reg_info_.write_block_size;

    // Channel 0: Xdata buffer -> Flash controller
    uint8_t dma_desc[8] = {
        HIBYTE(reg_info_.dma_data_offset),// src[15:8]
        LOBYTE(reg_info_.dma_data_offset),// src[7:0]
        HIBYTE(reg_info_.fwdata),    		// dest[15:8]
        LOBYTE(reg_info_.fwdata),      	// dest[7:0]
        HIBYTE(WRITE_BLOCK_SIZE),			// block size[15:8]
        LOBYTE(WRITE_BLOCK_SIZE),			// block size[7:0]
        18,								// trigger FLASH
        0x42 								// increment source
    };

    // Load dma descriptors
    write_xdata_memory(reg_info_.dma0_cfg_offset, dma_desc, sizeof(dma_desc));

    // Set the pointer to the DMA descriptors
    write_xdata_memory(reg_info_.dma0_cfgl, LOBYTE(reg_info_.dma0_cfg_offset));
    write_xdata_memory(reg_info_.dma0_cfgh, HIBYTE(reg_info_.dma0_cfg_offset));

    size_t faddr = (size_t)-1;

    ByteVector data;
    section_store.create_image(FLASH_EMPTY_BYTE, data);
    data.resize(align_up(section_store.upper_address(), WRITE_BLOCK_SIZE),
                FLASH_EMPTY_BYTE);

    pw_.write_start(data.size());

    for (size_t i = 0; i < (data.size() / WRITE_BLOCK_SIZE); i++)
    {
        pw_.write_progress(WRITE_BLOCK_SIZE);

        size_t offset = WRITE_BLOCK_SIZE * i;
        if (empty_block(&data[offset], WRITE_BLOCK_SIZE))
            continue;

        size_t next_faddr = offset / reg_info_.flash_word_size;
        if (next_faddr != faddr)
        {
            write_xdata_memory(reg_info_.faddrl, LOBYTE(next_faddr));
            write_xdata_memory(reg_info_.faddrh, HIBYTE(next_faddr));
            faddr = next_faddr + WRITE_BLOCK_SIZE / reg_info_.flash_word_size;
        }

        write_xdata_memory(reg_info_.dma_data_offset, &data[offset], WRITE_BLOCK_SIZE);

        write_xdata_memory(reg_info_.dma_arm, 0x01);
        write_xdata_memory(reg_info_.fctl, reg_info_.fctl_write);
        while ((read_xdata_memory(reg_info_.fctl) & FCTL_BUSY));
    }
    pw_.write_finish();
}
Beispiel #3
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;
}
Beispiel #4
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 */
}
Beispiel #5
0
/*
 * If release is set, we rewind the current volume,
 * which we no longer want, and ask the user (console)
 * to mount the next volume.
 *
 *  Continue trying until we get it, and then ensure
 *  that we can write on it.
 *
 * This routine returns a 0 only if it is REALLY
 *  impossible to get the requested Volume.
 *
 */
bool DCR::mount_next_write_volume()
{
   int retry = 0;
   bool ask = false, recycle, autochanger;
   bool do_find = true;
   int mode;
   DCR *dcr = this;

   Dmsg2(150, "Enter mount_next_volume(release=%d) dev=%s\n", dev->must_unload(),
      dev->print_name());

   init_device_wait_timers(dcr);
   lock_volumes();
   
   /*
    * Attempt to mount the next volume. If something non-fatal goes
    *  wrong, we come back here to re-try (new op messages, re-read
    *  Volume, ...)
    */
mount_next_vol:
   Dmsg1(150, "mount_next_vol retry=%d\n", retry);
   /* Ignore retry if this is poll request */
   if (!dev->poll && retry++ > 4) {
      /* Last ditch effort before giving up, force operator to respond */
      VolCatInfo.Slot = 0;
      unlock_volumes();
      if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) {
         Jmsg(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s.\n"),
              dev->print_name());
         goto no_lock_bail_out;
      }
      lock_volumes();
      Dmsg1(150, "Continue after dir_ask_sysop_to_mount. must_load=%d\n", dev->must_load());
   }
   if (job_canceled(jcr)) {
      Jmsg(jcr, M_FATAL, 0, _("Job %d canceled.\n"), jcr->JobId);
      goto bail_out;
   }
   recycle = false;

   if (retry >= 2) {
      do_find = false; 
   }

   if (dev->must_unload()) {
      ask = true;                     /* ask operator to mount tape */
      do_find = true;                 /* re-find a volume after unload */
   }
   do_unload();
   do_swapping(true /*is_writing*/);
   do_load(true /*is_writing*/);

   if (do_find && !find_a_volume()) {
      goto no_lock_bail_out;
   }

   if (job_canceled(jcr)) {
      goto bail_out;
   }
   Dmsg3(150, "After find_next_append. Vol=%s Slot=%d Parts=%d\n",
         VolCatInfo.VolCatName, VolCatInfo.Slot, VolCatInfo.VolCatParts);
   
   /*
    * Get next volume and ready it for append
    * This code ensures that the device is ready for
    * writing. We start from the assumption that there
    * may not be a tape mounted.
    *
    * If the device is a file, we create the output
    * file. If it is a tape, we check the volume name
    * and move the tape to the end of data.
    *
    */
   if (autoload_device(dcr, true/*writing*/, NULL) > 0) {
      autochanger = true;
      ask = false;
   } else {
      autochanger = false;
      VolCatInfo.Slot = 0;
      ask = retry >= 2;
   }
   Dmsg1(150, "autoload_dev returns %d\n", autochanger);
   /*
    * If we autochanged to correct Volume or (we have not just
    *   released the Volume AND we can automount) we go ahead
    *   and read the label. If there is no tape in the drive,
    *   we will fail, recurse and ask the operator the next time.
    */
   if (!dev->must_unload() && dev->is_tape() && dev->has_cap(CAP_AUTOMOUNT)) {
      Dmsg0(250, "(1)Ask=0\n");
      ask = false;                 /* don't ask SYSOP this time */
   }
   /* Don't ask if not removable */
   if (!dev->is_removable()) {
      Dmsg0(250, "(2)Ask=0\n");
      ask = false;
   }
   Dmsg2(250, "Ask=%d autochanger=%d\n", ask, autochanger);

   if (ask) {
      unlock_volumes();
      if (!dir_ask_sysop_to_mount_volume(dcr, ST_APPEND)) {
         Dmsg0(150, "Error return ask_sysop ...\n");
         goto no_lock_bail_out;
      }
      lock_volumes();
   }
   if (job_canceled(jcr)) {
      goto bail_out;
   }
   Dmsg3(150, "want vol=%s devvol=%s dev=%s\n", VolumeName, 
      dev->VolHdr.VolumeName, dev->print_name());

   if (dev->poll && dev->has_cap(CAP_CLOSEONPOLL)) {
      dev->close();
   }

   /* Ensure the device is open */
   if (dev->has_cap(CAP_STREAM)) {
      mode = OPEN_WRITE_ONLY;
   } else {
      mode = OPEN_READ_WRITE;
   }
   /* Try autolabel if enabled */
   if (dev->open(dcr, mode) < 0) {
      try_autolabel(false);      /* try to create a new volume label */
   }
   while (dev->open(dcr, mode) < 0) {
      Dmsg1(150, "open_device failed: ERR=%s\n", dev->bstrerror());
      if ((dev->is_file() && dev->is_removable()) || dev->is_dvd()) {
         bool ok = true;
         Dmsg0(150, "call scan_dir_for_vol\n");
         if (dev->is_dvd()) {
            if (!dev->mount(0)) {
               ok = false;
            }
         }
         if (ok && dev->scan_dir_for_volume(dcr)) {
            if (dev->open(dcr, mode) >= 0) {
               break;                    /* got a valid volume */
            }
         }
         if (ok && dev->is_dvd()) {
            dev->unmount(0);
         }
      }
      if (try_autolabel(false) == try_read_vol) {
         break;                       /* created a new volume label */
      }
      Dmsg0(50, "set_unload\n");
      dev->set_unload();              /* force ask sysop */
      ask = true;
      Dmsg0(150, "goto mount_next_vol\n");
      goto mount_next_vol;
   }

   /*
    * Now check the volume label to make sure we have the right tape mounted
    */
read_volume:
   switch (check_volume_label(ask, autochanger)) {
   case check_next_vol:
      Dmsg0(50, "set_unload\n");
      dev->set_unload();                 /* want a different Volume */
      Dmsg0(150, "goto mount_next_vol\n");
      goto mount_next_vol;
   case check_read_vol:
      goto read_volume;
   case check_error:
      goto bail_out;
   case check_ok:
      break;
   }

   /*
    * See if we have a fresh tape or a tape with data.
    *
    * Note, if the LabelType is PRE_LABEL, it was labeled
    *  but never written. If so, rewrite the label but set as
    *  VOL_LABEL.  We rewind and return the label (reconstructed)
    *  in the block so that in the case of a new tape, data can
    *  be appended just after the block label.  If we are writing
    *  a second volume, the calling routine will write the label
    *  before writing the overflow block.
    *
    *  If the tape is marked as Recycle, we rewrite the label.
    */
   recycle = strcmp(dev->VolCatInfo.VolCatStatus, "Recycle") == 0;
   if (dev->VolHdr.LabelType == PRE_LABEL || recycle) {
      if (!rewrite_volume_label(dcr, recycle)) {
         mark_volume_in_error();
         goto mount_next_vol;
      }
   } else {
      /*
       * OK, at this point, we have a valid Bacula label, but
       * we need to position to the end of the volume, since we are
       * just now putting it into append mode.
       */
      Dmsg0(200, "Device previously written, moving to end of data\n");
      Jmsg(jcr, M_INFO, 0, _("Volume \"%s\" previously written, moving to end of data.\n"),
         VolumeName);

      if (!dev->eod(dcr)) {
         Jmsg(jcr, M_ERROR, 0, _("Unable to position to end of data on device %s: ERR=%s\n"),
            dev->print_name(), dev->bstrerror());
         mark_volume_in_error();
         goto mount_next_vol;
      }
      if (!is_eod_valid()) {
         Dmsg0(150, "goto mount_next_vol\n");
         goto mount_next_vol;
      }

      dev->VolCatInfo.VolCatMounts++;      /* Update mounts */
      Dmsg1(150, "update volinfo mounts=%d\n", dev->VolCatInfo.VolCatMounts);
      if (!dir_update_volume_info(dcr, false, false)) {
         goto bail_out;
      }
      
      /* Return an empty block */
      empty_block(block);             /* we used it for reading so set for write */
   }
   dev->set_append();
   Dmsg1(150, "set APPEND, normal return from mount_next_write_volume. dev=%s\n",
      dev->print_name());

   unlock_volumes();
   return true;

bail_out:
   unlock_volumes();

no_lock_bail_out:
   return false;
}
Beispiel #6
0
/*
 * Write a block to the spool file
 *
 *  Returns: true on success or EOT
 *           false on hard error
 */
bool write_block_to_spool_file(DCR *dcr)
{
    uint32_t wlen, hlen;               /* length to write */
    bool despool = false;
    DEV_BLOCK *block = dcr->block;

    if (job_canceled(dcr->jcr)) {
        return false;
    }
    ASSERT(block->binbuf == ((uint32_t) (block->bufp - block->buf)));
    if (block->binbuf <= WRITE_BLKHDR_LENGTH) {  /* Does block have data in it? */
        return true;
    }

    hlen = sizeof(spool_hdr);
    wlen = block->binbuf;
    P(dcr->dev->spool_mutex);
    dcr->job_spool_size += hlen + wlen;
    dcr->dev->spool_size += hlen + wlen;
    if ((dcr->max_job_spool_size > 0 && dcr->job_spool_size >= dcr->max_job_spool_size) ||
            (dcr->dev->max_spool_size > 0 && dcr->dev->spool_size >= dcr->dev->max_spool_size)) {
        despool = true;
    }
    V(dcr->dev->spool_mutex);
    P(mutex);
    spool_stats.data_size += hlen + wlen;
    if (spool_stats.data_size > spool_stats.max_data_size) {
        spool_stats.max_data_size = spool_stats.data_size;
    }
    V(mutex);
    if (despool) {
        char ec1[30], ec2[30];
        if (dcr->max_job_spool_size > 0) {
            Jmsg(dcr->jcr, M_INFO, 0, _("User specified Job spool size reached: "
                                        "JobSpoolSize=%s MaxJobSpoolSize=%s\n"),
                 edit_uint64_with_commas(dcr->job_spool_size, ec1),
                 edit_uint64_with_commas(dcr->max_job_spool_size, ec2));
        } else {
            Jmsg(dcr->jcr, M_INFO, 0, _("User specified Device spool size reached: "
                                        "DevSpoolSize=%s MaxDevSpoolSize=%s\n"),
                 edit_uint64_with_commas(dcr->dev->spool_size, ec1),
                 edit_uint64_with_commas(dcr->dev->max_spool_size, ec2));
        }

        if (!despool_data(dcr, false)) {
            Pmsg0(000, _("Bad return from despool in write_block.\n"));
            return false;
        }
        /* Despooling cleared these variables so reset them */
        P(dcr->dev->spool_mutex);
        dcr->job_spool_size += hlen + wlen;
        dcr->dev->spool_size += hlen + wlen;
        V(dcr->dev->spool_mutex);
        Jmsg(dcr->jcr, M_INFO, 0, _("Spooling data again ...\n"));
    }


    if (!write_spool_header(dcr)) {
        return false;
    }
    if (!write_spool_data(dcr)) {
        return false;
    }

    Dmsg2(800, "Wrote block FI=%d LI=%d\n", block->FirstIndex, block->LastIndex);
    empty_block(block);
    return true;
}
Beispiel #7
0
/*
 * Read the volume label
 *
 *  If dcr->VolumeName == NULL, we accept any Bareos Volume
 *  If dcr->VolumeName[0] == 0, we accept any Bareos Volume
 *  otherwise dcr->VolumeName must match the Volume.
 *
 *  If VolName given, ensure that it matches
 *
 *  Returns VOL_  code as defined in record.h
 *    VOL_NOT_READ
 *    VOL_OK                          good label found
 *    VOL_NO_LABEL                    volume not labeled
 *    VOL_IO_ERROR                    I/O error reading tape
 *    VOL_NAME_ERROR                  label has wrong name
 *    VOL_CREATE_ERROR                Error creating label
 *    VOL_VERSION_ERROR               label has wrong version
 *    VOL_LABEL_ERROR                 bad label type
 *    VOL_NO_MEDIA                    no media in drive
 *
 *  The dcr block is emptied on return, and the Volume is
 *    rewound.
 */
int read_dev_volume_label(DCR *dcr)
{
   JCR *jcr = dcr->jcr;
   DEVICE * volatile dev = dcr->dev;
   char *VolName = dcr->VolumeName;
   DEV_RECORD *record;
   bool ok = false;
   DEV_BLOCK *block = dcr->block;
   int status;
   bool want_ansi_label;
   bool have_ansi_label = false;

   Dmsg4(100, "Enter read_volume_label res=%d device=%s vol=%s dev_Vol=%s\n",
      dev->num_reserved(), dev->print_name(), VolName,
      dev->VolHdr.VolumeName[0]?dev->VolHdr.VolumeName:"*NULL*");

   if (!dev->is_open()) {
      if (!dev->open(dcr, OPEN_READ_ONLY)) {
         return VOL_IO_ERROR;
      }
   }

   dev->clear_labeled();
   dev->clear_append();
   dev->clear_read();
   dev->label_type = B_BAREOS_LABEL;

   if (!dev->rewind(dcr)) {
      Mmsg(jcr->errmsg, _("Couldn't rewind device %s: ERR=%s\n"),
         dev->print_name(), dev->print_errmsg());
      Dmsg1(130, "return VOL_NO_MEDIA: %s", jcr->errmsg);
      return VOL_NO_MEDIA;
   }
   bstrncpy(dev->VolHdr.Id, "**error**", sizeof(dev->VolHdr.Id));

   /*
    * The stored plugin handling the bsdEventLabelRead event can abort
    * the reading of the label by returning a non bRC_OK.
    */
   if (generate_plugin_event(jcr, bsdEventLabelRead, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelRead plugin event.\n");
      return VOL_NO_MEDIA;
   }

   /*
    * Read ANSI/IBM label if so requested
    */
   want_ansi_label = dcr->VolCatInfo.LabelType != B_BAREOS_LABEL ||
                     dcr->device->label_type != B_BAREOS_LABEL;
   if (want_ansi_label || dev->has_cap(CAP_CHECKLABELS)) {
      status = read_ansi_ibm_label(dcr);
      /*
       * If we want a label and didn't find it, return error
       */
      if (want_ansi_label && status != VOL_OK) {
         goto bail_out;
      }
      if (status == VOL_NAME_ERROR || status == VOL_LABEL_ERROR) {
         Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
              dev->print_name(), VolName, dev->VolHdr.VolumeName);
         if (!dev->poll && jcr->label_errors++ > 100) {
            Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
         }
         goto bail_out;
      }
      if (status != VOL_OK) {        /* Not an ANSI/IBM label, so re-read */
         dev->rewind(dcr);
      } else {
         have_ansi_label = true;
      }
   }

   /*
    * Read the Bareos Volume label block
    */
   record = new_record();
   empty_block(block);

   Dmsg0(130, "Big if statement in read_volume_label\n");
   if (!dcr->read_block_from_dev(NO_BLOCK_NUMBER_CHECK)) {
      Mmsg(jcr->errmsg, _("Requested Volume \"%s\" on %s is not a Bareos "
           "labeled Volume, because: ERR=%s"), NPRT(VolName),
           dev->print_name(), dev->print_errmsg());
      Dmsg1(130, "%s", jcr->errmsg);
   } else if (!read_record_from_block(dcr, record)) {
      Mmsg(jcr->errmsg, _("Could not read Volume label from block.\n"));
      Dmsg1(130, "%s", jcr->errmsg);
   } else if (!unser_volume_label(dev, record)) {
      Mmsg(jcr->errmsg, _("Could not unserialize Volume label: ERR=%s\n"),
         dev->print_errmsg());
      Dmsg1(130, "%s", jcr->errmsg);
   } else if (!bstrcmp(dev->VolHdr.Id, BareosId) &&
              !bstrcmp(dev->VolHdr.Id, OldBaculaId) &&
              !bstrcmp(dev->VolHdr.Id, OlderBaculaId)) {
      Mmsg(jcr->errmsg, _("Volume Header Id bad: %s\n"), dev->VolHdr.Id);
      Dmsg1(130, "%s", jcr->errmsg);
   } else {
      ok = true;
   }
   free_record(record);              /* finished reading Volume record */

   if (!dev->is_volume_to_unload()) {
      dev->clear_unload();
   }

   if (!ok) {
      if (forge_on || jcr->ignore_label_errors) {
         dev->set_labeled();         /* set has Bareos label */
         Jmsg(jcr, M_ERROR, 0, "%s", jcr->errmsg);
         goto ok_out;
      }
      Dmsg0(100, "No volume label - bailing out\n");
      status = VOL_NO_LABEL;
      goto bail_out;
   }

   /*
    * At this point, we have read the first Bareos block, and
    * then read the Bareos Volume label. Now we need to
    * make sure we have the right Volume.
    */
   if (dev->VolHdr.VerNum != BareosTapeVersion &&
       dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion1 &&
       dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion2 &&
       dev->VolHdr.VerNum != OldCompatibleBareosTapeVersion3) {
      Mmsg(jcr->errmsg, _("Volume on %s has wrong Bareos version. Wanted %d got %d\n"),
           dev->print_name(), BareosTapeVersion, dev->VolHdr.VerNum);
      Dmsg1(130, "VOL_VERSION_ERROR: %s", jcr->errmsg);
      status = VOL_VERSION_ERROR;
      goto bail_out;
   }

   /*
    * We are looking for either an unused Bareos tape (PRE_LABEL) or
    * a Bareos volume label (VOL_LABEL)
    */
   if (dev->VolHdr.LabelType != PRE_LABEL && dev->VolHdr.LabelType != VOL_LABEL) {
      Mmsg(jcr->errmsg, _("Volume on %s has bad Bareos label type: %x\n"),
          dev->print_name(), dev->VolHdr.LabelType);
      Dmsg1(130, "%s", jcr->errmsg);
      if (!dev->poll && jcr->label_errors++ > 100) {
         Jmsg(jcr, M_FATAL, 0, _("Too many tries: %s"), jcr->errmsg);
      }
      Dmsg0(150, "return VOL_LABEL_ERROR\n");
      status = VOL_LABEL_ERROR;
      goto bail_out;
   }

   dev->set_labeled();               /* set has Bareos label */

   /* Compare Volume Names */
   Dmsg2(130, "Compare Vol names: VolName=%s hdr=%s\n", VolName?VolName:"*", dev->VolHdr.VolumeName);
   if (VolName && *VolName && *VolName != '*' && !bstrcmp(dev->VolHdr.VolumeName, VolName)) {
      Mmsg(jcr->errmsg, _("Wrong Volume mounted on device %s: Wanted %s have %s\n"),
           dev->print_name(), VolName, dev->VolHdr.VolumeName);
      Dmsg1(130, "%s", jcr->errmsg);
      /*
       * Cancel Job if too many label errors
       *  => we are in a loop
       */
      if (!dev->poll && jcr->label_errors++ > 100) {
         Jmsg(jcr, M_FATAL, 0, "Too many tries: %s", jcr->errmsg);
      }
      Dmsg0(150, "return VOL_NAME_ERROR\n");
      status = VOL_NAME_ERROR;
      goto bail_out;
   }

   if (debug_level >= 200) {
      dump_volume_label(dev);
   }

   Dmsg0(130, "Leave read_volume_label() VOL_OK\n");
   /*
    * If we are a streaming device, we only get one chance to read
    */
   if (!dev->has_cap(CAP_STREAM)) {
      dev->rewind(dcr);
      if (have_ansi_label) {
         status = read_ansi_ibm_label(dcr);
         /*
          * If we want a label and didn't find it, return error
          */
         if (status != VOL_OK) {
            goto bail_out;
         }
      }
   }

   Dmsg1(100, "Call reserve_volume=%s\n", dev->VolHdr.VolumeName);
   if (reserve_volume(dcr, dev->VolHdr.VolumeName) == NULL) {
      Mmsg2(jcr->errmsg, _("Could not reserve volume %s on %s\n"),
           dev->VolHdr.VolumeName, dev->print_name());
      Dmsg2(150, "Could not reserve volume %s on %s\n", dev->VolHdr.VolumeName, dev->print_name());
      status = VOL_NAME_ERROR;
      goto bail_out;
   }

ok_out:
   /*
    * The stored plugin handling the bsdEventLabelVerified event can override
    * the return value e.g. although we think the volume label is ok the plugin
    * has reasons to override that. So when the plugin returns something else
    * then bRC_OK it want to tell us the volume is not OK to use and as
    * such we return VOL_NAME_ERROR as error although it might not be te
    * best error it should be sufficient.
    */
   if (generate_plugin_event(jcr, bsdEventLabelVerified, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelVerified plugin event.\n");
      status = VOL_NAME_ERROR;
      goto bail_out;
   }
   empty_block(block);
   return VOL_OK;

bail_out:
   empty_block(block);
   dev->rewind(dcr);
   Dmsg1(150, "return %d\n", status);
   return status;
}
Beispiel #8
0
/*
 * Write a Volume Label
 *  !!! Note, this is ONLY used for writing
 *            a fresh volume label.  Any data
 *            after the label will be destroyed,
 *            in fact, we write the label 5 times !!!!
 *
 *  This routine should be used only when labeling a blank tape.
 */
bool write_new_volume_label_to_dev(DCR *dcr, const char *VolName,
                                   const char *PoolName, bool relabel)
{
   JCR *jcr = dcr->jcr;
   DEVICE *dev = dcr->dev;
   DEV_BLOCK *block = dcr->block;

   Dmsg0(150, "write_volume_label()\n");
   if (*VolName == 0) {
      Pmsg0(0, "=== ERROR: write_new_volume_label_to_dev called with NULL VolName\n");
      goto bail_out;
   }

   if (relabel) {
      volume_unused(dcr);             /* mark current volume unused */
      /* Truncate device */
      if (!dev->truncate(dcr)) {
         goto bail_out;
      }
      if (!dev->is_tape()) {
         dev->close(dcr);             /* make sure file closed for rename */
      }
   }

   /* Set the new filename for open, ... */
   dev->setVolCatName(VolName);
   dcr->setVolCatName(VolName);
   Dmsg1(150, "New VolName=%s\n", VolName);
   if (!dev->open(dcr, OPEN_READ_WRITE)) {
      /* If device is not tape, attempt to create it */
      if (dev->is_tape() || !dev->open(dcr, CREATE_READ_WRITE)) {
         Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
               dev->print_name(), dcr->VolumeName, dev->bstrerror());
         goto bail_out;
      }
   }
   Dmsg1(150, "Label type=%d\n", dev->label_type);

   /*
    * Let any stored plugin know that we are about to write a new label to the volume.
    */
   if (generate_plugin_event(jcr, bsdEventLabelWrite, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelWrite plugin event.\n");
      goto bail_out;
   }

   for ( ;; ) {
      empty_block(block);
      if (!dev->rewind(dcr)) {
         Dmsg2(130, "Bad status on %s from rewind: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         if (!forge_on) {
            goto bail_out;
         }
      }

      /* Temporarily mark in append state to enable writing */
      dev->set_append();

      /* Create PRE_LABEL */
      create_volume_label(dev, VolName, PoolName);

      /*
       * If we have already detected an ANSI label, re-read it
       *   to skip past it. Otherwise, we write a new one if
       *   so requested.
       */
      if (dev->label_type != B_BAREOS_LABEL) {
         if (read_ansi_ibm_label(dcr) != VOL_OK) {
            dev->rewind(dcr);
            goto bail_out;
         }
      } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, VolName)) {
         goto bail_out;
      }

      create_volume_label_record(dcr, dev, dcr->rec);
      dcr->rec->Stream = 0;
      dcr->rec->maskedStream = 0;

      if (!write_record_to_block(dcr, dcr->rec)) {
         Dmsg2(130, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         goto bail_out;
      } else {
         Dmsg2(130, "Wrote label of %d bytes to %s\n", dcr->rec->data_len, dev->print_name());
      }

      Dmsg0(130, "Call write_block_to_dev()\n");
      if (!dcr->write_block_to_dev()) {
         Dmsg2(130, "Bad Label write on %s: ERR=%s\n", dev->print_name(), dev->print_errmsg());
         goto bail_out;
      }
      break;
   }
   dev = dcr->dev;

   Dmsg0(130, " Wrote block to device\n");

   if (dev->weof(1)) {
      dev->set_labeled();
      write_ansi_ibm_labels(dcr, ANSI_EOF_LABEL, dev->VolHdr.VolumeName);
   }

   if (debug_level >= 20)  {
      dump_volume_label(dev);
   }
   Dmsg0(100, "Call reserve_volume\n");
   if (reserve_volume(dcr, VolName) == NULL) {
      Mmsg2(jcr->errmsg, _("Could not reserve volume %s on %s\n"),
           dev->VolHdr.VolumeName, dev->print_name());
      Dmsg1(100, "%s", jcr->errmsg);
      goto bail_out;
   }
   dev = dcr->dev;                    /* may have changed in reserve_volume */

   dev->clear_append();               /* remove append since this is PRE_LABEL */
   return true;

bail_out:
   volume_unused(dcr);
   dev->clear_volhdr();
   dev->clear_append();               /* remove append since this is PRE_LABEL */
   return false;
}