X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fdev.c;h=2ff57f807626f8de23edf1317b0b068e2f67791c;hb=334b3c15123147b0f4101e4257a7d42e660f68b4;hp=143eaab02146599e23ae3384b72715f8aeee765c;hpb=6457aa352c6e68e6eda99da79f12ba1f7cd91755;p=bacula%2Fbacula diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 143eaab021..2ff57f8076 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -1,38 +1,30 @@ /* - Bacula® - The Network Backup Solution - - Copyright (C) 2000-2007 Free Software Foundation Europe e.V. - - The main author of Bacula is Kern Sibbald, with contributions from - many others, a complete list can be found in the file AUTHORS. - This program is Free Software; you can redistribute it and/or - modify it under the terms of version two of the GNU General Public - License as published by the Free Software Foundation plus additions - that are listed in the file LICENSE. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. - - Bacula® is a registered trademark of John Walker. - The licensor of Bacula is the Free Software Foundation Europe - (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich, - Switzerland, email:ftf@fsfeurope.org. + Bacula(R) - The Network Backup Solution + + Copyright (C) 2000-2015 Kern Sibbald + Copyright (C) 2000-2014 Free Software Foundation Europe e.V. + + The original author of Bacula is Kern Sibbald, with contributions + from many others, a complete list can be found in the file AUTHORS. + + You may use this file and others of this release according to the + license defined in the LICENSE file, which includes the Affero General + Public License, v3.0 ("AGPLv3") and some additional permissions and + terms pursuant to its AGPLv3 Section 7. + + This notice must be preserved when any source code is + conveyed and/or propagated. + + Bacula(R) is a registered trademark of Kern Sibbald. */ /* * * dev.c -- low level operations on device (storage device) * - * Kern Sibbald, MM + * written by, Kern Sibbald, MM * * NOTE!!!! None of these routines are reentrant. You must - * use dev->r_dlock() and dev->unlock() at a higher level, + * use dev->rLock() and dev->Unlock() at a higher level, * or use the xxx_device() equivalents. By moving the * thread synchronization to a higher level, we permit * the higher level routines to "seize" the device and @@ -47,18 +39,11 @@ * daemon. More complicated coding (double buffering, writer * thread, ...) is left for a later version. * - * Unfortunately, I have had to add more and more complication - * to this code. This was not foreseen as noted above, and as - * a consequence has lead to something more contorted than is - * really necessary -- KES. Note, this contortion has been - * corrected to a large extent by a rewrite (Apr MMI). - * - * Version $Id$ */ /* * Handling I/O errors and end of tape conditions are a bit tricky. - * This is how it is currently done when writting. + * This is how it is currently done when writing. * On either an I/O error or end of tape, * we will stop writing on the physical device (no I/O recovery is * attempted at least in this daemon). The state flag will be sent @@ -84,18 +69,34 @@ * business with no lost data. */ - #include "bacula.h" #include "stored.h" -#ifndef O_NONBLOCK +#ifndef O_NONBLOCK #define O_NONBLOCK 0 #endif +/* Imported functions */ +extern void set_os_device_parameters(DCR *dcr); +extern bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat); +extern uint32_t status_dev(DEVICE *dev); + /* Forward referenced functions */ -void set_os_device_parameters(DCR *dcr); -static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat); -static char *mode_to_str(int mode); +const char *mode_to_str(int mode); +DEVICE *m_init_dev(JCR *jcr, DEVRES *device); +/* + * Device types for printing + */ +static const char *prt_dev_types[] = { + "*none*", + "file", + "tape", + "DVD", + "FIFO", + "Vtape", + "FTP", + "VTL" +}; /* * Allocate and initialize the DEVICE structure @@ -108,22 +109,28 @@ static char *mode_to_str(int mode); * is the directory in which the file will be placed. * */ -DEVICE * -init_dev(JCR *jcr, DEVRES *device) +DEVICE *init_dev(JCR *jcr, DEVRES *device) +{ + generate_global_plugin_event(bsdGlobalEventDeviceInit, device); + DEVICE *dev = m_init_dev(jcr, device); + return dev; +} + +DEVICE *m_init_dev(JCR *jcr, DEVRES *device) { struct stat statp; int errstat; DCR *dcr = NULL; - DEVICE *dev; - + 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.strerror()); + 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)) { @@ -132,6 +139,13 @@ init_dev(JCR *jcr, DEVRES *device) 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"), @@ -140,11 +154,34 @@ init_dev(JCR *jcr, DEVRES *device) } else { device->dev_type = B_DVD_DEV; } + if (strcmp(device->device_name, "/dev/null") == 0) { + device->dev_type = B_NULL_DEV; + } } - - dev = (DEVICE *)malloc(sizeof(DEVICE)); - memset(dev, 0, sizeof(DEVICE)); - dev->Slot = -1; /* unknown */ + 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); @@ -154,19 +191,23 @@ init_dev(JCR *jcr, DEVRES *device) 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->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_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 { @@ -176,86 +217,132 @@ init_dev(JCR *jcr, DEVRES *device) if (dev->vol_poll_interval && dev->vol_poll_interval < 60) { dev->vol_poll_interval = 60; } - /* Link the dev and device structures together */ - dev->device = device; - device->dev = dev; + + 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 the mount point is available * - Check that (un)mount commands are defined */ - if ((dev->is_file() || dev->is_dvd()) && dev->requires_mount()) { + 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.strerror()); + Jmsg2(jcr, M_ERROR_TERM, 0, _("Unable to stat mount point %s: ERR=%s\n"), + device->mount_point, be.bstrerror()); } - } - if (dev->is_dvd()) { + 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")); } - if (!device->write_part_command) { - Jmsg0(jcr, M_ERROR_TERM, 0, _("Write part command must be 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; } - if (dev->max_block_size > 1000000) { + /* 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) { - Jmsg2(jcr, M_WARNING, 0, _("Max block size %u not multiple of device %s block size.\n"), - dev->max_block_size, dev->print_name()); + 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 = pthread_mutex_init(&dev->m_mutex, NULL)) != 0) { + if ((errstat = dev->init_mutex()) != 0) { berrno be; dev->dev_errno = errstat; - Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.strerror(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.strerror(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.strerror(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 mutex: ERR=%s\n"), be.strerror(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.strerror(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(29, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name); + Dmsg2(100, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name); dev->initiated = true; - + return dev; } @@ -263,8 +350,8 @@ init_dev(JCR *jcr, DEVRES *device) * Open the device with the operating system and * initialize buffer pointers. * - * Returns: -1 on error - * fd on success + * Returns: true on success + * false on error * * Note, for a tape, the VolName is the name we give to the * volume (not really used here), but for a file, the @@ -272,48 +359,44 @@ init_dev(JCR *jcr, DEVRES *device) * In the case of a file, the full name is the device name * (archive_name) with the VolName concatenated. */ -int -DEVICE::open(DCR *dcr, int omode) +bool DEVICE::open(DCR *dcr, int omode) { int preserve = 0; if (is_open()) { if (openmode == omode) { - return m_fd; + return true; } else { - if (is_tape()) { - tape_close(m_fd); - } else { - ::close(m_fd); - } + Dmsg1(200, "Close fd=%d for mode change in open().\n", m_fd); + d_close(m_fd); clear_opened(); - Dmsg0(100, "Close fd for mode change.\n"); preserve = state & (ST_LABEL|ST_APPEND|ST_READ); } } if (dcr) { - bstrncpy(VolCatInfo.VolCatName, dcr->VolumeName, sizeof(VolCatInfo.VolCatName)); + dcr->setVolCatName(dcr->VolumeName); + VolCatInfo = dcr->VolCatInfo; /* structure assign */ } - Dmsg4(29, "open dev: type=%d dev_name=%s vol=%s mode=%s\n", dev_type, - print_name(), VolCatInfo.VolCatName, mode_to_str(omode)); - state &= ~(ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); - Slot = -1; /* unknown slot */ + state &= ~(ST_NOSPACE|ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); label_type = B_BACULA_LABEL; + if (is_tape() || is_fifo()) { open_tape_device(dcr, omode); - } else if (is_dvd()) { - Dmsg1(100, "call open_dvd_device mode=%s\n", mode_to_str(omode)); - open_dvd_device(dcr, omode); + } else if (is_ftp()) { + open_device(dcr, omode); } else { Dmsg1(100, "call open_file_device mode=%s\n", mode_to_str(omode)); open_file_device(dcr, omode); } state |= preserve; /* reset any important state info */ Dmsg2(100, "preserve=0x%x fd=%d\n", preserve, m_fd); - return m_fd; + + Dmsg7(100, "open dev: fd=%d dev=%p dcr=%p vol=%s type=%d dev_name=%s mode=%s\n", + m_fd, getVolCatName(), this, dcr, dev_type, print_name(), mode_to_str(omode)); + return m_fd >= 0; } -void DEVICE::set_mode(int new_mode) +void DEVICE::set_mode(int new_mode) { switch (new_mode) { case CREATE_READ_WRITE: @@ -333,499 +416,19 @@ void DEVICE::set_mode(int new_mode) } } -/* - */ -void DEVICE::open_tape_device(DCR *dcr, int omode) -{ - file_size = 0; - int timeout = max_open_wait; -#if !defined(HAVE_WIN32) - struct mtop mt_com; - utime_t start_time = time(NULL); -#endif - - - Dmsg0(29, "Open dev: device is tape\n"); - - get_autochanger_loaded_slot(dcr); - - openmode = omode; - set_mode(omode); - - if (timeout < 1) { - timeout = 1; - } - errno = 0; - if (is_fifo() && timeout) { - /* Set open timer */ - tid = start_thread_timer(pthread_self(), timeout); - } - Dmsg2(100, "Try open %s mode=%s\n", print_name(), mode_to_str(omode)); -#if defined(HAVE_WIN32) - - /* Windows Code */ - if ((m_fd = tape_open(dev_name, mode)) < 0) { - dev_errno = errno; - } - -#else - - /* UNIX Code */ - /* If busy retry each second for max_open_wait seconds */ - for ( ;; ) { - /* Try non-blocking open */ - m_fd = ::open(dev_name, mode+O_NONBLOCK); - if (m_fd < 0) { - berrno be; - dev_errno = errno; - Dmsg5(050, "Open error on %s omode=%d mode=%x errno=%d: ERR=%s\n", - print_name(), omode, mode, errno, be.strerror()); - } else { - /* Tape open, now rewind it */ - Dmsg0(050, "Rewind after open\n"); - mt_com.mt_op = MTREW; - mt_com.mt_count = 1; - /* rewind only if dev is a tape */ - if (is_tape() && (ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0)) { - berrno be; - dev_errno = errno; /* set error status from rewind */ - ::close(m_fd); - clear_opened(); - Dmsg2(100, "Rewind error on %s close: ERR=%s\n", print_name(), - be.strerror(dev_errno)); - /* If we get busy, device is probably rewinding, try again */ - if (dev_errno != EBUSY) { - break; /* error -- no medium */ - } - } else { - /* Got fd and rewind worked, so we must have medium in drive */ - ::close(m_fd); - m_fd = ::open(dev_name, mode); /* open normally */ - if (m_fd < 0) { - berrno be; - dev_errno = errno; - Dmsg5(050, "Open error on %s omode=%d mode=%x errno=%d: ERR=%s\n", - print_name(), omode, mode, errno, be.strerror()); - break; - } - dev_errno = 0; - lock_door(); - set_os_device_parameters(dcr); /* do system dependent stuff */ - break; /* Successfully opened and rewound */ - } - } - bmicrosleep(5, 0); - /* Exceed wait time ? */ - if (time(NULL) - start_time >= max_open_wait) { - break; /* yes, get out */ - } - } -#endif - - if (!is_open()) { - berrno be; - Mmsg2(errmsg, _("Unable to open device %s: ERR=%s\n"), - print_name(), be.strerror(dev_errno)); - Dmsg1(100, "%s", errmsg); - } - - /* Stop any open() timer we started */ - if (tid) { - stop_thread_timer(tid); - tid = 0; - } - Dmsg1(29, "open dev: tape %d opened\n", m_fd); -} - -/* - * Open a file device - */ -void DEVICE::open_file_device(DCR *dcr, int omode) +void DEVICE::open_device(DCR *dcr, int omode) { - POOL_MEM archive_name(PM_FNAME); - - get_autochanger_loaded_slot(dcr); - - /* - * Handle opening of File Archive (not a tape) - */ - - pm_strcpy(archive_name, dev_name); - /* - * If this is a virtual autochanger (i.e. changer_res != NULL) - * we simply use the device name, assuming it has been - * appropriately setup by the "autochanger". - */ - if (!device->changer_res || device->changer_command[0] == 0) { - if (VolCatInfo.VolCatName[0] == 0) { - Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"), - print_name()); - clear_opened(); - return; - } - - if (!IsPathSeparator(archive_name.c_str()[strlen(archive_name.c_str())-1])) { - pm_strcat(archive_name, "/"); - } - pm_strcat(archive_name, VolCatInfo.VolCatName); - } - - mount(1); /* do mount if required */ - - openmode = omode; - set_mode(omode); - /* If creating file, give 0640 permissions */ - Dmsg3(29, "open disk: mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode), - archive_name.c_str(), mode); - /* Use system open() */ - if ((m_fd = ::open(archive_name.c_str(), mode, 0640)) < 0) { - berrno be; - dev_errno = errno; - Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), - be.strerror()); - Dmsg1(29, "open failed: %s", errmsg); - Emsg0(M_FATAL, 0, errmsg); - } else { - dev_errno = 0; - file = 0; - file_addr = 0; - } - Dmsg4(29, "open dev: disk fd=%d opened, part=%d/%d, part_size=%u\n", - m_fd, part, num_dvd_parts, part_size); + /* do nothing waiting to split open_file/tape_device */ } -/* - * Open a DVD device. N.B. at this point, dcr->VolCatInfo.VolCatName - * (NB:??? I think it's VolCatInfo.VolCatName that is right) - * has the desired Volume name, but there is NO assurance that - * any other field of VolCatInfo is correct. - */ -void DEVICE::open_dvd_device(DCR *dcr, int omode) -{ - POOL_MEM archive_name(PM_FNAME); - struct stat filestat; - - /* - * Handle opening of DVD Volume - */ - Dmsg2(29, "Enter: open_dvd_dev: DVD vol=%s mode=%s\n", - &dcr->VolCatInfo, mode_to_str(omode)); - - /* - * For a DVD we must always pull the state info from dcr->VolCatInfo - * This is a bit ugly, but is necessary because we need to open/close/re-open - * the dvd file in order to properly mount/unmount and access the - * DVD. So we store the state of the DVD as far as is known in the - * catalog in dcr->VolCatInfo, and thus we refresh the dev->VolCatInfo - * copy here, when opening. - */ - VolCatInfo = dcr->VolCatInfo; /* structure assignment */ - Dmsg1(100, "Volume=%s\n", VolCatInfo.VolCatName); - - if (VolCatInfo.VolCatName[0] == 0) { - Dmsg1(10, "Could not open DVD device %s. No Volume name given.\n", - print_name()); - Mmsg(errmsg, _("Could not open DVD device %s. No Volume name given.\n"), - print_name()); - clear_opened(); - return; - } - - if (part == 0) { - Dmsg0(100, "Set part=1\n"); - part = 1; /* count from 1 */ - file_size = 0; - } - part_size = 0; - if (num_dvd_parts != VolCatInfo.VolCatParts) { - num_dvd_parts = VolCatInfo.VolCatParts; - } - - /* - * If we are not trying to access the last part, set mode to - * OPEN_READ_ONLY as writing would be an error. - */ - Dmsg2(29, "open DVD part=%d num_dvd_parts=%d\n", part, num_dvd_parts); - /* Now find the name of the part that we want to access */ - if (part <= num_dvd_parts) { - omode = OPEN_READ_ONLY; - make_mounted_dvd_filename(this, archive_name); - set_part_spooled(false); - } else { - omode = OPEN_READ_WRITE; - make_spooled_dvd_filename(this, archive_name); - set_part_spooled(true); - } - set_mode(omode); - - // Clear any previous blank_dvd status - we will recalculate it here - blank_dvd = false; - - Dmsg3(99, "open_dvd_device: part=%d num_dvd_parts=%d, VolCatInfo.VolCatParts=%d\n", - part, num_dvd_parts, dcr->VolCatInfo.VolCatParts); - - if (mount(1)) { - Dmsg0(99, "DVD device mounted.\n"); - if (num_dvd_parts == 0 && !truncating) { - /* - * If we can mount the device, and we are not truncating the DVD, - * we usually want to abort. There is one exception, if there is - * only one 0-sized file on the DVD, with the right volume name, - * we continue (it's the method used by truncate_dvd to truncate a volume). - */ - if (!check_can_write_on_non_blank_dvd(dcr)) { - Mmsg(errmsg, _("The DVD in device %s contains data, please blank it before writing.\n"), print_name()); - Emsg0(M_FATAL, 0, errmsg); - unmount(1); /* Unmount the device, so the operator can change it. */ - clear_opened(); - return; - } - blank_dvd = true; - } else { - /* - * Ensure that we have the correct DVD loaded by looking for part1. - * We only succeed the open if it exists. Failure to do this could - * leave us trying to add a part to a different DVD! - */ - uint32_t oldpart = part; - struct stat statp; - POOL_MEM part1_name(PM_FNAME); - part = 1; - make_mounted_dvd_filename(this, part1_name); - part = oldpart; - if (stat(part1_name.c_str(), &statp) < 0) { - berrno be; - Mmsg(errmsg, _("Unable to stat DVD part 1 file %s: ERR=%s\n"), - part1_name.c_str(), be.strerror()); - Emsg0(M_FATAL, 0, errmsg); - clear_opened(); - return; - } - if (!S_ISREG(statp.st_mode)) { - /* It is not a regular file */ - Mmsg(errmsg, _("DVD part 1 is not a regular file %s.\n"), - part1_name.c_str()); - Emsg0(M_FATAL, 0, errmsg); - clear_opened(); - return; - } - } - } else { - Dmsg0(99, "DVD device mount failed.\n"); - /* We cannot mount the device */ - if (num_dvd_parts == 0) { - /* Run free space, check there is a media. */ - if (!update_freespace()) { - Emsg0(M_FATAL, 0, errmsg); - clear_opened(); - return; - } - if (have_media()) { - Dmsg1(29, "Could not mount device %s, this is not a problem (num_dvd_parts == 0), and have media.\n", print_name()); - } else { - Mmsg(errmsg, _("There is no valid DVD in device %s.\n"), print_name()); - Emsg0(M_FATAL, 0, errmsg); - clear_opened(); - return; - } - } else { - Mmsg(errmsg, _("Could not mount DVD device %s.\n"), print_name()); - Emsg0(M_FATAL, 0, errmsg); - clear_opened(); - return; - } - } - - Dmsg5(29, "open dev: DVD dev=%s mode=%s part=%d npart=%d volcatnparts=%d\n", - archive_name.c_str(), mode_to_str(omode), - part, num_dvd_parts, dcr->VolCatInfo.VolCatParts); - openmode = omode; - Dmsg2(100, "openmode=%d %s\n", openmode, mode_to_str(openmode)); - - - /* If creating file, give 0640 permissions */ - Dmsg3(29, "mode=%s open(%s, 0x%x, 0640)\n", mode_to_str(omode), - archive_name.c_str(), mode); - /* Use system open() */ - if ((m_fd = ::open(archive_name.c_str(), mode, 0640)) < 0) { - berrno be; - Mmsg2(errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), - be.strerror()); - // Should this be set if we try the create/open below - dev_errno = EIO; /* Interpreted as no device present by acquire.c:acquire_device_for_read(). */ - Dmsg1(29, "open failed: %s", errmsg); - - /* Previous open failed. See if we can recover */ - if ((omode == OPEN_READ_ONLY || omode == OPEN_READ_WRITE) && - (part > num_dvd_parts)) { - /* If the last part (on spool), doesn't exist when accessing, - * create it. In read/write mode a write will be allowed (higher - * level software thinks that we are extending a pre-existing - * media. Reads for READ_ONLY will report immediately an EOF - * Sometimes it is better to finish with an EOF than with an error. */ - Dmsg1(29, "Creating last part on spool: %s\n", archive_name.c_str()); - omode = CREATE_READ_WRITE; - set_mode(CREATE_READ_WRITE); - m_fd = ::open(archive_name.c_str(), mode, 0640); - set_mode(omode); - } - } - Dmsg1(100, "after open fd=%d\n", m_fd); - if (is_open()) { - if (omode == OPEN_READ_WRITE || omode == CREATE_READ_WRITE) { - set_append(); - } - /* Get size of file */ - if (fstat(m_fd, &filestat) < 0) { - berrno be; - dev_errno = errno; - Mmsg2(errmsg, _("Could not fstat: %s, ERR=%s\n"), archive_name.c_str(), - be.strerror()); - Dmsg1(29, "open failed: %s", errmsg); - /* Use system close() */ - ::close(m_fd); - clear_opened(); - } else { - part_size = filestat.st_size; - dev_errno = 0; - update_pos(dcr); /* update position */ - } - } -} - - -/* - * Rewind the device. - * Returns: true on success - * false on failure - */ -bool DEVICE::rewind(DCR *dcr) -{ - struct mtop mt_com; - unsigned int i; - bool first = true; - - Dmsg3(400, "rewind res=%d fd=%d %s\n", reserved_device, m_fd, print_name()); - state &= ~(ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ - block_num = file = 0; - file_size = 0; - file_addr = 0; - if (m_fd < 0) { - if (!is_dvd()) { /* In case of major error, the fd is not open on DVD, so we don't want to abort. */ - dev_errno = EBADF; - Mmsg1(errmsg, _("Bad call to rewind. Device %s not open\n"), - print_name()); - Emsg0(M_ABORT, 0, errmsg); - } - return false; - } - if (is_tape()) { - mt_com.mt_op = MTREW; - mt_com.mt_count = 1; - /* If we get an I/O error on rewind, it is probably because - * the drive is actually busy. We loop for (about 5 minutes) - * retrying every 5 seconds. - */ - for (i=max_rewind_wait; ; i -= 5) { - if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - clrerror(MTREW); - if (i == max_rewind_wait) { - Dmsg1(200, "Rewind error, %s. retrying ...\n", be.strerror()); - } - /* - * This is a gross hack, because if the user has the - * device mounted (i.e. open), then uses mtx to load - * a tape, the current open file descriptor is invalid. - * So, we close the drive and re-open it. - */ - if (first && dcr) { - int open_mode = openmode; - tape_close(m_fd); - clear_opened(); - open(dcr, open_mode); - if (m_fd < 0) { - return false; - } - first = false; - continue; - } -#ifdef HAVE_SUN_OS - if (dev_errno == EIO) { - Mmsg1(errmsg, _("No tape loaded or drive offline on %s.\n"), print_name()); - return false; - } -#else - if (dev_errno == EIO && i > 0) { - Dmsg0(200, "Sleeping 5 seconds.\n"); - bmicrosleep(5, 0); - continue; - } -#endif - Mmsg2(errmsg, _("Rewind error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - break; - } - } else if (is_file() || is_dvd()) { - if (lseek(dcr, (boffset_t)0, SEEK_SET) < 0) { - berrno be; - dev_errno = errno; - Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - } - return true; -} - -void DEVICE::block(int why) -{ - r_dlock(); /* need recursive lock to block */ - block_device(this, why); - r_dunlock(); -} - -void DEVICE::unblock(bool locked) -{ - if (!locked) { - dlock(); - } - unblock_device(this); - dunlock(); -} - - -const char *DEVICE::print_blocked() const -{ - switch (m_blocked) { - case BST_NOT_BLOCKED: - return "BST_NOT_BLOCKED"; - case BST_UNMOUNTED: - return "BST_UNMOUNTED"; - case BST_WAITING_FOR_SYSOP: - return "BST_WAITING_FOR_SYSOP"; - case BST_DOING_ACQUIRE: - return "BST_DOING_ACQUIRE"; - case BST_WRITING_LABEL: - return "BST_WRITING_LABEL"; - case BST_UNMOUNTED_WAITING_FOR_SYSOP: - return "BST_UNMOUNTED_WAITING_FOR_SYSOP"; - case BST_MOUNT: - return "BST_MOUNT"; - default: - return _("unknown blocked code"); - } -} /* * Called to indicate that we have just read an * EOF from the device. */ -void DEVICE::set_ateof() -{ +void DEVICE::set_ateof() +{ set_eof(); if (is_tape()) { file++; @@ -839,165 +442,14 @@ void DEVICE::set_ateof() * Called to indicate we are now at the end of the tape, and * writing is not possible. */ -void DEVICE::set_ateot() +void DEVICE::set_ateot() { /* Make tape effectively read-only */ + Dmsg0(200, "==== Set AtEof\n"); state |= (ST_EOF|ST_EOT|ST_WEOT); clear_append(); } -/* - * Position device to end of medium (end of data) - * Returns: true on succes - * false on error - */ -bool DEVICE::eod(DCR *dcr) -{ - struct mtop mt_com; - bool ok = true; - boffset_t pos; - int32_t os_file; - - if (m_fd < 0) { - dev_errno = EBADF; - Mmsg1(errmsg, _("Bad call to eod. Device %s not open\n"), print_name()); - return false; - } - -#if defined (__digital__) && defined (__unix__) - return fsf(VolCatInfo.VolCatFiles); -#endif - - Dmsg0(29, "eod\n"); - if (at_eot()) { - return true; - } - clear_eof(); /* remove EOF flag */ - block_num = file = 0; - file_size = 0; - file_addr = 0; - if (is_fifo() || is_prog()) { - return true; - } - if (!is_tape()) { - pos = lseek(dcr, (boffset_t)0, SEEK_END); -// Dmsg1(100, "====== Seek to %lld\n", pos); - if (pos >= 0) { - update_pos(dcr); - set_eot(); - return true; - } - dev_errno = errno; - berrno be; - Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } -#ifdef MTEOM - if (has_cap(CAP_FASTFSF) && !has_cap(CAP_EOM)) { - Dmsg0(100,"Using FAST FSF for EOM\n"); - /* If unknown position, rewind */ - if (get_os_tape_file() < 0) { - if (!rewind(NULL)) { - return false; - } - } - mt_com.mt_op = MTFSF; - /* - * ***FIXME*** fix code to handle case that INT16_MAX is - * not large enough. - */ - mt_com.mt_count = INT16_MAX; /* use big positive number */ - if (mt_com.mt_count < 0) { - mt_com.mt_count = INT16_MAX; /* brain damaged system */ - } - } - - if (has_cap(CAP_MTIOCGET) && (has_cap(CAP_FASTFSF) || has_cap(CAP_EOM))) { - if (has_cap(CAP_EOM)) { - Dmsg0(100,"Using EOM for EOM\n"); - mt_com.mt_op = MTEOM; - mt_com.mt_count = 1; - } - - if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - clrerror(mt_com.mt_op); - Dmsg1(50, "ioctl error: %s\n", be.strerror()); - update_pos(dcr); - Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - - os_file = get_os_tape_file(); - if (os_file < 0) { - berrno be; - clrerror(-1); - Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - Dmsg1(100, "EOD file=%d\n", os_file); - set_ateof(); - file = os_file; - } else { -#else - { -#endif - /* - * Rewind then use FSF until EOT reached - */ - if (!rewind(NULL)) { - return false; - } - /* - * Move file by file to the end of the tape - */ - int file_num; - for (file_num=file; !at_eot(); file_num++) { - Dmsg0(200, "eod: doing fsf 1\n"); - if (!fsf(1)) { - Dmsg0(200, "fsf error.\n"); - return false; - } - /* - * Avoid infinite loop by ensuring we advance. - */ - if (!at_eot() && file_num == (int)file) { - Dmsg1(100, "fsf did not advance from file %d\n", file_num); - set_ateof(); - os_file = get_os_tape_file(); - if (os_file >= 0) { - Dmsg2(100, "Adjust file from %d to %d\n", file_num, os_file); - file = os_file; - } - break; - } - } - } - /* - * Some drivers leave us after second EOF when doing - * MTEOM, so we must backup so that appending overwrites - * the second EOF. - */ - if (has_cap(CAP_BSFATEOM)) { - /* Backup over EOF */ - ok = bsf(1); - /* If BSF worked and fileno is known (not -1), set file */ - os_file = get_os_tape_file(); - if (os_file >= 0) { - Dmsg2(100, "BSFATEOF adjust file from %d to %d\n", file , os_file); - file = os_file; - } else { - file++; /* wing it -- not correct on all OSes */ - } - } else { - update_pos(dcr); /* update position */ - } - Dmsg1(200, "EOD dev->file=%d\n", file); - return ok; -} /* * Set the position of the device -- only for files and DVD @@ -1017,17 +469,16 @@ bool DEVICE::update_pos(DCR *dcr) return false; } - /* Find out where we are */ - if (is_file() || is_dvd()) { + if (is_file()) { file = 0; file_addr = 0; pos = lseek(dcr, (boffset_t)0, SEEK_CUR); if (pos < 0) { berrno be; dev_errno = errno; - Pmsg1(000, _("Seek error: ERR=%s\n"), be.strerror()); + Pmsg1(000, _("Seek error: ERR=%s\n"), be.bstrerror()); Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), - print_name(), be.strerror()); + print_name(), be.bstrerror()); ok = false; } else { file_addr = pos; @@ -1038,867 +489,297 @@ bool DEVICE::update_pos(DCR *dcr) return ok; } -/* - * Return the status of the device. This was meant - * to be a generic routine. Unfortunately, it doesn't - * seem possible (at least I do not know how to do it - * currently), which means that for the moment, this - * routine has very little value. - * - * Returns: status - */ -uint32_t status_dev(DEVICE *dev) +void DEVICE::set_slot(int32_t slot) { - struct mtget mt_stat; - uint32_t stat = 0; - - if (dev->state & (ST_EOT | ST_WEOT)) { - stat |= BMT_EOD; - Pmsg0(-20, " EOD"); - } - if (dev->state & ST_EOF) { - stat |= BMT_EOF; - Pmsg0(-20, " EOF"); - } - if (dev->is_tape()) { - stat |= BMT_TAPE; - Pmsg0(-20,_(" Bacula status:")); - Pmsg2(-20,_(" file=%d block=%d\n"), dev->file, dev->block_num); - if (tape_ioctl(dev->fd(), MTIOCGET, (char *)&mt_stat) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return 0; - } - Pmsg0(-20, _(" Device status:")); - -#if defined(HAVE_LINUX_OS) - if (GMT_EOF(mt_stat.mt_gstat)) { - stat |= BMT_EOF; - Pmsg0(-20, " EOF"); - } - if (GMT_BOT(mt_stat.mt_gstat)) { - stat |= BMT_BOT; - Pmsg0(-20, " BOT"); - } - if (GMT_EOT(mt_stat.mt_gstat)) { - stat |= BMT_EOT; - Pmsg0(-20, " EOT"); - } - if (GMT_SM(mt_stat.mt_gstat)) { - stat |= BMT_SM; - Pmsg0(-20, " SM"); - } - if (GMT_EOD(mt_stat.mt_gstat)) { - stat |= BMT_EOD; - Pmsg0(-20, " EOD"); - } - if (GMT_WR_PROT(mt_stat.mt_gstat)) { - stat |= BMT_WR_PROT; - Pmsg0(-20, " WR_PROT"); - } - if (GMT_ONLINE(mt_stat.mt_gstat)) { - stat |= BMT_ONLINE; - Pmsg0(-20, " ONLINE"); - } - if (GMT_DR_OPEN(mt_stat.mt_gstat)) { - stat |= BMT_DR_OPEN; - Pmsg0(-20, " DR_OPEN"); - } - if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { - stat |= BMT_IM_REP_EN; - Pmsg0(-20, " IM_REP_EN"); - } -#elif defined(HAVE_WIN32) - if (GMT_EOF(mt_stat.mt_gstat)) { - stat |= BMT_EOF; - Pmsg0(-20, " EOF"); - } - if (GMT_BOT(mt_stat.mt_gstat)) { - stat |= BMT_BOT; - Pmsg0(-20, " BOT"); - } - if (GMT_EOT(mt_stat.mt_gstat)) { - stat |= BMT_EOT; - Pmsg0(-20, " EOT"); - } - if (GMT_EOD(mt_stat.mt_gstat)) { - stat |= BMT_EOD; - Pmsg0(-20, " EOD"); - } - if (GMT_WR_PROT(mt_stat.mt_gstat)) { - stat |= BMT_WR_PROT; - Pmsg0(-20, " WR_PROT"); - } - if (GMT_ONLINE(mt_stat.mt_gstat)) { - stat |= BMT_ONLINE; - Pmsg0(-20, " ONLINE"); - } - if (GMT_DR_OPEN(mt_stat.mt_gstat)) { - stat |= BMT_DR_OPEN; - Pmsg0(-20, " DR_OPEN"); - } - if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { - stat |= BMT_IM_REP_EN; - Pmsg0(-20, " IM_REP_EN"); - } - -#endif /* !SunOS && !OSF */ - if (dev->has_cap(CAP_MTIOCGET)) { - Pmsg2(-20, _(" file=%d block=%d\n"), mt_stat.mt_fileno, mt_stat.mt_blkno); - } else { - Pmsg2(-20, _(" file=%d block=%d\n"), -1, -1); - } - } else { - stat |= BMT_ONLINE | BMT_BOT; - } - return stat; + m_slot = slot; + if (vol) vol->clear_slot(); } - -/* - * Load medium in device - * Returns: true on success - * false on failure - */ -bool load_dev(DEVICE *dev) +void DEVICE::clear_slot() { -#ifdef MTLOAD - struct mtop mt_com; -#endif - - if (dev->fd() < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to load_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return false; - } - if (!(dev->is_tape())) { - return true; - } -#ifndef MTLOAD - Dmsg0(200, "stored: MTLOAD command not available\n"); - berrno be; - dev->dev_errno = ENOTTY; /* function not available */ - Mmsg2(dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; -#else - - dev->block_num = dev->file = 0; - dev->file_size = 0; - dev->file_addr = 0; - mt_com.mt_op = MTLOAD; - mt_com.mt_count = 1; - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; - } - return true; -#endif + m_slot = -1; + if (vol) vol->set_slot(-1); } -/* - * Rewind device and put it offline - * Returns: true on success - * false on failure - */ -bool DEVICE::offline() -{ - struct mtop mt_com; - - if (!is_tape()) { - return true; /* device not open */ - } - - state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ - block_num = file = 0; - file_size = 0; - file_addr = 0; - unlock_door(); - mt_com.mt_op = MTOFFL; - mt_com.mt_count = 1; - if (tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - dev_errno = errno; - Mmsg2(errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - Dmsg1(100, "Offlined device %s\n", print_name()); - return true; -} - -bool DEVICE::offline_or_rewind() -{ - if (m_fd < 0) { - return false; - } - if (has_cap(CAP_OFFLINEUNMOUNT)) { - return offline(); - } else { - /* - * Note, this rewind probably should not be here (it wasn't - * in prior versions of Bacula), but on FreeBSD, this is - * needed in the case the tape was "frozen" due to an error - * such as backspacing after writing and EOF. If it is not - * done, all future references to the drive get and I/O error. - */ - clrerror(MTREW); - return rewind(NULL); - } -} - -/* - * Foward space a file - * Returns: true on success - * false on failure - */ -bool DEVICE::fsf(int num) -{ - int32_t os_file; - struct mtop mt_com; - int stat = 0; - - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to fsf. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; - } - - if (!is_tape()) { - return true; - } - - if (at_eot()) { - dev_errno = 0; - Mmsg1(errmsg, _("Device %s at End of Tape.\n"), print_name()); - return false; - } - if (at_eof()) { - Dmsg0(200, "ST_EOF set on entry to FSF\n"); - } - - Dmsg0(100, "fsf\n"); - block_num = 0; - /* - * If Fast forward space file is set, then we - * use MTFSF to forward space and MTIOCGET - * to get the file position. We assume that - * the SCSI driver will ensure that we do not - * forward space past the end of the medium. - */ - if (has_cap(CAP_FSF) && has_cap(CAP_MTIOCGET) && has_cap(CAP_FASTFSF)) { - mt_com.mt_op = MTFSF; - mt_com.mt_count = num; - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0 || (os_file=get_os_tape_file()) < 0) { - berrno be; - set_eot(); - Dmsg0(200, "Set ST_EOT\n"); - clrerror(MTFSF); - Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - Dmsg1(200, "%s", errmsg); - return false; - } - Dmsg1(200, "fsf file=%d\n", os_file); - set_ateof(); - file = os_file; - return true; - - /* - * Here if CAP_FSF is set, and virtually all drives - * these days support it, we read a record, then forward - * space one file. Using this procedure, which is slow, - * is the only way we can be sure that we don't read - * two consecutive EOF marks, which means End of Data. - */ - } else if (has_cap(CAP_FSF)) { - POOLMEM *rbuf; - int rbuf_len; - Dmsg0(200, "FSF has cap_fsf\n"); - if (max_block_size == 0) { - rbuf_len = DEFAULT_BLOCK_SIZE; - } else { - rbuf_len = max_block_size; - } - rbuf = get_memory(rbuf_len); - mt_com.mt_op = MTFSF; - mt_com.mt_count = 1; - while (num-- && !at_eot()) { - Dmsg0(100, "Doing read before fsf\n"); - if ((stat = this->read((char *)rbuf, rbuf_len)) < 0) { - if (errno == ENOMEM) { /* tape record exceeds buf len */ - stat = rbuf_len; /* This is OK */ - /* - * On IBM drives, they return ENOSPC at EOM - * instead of EOF status - */ - } else if (at_eof() && errno == ENOSPC) { - stat = 0; - } else { - berrno be; - set_eot(); - clrerror(-1); - Dmsg2(100, "Set ST_EOT read errno=%d. ERR=%s\n", dev_errno, - be.strerror()); - Mmsg2(errmsg, _("read error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - Dmsg1(100, "%s", errmsg); - break; - } - } - if (stat == 0) { /* EOF */ - Dmsg1(100, "End of File mark from read. File=%d\n", file+1); - /* Two reads of zero means end of tape */ - if (at_eof()) { - set_eot(); - Dmsg0(100, "Set ST_EOT\n"); - break; - } else { - set_ateof(); - continue; - } - } else { /* Got data */ - clear_eot(); - clear_eof(); - } - - Dmsg0(100, "Doing MTFSF\n"); - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0) { /* error => EOT */ - berrno be; - set_eot(); - Dmsg0(100, "Set ST_EOT\n"); - clrerror(MTFSF); - Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - Dmsg0(100, "Got < 0 for MTFSF\n"); - Dmsg1(100, "%s", errmsg); - } else { - set_ateof(); - } - } - free_memory(rbuf); - - /* - * No FSF, so use FSR to simulate it - */ - } else { - Dmsg0(200, "Doing FSR for FSF\n"); - while (num-- && !at_eot()) { - fsr(INT32_MAX); /* returns -1 on EOF or EOT */ - } - if (at_eot()) { - dev_errno = 0; - Mmsg1(errmsg, _("Device %s at End of Tape.\n"), print_name()); - stat = -1; - } else { - stat = 0; - } - } - Dmsg1(200, "Return %d from FSF\n", stat); - if (at_eof()) { - Dmsg0(200, "ST_EOF set on exit FSF\n"); - } - if (at_eot()) { - Dmsg0(200, "ST_EOT set on exit FSF\n"); - } - Dmsg1(200, "Return from FSF file=%d\n", file); - return stat == 0; -} - -/* - * Backward space a file - * Returns: false on failure - * true on success - */ -bool DEVICE::bsf(int num) -{ - struct mtop mt_com; - int stat; - - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to bsf. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; - } - - if (!is_tape()) { - Mmsg1(errmsg, _("Device %s cannot BSF because it is not a tape.\n"), - print_name()); - return false; - } - - Dmsg0(29, "bsf\n"); - clear_eot(); - clear_eof(); - file -= num; - file_addr = 0; - file_size = 0; - mt_com.mt_op = MTBSF; - mt_com.mt_count = num; - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0) { - berrno be; - clrerror(MTBSF); - Mmsg2(errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - } - return stat == 0; -} - - -/* - * Foward space num records - * Returns: false on failure - * true on success - */ -bool DEVICE::fsr(int num) -{ - struct mtop mt_com; - int stat; - - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to fsr. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; - } - - if (!is_tape()) { - return false; - } - - if (!has_cap(CAP_FSR)) { - Mmsg1(errmsg, _("ioctl MTFSR not permitted on %s.\n"), print_name()); - return false; - } - - Dmsg1(29, "fsr %d\n", num); - mt_com.mt_op = MTFSR; - mt_com.mt_count = num; - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat == 0) { - clear_eof(); - block_num += num; - } else { - berrno be; - struct mtget mt_stat; - clrerror(MTFSR); - Dmsg1(100, "FSF fail: ERR=%s\n", be.strerror()); - if (dev_get_os_pos(this, &mt_stat)) { - Dmsg4(100, "Adjust from %d:%d to %d:%d\n", file, - block_num, mt_stat.mt_fileno, mt_stat.mt_blkno); - file = mt_stat.mt_fileno; - block_num = mt_stat.mt_blkno; - } else { - if (at_eof()) { - set_eot(); - } else { - set_ateof(); - } - } - Mmsg3(errmsg, _("ioctl MTFSR %d error on %s. ERR=%s.\n"), - num, print_name(), be.strerror()); +const char *DEVICE::print_type() const +{ + return prt_dev_types[device->dev_type]; +} + +/* + * Set to unload the current volume in the drive + */ +void DEVICE::set_unload() +{ + if (!m_unload && VolHdr.VolumeName[0] != 0) { + m_unload = true; + memcpy(UnloadVolName, VolHdr.VolumeName, sizeof(UnloadVolName)); + notify_newvol_in_attached_dcrs(NULL); } - return stat == 0; } /* - * Backward space a record - * Returns: false on failure - * true on success + * Clear volume header */ -bool DEVICE::bsr(int num) +void DEVICE::clear_volhdr() { - struct mtop mt_com; - int stat; + Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName); + memset(&VolHdr, 0, sizeof(VolHdr)); + setVolCatInfo(false); +} - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to bsr_dev. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; - } +void DEVICE::set_nospace() +{ + state |= ST_NOSPACE; +} - if (!is_tape()) { - return false; - } +void DEVICE::clear_nospace() +{ + state &= ~ST_NOSPACE; +} - if (!has_cap(CAP_BSR)) { - Mmsg1(errmsg, _("ioctl MTBSR not permitted on %s.\n"), print_name()); - return false; - } +/* Put device in append mode */ +void DEVICE::set_append() +{ + state &= ~(ST_NOSPACE|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ + state |= ST_APPEND; +} - Dmsg0(29, "bsr_dev\n"); - block_num -= num; - clear_eof(); - clear_eot(); - mt_com.mt_op = MTBSR; - mt_com.mt_count = num; - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0) { - berrno be; - clrerror(MTBSR); - Mmsg2(errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - } - return stat == 0; +/* Clear append mode */ +void DEVICE::clear_append() +{ + state &= ~ST_APPEND; } -void DEVICE::lock_door() +/* Put device in read mode */ +void DEVICE::set_read() { -#ifdef MTLOCK - struct mtop mt_com; - mt_com.mt_op = MTLOCK; - mt_com.mt_count = 1; - tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); -#endif + state &= ~(ST_APPEND|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ + state |= ST_READ; } -void DEVICE::unlock_door() +/* Clear read mode */ +void DEVICE::clear_read() { -#ifdef MTUNLOCK - struct mtop mt_com; - mt_com.mt_op = MTUNLOCK; - mt_com.mt_count = 1; - tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); -#endif + state &= ~ST_READ; } - /* - * Reposition the device to file, block - * Returns: false on failure - * true on success + * Get freespace using OS calls + * TODO: See if it's working with mount commands */ -bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock) +bool DEVICE::get_os_device_freespace() { - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to reposition. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; - } + int64_t freespace, totalspace; - if (!is_tape()) { - boffset_t pos = (((boffset_t)rfile)<<32) | rblock; - Dmsg1(100, "===== lseek to %d\n", (int)pos); - if (lseek(dcr, pos, SEEK_SET) == (boffset_t)-1) { - berrno be; - dev_errno = errno; - Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - return false; - } - file = rfile; - block_num = rblock; - file_addr = pos; + if (!is_file()) { return true; } + if (fs_get_free_space(dev_name, &freespace, &totalspace) == 0) { + set_freespace(freespace, totalspace, 0, true); + Mmsg(errmsg, ""); + return true; - /* After this point, we are tape only */ - Dmsg4(100, "reposition from %u:%u to %u:%u\n", file, block_num, rfile, rblock); - if (rfile < file) { - Dmsg0(100, "Rewind\n"); - if (!rewind(NULL)) { - return false; - } - } - if (rfile > file) { - Dmsg1(100, "fsf %d\n", rfile-file); - if (!fsf(rfile-file)) { - Dmsg1(100, "fsf failed! ERR=%s\n", bstrerror()); - return false; - } - Dmsg2(100, "wanted_file=%d at_file=%d\n", rfile, file); - } - if (rblock < block_num) { - Dmsg2(100, "wanted_blk=%d at_blk=%d\n", rblock, block_num); - Dmsg0(100, "bsf 1\n"); - bsf(1); - Dmsg0(100, "fsf 1\n"); - fsf(1); - Dmsg2(100, "wanted_blk=%d at_blk=%d\n", rblock, block_num); - } - if (has_cap(CAP_POSITIONBLOCKS) && rblock > block_num) { - /* Ignore errors as Bacula can read to the correct block */ - Dmsg1(100, "fsr %d\n", rblock-block_num); - return fsr(rblock-block_num); } else { - while (rblock > block_num) { - if (!read_block_from_dev(dcr, NO_BLOCK_NUMBER_CHECK)) { - berrno be; - dev_errno = errno; - Dmsg2(30, "Failed to find requested block on %s: ERR=%s", - print_name(), be.strerror()); - return false; - } - Dmsg2(300, "moving forward wanted_blk=%d at_blk=%d\n", rblock, block_num); - } + set_freespace(0, 0, 0, false); /* No valid freespace */ } - return true; + return false; } +/* Update the free space on the device */ +bool DEVICE::update_freespace() +{ + POOL_MEM ocmd(PM_FNAME); + POOLMEM* results; + char* icmd; + char* p; + uint64_t free, total; + char ed1[50]; + bool ok = false; + int status; + berrno be; + if (!is_file()) { + Mmsg(errmsg, ""); + return true; + } -/* - * Write an end of file on the device - * Returns: true on success - * false on failure - */ -bool DEVICE::weof(int num) -{ - struct mtop mt_com; - int stat; - Dmsg0(129, "weof_dev\n"); - - if (!is_open()) { - dev_errno = EBADF; - Mmsg0(errmsg, _("Bad call to weof_dev. Device not open\n")); - Emsg0(M_FATAL, 0, errmsg); - return false; + /* The device must be mounted in order for freespace to work */ + if (requires_mount()) { + mount(1); } - file_size = 0; - if (!is_tape()) { + if (get_os_device_freespace()) { + Dmsg4(20, "get_os_device_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", + edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media()); return true; } - if (!can_append()) { - Mmsg0(errmsg, _("Attempt to WEOF on non-appendable Volume\n")); - Emsg0(M_FATAL, 0, errmsg); + + icmd = device->free_space_command; + + if (!icmd) { + set_freespace(0, 0, 0, false); + Dmsg2(20, "ERROR: update_free_space_dev: free_space=%s, free_space_errno=%d (!icmd)\n", + edit_uint64(free_space, ed1), free_space_errno); + Mmsg(errmsg, _("No FreeSpace command defined.\n")); return false; } - - clear_eof(); - clear_eot(); - mt_com.mt_op = MTWEOF; - mt_com.mt_count = num; - stat = tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - if (stat == 0) { - block_num = 0; - file += num; - file_addr = 0; - } else { - berrno be; - clrerror(MTWEOF); - if (stat == -1) { - Mmsg2(errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), - print_name(), be.strerror()); - } - } - return stat == 0; -} + edit_mount_codes(ocmd, icmd); -/* - * If implemented in system, clear the tape - * error status. - */ -void DEVICE::clrerror(int func) -{ - const char *msg = NULL; - char buf[100]; + Dmsg1(20, "update_freespace: cmd=%s\n", ocmd.c_str()); - dev_errno = errno; /* save errno */ - if (errno == EIO) { - VolCatInfo.VolCatErrors++; - } + results = get_pool_memory(PM_MESSAGE); - if (!is_tape()) { - return; - } + Dmsg1(20, "Run freespace prog=%s\n", ocmd.c_str()); + status = run_program_full_output(ocmd.c_str(), max_open_wait/2, results); + Dmsg2(20, "Freespace status=%d result=%s\n", status, results); + /* Should report "1223232 12323232\n" "free total\n" */ + if (status == 0) { + free = str_to_int64(results) * 1024; + p = results; - if (errno == ENOTTY || errno == ENOSYS) { /* Function not implemented */ - switch (func) { - case -1: - break; /* ignore message printed later */ - case MTWEOF: - msg = "WTWEOF"; - clear_cap(CAP_EOF); /* turn off feature */ - break; -#ifdef MTEOM - case MTEOM: - msg = "WTEOM"; - clear_cap(CAP_EOM); /* turn off feature */ - break; -#endif - case MTFSF: - msg = "MTFSF"; - clear_cap(CAP_FSF); /* turn off feature */ - break; - case MTBSF: - msg = "MTBSF"; - clear_cap(CAP_BSF); /* turn off feature */ - break; - case MTFSR: - msg = "MTFSR"; - clear_cap(CAP_FSR); /* turn off feature */ - break; - case MTBSR: - msg = "MTBSR"; - clear_cap(CAP_BSR); /* turn off feature */ - break; - case MTREW: - msg = "MTREW"; - break; -#ifdef MTSETBLK - case MTSETBLK: - msg = "MTSETBLK"; - break; -#endif -#ifdef MTSETDRVBUFFER - case MTSETDRVBUFFER: - msg = "MTSETDRVBUFFER"; - break; -#endif -#ifdef MTRESET - case MTRESET: - msg = "MTRESET"; - break; -#endif + if (skip_nonspaces(&p)) { + total = str_to_int64(p) * 1024; -#ifdef MTSETBSIZ - case MTSETBSIZ: - msg = "MTSETBSIZ"; - break; -#endif -#ifdef MTSRSZ - case MTSRSZ: - msg = "MTSRSZ"; - break; -#endif -#ifdef MTLOAD - case MTLOAD: - msg = "MTLOAD"; - break; -#endif -#ifdef MTUNLOCK - case MTUNLOCK: - msg = "MTUNLOCK"; - break; -#endif - case MTOFFL: - msg = "MTOFFL"; - break; - default: - bsnprintf(buf, sizeof(buf), _("unknown func code %d"), func); - msg = buf; - break; - } - if (msg != NULL) { - dev_errno = ENOSYS; - Mmsg1(errmsg, _("I/O function \"%s\" not supported on this device.\n"), msg); - Emsg0(M_ERROR, 0, errmsg); + } else { + total = 0; } - } - /* - * Now we try different methods of clearing the error - * status on the drive so that it is not locked for - * further operations. - */ + Dmsg1(400, "Free space program run: Freespace=%s\n", results); + if (free >= 0) { + set_freespace(free, total, 0, true); /* have valid freespace */ + Mmsg(errmsg, ""); + ok = true; + } + } else { + set_freespace(0, 0, EPIPE, false); /* no valid freespace */ + Mmsg2(errmsg, _("Cannot run free space command. Results=%s ERR=%s\n"), + results, be.bstrerror(status)); - /* On some systems such as NetBSD, this clears all errors */ - get_os_tape_file(); + dev_errno = free_space_errno; + Dmsg4(20, "Cannot get free space on device %s. free_space=%s, " + "free_space_errno=%d ERR=%s\n", + print_name(), edit_uint64(free_space, ed1), + free_space_errno, errmsg); + } + free_pool_memory(results); + Dmsg4(20, "leave update_freespace: free_space=%s freespace_ok=%d free_space_errno=%d have_media=%d\n", + edit_uint64(free_space, ed1), !!is_freespace_ok(), free_space_errno, !!have_media()); + return ok; +} -/* Found on Solaris */ -#ifdef MTIOCLRERR +void DEVICE::updateVolCatBytes(uint64_t bytes) { - tape_ioctl(m_fd, MTIOCLRERR); - Dmsg0(200, "Did MTIOCLRERR\n"); + DEVICE *dev = this; + Lock_VolCatInfo(); + dev->VolCatInfo.VolCatAmetaBytes += bytes; + Dmsg1(200, "updateVolBytes ameta=%lld\n", + dev->VolCatInfo.VolCatAmetaBytes); + dev->VolCatInfo.VolCatBytes += bytes; + setVolCatInfo(false); + Unlock_VolCatInfo(); } -#endif -/* Typically on FreeBSD */ -#ifdef MTIOCERRSTAT +void DEVICE::updateVolCatBlocks(uint32_t blocks) { - berrno be; - /* Read and clear SCSI error status */ - union mterrstat mt_errstat; - Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev_errno, - be.strerror(dev_errno)); - tape_ioctl(m_fd, MTIOCERRSTAT, (char *)&mt_errstat); + DEVICE *dev = this; + Lock_VolCatInfo(); + dev->VolCatInfo.VolCatAmetaBlocks += blocks; + dev->VolCatInfo.VolCatBlocks += blocks; + setVolCatInfo(false); + Unlock_VolCatInfo(); } -#endif -/* Clear Subsystem Exception OSF1 */ -#ifdef MTCSE + +void DEVICE::updateVolCatWrites(uint32_t writes) { - struct mtop mt_com; - mt_com.mt_op = MTCSE; - mt_com.mt_count = 1; - /* Clear any error condition on the tape */ - tape_ioctl(m_fd, MTIOCTOP, (char *)&mt_com); - Dmsg0(200, "Did MTCSE\n"); -} -#endif + DEVICE *dev = this; + Lock_VolCatInfo(); + dev->VolCatInfo.VolCatAmetaWrites += writes; + dev->VolCatInfo.VolCatWrites += writes; + setVolCatInfo(false); + Unlock_VolCatInfo(); } - -/* - * Clear volume header - */ -void DEVICE::clear_volhdr() +void DEVICE::updateVolCatReads(uint32_t reads) { - /* If we have an unused volume associated with this drive, free it */ - if (vol && !is_busy()) { - free_volume(this); - } - Dmsg1(100, "Clear volhdr vol=%s\n", VolHdr.VolumeName); - memset(&VolHdr, 0, sizeof(VolHdr)); + DEVICE *dev = this; + Lock_VolCatInfo(); + dev->VolCatInfo.VolCatAmetaReads += reads; + dev->VolCatInfo.VolCatReads += reads; + setVolCatInfo(false); + Unlock_VolCatInfo(); } +void DEVICE::updateVolCatReadBytes(uint64_t bytes) +{ + DEVICE *dev = this; + Lock_VolCatInfo(); + dev->VolCatInfo.VolCatAmetaRBytes += bytes; + dev->VolCatInfo.VolCatRBytes += bytes; + setVolCatInfo(false); + Unlock_VolCatInfo(); +} /* * Close the device */ -void DEVICE::close() +bool DEVICE::close() { - Dmsg1(100, "close_dev %s\n", print_name()); - if (has_cap(CAP_OFFLINEUNMOUNT)) { - offline(); - } + bool ok = true; + + Dmsg4(40, "close_dev vol=%s fd=%d dev=%p dev=%s\n", + VolHdr.VolumeName, m_fd, this, print_name()); + offline_or_rewind(); if (!is_open()) { - Dmsg2(100, "device %s already closed vol=%s\n", print_name(), + Dmsg2(200, "device %s already closed vol=%s\n", print_name(), VolHdr.VolumeName); - return; /* already closed */ + return true; /* already closed */ } switch (dev_type) { + case B_VTL_DEV: + case B_VTAPE_DEV: case B_TAPE_DEV: - unlock_door(); - tape_close(m_fd); - break; + unlock_door(); + /* Fall through wanted */ default: - ::close(m_fd); + if (d_close(m_fd) != 0) { + berrno be; + dev_errno = errno; + Mmsg2(errmsg, _("Error closing device %s. ERR=%s.\n"), + print_name(), be.bstrerror()); + ok = false; + } + break; } + unmount(1); /* do unmount if required */ + /* Clean up device packet so it can be reused */ clear_opened(); - state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF); + + /* + * Be careful not to clear items needed by the DVD driver + * when it is closing a single part. + */ + state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF| + ST_NOSPACE|ST_MOUNTED|ST_MEDIA|ST_SHORT); label_type = B_BACULA_LABEL; file = block_num = 0; file_size = 0; file_addr = 0; EndFile = EndBlock = 0; openmode = 0; - Slot = -1; /* unknown slot */ clear_volhdr(); memset(&VolCatInfo, 0, sizeof(VolCatInfo)); if (tid) { stop_thread_timer(tid); tid = 0; } + return ok; } /* @@ -1907,10 +788,10 @@ void DEVICE::close() * difference between close_part() and close() is that close_part() * saves the state information of the device (e.g. the Volume lable, * the Volume Catalog record, ... This permits opening and closing - * the Volume parts multiple times without losing track of what the + * the Volume parts multiple times without losing track of what the * main Volume parameters are. */ -void DEVICE::close_part(DCR *dcr) +void DEVICE::close_part(DCR * /*dcr*/) { VOLUME_LABEL saveVolHdr; VOLUME_CAT_INFO saveVolCatInfo; /* Volume Catalog Information */ @@ -1921,221 +802,35 @@ void DEVICE::close_part(DCR *dcr) close(); /* close current part */ VolHdr = saveVolHdr; /* structure assignment */ VolCatInfo = saveVolCatInfo; /* structure assignment */ - dcr->VolCatInfo = saveVolCatInfo; /* structure assignment */ -} - -boffset_t DEVICE::lseek(DCR *dcr, boffset_t offset, int whence) -{ - switch (dev_type) { - case B_DVD_DEV: - return lseek_dvd(dcr, offset, whence); - case B_FILE_DEV: -#if defined(HAVE_WIN32) - return ::_lseeki64(m_fd, (__int64)offset, whence); -#else - return ::lseek(m_fd, (off_t)offset, whence); -#endif - } - return -1; -} - - -bool DEVICE::truncate(DCR *dcr) /* We need the DCR for DVD-writing */ -{ - Dmsg1(100, "truncate %s\n", print_name()); - switch (dev_type) { - case B_TAPE_DEV: - /* maybe we should rewind and write and eof ???? */ - return true; /* we don't really truncate tapes */ - case B_DVD_DEV: - return truncate_dvd(dcr); - case B_FILE_DEV: - /* ***FIXME*** we really need to unlink() the file so that - * its name can be changed for a relabel. - */ - if (ftruncate(m_fd, 0) != 0) { - berrno be; - Mmsg2(errmsg, _("Unable to truncate device %s. ERR=%s\n"), - print_name(), be.strerror()); - return false; - } - return true; - } - return false; } -/* Mount the device. +/* * If timeout, wait until the mount command returns 0. * If !timeout, try to mount the device only once. */ -bool DEVICE::mount(int timeout) +bool DEVICE::mount(int timeout) { Dmsg0(190, "Enter mount\n"); - if (is_mounted()) { - return true; - } else if (requires_mount()) { - return do_mount(1, timeout); - } + if (!is_mounted() && device->mount_command) { + return mount_file(1, timeout); + } return true; } -/* Unmount the device +/* + * Unmount the device * If timeout, wait until the unmount command returns 0. * If !timeout, try to unmount the device only once. */ -bool DEVICE::unmount(int timeout) +bool DEVICE::unmount(int timeout) { - Dmsg0(90, "Enter unmount\n"); - if (is_mounted()) { - return do_mount(0, timeout); - } + Dmsg0(100, "Enter unmount\n"); + if (is_mounted() && requires_mount() && device->unmount_command) { + return mount_file(0, timeout); + } return true; } -/* (Un)mount the device */ -bool DEVICE::do_mount(int mount, int dotimeout) -{ - POOL_MEM ocmd(PM_FNAME); - POOLMEM *results; - char *icmd; - int status, timeout; - - sm_check(__FILE__, __LINE__, false); - if (mount) { - if (is_mounted()) { - Dmsg0(200, "======= mount=1\n"); - return true; - } - icmd = device->mount_command; - } else { - if (!is_mounted()) { - Dmsg0(200, "======= mount=0\n"); - return true; - } - icmd = device->unmount_command; - } - - clear_freespace_ok(); - edit_mount_codes(ocmd, icmd); - - Dmsg2(100, "do_mount: cmd=%s mounted=%d\n", ocmd.c_str(), !!is_mounted()); - - if (dotimeout) { - /* Try at most 1 time to (un)mount the device. This should perhaps be configurable. */ - timeout = 1; - } else { - timeout = 0; - } - results = get_memory(4000); - results[0] = 0; - - /* If busy retry each second */ - Dmsg1(20, "do_mount run_prog=%s\n", ocmd.c_str()); - while ((status = run_program_full_output(ocmd.c_str(), - max_open_wait/2, results)) != 0) { - /* Doesn't work with internationalization (This is not a problem) */ - if (mount && fnmatch("*is already mounted on*", results, 0) == 0) { - break; - } - if (!mount && fnmatch("* not mounted*", results, 0) == 0) { - break; - } - if (timeout-- > 0) { - /* Sometimes the device cannot be mounted because it is already mounted. - * Try to unmount it, then remount it */ - if (mount) { - Dmsg1(400, "Trying to unmount the device %s...\n", print_name()); - do_mount(0, 0); - } - bmicrosleep(1, 0); - continue; - } - if (status != 0) { - berrno be; - Dmsg5(40, "Device %s cannot be %smounted. stat=%d result=%s ERR=%s\n", print_name(), - (mount ? "" : "un"), status, results, be.strerror(status)); - Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"), - print_name(), (mount ? "" : "un"), be.strerror(status)); - } else { - Dmsg4(40, "Device %s cannot be %smounted. stat=%d ERR=%s\n", print_name(), - (mount ? "" : "un"), status, results); - Mmsg(errmsg, _("Device %s cannot be %smounted. ERR=%s\n"), - print_name(), (mount ? "" : "un"), results); - } - /* - * Now, just to be sure it is not mounted, try to read the - * filesystem. - */ - DIR* dp; - struct dirent *entry, *result; - int name_max; - int count; - - name_max = pathconf(".", _PC_NAME_MAX); - if (name_max < 1024) { - name_max = 1024; - } - - if (!(dp = opendir(device->mount_point))) { - berrno be; - dev_errno = errno; - Dmsg3(29, "do_mount: failed to open dir %s (dev=%s), ERR=%s\n", - device->mount_point, print_name(), be.strerror()); - goto get_out; - } - - entry = (struct dirent *)malloc(sizeof(struct dirent) + name_max + 1000); - count = 0; - while (1) { - if ((readdir_r(dp, entry, &result) != 0) || (result == NULL)) { - dev_errno = EIO; - Dmsg2(129, "do_mount: failed to find suitable file in dir %s (dev=%s)\n", - device->mount_point, print_name()); - break; - } - if ((strcmp(result->d_name, ".")) && (strcmp(result->d_name, "..")) && (strcmp(result->d_name, ".keep"))) { - count++; /* result->d_name != ., .. or .keep (Gentoo-specific) */ - break; - } else { - Dmsg2(129, "do_mount: ignoring %s in %s\n", result->d_name, device->mount_point); - } - } - free(entry); - closedir(dp); - - Dmsg1(29, "do_mount: got %d files in the mount point (not counting ., .. and .keep)\n", count); - - if (count > 0) { - /* If we got more than ., .. and .keep */ - /* there must be something mounted */ - if (mount) { - Dmsg1(100, "Did Mount by count=%d\n", count); - break; - } else { - /* An unmount request. We failed to unmount - report an error */ - set_mounted(true); - free_pool_memory(results); - Dmsg0(200, "== error mount=1 wanted unmount\n"); - return false; - } - } -get_out: - set_mounted(false); - sm_check(__FILE__, __LINE__, false); - free_pool_memory(results); - Dmsg0(200, "============ mount=0\n"); - return false; - } - - set_mounted(mount); /* set/clear mounted flag */ - free_pool_memory(results); - /* Do not check free space when unmounting */ - if (mount && !update_freespace()) { - return false; - } - Dmsg1(200, "============ mount=%d\n", mount); - return true; -} /* * Edit codes into (Un)MountCommand, Write(First)PartCommand @@ -2155,7 +850,7 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) const char *p; const char *str; char add[20]; - + POOL_MEM archive_name(PM_FNAME); omsg.c_str()[0] = 0; @@ -2187,10 +882,6 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) case 'm': str = device->mount_point; break; - case 'v': - make_spooled_dvd_filename(this, archive_name); - str = archive_name.c_str(); - break; default: add[0] = '%'; add[1] = *p; @@ -2209,12 +900,15 @@ void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) } } -/* return the last timer interval (ms) */ +/* return the last timer interval (ms) + * or 0 if something goes wrong + */ btime_t DEVICE::get_timer_count() { - btime_t old = last_timer; + btime_t temp = last_timer; last_timer = get_current_btime(); - return last_timer - old; + temp = last_timer - temp; /* get elapsed time */ + return (temp>0)?temp:0; /* take care of skewed clock */ } /* read from fd */ @@ -2224,11 +918,7 @@ ssize_t DEVICE::read(void *buf, size_t len) get_timer_count(); - if (this->is_tape()) { - read_len = tape_read(m_fd, buf, len); - } else { - read_len = ::read(m_fd, buf, len); - } + read_len = d_read(m_fd, buf, len); last_tick = get_timer_count(); @@ -2239,7 +929,7 @@ ssize_t DEVICE::read(void *buf, size_t len) DevReadBytes += read_len; } - return read_len; + return read_len; } /* write to fd */ @@ -2249,11 +939,7 @@ ssize_t DEVICE::write(const void *buf, size_t len) get_timer_count(); - if (this->is_tape()) { - write_len = tape_write(m_fd, buf, len); - } else { - write_len = ::write(m_fd, buf, len); - } + write_len = d_write(m_fd, buf, len); last_tick = get_timer_count(); @@ -2264,7 +950,7 @@ ssize_t DEVICE::write(const void *buf, size_t len) DevWriteBytes += write_len; } - return write_len; + return write_len; } /* Return the resource name for the device */ @@ -2273,30 +959,78 @@ const char *DEVICE::name() const return device->hdr.name; } -/* Returns file position on tape or -1 */ -int32_t DEVICE::get_os_tape_file() +uint32_t DEVICE::get_file() { - struct mtget mt_stat; + if (is_dvd() || is_tape()) { + return file; + } else { + uint64_t bytes = VolCatInfo.VolCatAdataBytes + VolCatInfo.VolCatAmetaBytes; + return (uint32_t)(bytes >> 32); + } +} - if (has_cap(CAP_MTIOCGET) && - tape_ioctl(m_fd, MTIOCGET, (char *)&mt_stat) == 0) { - return mt_stat.mt_fileno; +uint32_t DEVICE::get_block_num() +{ + if (is_dvd() || is_tape()) { + return block_num; + } else { + return VolCatInfo.VolCatAdataBlocks + VolCatInfo.VolCatAmetaBlocks; } - return -1; } -char * -dev_vol_name(DEVICE *dev) +/* + * Walk through all attached jcrs indicating the volume has changed + * Note: If you have the new VolumeName, it is passed here, + * otherwise pass a NULL. + */ +void +DEVICE::notify_newvol_in_attached_dcrs(const char *newVolumeName) +{ + Dmsg2(140, "Notify dcrs of vol change. oldVolume=%s NewVolume=%s\n", + getVolCatName(), newVolumeName?"*None*":newVolumeName); + Lock_dcrs(); + DCR *mdcr; + foreach_dlist(mdcr, attached_dcrs) { + if (mdcr->jcr->JobId == 0) { + continue; /* ignore console */ + } + mdcr->NewVol = true; + mdcr->NewFile = true; + if (newVolumeName && mdcr->VolumeName != newVolumeName) { + bstrncpy(mdcr->VolumeName, newVolumeName, sizeof(mdcr->VolumeName)); + Dmsg2(140, "Set NewVol=%s in JobId=%d\n", mdcr->VolumeName, mdcr->jcr->JobId); + } + } + Unlock_dcrs(); +} + +/* + * Walk through all attached jcrs indicating the File has changed + */ +void +DEVICE::notify_newfile_in_attached_dcrs() { - return dev->VolCatInfo.VolCatName; + Dmsg1(140, "Notify dcrs of file change. Volume=%s\n", getVolCatName()); + Lock_dcrs(); + DCR *mdcr; + foreach_dlist(mdcr, attached_dcrs) { + if (mdcr->jcr->JobId == 0) { + continue; /* ignore console */ + } + Dmsg1(140, "Notify JobI=%d\n", mdcr->jcr->JobId); + mdcr->NewFile = true; + } + Unlock_dcrs(); } + /* * Free memory allocated for the device */ void DEVICE::term(void) { + DEVICE *dev = NULL; Dmsg1(900, "term dev: %s\n", print_name()); close(); if (dev_name) { @@ -2315,15 +1049,48 @@ void DEVICE::term(void) pthread_cond_destroy(&wait); pthread_cond_destroy(&wait_next_vol); pthread_mutex_destroy(&spool_mutex); -// rwl_destroy(&lock); + pthread_mutex_destroy(&freespace_mutex); if (attached_dcrs) { delete attached_dcrs; attached_dcrs = NULL; } - if (device) { + /* We let the DEVRES pointer if not our device */ + if (device && device->dev == this) { device->dev = NULL; } - free((char *)this); + delete this; + if (dev) { + dev->term(); + } +} + +/* Get freespace values */ +void DEVICE::get_freespace(uint64_t *freeval, uint64_t *totalval) +{ + get_os_device_freespace(); + P(freespace_mutex); + if (is_freespace_ok()) { + *freeval = free_space; + *totalval = total_space; + } else { + *freeval = *totalval = 0; + } + V(freespace_mutex); +} + +/* Set freespace values */ +void DEVICE::set_freespace(uint64_t freeval, uint64_t totalval, int errnoval, bool valid) +{ + P(freespace_mutex); + free_space = freeval; + total_space = totalval; + free_space_errno = errnoval; + if (valid) { + set_freespace_ok(); + } else { + clear_freespace_ok(); + } + V(freespace_mutex); } /* @@ -2342,7 +1109,6 @@ void init_device_wait_timers(DCR *dcr) dev->rem_wait_sec = dev->wait_sec; dev->num_wait = 0; dev->poll = false; - dev->BadVolName[0] = 0; jcr->min_wait = 60 * 60; jcr->max_wait = 24 * 60 * 60; @@ -2366,7 +1132,7 @@ void init_jcr_device_wait_timers(JCR *jcr) /* - * The dev timers are used for waiting on a particular device + * The dev timers are used for waiting on a particular device * * Returns: true if time doubled * false if max time expired @@ -2385,119 +1151,7 @@ bool double_dev_wait_time(DEVICE *dev) return true; } - -void set_os_device_parameters(DCR *dcr) -{ - DEVICE *dev = dcr->dev; - - if (strcmp(dev->dev_name, "/dev/null") == 0) { - return; /* no use trying to set /dev/null */ - } - -#if defined(HAVE_LINUX_OS) || defined(HAVE_WIN32) - struct mtop mt_com; - - Dmsg0(050, "In set_os_device_parameters\n"); -#if defined(MTSETBLK) - if (dev->min_block_size == dev->max_block_size && - dev->min_block_size == 0) { /* variable block mode */ - mt_com.mt_op = MTSETBLK; - mt_com.mt_count = 0; - Dmsg0(050, "Set block size to zero\n"); - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTSETBLK); - } - } -#endif -#if defined(MTSETDRVBUFFER) - if (getpid() == 0) { /* Only root can do this */ - mt_com.mt_op = MTSETDRVBUFFER; - mt_com.mt_count = MT_ST_CLEARBOOLEANS; - if (!dev->has_cap(CAP_TWOEOF)) { - mt_com.mt_count |= MT_ST_TWO_FM; - } - if (dev->has_cap(CAP_EOM)) { - mt_com.mt_count |= MT_ST_FAST_MTEOM; - } - Dmsg0(050, "MTSETDRVBUFFER\n"); - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTSETDRVBUFFER); - } - } -#endif - return; -#endif - -#ifdef HAVE_NETBSD_OS - struct mtop mt_com; - if (dev->min_block_size == dev->max_block_size && - dev->min_block_size == 0) { /* variable block mode */ - mt_com.mt_op = MTSETBSIZ; - mt_com.mt_count = 0; - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTSETBSIZ); - } - /* Get notified at logical end of tape */ - mt_com.mt_op = MTEWARN; - mt_com.mt_count = 1; - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTEWARN); - } - } - return; -#endif - -#if HAVE_FREEBSD_OS || HAVE_OPENBSD_OS - struct mtop mt_com; - if (dev->min_block_size == dev->max_block_size && - dev->min_block_size == 0) { /* variable block mode */ - mt_com.mt_op = MTSETBSIZ; - mt_com.mt_count = 0; - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTSETBSIZ); - } - } -#if defined(MTIOCSETEOTMODEL) - uint32_t neof; - if (dev->has_cap(CAP_TWOEOF)) { - neof = 2; - } else { - neof = 1; - } - if (ioctl(dev->fd(), MTIOCSETEOTMODEL, (caddr_t)&neof) < 0) { - berrno be; - dev->dev_errno = errno; /* save errno */ - Mmsg2(dev->errmsg, _("Unable to set eotmodel on device %s: ERR=%s\n"), - dev->print_name(), be.strerror(dev->dev_errno)); - Jmsg(dcr->jcr, M_FATAL, 0, dev->errmsg); - } -#endif - return; -#endif - -#ifdef HAVE_SUN_OS - struct mtop mt_com; - if (dev->min_block_size == dev->max_block_size && - dev->min_block_size == 0) { /* variable block mode */ - mt_com.mt_op = MTSRSZ; - mt_com.mt_count = 0; - if (tape_ioctl(dev->fd(), MTIOCTOP, (char *)&mt_com) < 0) { - dev->clrerror(MTSRSZ); - } - } - return; -#endif -} - -static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat) -{ - Dmsg0(050, "dev_get_os_pos\n"); - return dev->has_cap(CAP_MTIOCGET) && - tape_ioctl(dev->fd(), MTIOCGET, (char *)mt_stat) == 0 && - mt_stat->mt_fileno >= 0; -} - -static char *modes[] = { +static const char *modes[] = { "CREATE_READ_WRITE", "OPEN_READ_WRITE", "OPEN_READ_ONLY", @@ -2505,7 +1159,7 @@ static char *modes[] = { }; -static char *mode_to_str(int mode) +const char *mode_to_str(int mode) { static char buf[100]; if (mode < 1 || mode > 4) {