Example #1
0
bool dvd_close_job(DCR *dcr)
{
   DEVICE *dev = dcr->dev;
   JCR *jcr = dcr->jcr;
   bool ok = true;

   /*
    * If the device is a dvd and WritePartAfterJob
    * is set to yes, open the next part, so, in case of a device
    * that requires mount, it will be written to the device.
    */
   if (dev->is_dvd() && jcr->write_part_after_job && (dev->part_size > 0)) {
      Dmsg1(400, "Writing last part=%d write_partafter_job is set.\n",
         dev->part);
      if (dev->part < dev->num_dvd_parts+1) {
         Jmsg3(jcr, M_FATAL, 0, _("Error writing. Current part less than total number of parts (%d/%d, device=%s)\n"),
               dev->part, dev->num_dvd_parts, dev->print_name());
         dev->dev_errno = EIO;
         ok = false;
      }
      
      if (ok && !dvd_write_part(dcr)) {
         Jmsg2(jcr, M_FATAL, 0, _("Unable to write last on %s: ERR=%s\n"),
               dev->print_name(), dev->bstrerror());
         dev->dev_errno = EIO;
         ok = false;
      }
   }
   return ok;
}
Example #2
0
void win_error(JCR *jcr, const char *prefix, POOLMEM *win32_ofile)
{
    DWORD lerror = GetLastError();
    LPTSTR msg;
    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                  NULL,
                  lerror,
                  0,
                  (LPTSTR)&msg,
                  0,
                  NULL);
    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
    strip_trailing_junk(msg);
    Jmsg3(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
    LocalFree(msg);
}
Example #3
0
File: mac.c Project: AlD/bareos
/*
 * Get response from Storage daemon to a command we sent.
 * Check that the response is OK.
 *
 * Returns: false on failure
 *          true on success
 */
static bool response(JCR *jcr, BSOCK *sd, char *resp, const char *cmd)
{
   if (sd->errors) {
      return false;
                  }
   if (bget_msg(sd) > 0) {
      Dmsg1(110, "<stored: %s", sd->msg);
      if (bstrcmp(sd->msg, resp)) {
         return true;
      }
   }
   if (job_canceled(jcr)) {
      return false;                   /* if canceled avoid useless error messages */
   }
   if (is_bnet_error(sd)) {
      Jmsg2(jcr, M_FATAL, 0, _("Comm error with SD. bad response to %s. ERR=%s\n"),
            cmd, bnet_strerror(sd));
   } else {
      Jmsg3(jcr, M_FATAL, 0, _("Bad response to %s command. Wanted %s, got %s\n"),
            cmd, resp, sd->msg);
   }
   return false;
}
Example #4
0
/*********************************************************************
 * Acquire device for reading.
 *  The drive should have previously been reserved by calling
 *  reserve_device_for_read(). We read the Volume label from the block and
 *  leave the block pointers just after the label.
 *
 *  Returns: NULL if failed for any reason
 *           dcr  if successful
 */
bool acquire_device_for_read(DCR *dcr)
{
   DEVICE *dev;
   JCR *jcr = dcr->jcr;
   bool ok = false;
   bool tape_previously_mounted;
   VOL_LIST *vol;
   bool try_autochanger = true;
   int i;
   int vol_label_status;
   int retry = 0;

   Enter(rdbglvl);
   dev = dcr->dev;
   dev->Lock_read_acquire();
   Dmsg2(rdbglvl, "dcr=%p dev=%p\n", dcr, dcr->dev);
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
   dev->dblock(BST_DOING_ACQUIRE);

   if (dev->num_writers > 0) {
      Jmsg2(jcr, M_FATAL, 0, _("Acquire read: num_writers=%d not zero. Job %d canceled.\n"),
         dev->num_writers, jcr->JobId);
      goto get_out;
   }

   /* Find next Volume, if any */
   vol = jcr->VolList;
   if (!vol) {
      char ed1[50];
      Jmsg(jcr, M_FATAL, 0, _("No volumes specified for reading. Job %s canceled.\n"),
         edit_int64(jcr->JobId, ed1));
      goto get_out;
   }
   jcr->CurReadVolume++;
   for (i=1; i<jcr->CurReadVolume; i++) {
      vol = vol->next;
   }
   if (!vol) {
      Jmsg(jcr, M_FATAL, 0, _("Logic error: no next volume to read. Numvol=%d Curvol=%d\n"),
         jcr->NumReadVolumes, jcr->CurReadVolume);
      goto get_out;                   /* should not happen */
   }
   set_dcr_from_vol(dcr, vol);

   Dmsg2(rdbglvl, "Want Vol=%s Slot=%d\n", vol->VolumeName, vol->Slot);

   /*
    * If the MediaType requested for this volume is not the
    *  same as the current drive, we attempt to find the same
    *  device that was used to write the orginal volume.  If
    *  found, we switch to using that device.
    *
    *  N.B. A lot of routines rely on the dcr pointer not changing
    *    read_records.c even has multiple dcrs cached, so we take care
    *    here to release all important parts of the dcr and re-acquire
    *    them such as the block pointer (size may change), but we do
    *    not release the dcr.
    */
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);
   if (dcr->media_type[0] && !bstrcmp(dcr->media_type, dev->device->media_type)) {
      RCTX rctx;
      DIRSTORE *store;
      int status;

      Jmsg3(jcr, M_INFO, 0, _("Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
                              "  device=%s\n"),
            dcr->media_type, dev->device->media_type, dev->print_name());
      Dmsg3(rdbglvl, "Changing read device. Want Media Type=\"%s\" have=\"%s\"\n"
                     "  device=%s\n",
            dcr->media_type, dev->device->media_type, dev->print_name());

      dev->dunblock(DEV_UNLOCKED);

      lock_reservations();
      memset(&rctx, 0, sizeof(RCTX));
      rctx.jcr = jcr;
      jcr->read_dcr = dcr;
      jcr->reserve_msgs = New(alist(10, not_owned_by_alist));
      rctx.any_drive = true;
      rctx.device_name = vol->device;
      store = new DIRSTORE;
      memset(store, 0, sizeof(DIRSTORE));
      store->name[0] = 0; /* No dir name */
      bstrncpy(store->media_type, vol->MediaType, sizeof(store->media_type));
      bstrncpy(store->pool_name, dcr->pool_name, sizeof(store->pool_name));
      bstrncpy(store->pool_type, dcr->pool_type, sizeof(store->pool_type));
      store->append = false;
      rctx.store = store;
      clean_device(dcr);                     /* clean up the dcr */

      /*
       * Search for a new device
       */
      status = search_res_for_device(rctx);
      release_reserve_messages(jcr);         /* release queued messages */
      unlock_reservations();

      if (status == 1) { /* found new device to use */
         /*
          * Switching devices, so acquire lock on new device,
          *   then release the old one.
          */
         dcr->dev->Lock_read_acquire();      /* lock new one */
         dev->Unlock_read_acquire();         /* release old one */
         dev = dcr->dev;                     /* get new device pointer */
         dev->dblock(BST_DOING_ACQUIRE);

         dcr->VolumeName[0] = 0;
         Jmsg(jcr, M_INFO, 0, _("Media Type change.  New read device %s chosen.\n"),
            dev->print_name());
         Dmsg1(50, "Media Type change.  New read device %s chosen.\n", dev->print_name());
         bstrncpy(dcr->VolumeName, vol->VolumeName, sizeof(dcr->VolumeName));
         dcr->setVolCatName(vol->VolumeName);
         bstrncpy(dcr->media_type, vol->MediaType, sizeof(dcr->media_type));
         dcr->VolCatInfo.Slot = vol->Slot;
         dcr->VolCatInfo.InChanger = vol->Slot > 0;
         bstrncpy(dcr->pool_name, store->pool_name, sizeof(dcr->pool_name));
         bstrncpy(dcr->pool_type, store->pool_type, sizeof(dcr->pool_type));
      } else {
         /* error */
         Jmsg1(jcr, M_FATAL, 0, _("No suitable device found to read Volume \"%s\"\n"),
            vol->VolumeName);
         Dmsg1(rdbglvl, "No suitable device found to read Volume \"%s\"\n", vol->VolumeName);
         goto get_out;
      }
   }
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);

   dev->clear_unload();

   if (dev->vol && dev->vol->is_swapping()) {
      dev->vol->set_slot(vol->Slot);
      Dmsg3(rdbglvl, "swapping: slot=%d Vol=%s dev=%s\n", dev->vol->get_slot(),
         dev->vol->vol_name, dev->print_name());
   }

   init_device_wait_timers(dcr);

   tape_previously_mounted = dev->can_read() || dev->can_append() ||
                             dev->is_labeled();
// tape_initially_mounted = tape_previously_mounted;

   /* Volume info is always needed because of VolParts */
   Dmsg1(rdbglvl, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
   if (!dcr->dir_get_volume_info(GET_VOL_INFO_FOR_READ)) {
      Dmsg2(rdbglvl, "dir_get_vol_info failed for vol=%s: %s\n",
            dcr->VolumeName, jcr->errmsg);
      Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
   }
   dev->set_load();                /* set to load volume */

   for ( ;; ) {
      /* If not polling limit retries */
      if (!dev->poll && retry++ > 10) {
         break;
      }
      dev->clear_labeled();              /* force reread of label */
      if (job_canceled(jcr)) {
         char ed1[50];
         Mmsg1(dev->errmsg, _("Job %s canceled.\n"), edit_int64(jcr->JobId, ed1));
         Jmsg(jcr, M_INFO, 0, dev->errmsg);
         goto get_out;                /* error return */
      }

      dcr->do_unload();
      dcr->do_swapping(false/*!is_writing*/);
      dcr->do_load(false /*!is_writing*/);
      set_dcr_from_vol(dcr, vol);          /* refresh dcr with desired volume info */

      /*
       * This code ensures that the device is ready for
       * reading. If it is a file, it opens it.
       * If it is a tape, it checks the volume name
       */
      Dmsg1(rdbglvl, "stored: open vol=%s\n", dcr->VolumeName);
      if (!dev->open(dcr, OPEN_READ_ONLY)) {
         if (!dev->poll) {
            Jmsg3(jcr, M_WARNING, 0, _("Read open device %s Volume \"%s\" failed: ERR=%s\n"),
                  dev->print_name(), dcr->VolumeName, dev->bstrerror());
         }
         goto default_path;
      }
      Dmsg1(rdbglvl, "opened dev %s OK\n", dev->print_name());

      /* Read Volume Label */
      Dmsg0(rdbglvl, "calling read-vol-label\n");
      vol_label_status = read_dev_volume_label(dcr);
      switch (vol_label_status) {
      case VOL_OK:
         Dmsg0(rdbglvl, "Got correct volume.\n");
         ok = true;
         dev->VolCatInfo = dcr->VolCatInfo;     /* structure assignment */
         break;                    /* got it */
      case VOL_IO_ERROR:
         Dmsg0(rdbglvl, "IO Error\n");
         /*
          * Send error message generated by read_dev_volume_label()
          *  only we really had a tape mounted. This supresses superfluous
          *  error messages when nothing is mounted.
          */
         if (tape_previously_mounted) {
            Jmsg(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
         }
         goto default_path;
      case VOL_NAME_ERROR:
         Dmsg3(rdbglvl, "Vol name=%s want=%s drv=%s.\n", dev->VolHdr.VolumeName,
               dcr->VolumeName, dev->print_name());
         if (dev->is_volume_to_unload()) {
            goto default_path;
         }
         dev->set_unload();              /* force unload of unwanted tape */
         if (!unload_autochanger(dcr, -1)) {
            /* at least free the device so we can re-open with correct volume */
            dev->close(dcr);
            free_volume(dev);
         }
         dev->set_load();
         /* Fall through */
      default:
         Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
default_path:
         Dmsg0(rdbglvl, "default path\n");
         tape_previously_mounted = true;

         /*
          * If the device requires mount, close it, so the device can be ejected.
          */
         if (dev->requires_mount()) {
            dev->close(dcr);
            free_volume(dev);
         }

         /* Call autochanger only once unless ask_sysop called */
         if (try_autochanger) {
            int status;
            Dmsg2(rdbglvl, "calling autoload Vol=%s Slot=%d\n",
                  dcr->VolumeName, dcr->VolCatInfo.Slot);
            status = autoload_device(dcr, 0, NULL);
            if (status > 0) {
               try_autochanger = false;
               continue;              /* try reading volume mounted */
            }
         }

         /* Mount a specific volume and no other */
         Dmsg0(rdbglvl, "calling dir_ask_sysop\n");
         if (!dcr->dir_ask_sysop_to_mount_volume(ST_READREADY)) {
            goto get_out;             /* error return */
         }

         /* Volume info is always needed because of VolParts */
         Dmsg1(150, "dir_get_volume_info vol=%s\n", dcr->VolumeName);
         if (!dcr->dir_get_volume_info(GET_VOL_INFO_FOR_READ)) {
            Dmsg2(150, "dir_get_vol_info failed for vol=%s: %s\n",
                  dcr->VolumeName, jcr->errmsg);
            Jmsg1(jcr, M_WARNING, 0, "Read acquire: %s", jcr->errmsg);
         }
         dev->set_load();                /* set to load volume */

         try_autochanger = true;      /* permit trying the autochanger again */

         continue;                    /* try reading again */
      } /* end switch */
      break;
   } /* end for loop */

   if (!ok) {
      Jmsg1(jcr, M_FATAL, 0, _("Too many errors trying to mount device %s for reading.\n"),
            dev->print_name());
      goto get_out;
   }

   dev->clear_append();
   dev->set_read();
   jcr->sendJobStatus(JS_Running);
   Jmsg(jcr, M_INFO, 0, _("Ready to read from volume \"%s\" on device %s.\n"),
      dcr->VolumeName, dev->print_name());

get_out:
   dev->Lock();
   dcr->clear_reserved();
   /*
    * Normally we are blocked, but in at least one error case above
    *   we are not blocked because we unsuccessfully tried changing
    *   devices.
    */
   if (dev->is_blocked()) {
      dev->dunblock(DEV_LOCKED);
   } else {
      dev->Unlock();               /* dunblock() unlock the device too */
   }
   Dmsg2(rdbglvl, "dcr=%p dev=%p\n", dcr, dcr->dev);
   Dmsg2(rdbglvl, "MediaType dcr=%s dev=%s\n", dcr->media_type, dev->device->media_type);

   dev->Unlock_read_acquire();

   Leave(rdbglvl);
   return ok;
}
Example #5
0
/*
 * Authenticate Storage daemon connection
 */
bool authenticate_storage_daemon(JCR *jcr, STORERES *store)
{
   BSOCK *sd = jcr->store_bsock;
   char dirname[MAX_NAME_LENGTH];
   int tls_local_need = BNET_TLS_NONE;
   int tls_remote_need = BNET_TLS_NONE;
   int compatible = true;
   bool auth_success = false;

   /*
    * Send my name to the Storage daemon then do authentication
    */
   bstrncpy(dirname, director->hdr.name, sizeof(dirname));
   bash_spaces(dirname);
   /* Timeout Hello after 1 min */
   btimer_t *tid = start_bsock_timer(sd, AUTH_TIMEOUT);
   if (!sd->fsend(hello, dirname)) {
      stop_bsock_timer(tid);
      Dmsg1(dbglvl, _("Error sending Hello to Storage daemon. ERR=%s\n"), bnet_strerror(sd));
      Jmsg(jcr, M_FATAL, 0, _("Error sending Hello to Storage daemon. ERR=%s\n"), bnet_strerror(sd));
      return 0;
   }

   /* TLS Requirement */
   if (store->tls_enable) {
     if (store->tls_require) {
        tls_local_need = BNET_TLS_REQUIRED;
     } else {
        tls_local_need = BNET_TLS_OK;
     }
   }

   if (store->tls_authenticate) {
      tls_local_need = BNET_TLS_REQUIRED;
   }

   auth_success = cram_md5_respond(sd, store->password, &tls_remote_need, &compatible);
   if (auth_success) {
      auth_success = cram_md5_challenge(sd, store->password, tls_local_need, compatible);
      if (!auth_success) {
         Dmsg1(dbglvl, "cram_challenge failed for %s\n", sd->who());
      }
   } else {
      Dmsg1(dbglvl, "cram_respond failed for %s\n", sd->who());
   }

   if (!auth_success) {
      stop_bsock_timer(tid);
      Dmsg0(dbglvl, _("Director and Storage daemon passwords or names not the same.\n"));
      Jmsg2(jcr, M_FATAL, 0,
            _("Director unable to authenticate with Storage daemon at \"%s:%d\". Possible causes:\n"
            "Passwords or names not the same or\n"
            "Maximum Concurrent Jobs exceeded on the SD or\n"
            "SD networking messed up (restart daemon).\n"
            "Please see " MANUAL_AUTH_URL " for help.\n"),
            sd->host(), sd->port());
      return 0;
   }

   /* Verify that the remote host is willing to meet our TLS requirements */
   if (tls_remote_need < tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) {
      stop_bsock_timer(tid);
      Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server did not advertise required TLS support.\n"));
      return 0;
   }

   /* Verify that we are willing to meet the remote host's requirements */
   if (tls_remote_need > tls_local_need && tls_local_need != BNET_TLS_OK && tls_remote_need != BNET_TLS_OK) {
      stop_bsock_timer(tid);
      Jmsg(jcr, M_FATAL, 0, _("Authorization problem: Remote server requires TLS.\n"));
      return 0;
   }

   /* Is TLS Enabled? */
   if (tls_local_need >= BNET_TLS_OK && tls_remote_need >= BNET_TLS_OK) {
      /* Engage TLS! Full Speed Ahead! */
      if (!bnet_tls_client(store->tls_ctx, sd, NULL)) {
         stop_bsock_timer(tid);
         Jmsg(jcr, M_FATAL, 0, _("TLS negotiation failed with SD at \"%s:%d\"\n"),
            sd->host(), sd->port());
         return 0;
      }
      if (store->tls_authenticate) {       /* authentication only? */
         sd->free_tls();                   /* yes, stop tls */
      }
   }

   Dmsg1(116, ">stored: %s", sd->msg);
   if (sd->recv() <= 0) {
      stop_bsock_timer(tid);
      Jmsg3(jcr, M_FATAL, 0, _("bdird<stored: \"%s:%s\" bad response to Hello command: ERR=%s\n"),
         sd->who(), sd->host(), sd->bstrerror());
      return 0;
   }
   Dmsg1(110, "<stored: %s", sd->msg);
   stop_bsock_timer(tid);
   if (!bstrncmp(sd->msg, OKhello, sizeof(OKhello))) {
      Dmsg0(dbglvl, _("Storage daemon rejected Hello command\n"));
      Jmsg2(jcr, M_FATAL, 0, _("Storage daemon at \"%s:%d\" rejected Hello command\n"),
         sd->host(), sd->port());
      return 0;
   }
   return 1;
}
Example #6
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;
}
Example #7
0
/*
 * This routine is called only during a Verify
 */
void get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId)
{
   BSOCK   *fd;
   int n, len;
   FILE_DBR fdbr;
   struct stat statf;                 /* file stat */
   struct stat statc;                 /* catalog stat */
   char buf[MAXSTRING];
   POOLMEM *fname = get_pool_memory(PM_MESSAGE);
   int do_Digest = CRYPTO_DIGEST_NONE;
   int32_t file_index = 0;

   memset(&fdbr, 0, sizeof(FILE_DBR));
   fd = jcr->file_bsock;
   fdbr.JobId = JobId;
   jcr->FileIndex = 0;

   Dmsg0(20, "bdird: waiting to receive file attributes\n");
   /*
    * Get Attributes and Signature from File daemon
    * We expect:
    *   FileIndex
    *   Stream
    *   Options or Digest (MD5/SHA1)
    *   Filename
    *   Attributes
    *   Link name  ???
    */
   while ((n=bget_dirmsg(fd)) >= 0 && !job_canceled(jcr)) {
      int32_t stream, full_stream;
      char *attr, *p, *fn;
      char Opts_Digest[MAXSTRING];        /* Verify Opts or MD5/SHA1 digest */

      if (job_canceled(jcr)) {
         free_pool_memory(fname);
         return;
      }
      fname = check_pool_memory_size(fname, fd->msglen);
      jcr->fname = check_pool_memory_size(jcr->fname, fd->msglen);
      Dmsg1(200, "Atts+Digest=%s\n", fd->msg);
      if ((len = sscanf(fd->msg, "%ld %d %100s", &file_index, &full_stream,
            fname)) != 3) {
         Jmsg3(jcr, M_FATAL, 0, _("bird<filed: bad attributes, expected 3 fields got %d\n"
" mslen=%d msg=%s\n"), len, fd->msglen, fd->msg);
         free_pool_memory(fname);
         return;
      }
      stream = full_stream & STREAMMASK_TYPE;
      Dmsg4(30, "Got hdr: FilInx=%d FullStream=%d Stream=%d fname=%s.\n", file_index, full_stream, stream, fname);

      /*
       * We read the Options or Signature into fname
       *  to prevent overrun, now copy it to proper location.
       */
      bstrncpy(Opts_Digest, fname, sizeof(Opts_Digest));
      p = fd->msg;
      skip_nonspaces(&p);             /* skip FileIndex */
      skip_spaces(&p);
      skip_nonspaces(&p);             /* skip Stream */
      skip_spaces(&p);
      skip_nonspaces(&p);             /* skip Opts_Digest */
      p++;                            /* skip space */
      fn = fname;
      while (*p != 0) {
         *fn++ = *p++;                /* copy filename */
      }
      *fn = *p++;                     /* term filename and point to attribs */
      attr = p;
      /*
       * Got attributes stream, decode it
       */
      if (stream == STREAM_UNIX_ATTRIBUTES || stream == STREAM_UNIX_ATTRIBUTES_EX) {
         int32_t LinkFIf, LinkFIc;
         Dmsg2(400, "file_index=%d attr=%s\n", file_index, attr);
         jcr->JobFiles++;
         jcr->FileIndex = file_index;    /* remember attribute file_index */
         jcr->previous_jr.FileIndex = file_index;
         decode_stat(attr, &statf, sizeof(statf), &LinkFIf);  /* decode file stat packet */
         do_Digest = CRYPTO_DIGEST_NONE;
         jcr->fn_printed = false;
         pm_strcpy(jcr->fname, fname);  /* move filename into JCR */

         Dmsg2(040, "dird<filed: stream=%d %s\n", stream, jcr->fname);
         Dmsg1(020, "dird<filed: attr=%s\n", attr);

         /*
          * Find equivalent record in the database
          */
         fdbr.FileId = 0;
         if (!db_get_file_attributes_record(jcr, jcr->db, jcr->fname,
              &jcr->previous_jr, &fdbr)) {
            Jmsg(jcr, M_INFO, 0, _("New file: %s\n"), jcr->fname);
            Dmsg1(020, _("File not in catalog: %s\n"), jcr->fname);
            jcr->setJobStatus(JS_Differences);
            continue;
         } else {
            /*
             * mark file record as visited by stuffing the
             * current JobId, which is unique, into the MarkId field.
             */
            db_mark_file_record(jcr, jcr->db, fdbr.FileId, jcr->JobId);
         }

         Dmsg3(400, "Found %s in catalog. inx=%d Opts=%s\n", jcr->fname,
            file_index, Opts_Digest);
         decode_stat(fdbr.LStat, &statc, sizeof(statc), &LinkFIc); /* decode catalog stat */
         /*
          * Loop over options supplied by user and verify the
          * fields he requests.
          */
         for (p=Opts_Digest; *p; p++) {
            char ed1[30], ed2[30];
            switch (*p) {
            case 'i':                /* compare INODEs */
               if (statc.st_ino != statf.st_ino) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_ino   differ. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_ino, ed1),
                     edit_uint64((uint64_t)statf.st_ino, ed2));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'p':                /* permissions bits */
               if (statc.st_mode != statf.st_mode) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_mode  differ. Cat: %x File: %x\n"),
                     (uint32_t)statc.st_mode, (uint32_t)statf.st_mode);
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'n':                /* number of links */
               if (statc.st_nlink != statf.st_nlink) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_nlink differ. Cat: %d File: %d\n"),
                     (uint32_t)statc.st_nlink, (uint32_t)statf.st_nlink);
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'u':                /* user id */
               if (statc.st_uid != statf.st_uid) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_uid   differ. Cat: %u File: %u\n"),
                     (uint32_t)statc.st_uid, (uint32_t)statf.st_uid);
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'g':                /* group id */
               if (statc.st_gid != statf.st_gid) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_gid   differ. Cat: %u File: %u\n"),
                     (uint32_t)statc.st_gid, (uint32_t)statf.st_gid);
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 's':                /* size */
               if (statc.st_size != statf.st_size) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_size  differ. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_size, ed1),
                     edit_uint64((uint64_t)statf.st_size, ed2));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'a':                /* access time */
               if (statc.st_atime != statf.st_atime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_atime differs\n"));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'm':
               if (statc.st_mtime != statf.st_mtime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_mtime differs\n"));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'c':                /* ctime */
               if (statc.st_ctime != statf.st_ctime) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_ctime differs\n"));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case 'd':                /* file size decrease */
               if (statc.st_size > statf.st_size) {
                  prt_fname(jcr);
                  Jmsg(jcr, M_INFO, 0, _("      st_size  decrease. Cat: %s File: %s\n"),
                     edit_uint64((uint64_t)statc.st_size, ed1),
                     edit_uint64((uint64_t)statf.st_size, ed2));
                  jcr->setJobStatus(JS_Differences);
               }
               break;
            case '5':                /* compare MD5 */
               Dmsg1(500, "set Do_MD5 for %s\n", jcr->fname);
               do_Digest = CRYPTO_DIGEST_MD5;
               break;
            case '1':                 /* compare SHA1 */
               do_Digest = CRYPTO_DIGEST_SHA1;
               break;
            case ':':
            case 'V':
            default:
               break;
            }
         }
      /*
       * Got Digest Signature from Storage daemon
       *  It came across in the Opts_Digest field.
       */
      } else if (crypto_digest_stream_type(stream) != CRYPTO_DIGEST_NONE) {
         Dmsg2(400, "stream=Digest inx=%d Digest=%s\n", file_index, Opts_Digest);
         /*
          * When ever we get a digest it MUST have been
          * preceded by an attributes record, which sets attr_file_index
          */
         if (jcr->FileIndex != (uint32_t)file_index) {
            Jmsg2(jcr, M_FATAL, 0, _("MD5/SHA1 index %d not same as attributes %d\n"),
               file_index, jcr->FileIndex);
            free_pool_memory(fname);
            return;
         }
         if (do_Digest != CRYPTO_DIGEST_NONE) {
            db_escape_string(jcr, jcr->db, buf, Opts_Digest, strlen(Opts_Digest));
            if (strcmp(buf, fdbr.Digest) != 0) {
               prt_fname(jcr);
               Jmsg(jcr, M_INFO, 0, _("      %s differs. File=%s Cat=%s\n"),
                    stream_to_ascii(stream), buf, fdbr.Digest);
               jcr->setJobStatus(JS_Differences);
            }
            do_Digest = CRYPTO_DIGEST_NONE;
         }
      }
      jcr->JobFiles = file_index;
   }
   if (fd->is_error()) {
      berrno be;
      Jmsg2(jcr, M_FATAL, 0, _("bdird<filed: bad attributes from filed n=%d : %s\n"),
                        n, be.bstrerror());
      free_pool_memory(fname);
      return;
   }

   /* Now find all the files that are missing -- i.e. all files in
    *  the database where the MarkId != current JobId
    */
   jcr->fn_printed = false;
   bsnprintf(buf, sizeof(buf),
      "SELECT Path.Path,Filename.Name FROM File,Path,Filename "
      "WHERE File.JobId=%d AND File.FileIndex > 0 "
      "AND File.MarkId!=%d AND File.PathId=Path.PathId "
      "AND File.FilenameId=Filename.FilenameId",
         JobId, jcr->JobId);
   /* missing_handler is called for each file found */
   db_sql_query(jcr->db, buf, missing_handler, (void *)jcr);
   if (jcr->fn_printed) {
      jcr->setJobStatus(JS_Differences);
   }
   free_pool_memory(fname);
}
Example #8
0
DEVICE *m_init_dev(JCR *jcr, DEVRES *device)
{
   struct stat statp;
   int errstat;
   DCR *dcr = NULL;
   DEVICE *dev = NULL;
   uint32_t max_bs;

   /* If no device type specified, try to guess */
   if (!device->dev_type) {
      /* Check that device is available */
      if (stat(device->device_name, &statp) < 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to stat device %s: ERR=%s\n"),
            device->device_name, be.bstrerror());
         return NULL;
      }
      if (S_ISDIR(statp.st_mode)) {
         device->dev_type = B_FILE_DEV;
      } else if (S_ISCHR(statp.st_mode)) {
         device->dev_type = B_TAPE_DEV;
      } else if (S_ISFIFO(statp.st_mode)) {
         device->dev_type = B_FIFO_DEV;
#ifdef USE_VTAPE
      /* must set DeviceType = Vtape
       * in normal mode, autodetection is disabled
       */
      } else if (S_ISREG(statp.st_mode)) {
         device->dev_type = B_VTAPE_DEV;
#endif
      } else if (!(device->cap_bits & CAP_REQMOUNT)) {
         Jmsg2(jcr, M_ERROR, 0, _("%s is an unknown device type. Must be tape or directory\n"
               " or have RequiresMount=yes for DVD. st_mode=%x\n"),
            device->device_name, statp.st_mode);
         return NULL;
      } else {
         device->dev_type = B_DVD_DEV;
      }
      if (strcmp(device->device_name, "/dev/null") == 0) {
         device->dev_type = B_NULL_DEV;
      }
   }
   switch (device->dev_type) {
   case B_DVD_DEV:
      Jmsg0(jcr, M_FATAL, 0, _("DVD support is now deprecated.\n"));
      return NULL;
   case B_VTAPE_DEV:
      dev = New(vtape);
      break;
#ifdef USE_FTP
   case B_FTP_DEV:
      dev = New(ftp_device);
      break;
#endif
   case B_TAPE_DEV:
      dev = New(tape_dev);
      break;
   case B_FILE_DEV:
   case B_FIFO_DEV:
   case B_NULL_DEV:
      dev = New(file_dev);
      break;
   default:
      return NULL;
   }
   dev->clear_slot();         /* unknown */

   /* Copy user supplied device parameters from Resource */
   dev->dev_name = get_memory(strlen(device->device_name)+1);
   pm_strcpy(dev->dev_name, device->device_name);
   dev->prt_name = get_memory(strlen(device->device_name) + strlen(device->hdr.name) + 20);
   /* We edit "Resource-name" (physical-name) */
   Mmsg(dev->prt_name, "\"%s\" (%s)", device->hdr.name, device->device_name);
   Dmsg1(400, "Allocate dev=%s\n", dev->print_name());
   dev->capabilities = device->cap_bits;
   dev->min_free_space = device->min_free_space;
   dev->min_block_size = device->min_block_size;
   dev->max_block_size = device->max_block_size;
   dev->max_volume_size = device->max_volume_size;
   dev->max_file_size = device->max_file_size;
   dev->max_concurrent_jobs = device->max_concurrent_jobs;
   dev->volume_capacity = device->volume_capacity;
   dev->max_rewind_wait = device->max_rewind_wait;
   dev->max_open_wait = device->max_open_wait;
   dev->vol_poll_interval = device->vol_poll_interval;
   dev->max_spool_size = device->max_spool_size;
   dev->drive_index = device->drive_index;
   dev->enabled = device->enabled;
   dev->autoselect = device->autoselect;
   dev->read_only = device->read_only;
   dev->dev_type = device->dev_type;
   dev->device = device;
   if (dev->is_tape()) { /* No parts on tapes */
      dev->max_part_size = 0;
   } else {
      dev->max_part_size = device->max_part_size;
   }
   /* Sanity check */
   if (dev->vol_poll_interval && dev->vol_poll_interval < 60) {
      dev->vol_poll_interval = 60;
   }

   if (!device->dev) {
      /* The first time we create a DEVICE from the DEVRES, we keep a pointer
       * to the DEVICE accessible from the DEVRES.
       */
      device->dev = dev;
   }

   if (dev->is_fifo()) {
      dev->capabilities |= CAP_STREAM; /* set stream device */
   }

   /* If the device requires mount :
    * - Check that the mount point is available
    * - Check that (un)mount commands are defined
    */
   if (dev->is_file() && dev->requires_mount()) {
      if (!device->mount_point || stat(device->mount_point, &statp) < 0) {
         berrno be;
         dev->dev_errno = errno;
         Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"),
            device->mount_point, be.bstrerror());
      }

      if (!device->mount_command || !device->unmount_command) {
         Jmsg0(jcr, M_ERROR_TERM, 0, _("Mount and unmount commands must defined for a device which requires mount.\n"));
      }
   } 

   /* Keep the device ID in the DEVICE struct to identify the hardware */
   if (dev->is_file() && stat(dev->archive_name(), &statp) == 0) {
         dev->devno = statp.st_dev;
   } 

   /* Sanity check */
   if (dev->max_block_size == 0) {
      max_bs = DEFAULT_BLOCK_SIZE;
   } else {
      max_bs = dev->max_block_size;
   }
   if (dev->min_block_size > max_bs) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Min block size > max on device %s\n"),
           dev->print_name());
   }
   if (dev->max_block_size > 4096000) {
      Jmsg3(jcr, M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"),
         dev->max_block_size, dev->print_name(), DEFAULT_BLOCK_SIZE);
      dev->max_block_size = 0;
   }
   if (dev->max_block_size % TAPE_BSIZE != 0) {
      Jmsg3(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size=%d.\n"),
         dev->max_block_size, dev->print_name(), TAPE_BSIZE);
   }
   if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Max Vol Size < 8 * Max Block Size for device %s\n"),
           dev->print_name());
   }

   dev->errmsg = get_pool_memory(PM_EMSG);
   *dev->errmsg = 0;

   if ((errstat = dev->init_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = pthread_cond_init(&dev->wait, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = pthread_cond_init(&dev->wait_next_vol, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = pthread_mutex_init(&dev->spool_mutex, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init spool mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = dev->init_acquire_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = dev->init_read_acquire_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init read acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = dev->init_volcat_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init volcat mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
   if ((errstat = dev->init_dcrs_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init dcrs mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   dev->set_mutex_priorities();

#ifdef xxx
   if ((errstat = rwl_init(&dev->lock)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
#endif

   dev->clear_opened();
   dev->attached_dcrs = New(dlist(dcr, &dcr->dev_link));
   Dmsg2(100, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name);
   dev->initiated = true;

   return dev;
}
Example #9
0
File: dev.c Project: dl5rcw/bareos
/*
 * Set the block size of the device.
 * If the volume block size is zero, we set the max block size to what is
 * configured in the device resource i.e. dev->device->max_block_size.
 *
 * If dev->device->max_block_size is zero, do nothing and leave dev->max_block_size as it is.
 */
void DEVICE::set_blocksizes(DCR *dcr) {

   DEVICE* dev = this;
   JCR* jcr = dcr->jcr;
   uint32_t max_bs;

   Dmsg4(100, "Device %s has dev->device->max_block_size of %u and dev->max_block_size of %u, dcr->VolMaxBlocksize is %u\n",
         dev->print_name(), dev->device->max_block_size, dev->max_block_size, dcr->VolMaxBlocksize);

   if (dcr->VolMaxBlocksize == 0 && dev->device->max_block_size != 0) {
      Dmsg2(100, "setting dev->max_block_size to dev->device->max_block_size=%u "
                 "on device %s because dcr->VolMaxBlocksize is 0\n",
            dev->device->max_block_size, dev->print_name());
      dev->min_block_size = dev->device->min_block_size;
      dev->max_block_size = dev->device->max_block_size;
   } else if (dcr->VolMaxBlocksize != 0) {
       dev->min_block_size = dcr->VolMinBlocksize;
       dev->max_block_size = dcr->VolMaxBlocksize;
   }

   /*
    * Sanity check
    */
   if (dev->max_block_size == 0) {
      max_bs = DEFAULT_BLOCK_SIZE;
   } else {
      max_bs = dev->max_block_size;
   }

   if (dev->min_block_size > max_bs) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Min block size > max on device %s\n"), dev->print_name());
   }

   if (dev->max_block_size > MAX_BLOCK_LENGTH) {
      Jmsg3(jcr, M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"),
            dev->max_block_size, dev->print_name(), DEFAULT_BLOCK_SIZE);
      dev->max_block_size = 0;
   }

   if (dev->max_block_size % TAPE_BSIZE != 0) {
      Jmsg3(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size=%d.\n"),
            dev->max_block_size, dev->print_name(), TAPE_BSIZE);
   }

   if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Max Vol Size < 8 * Max Block Size for device %s\n"), dev->print_name());
   }

   Dmsg3(100, "set minblocksize to %d, maxblocksize to %d on device %s\n",
         dev->min_block_size, dev->max_block_size, dev->print_name());

   /*
    * If blocklen is not dev->max_block_size create a new block with the right size.
    * (as header is always dev->label_block_size which is preset with DEFAULT_BLOCK_SIZE)
    */
   if (dcr->block) {
     if (dcr->block->buf_len != dev->max_block_size) {
         Dmsg2(100, "created new block of buf_len: %u on device %s\n",
               dev->max_block_size, dev->print_name());
         free_block(dcr->block);
         dcr->block = new_block(dev);
         Dmsg2(100, "created new block of buf_len: %u on device %s, freeing block\n",
               dcr->block->buf_len, dev->print_name());
      }
   }
}
Example #10
0
File: dev.c Project: dl5rcw/bareos
static inline DEVICE *m_init_dev(JCR *jcr, DEVRES *device, bool new_init)
{
   struct stat statp;
   int errstat;
   DCR *dcr = NULL;
   DEVICE *dev = NULL;
   uint32_t max_bs;

   Dmsg1(400, "max_block_size in device res is %u\n", device->max_block_size);

   /*
    * If no device type specified, try to guess
    */
   if (!device->dev_type) {
      /*
       * Check that device is available
       */
      if (stat(device->device_name, &statp) < 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to stat device %s: ERR=%s\n"),
            device->device_name, be.bstrerror());
         return NULL;
      }
      if (S_ISDIR(statp.st_mode)) {
         device->dev_type = B_FILE_DEV;
      } else if (S_ISCHR(statp.st_mode)) {
         device->dev_type = B_TAPE_DEV;
      } else if (S_ISFIFO(statp.st_mode)) {
         device->dev_type = B_FIFO_DEV;
      } else if (!bit_is_set(CAP_REQMOUNT, device->cap_bits)) {
         Jmsg2(jcr, M_ERROR, 0,
               _("%s is an unknown device type. Must be tape or directory, st_mode=%x\n"),
               device->device_name, statp.st_mode);
         return NULL;
      }
   }

   /*
    * See what type of device is wanted.
    */
   switch (device->dev_type) {
   /*
    * When using dynamic loading use the init_backend_dev() function
    * for any type of device not being of the type file.
    */
#ifndef HAVE_DYNAMIC_SD_BACKENDS
#ifdef HAVE_GFAPI
   case B_GFAPI_DEV:
      dev = New(gfapi_device);
      break;
#endif
#ifdef HAVE_OBJECTSTORE
   case B_OBJECT_STORE_DEV:
      dev = New(object_store_device);
      break;
#endif
#ifdef HAVE_RADOS
   case B_RADOS_DEV:
      dev = New(rados_device);
      break;
#endif
#ifdef HAVE_CEPHFS
   case B_CEPHFS_DEV:
      dev = New(cephfs_device);
      break;
#endif
#ifdef HAVE_ELASTO
   case B_ELASTO_DEV:
      dev = New(elasto_device);
      break;
#endif
#ifdef HAVE_WIN32
   case B_TAPE_DEV:
      dev = New(win32_tape_device);
      break;
   case B_FIFO_DEV:
      dev = New(win32_fifo_device);
      break;
#else
   case B_TAPE_DEV:
      dev = New(unix_tape_device);
      break;
   case B_FIFO_DEV:
      dev = New(unix_fifo_device);
      break;
#endif
#endif /* HAVE_DYNAMIC_SD_BACKENDS */
#ifdef HAVE_WIN32
   case B_FILE_DEV:
      dev = New(win32_file_device);
      break;
#else
   case B_FILE_DEV:
      dev = New(unix_file_device);
      break;
#endif
   default:
#ifdef HAVE_DYNAMIC_SD_BACKENDS
      dev = init_backend_dev(jcr, device->dev_type);
#endif
      break;
   }

   if (!dev) {
      Jmsg2(jcr, M_ERROR, 0, _("%s has an unknown device type %d\n"),
            device->device_name, device->dev_type);
      return NULL;
   }
   dev->clear_slot();         /* unknown */

   /*
    * Copy user supplied device parameters from Resource
    */
   dev->dev_name = get_memory(strlen(device->device_name) + 1);
   pm_strcpy(dev->dev_name, device->device_name);
   if (device->device_options) {
      dev->dev_options = get_memory(strlen(device->device_options) + 1);
      pm_strcpy(dev->dev_options, device->device_options);
   }
   dev->prt_name = get_memory(strlen(device->device_name) + strlen(device->name()) + 20);

   /*
    * We edit "Resource-name" (physical-name)
    */
   Mmsg(dev->prt_name, "\"%s\" (%s)", device->name(), device->device_name);
   Dmsg1(400, "Allocate dev=%s\n", dev->print_name());
   copy_bits(CAP_MAX, device->cap_bits, dev->capabilities);

   /*
    * current block sizes
    */
   dev->min_block_size = device->min_block_size;
   dev->max_block_size = device->max_block_size;
   dev->max_volume_size = device->max_volume_size;
   dev->max_file_size = device->max_file_size;
   dev->max_concurrent_jobs = device->max_concurrent_jobs;
   dev->volume_capacity = device->volume_capacity;
   dev->max_rewind_wait = device->max_rewind_wait;
   dev->max_open_wait = device->max_open_wait;
   dev->max_open_vols = device->max_open_vols;
   dev->vol_poll_interval = device->vol_poll_interval;
   dev->max_spool_size = device->max_spool_size;
   dev->drive = device->drive;
   dev->drive_index = device->drive_index;
   dev->autoselect = device->autoselect;
   dev->norewindonclose = device->norewindonclose;
   dev->dev_type = device->dev_type;
   dev->device = device;

   /*
    * Sanity check
    */
   if (dev->vol_poll_interval && dev->vol_poll_interval < 60) {
      dev->vol_poll_interval = 60;
   }
   device->dev = dev;

   if (dev->is_fifo()) {
      dev->set_cap(CAP_STREAM);       /* set stream device */
   }

   /*
    * If the device requires mount :
    * - Check that the mount point is available
    * - Check that (un)mount commands are defined
    */
   if (dev->is_file() && dev->requires_mount()) {
      if (!device->mount_point || stat(device->mount_point, &statp) < 0) {
         berrno be;
         dev->dev_errno = errno;
         Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"),
            device->mount_point, be.bstrerror());
      }

      if (!device->mount_command || !device->unmount_command) {
         Jmsg0(jcr, M_ERROR_TERM, 0, _("Mount and unmount commands must defined for a device which requires mount.\n"));
      }
   }

   /*
    * Sanity check
    */
   if (dev->max_block_size == 0) {
      max_bs = DEFAULT_BLOCK_SIZE;
   } else {
      max_bs = dev->max_block_size;
   }
   if (dev->min_block_size > max_bs) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Min block size > max on device %s\n"), dev->print_name());
   }
   if (dev->max_block_size > MAX_BLOCK_LENGTH) {
      Jmsg3(jcr, M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"),
            dev->max_block_size, dev->print_name(), DEFAULT_BLOCK_SIZE);
      dev->max_block_size = 0;
   }
   if (dev->max_block_size % TAPE_BSIZE != 0) {
      Jmsg3(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size=%d.\n"),
            dev->max_block_size, dev->print_name(), TAPE_BSIZE);
   }
   if (dev->max_volume_size != 0 && dev->max_volume_size < (dev->max_block_size << 4)) {
      Jmsg(jcr, M_ERROR_TERM, 0, _("Max Vol Size < 8 * Max Block Size for device %s\n"), dev->print_name());
   }

   dev->errmsg = get_pool_memory(PM_EMSG);
   *dev->errmsg = 0;

   if ((errstat = dev->init_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   if ((errstat = pthread_cond_init(&dev->wait, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   if ((errstat = pthread_cond_init(&dev->wait_next_vol, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   if ((errstat = pthread_mutex_init(&dev->spool_mutex, NULL)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init spool mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   if ((errstat = dev->init_acquire_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   if ((errstat = dev->init_read_acquire_mutex()) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init read acquire mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }

   dev->set_mutex_priorities();

#ifdef xxx
   if ((errstat = rwl_init(&dev->lock)) != 0) {
      berrno be;
      dev->dev_errno = errstat;
      Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.bstrerror(errstat));
      Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg);
   }
#endif

   dev->clear_opened();
   dev->attached_dcrs = New(dlist(dcr, &dcr->dev_link));
   Dmsg2(100, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name);
   dev->initiated = true;
   Dmsg3(100, "dev=%s dev_max_bs=%u max_bs=%u\n", dev->dev_name, dev->device->max_block_size, dev->max_block_size);

   return dev;
}
Example #11
0
/*
 * Append Data sent from File daemon
 */
bool do_append_data(JCR *jcr, BSOCK *bs, const char *what)
{
   int32_t n, file_index, stream, last_file_index, job_elapsed;
   bool ok = true;
   char buf1[100];
   DCR *dcr = jcr->dcr;
   DEVICE *dev;
   POOLMEM *rec_data;
   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());

   if (!bs->set_buffer_size(dcr->device->max_network_buffer_size, BNET_SETBUF_WRITE)) {
      Jmsg0(jcr, M_FATAL, 0, _("Unable to set network buffer size.\n"));
      goto bail_out;
   }

   if (!acquire_device_for_append(dcr)) {
      goto bail_out;
   }

   if (generate_plugin_event(jcr, bsdEventSetupRecordTranslation, dcr) != bRC_OK) {
      goto bail_out;
   }

   jcr->sendJobStatus(JS_Running);

   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());

   if (!begin_data_spool(dcr) ) {
      goto bail_out;
   }

   if (!begin_attribute_spool(jcr)) {
      discard_data_spool(dcr);
      goto bail_out;
   }

   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 daemon to send data
    */
   if (!bs->fsend(OK_data)) {
      berrno be;
      Jmsg2(jcr, M_FATAL, 0, _("Network send error to %s. ERR=%s\n"),
            what, be.bstrerror(bs->b_errno));
      ok = false;
   }

   /*
    * Get Data from 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 daemon.
       *
       * The stream header consists of the following:
       * - file_index (sequential Bareos file index, base 1)
       * - stream     (Bareos 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(bs)) <= 0) {
         if (n == BNET_SIGNAL && bs->msglen == BNET_EOD) {
            break;                    /* end of data */
         }
         Jmsg2(jcr, M_FATAL, 0, _("Error reading data header from %s. ERR=%s\n"),
               what, bs->bstrerror());
         possible_incomplete_job(jcr, last_file_index);
         ok = false;
         break;
      }

      if (sscanf(bs->msg, "%ld %ld", &file_index, &stream) != 2) {
         Jmsg2(jcr, M_FATAL, 0, _("Malformed data header from %s: %s\n"),
               what, bs->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;
      }
      Jmsg3(jcr, M_FATAL, 0, _("FI=%d from %s not positive or sequential=%d\n"),
            file_index, what, 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 daemon. The data stream is just raw bytes.
       * We save the original data pointer from the record so we can restore
       * that after the loop ends.
       */
      rec_data = dcr->rec->data;
      while ((n = bget_msg(bs)) > 0 && !jcr->is_job_canceled()) {
         dcr->rec->VolSessionId = jcr->VolSessionId;
         dcr->rec->VolSessionTime = jcr->VolSessionTime;
         dcr->rec->FileIndex = file_index;
         dcr->rec->Stream = stream;
         dcr->rec->maskedStream = stream & STREAMMASK_TYPE; /* strip high bits */
         dcr->rec->data_len = bs->msglen;
         dcr->rec->data = bs->msg; /* use message buffer */

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

         ok = dcr->write_record();
         if (!ok) {
            Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
                  dcr->dev->print_name(), dcr->dev->bstrerror());
            break;
         }

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

      /*
       * Restore the original data pointer.
       */
      dcr->rec->data = rec_data;

      if (bs->is_error()) {
         if (!jcr->is_job_canceled()) {
            Dmsg2(350, "Network read error from %s. ERR=%s\n",
                  what, bs->bstrerror());
            Jmsg2(jcr, M_FATAL, 0, _("Network error reading from %s. ERR=%s\n"),
                  what, bs->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 && bs == jcr->file_bsock) {
      /*
       * Terminate connection with FD
       */
      bs->fsend(OK_append);
      do_fd_commands(jcr);               /* finish dialog with FD */
   } else if (bs == jcr->store_bsock) {
      bs->fsend(OK_replicate);
   } else {
      bs->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;
      }
      Dmsg0(90, "back from write_end_session_label()\n");

      /*
       * Flush out final partial block of this session
       */
      if (!dcr->write_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_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);
   }

   /*
    * Don't use time_t for job_elapsed as time_t can be 32 or 64 bits,
    * and the subsequent Jmsg() editing will break
    */
   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;

bail_out:
   jcr->setJobStatus(JS_ErrorTerminated);
   return false;
}
Example #12
0
/*
 * Write a volume label. This is ONLY called if we have a valid Bareos
 *   label of type PRE_LABEL or we are recyling an existing Volume.
 *
 *  Returns: true if OK
 *           false if unable to write it
 */
bool DCR::rewrite_volume_label(bool recycle)
{
   DCR *dcr = this;

   if (!dev->open(dcr, OPEN_READ_WRITE)) {
      Jmsg3(jcr, M_WARNING, 0, _("Open device %s Volume \"%s\" failed: ERR=%s\n"),
            dev->print_name(), dcr->VolumeName, dev->bstrerror());
      return false;
   }

   Dmsg2(190, "set append found freshly labeled volume. fd=%d dev=%x\n", dev->fd(), dev);

   /*
    * Let any stored plugin know that we are (re)writing the label.
    */
   if (generate_plugin_event(jcr, bsdEventLabelWrite, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelWrite plugin event.\n");
      return false;
   }

   dev->VolHdr.LabelType = VOL_LABEL; /* set Volume label */
   dev->set_append();
   if (!write_volume_label_to_block(dcr)) {
      Dmsg0(200, "Error from write volume label.\n");
      return false;
   }

   Dmsg1(150, "wrote vol label to block. Vol=%s\n", dcr->VolumeName);

   dev->setVolCatInfo(false);
   dev->VolCatInfo.VolCatBytes = 0;        /* reset byte count */

   /*
    * If we are not dealing with a streaming device,
    *  write the block now to ensure we have write permission.
    *  It is better to find out now rather than later.
    * We do not write the block now if this is an ANSI label. This
    *  avoids re-writing the ANSI label, which we do not want to do.
    */
   if (!dev->has_cap(CAP_STREAM)) {
      if (!dev->rewind(dcr)) {
         Jmsg2(jcr, M_FATAL, 0, _("Rewind error on device %s: ERR=%s\n"),
               dev->print_name(), dev->print_errmsg());
         return false;
      }
      if (recycle) {
         Dmsg1(150, "Doing recycle. Vol=%s\n", dcr->VolumeName);
         if (!dev->truncate(dcr)) {
            Jmsg2(jcr, M_FATAL, 0, _("Truncate error on device %s: ERR=%s\n"),
                  dev->print_name(), dev->print_errmsg());
            return false;
         }
         if (!dev->open(dcr, OPEN_READ_WRITE)) {
            Jmsg2(jcr, M_FATAL, 0,
               _("Failed to re-open after truncate on device %s: ERR=%s\n"),
               dev->print_name(), dev->print_errmsg());
            return false;
         }
      }

      /*
       * 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);
            return false;
         }
      } else if (!write_ansi_ibm_labels(dcr, ANSI_VOL_LABEL, dev->VolHdr.VolumeName)) {
         return false;
      }

      /* Attempt write to check write permission */
      Dmsg1(200, "Attempt to write to device fd=%d.\n", dev->fd());
      if (!dcr->write_block_to_dev()) {
         Jmsg2(jcr, M_ERROR, 0, _("Unable to write device %s: ERR=%s\n"),
            dev->print_name(), dev->print_errmsg());
         Dmsg0(200, "===ERROR write block to dev\n");
         return false;
      }
   }
   dev->set_labeled();
   /* Set or reset Volume statistics */
   dev->VolCatInfo.VolCatJobs = 0;
   dev->VolCatInfo.VolCatFiles = 0;
   dev->VolCatInfo.VolCatErrors = 0;
   dev->VolCatInfo.VolCatBlocks = 0;
   dev->VolCatInfo.VolCatRBytes = 0;
   if (recycle) {
      dev->VolCatInfo.VolCatMounts++;
      dev->VolCatInfo.VolCatRecycles++;
      dir_create_jobmedia_record(dcr, true);
   } else {
      dev->VolCatInfo.VolCatMounts = 1;
      dev->VolCatInfo.VolCatRecycles = 0;
      dev->VolCatInfo.VolCatWrites = 1;
      dev->VolCatInfo.VolCatReads = 1;
   }
   Dmsg1(150, "dir_update_vol_info. Set Append vol=%s\n", dcr->VolumeName);
   dev->VolCatInfo.VolFirstWritten = time(NULL);
   bstrncpy(dev->VolCatInfo.VolCatStatus, "Append", sizeof(dev->VolCatInfo.VolCatStatus));
   dev->setVolCatName(dcr->VolumeName);
   if (!dir_update_volume_info(dcr, true, true)) {  /* indicate doing relabel */
      return false;
   }
   if (recycle) {
      Jmsg(jcr, M_INFO, 0, _("Recycled volume \"%s\" on device %s, all previous data lost.\n"),
         dcr->VolumeName, dev->print_name());
   } else {
      Jmsg(jcr, M_INFO, 0, _("Wrote label to prelabeled Volume \"%s\" on device %s\n"),
         dcr->VolumeName, dev->print_name());
   }
   /*
    * End writing real Volume label (from pre-labeled tape), or recycling
    *  the volume.
    */
   Dmsg1(150, "OK from rewrite vol label. Vol=%s\n", dcr->VolumeName);

   /*
    * Let any stored plugin know the label was rewritten and as such is verified .
    */
   if (generate_plugin_event(jcr, bsdEventLabelVerified, dcr) != bRC_OK) {
      Dmsg0(200, "Error from bsdEventLabelVerified plugin event.\n");
      return false;
   }

   return true;
}
Example #13
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;
}
Example #14
0
/*
 * Returns false on error
 *         true  on OK, with full_path set to where config file should be
 */
bool CONFIG::find_config_path(POOL_MEM &full_path)
{
   bool found = false;
   POOL_MEM config_dir;
   POOL_MEM config_path_file;

   if (!m_cf) {
      /*
       * No path is given, so use the defaults.
       */
      found = get_config_file(full_path, get_default_configdir(), m_config_default_filename);
      if (!found) {
         config_path_file.strcpy(full_path);
         found = get_config_include_path(full_path, get_default_configdir());
      }
      if (!found) {
         Jmsg2(NULL, M_ERROR, 0,
               _("Failed to read config file at the default locations "
                 "\"%s\" (config file path) and \"%s\" (config include directory).\n"),
               config_path_file.c_str(), full_path.c_str());
      }
   } else if (path_exists(m_cf)) {
      /*
       * Path is given and exists.
       */
      if (path_is_directory(m_cf)) {
         found = get_config_file(full_path, m_cf, m_config_default_filename);
         if (!found) {
            config_path_file.strcpy(full_path);
            found = get_config_include_path(full_path, m_cf);
         }
         if (!found) {
            Jmsg3(NULL, M_ERROR, 0,
                  _("Failed to find configuration files under directory \"%s\". "
                  "Did look for \"%s\" (config file path) and \"%s\" (config include directory).\n"),
                  m_cf, config_path_file.c_str(), full_path.c_str());
         }
      } else {
         full_path.strcpy(m_cf);
         path_get_directory(config_dir, full_path);
         m_config_dir = bstrdup(config_dir.c_str());
         found = true;
      }
   } else if (!m_config_default_filename) {
      /*
       * Compatibility with older versions.
       * If m_config_default_filename is not set,
       * m_cf may contain what is expected in m_config_default_filename.
       */
      found = get_config_file(full_path, get_default_configdir(), m_cf);
      if (!found) {
         Jmsg2(NULL, M_ERROR, 0,
               _("Failed to find configuration files at \"%s\" and \"%s\".\n"),
               m_cf, full_path.c_str());
      }
   } else {
      Jmsg1(NULL, M_ERROR, 0, _("Failed to read config file \"%s\"\n"), m_cf);
   }

   if (found) {
      set_env("BAREOS_CFGDIR", m_config_dir);
   }

   return found;
}
Example #15
0
/**
 * Set file modes, permissions and times
 *
 *  fname is the original filename
 *  ofile is the output filename (may be in a different directory)
 *
 * Returns:  true  on success
 *           false on failure
 */
bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
   struct utimbuf ut;
   mode_t old_mask;
   bool ok = true;
   boffset_t fsize;
 
   if (uid_set) {
      my_uid = getuid();
      my_gid = getgid();
      uid_set = true;
   }

#if defined(HAVE_WIN32)
   if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
       set_win32_attributes(jcr, attr, ofd)) {
       if (is_bopen(ofd)) {
           bclose(ofd);
       }
       pm_strcpy(attr->ofname, "*none*");
       return true;
   }
   if (attr->data_stream == STREAM_WIN32_DATA ||
       attr->data_stream == STREAM_WIN32_GZIP_DATA ||
       attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
      if (is_bopen(ofd)) {
         bclose(ofd);
      }
      pm_strcpy(attr->ofname, "*none*");
      return true;
   }


   /**
    * If Windows stuff failed, e.g. attempt to restore Unix file
    *  to Windows, simply fall through and we will do it the
    *  universal way.
    */
#endif

   old_mask = umask(0);
   if (is_bopen(ofd)) {
      char ec1[50], ec2[50];
      fsize = blseek(ofd, 0, SEEK_END);
      bclose(ofd);                    /* first close file */
      if (attr->type == FT_REG && fsize > 0 && attr->statp.st_size > 0 && 
                        fsize != (boffset_t)attr->statp.st_size) {
         Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
            attr->ofname, edit_uint64(attr->statp.st_size, ec1),
            edit_uint64(fsize, ec2));
      }
   }

   /**
    * We do not restore sockets, so skip trying to restore their
    *   attributes.
    */
   if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
      goto bail_out;
   }

   ut.actime = attr->statp.st_atime;
   ut.modtime = attr->statp.st_mtime;

   /* ***FIXME**** optimize -- don't do if already correct */
   /**
    * For link, change owner of link using lchown, but don't
    *   try to do a chmod as that will update the file behind it.
    */
   if (attr->type == FT_LNK) {
      /** Change owner of link, not of real file */
      if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
   } else {
      if (chown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
      if (chmod(attr->ofname, attr->statp.st_mode) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }

      /**
       * Reset file times.
       */
      if (utime(attr->ofname, &ut) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
#ifdef HAVE_CHFLAGS
      /**
       * FreeBSD user flags
       *
       * Note, this should really be done before the utime() above,
       *  but if the immutable bit is set, it will make the utimes()
       *  fail.
       */
      if (chflags(attr->ofname, attr->statp.st_flags) < 0 && my_uid == 0) {
         berrno be;
         Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"),
            attr->ofname, be.bstrerror());
         ok = false;
      }
#endif
   }

bail_out:
   pm_strcpy(attr->ofname, "*none*");
   umask(old_mask);
   return ok;
}
Example #16
0
/*
 * Write an ANSI or IBM 80 character tape label
 *   Type determines whether we are writing HDR, EOF, or EOV labels
 *   Assume we are positioned to write the labels
 *   Returns:  true of OK
 *             false if error
 */
bool write_ansi_ibm_labels(DCR *dcr, int type, const char *VolName)
{
    DEVICE *dev = dcr->dev;
    JCR *jcr = dcr->jcr;
    char ansi_volname[7];              /* 6 char + \0 */
    char label[80];                    /* tape label */
    char date[20];                     /* ansi date buffer */
    time_t now;
    int len, status, label_type;

    /*
     * If the Device requires a specific label type use it,
     * otherwise, use the type requested by the Director
     */
    if (dcr->device->label_type != B_BAREOS_LABEL) {
        label_type = dcr->device->label_type;   /* force label type */
    } else {
        label_type = dcr->VolCatInfo.LabelType; /* accept Dir type */
    }

    switch (label_type) {
    case B_BAREOS_LABEL:
        return true;
    case B_ANSI_LABEL:
    case B_IBM_LABEL:
        ser_declare;
        Dmsg1(100, "Write ANSI label type=%d\n", label_type);
        len = strlen(VolName);
        if (len > 6) {
            Jmsg1(jcr, M_FATAL, 0, _("ANSI Volume label name \"%s\" longer than 6 chars.\n"),
                  VolName);
            return false;
        }
        /* ANSI labels have 6 characters, and are padded with spaces
         * 'vol1\0' => 'vol1   \0'
         */
        strcpy(ansi_volname, VolName);
        for(int i=len; i < 6; i++) {
            ansi_volname[i]=' ';
        }
        ansi_volname[6]='\0';     /* only for debug */

        if (type == ANSI_VOL_LABEL) {
            ser_begin(label, sizeof(label));
            ser_bytes("VOL1", 4);
            ser_bytes(ansi_volname, 6);
            /* Write VOL1 label */
            if (label_type == B_IBM_LABEL) {
                ascii_to_ebcdic(label, label, sizeof(label));
            } else {
                label[79] = '3';                /* ANSI label flag */
            }
            status = dev->write(label, sizeof(label));
            if (status != sizeof(label)) {
                berrno be;
                Jmsg3(jcr, M_FATAL, 0,  _("Could not write ANSI VOL1 label. Wanted size=%d got=%d ERR=%s\n"),
                      sizeof(label), status, be.bstrerror());
                return false;
            }
        }

        /* Now construct HDR1 label */
        memset(label, ' ', sizeof(label));
        ser_begin(label, sizeof(label));
        ser_bytes(labels[type], 3);
        ser_bytes("1", 1);
        ser_bytes("BAREOS.DATA", 11);            /* Filename field */
        ser_begin(&label[21], sizeof(label)-21); /* fileset field */
        ser_bytes(ansi_volname, 6);              /* write Vol Ser No. */
        ser_begin(&label[27], sizeof(label)-27);
        ser_bytes("00010001000100", 14);  /* File section, File seq no, Generation no */
        now = time(NULL);
        ser_bytes(ansi_date(now, date), 6); /* current date */
        ser_bytes(ansi_date(now - 24 * 3600, date), 6); /* created yesterday */
        ser_bytes(" 000000Bareos              ", 27);
        /* Write HDR1 label */
        if (label_type == B_IBM_LABEL) {
            ascii_to_ebcdic(label, label, sizeof(label));
        }

        /*
         * This could come at the end of a tape, ignore
         *  EOT errors.
         */
        status = dev->write(label, sizeof(label));
        if (status != sizeof(label)) {
            berrno be;
            if (status == -1) {
                dev->clrerror(-1);
                if (dev->dev_errno == 0) {
                    dev->dev_errno = ENOSPC; /* out of space */
                }
                if (dev->dev_errno != ENOSPC) {
                    Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"),
                          be.bstrerror());
                    return false;
                }
            } else {
                Jmsg(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label.\n"));
                return false;
            }
        }

        /* Now construct HDR2 label */
        memset(label, ' ', sizeof(label));
        ser_begin(label, sizeof(label));
        ser_bytes(labels[type], 3);
        ser_bytes("2D3200032000", 12);
        /* Write HDR2 label */
        if (label_type == B_IBM_LABEL) {
            label[4] = 'V';
            ascii_to_ebcdic(label, label, sizeof(label));
        }
        status = dev->write(label, sizeof(label));
        if (status != sizeof(label)) {
            berrno be;
            if (status == -1) {
                dev->clrerror(-1);
                if (dev->dev_errno == 0) {
                    dev->dev_errno = ENOSPC; /* out of space */
                }
                if (dev->dev_errno != ENOSPC) {
                    Jmsg1(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label. ERR=%s\n"),
                          be.bstrerror());
                    return false;
                }
                dev->weof(1);
                return true;
            } else {
                Jmsg(jcr, M_FATAL, 0, _("Could not write ANSI HDR1 label.\n"));
                return false;
            }
        }
        if (!dev->weof(1)) {
            Jmsg(jcr, M_FATAL, 0, _("Error writing EOF to tape. ERR=%s"), dev->errmsg);
            return false;
        }
        return true;
    default:
        Jmsg0(jcr, M_ABORT, 0, _("write_ansi_ibm_label called for non-ANSI/IBM type\n"));
        return false; /* should not get here */
    }
}
Example #17
0
/*
 * Note, we receive the whole attribute record, but we select out only the stat
 * packet, VolSessionId, VolSessionTime, FileIndex, file type, and file name to
 * store in the catalog.
 */
static void update_attribute(JCR *jcr, char *msg, int32_t msglen)
{
   unser_declare;
   uint32_t VolSessionId, VolSessionTime;
   int32_t Stream;
   uint32_t FileIndex;
   char *p;
   int len;
   char *fname, *attr;
   ATTR_DBR *ar = NULL;
   uint32_t reclen;

   /*
    * Start transaction allocates jcr->attr and jcr->ar if needed
    */
   db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
   ar = jcr->ar;

   /*
    * Start by scanning directly in the message buffer to get Stream
    * there may be a cached attr so we cannot yet write into
    * jcr->attr or jcr->ar
    */
   p = msg;
   skip_nonspaces(&p);                /* UpdCat */
   skip_spaces(&p);
   skip_nonspaces(&p);                /* Job=nnn */
   skip_spaces(&p);
   skip_nonspaces(&p);                /* "FileAttributes" */
   p += 1;

   /*
    * The following "SD header" fields are serialized
    */
   unser_begin(p, 0);
   unser_uint32(VolSessionId);        /* VolSessionId */
   unser_uint32(VolSessionTime);      /* VolSessionTime */
   unser_int32(FileIndex);            /* FileIndex */
   unser_int32(Stream);               /* Stream */
   unser_uint32(reclen);              /* Record length */
   p += unser_length(p);              /* Raw record follows */

   /**
    * At this point p points to the raw record, which varies according
    *  to what kind of a record (Stream) was sent.  Note, the integer
    *  fields at the beginning of these "raw" records are in ASCII with
    *  spaces between them so one can use scanf or manual scanning to
    *  extract the fields.
    *
    * File Attributes
    *   File_index
    *   File type
    *   Filename (full path)
    *   Encoded attributes
    *   Link name (if type==FT_LNK or FT_LNKSAVED)
    *   Encoded extended-attributes (for Win32)
    *   Delta sequence number (32 bit int)
    *
    * Restore Object
    *   File_index
    *   File_type
    *   Object_index
    *   Object_len (possibly compressed)
    *   Object_full_len (not compressed)
    *   Object_compression
    *   Plugin_name
    *   Object_name
    *   Binary Object data
    */

   Dmsg1(400, "UpdCat msg=%s\n", msg);
   Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d reclen=%d\n",
         VolSessionId, VolSessionTime, FileIndex, Stream, reclen);

   jcr->SDJobBytes += reclen; /* update number of bytes transferred for quotas */

   /*
    * Depending on the stream we are handling dispatch.
    */
   switch (Stream) {
   case STREAM_UNIX_ATTRIBUTES:
   case STREAM_UNIX_ATTRIBUTES_EX:
      if (jcr->cached_attribute) {
         Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
         if (!db_create_attributes_record(jcr, jcr->db, ar)) {
            Jmsg1(jcr, M_FATAL, 0, _("Attribute create error: ERR=%s"), db_strerror(jcr->db));
         }
         jcr->cached_attribute = false;
      }

      /*
       * Any cached attr is flushed so we can reuse jcr->attr and jcr->ar
       */
      jcr->attr = check_pool_memory_size(jcr->attr, msglen);
      memcpy(jcr->attr, msg, msglen);
      p = jcr->attr - msg + p;         /* point p into jcr->attr */
      skip_nonspaces(&p);              /* skip FileIndex */
      skip_spaces(&p);
      ar->FileType = str_to_int32(p);
      skip_nonspaces(&p);              /* skip FileType */
      skip_spaces(&p);
      fname = p;
      len = strlen(fname);             /* length before attributes */
      attr = &fname[len+1];
      ar->DeltaSeq = 0;
      if (ar->FileType == FT_REG) {
         p = attr + strlen(attr) + 1;  /* point to link */
         p = p + strlen(p) + 1;        /* point to extended attributes */
         p = p + strlen(p) + 1;        /* point to delta sequence */
         /*
          * Older FDs don't have a delta sequence, so check if it is there
          */
         if (p - jcr->attr < msglen) {
            ar->DeltaSeq = str_to_int32(p); /* delta_seq */
         }
      }

      Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
      Dmsg1(400, "dird<stored: attr=%s\n", attr);

      ar->attr = attr;
      ar->fname = fname;
      if (ar->FileType == FT_DELETED) {
         ar->FileIndex = 0;     /* special value */
      } else {
         ar->FileIndex = FileIndex;
      }
      ar->Stream = Stream;
      ar->link = NULL;
      if (jcr->mig_jcr) {
         ar->JobId = jcr->mig_jcr->JobId;
      } else {
         ar->JobId = jcr->JobId;
      }
      ar->Digest = NULL;
      ar->DigestType = CRYPTO_DIGEST_NONE;
      jcr->cached_attribute = true;

      Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
      Dmsg1(400, "dird<filed: attr=%s\n", attr);
      break;
   case STREAM_RESTORE_OBJECT: {
      ROBJECT_DBR ro;

      memset(&ro, 0, sizeof(ro));
      ro.Stream = Stream;
      ro.FileIndex = FileIndex;
      if (jcr->mig_jcr) {
         ro.JobId = jcr->mig_jcr->JobId;
      } else {
         ro.JobId = jcr->JobId;
      }

      Dmsg1(100, "Robj=%s\n", p);

      skip_nonspaces(&p);                  /* skip FileIndex */
      skip_spaces(&p);
      ro.FileType = str_to_int32(p);        /* FileType */
      skip_nonspaces(&p);
      skip_spaces(&p);
      ro.object_index = str_to_int32(p);    /* Object Index */
      skip_nonspaces(&p);
      skip_spaces(&p);
      ro.object_len = str_to_int32(p);      /* object length possibly compressed */
      skip_nonspaces(&p);
      skip_spaces(&p);
      ro.object_full_len = str_to_int32(p); /* uncompressed object length */
      skip_nonspaces(&p);
      skip_spaces(&p);
      ro.object_compression = str_to_int32(p); /* compression */
      skip_nonspaces(&p);
      skip_spaces(&p);

      ro.plugin_name = p;                      /* point to plugin name */
      len = strlen(ro.plugin_name);
      ro.object_name = &ro.plugin_name[len+1]; /* point to object name */
      len = strlen(ro.object_name);
      ro.object = &ro.object_name[len+1];      /* point to object */
      ro.object[ro.object_len] = 0;            /* add zero for those who attempt printing */

      Dmsg7(100, "oname=%s stream=%d FT=%d FI=%d JobId=%d, obj_len=%d\nobj=\"%s\"\n",
            ro.object_name, ro.Stream, ro.FileType, ro.FileIndex, ro.JobId,
            ro.object_len, ro.object);

      /*
       * Store it.
       */
      if (!db_create_restore_object_record(jcr, jcr->db, &ro)) {
         Jmsg1(jcr, M_FATAL, 0, _("Restore object create error. %s"), db_strerror(jcr->db));
      }
      break;
   }
   default:
      if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
         fname = p;
         if (ar->FileIndex != FileIndex) {
            Jmsg3(jcr, M_WARNING, 0, _("%s not same File=%d as attributes=%d\n"),
               stream_to_ascii(Stream), FileIndex, ar->FileIndex);
         } else {
            /*
             * Update digest in catalog
             */
            char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
            int len = 0;
            int type = CRYPTO_DIGEST_NONE;

            switch(Stream) {
            case STREAM_MD5_DIGEST:
               len = CRYPTO_DIGEST_MD5_SIZE;
               type = CRYPTO_DIGEST_MD5;
               break;
            case STREAM_SHA1_DIGEST:
               len = CRYPTO_DIGEST_SHA1_SIZE;
               type = CRYPTO_DIGEST_SHA1;
               break;
            case STREAM_SHA256_DIGEST:
               len = CRYPTO_DIGEST_SHA256_SIZE;
               type = CRYPTO_DIGEST_SHA256;
               break;
            case STREAM_SHA512_DIGEST:
               len = CRYPTO_DIGEST_SHA512_SIZE;
               type = CRYPTO_DIGEST_SHA512;
               break;
            default:
               /*
                * Never reached ...
                */
               Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"), Stream);
            }

            bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true);

            Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf),
                  digestbuf, Stream);

            if (jcr->cached_attribute) {
               ar->Digest = digestbuf;
               ar->DigestType = type;

               Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname);

               /*
                * Update BaseFile table
                */
               if (!db_create_attributes_record(jcr, jcr->db, ar)) {
                  Jmsg1(jcr, M_FATAL, 0, _("attribute create error. %s"), db_strerror(jcr->db));
               }
               jcr->cached_attribute = false;
            } else {
               if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
                  Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"), db_strerror(jcr->db));
               }
            }
         }
      }
      break;
   }
}
Example #18
0
/*
 * Restore all file attributes like owner, mode and file times.
 */
static inline bool restore_file_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
    bool ok = true;
    bool suppress_errors;
#if defined(HAVE_FCHOWN) || defined(HAVE_FCHMOD) || defined(HAVE_FUTIMES) || defined(FUTIMENS)
    bool file_is_open;

    /*
     * Save if we are working on an open file.
     */
    file_is_open = is_bopen(ofd);
#endif

    /*
     * See if we want to print errors.
     */
    suppress_errors = (debug_level >= 100 || my_uid != 0);

    /*
     * Restore owner and group.
     */
#ifdef HAVE_FCHOWN
    if (file_is_open) {
        if (fchown(ofd->fid, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
        if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    }

    /*
     * Restore filemode.
     */
#ifdef HAVE_FCHMOD
    if (file_is_open) {
        if (fchmod(ofd->fid, attr->statp.st_mode) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
#if defined(HAVE_WIN32)
        if (win32_chmod(attr->ofname, attr->statp.st_mode, attr->statp.st_rdev) < 0 && !suppress_errors) {
#else
        if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) {
#endif
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    }

    /*
     * Reset file times.
     */
#if defined(HAVE_FUTIMES)
    if (file_is_open) {
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (futimes(ofd->fid, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#elif defined(HAVE_FUTIMENS)
    if (file_is_open) {
        struct timespec restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_nsec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_nsec = 0;

        if (futimens(ofd->fid, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
    } else {
#else
    {
#endif
#if defined(HAVE_LUTIMES)
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (lutimes(attr->ofname, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#elif defined(HAVE_UTIMES)
        struct timeval restore_times[2];

        restore_times[0].tv_sec = attr->statp.st_atime;
        restore_times[0].tv_usec = 0;
        restore_times[1].tv_sec = attr->statp.st_mtime;
        restore_times[1].tv_usec = 0;

        if (utimes(attr->ofname, restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#else
        struct utimbuf restore_times;

        restore_times.actime = attr->statp.st_atime;
        restore_times.modtime = attr->statp.st_mtime;

        if (utime(attr->ofname, &restore_times) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file times %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#endif /* HAVE_LUTIMES */
    }

    return ok;
}

/**
 * Set file modes, permissions and times
 *
 *  fname is the original filename
 *  ofile is the output filename (may be in a different directory)
 *
 * Returns:  true  on success
 *           false on failure
 */
bool set_attributes(JCR *jcr, ATTR *attr, BFILE *ofd)
{
    mode_t old_mask;
    bool ok = true;
    bool suppress_errors;

    if (uid_set) {
        my_uid = getuid();
        my_gid = getgid();
        uid_set = true;
    }

    /*
     * See if we want to print errors.
     */
    suppress_errors = (debug_level >= 100 || my_uid != 0);

#if defined(HAVE_WIN32)
    if (attr->stream == STREAM_UNIX_ATTRIBUTES_EX &&
            set_win32_attributes(jcr, attr, ofd)) {
        if (is_bopen(ofd)) {
            bclose(ofd);
        }
        pm_strcpy(attr->ofname, "*None*");
        return true;
    }

    if (attr->data_stream == STREAM_WIN32_DATA ||
            attr->data_stream == STREAM_WIN32_GZIP_DATA ||
            attr->data_stream == STREAM_WIN32_COMPRESSED_DATA) {
        if (is_bopen(ofd)) {
            bclose(ofd);
        }
        pm_strcpy(attr->ofname, "*None*");
        return true;
    }

    /**
     * If Windows stuff failed, e.g. attempt to restore Unix file to Windows, simply fall
     * through and we will do it the universal way.
     */
#endif

    old_mask = umask(0);
    if (is_bopen(ofd)) {
        boffset_t fsize;
        char ec1[50], ec2[50];

        fsize = blseek(ofd, 0, SEEK_END);
        if (attr->type == FT_REG &&
                fsize > 0 &&
                attr->statp.st_size > 0 &&
                fsize != (boffset_t)attr->statp.st_size) {
            Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
                  attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(fsize, ec2));
        }
    } else {
        struct stat st;
        char ec1[50], ec2[50];

        if (lstat(attr->ofname, &st) == 0) {
            if (attr->type == FT_REG &&
                    st.st_size > 0 &&
                    attr->statp.st_size > 0 &&
                    st.st_size != attr->statp.st_size) {
                Jmsg3(jcr, M_ERROR, 0, _("File size of restored file %s not correct. Original %s, restored %s.\n"),
                      attr->ofname, edit_uint64(attr->statp.st_size, ec1), edit_uint64(st.st_size, ec2));
            }
        }
    }

    /**
     * We do not restore sockets, so skip trying to restore their attributes.
     */
    if (attr->type == FT_SPEC && S_ISSOCK(attr->statp.st_mode)) {
        goto bail_out;
    }

    /* ***FIXME**** optimize -- don't do if already correct */
    /**
     * For link, change owner of link using lchown, but don't try to do a chmod as that will update the file behind it.
     */
    if (attr->type == FT_LNK) {
        /*
         * Change owner of link, not of real file
         */
        if (lchown(attr->ofname, attr->statp.st_uid, attr->statp.st_gid) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file owner %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }

#ifdef HAVE_LCHMOD
        if (lchmod(attr->ofname, attr->statp.st_mode) < 0 && !suppress_errors) {
            berrno be;

            Jmsg2(jcr, M_ERROR, 0, _("Unable to set file modes %s: ERR=%s\n"), attr->ofname, be.bstrerror());
            ok = false;
        }
#endif
    } else {
        if (!ofd->cmd_plugin) {
            ok = restore_file_attributes(jcr, attr, ofd);

#ifdef HAVE_CHFLAGS
            /**
             * FreeBSD user flags
             *
             * Note, this should really be done before the utime() above,
             * but if the immutable bit is set, it will make the utimes()
             * fail.
             */
            if (chflags(attr->ofname, attr->statp.st_flags) < 0 && !suppress_errors) {
                berrno be;
                Jmsg2(jcr, M_ERROR, 0, _("Unable to set file flags %s: ERR=%s\n"), attr->ofname, be.bstrerror());
                ok = false;
            }
#endif
        }
    }

bail_out:
    if (is_bopen(ofd)) {
        bclose(ofd);
    }

    pm_strcpy(attr->ofname, "*None*");
    umask(old_mask);

    return ok;
}

#if !defined(HAVE_WIN32)
/*=============================================================*/
/*                                                             */
/*                 * * *  U n i x * * * *                      */
/*                                                             */
/*=============================================================*/

/**
 * It is possible to piggyback additional data e.g. ACLs on
 *   the encode_stat() data by returning the extended attributes
 *   here.  They must be "self-contained" (i.e. you keep track
 *   of your own length), and they must be in ASCII string
 *   format. Using this feature is not recommended.
 * The code below shows how to return nothing.  See the Win32
 *   code below for returning something in the attributes.
 */
int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
{
#ifdef HAVE_DARWIN_OS
    /**
     * We save the Mac resource fork length so that on a
     * restore, we can be sure we put back the whole resource.
     */
    char *p;

    *attribsEx = 0;                 /* no extended attributes (yet) */
    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
        return STREAM_UNIX_ATTRIBUTES;
    }
    p = attribsEx;
    if (bit_is_set(FO_HFSPLUS, ff_pkt->flags)) {
        p += to_base64((uint64_t)(ff_pkt->hfsinfo.rsrclength), p);
    }
    *p = 0;
#else
    *attribsEx = 0;                    /* no extended attributes */
#endif
    return STREAM_UNIX_ATTRIBUTES;
}
#else
/*=============================================================*/
/*                                                             */
/*                 * * *  W i n 3 2 * * * *                    */
/*                                                             */
/*=============================================================*/
int encode_attribsEx(JCR *jcr, char *attribsEx, FF_PKT *ff_pkt)
{
    char *p = attribsEx;
    WIN32_FILE_ATTRIBUTE_DATA atts;
    ULARGE_INTEGER li;

    attribsEx[0] = 0;                  /* no extended attributes */

    if (jcr->cmd_plugin || ff_pkt->type == FT_DELETED) {
        return STREAM_UNIX_ATTRIBUTES;
    }

    unix_name_to_win32(ff_pkt->sys_fname, ff_pkt->fname);
    if (p_GetFileAttributesExW)  {
        /**
         * Try unicode version
         */
        POOLMEM* pwszBuf = get_pool_memory(PM_FNAME);
        make_win32_path_UTF8_2_wchar(pwszBuf, ff_pkt->fname);

        BOOL b=p_GetFileAttributesExW((LPCWSTR)pwszBuf, GetFileExInfoStandard,
                                      (LPVOID)&atts);
        free_pool_memory(pwszBuf);

        if (!b) {
            win_error(jcr, "GetFileAttributesExW:", ff_pkt->sys_fname);
            return STREAM_UNIX_ATTRIBUTES;
        }
    } else {
        if (!p_GetFileAttributesExA)
            return STREAM_UNIX_ATTRIBUTES;

        if (!p_GetFileAttributesExA(ff_pkt->sys_fname, GetFileExInfoStandard,
                                    (LPVOID)&atts)) {
            win_error(jcr, "GetFileAttributesExA:", ff_pkt->sys_fname);
            return STREAM_UNIX_ATTRIBUTES;
        }
    }

    /*
     * Instead of using the current dwFileAttributes use the
     * ff_pkt->statp.st_rdev which contains the actual fileattributes we
     * want to save for this file.
     */
    atts.dwFileAttributes = ff_pkt->statp.st_rdev;

    p += to_base64((uint64_t)atts.dwFileAttributes, p);
    *p++ = ' ';                        /* separate fields with a space */
    li.LowPart = atts.ftCreationTime.dwLowDateTime;
    li.HighPart = atts.ftCreationTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    li.LowPart = atts.ftLastAccessTime.dwLowDateTime;
    li.HighPart = atts.ftLastAccessTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    li.LowPart = atts.ftLastWriteTime.dwLowDateTime;
    li.HighPart = atts.ftLastWriteTime.dwHighDateTime;
    p += to_base64((uint64_t)li.QuadPart, p);
    *p++ = ' ';
    p += to_base64((uint64_t)atts.nFileSizeHigh, p);
    *p++ = ' ';
    p += to_base64((uint64_t)atts.nFileSizeLow, p);
    *p = 0;

    return STREAM_UNIX_ATTRIBUTES_EX;
}