X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fdev.c;h=474fa45d411518191f24eb9df0370aa50aec828f;hb=8bc2739483be88049421740913d40929de7e118b;hp=2d0362d180d01e18703137ff49b04b908da7c170;hpb=6196aaac255ed8de92792075450cf9662372f9bb;p=bacula%2Fbacula diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 2d0362d180..474fa45d41 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -2,18 +2,18 @@ * * dev.c -- low level operations on device (storage device) * - * Kern Sibbald, MM + * Kern Sibbald, MM * * NOTE!!!! None of these routines are reentrant. You must - * use lock_device() and unlock_device() at a higher level, - * or use the xxx_device() equivalents. By moving the - * thread synchronization to a higher level, we permit + * use lock_device() and unlock_device() 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 - * to carry out operations without worrying about who - * set what lock (i.e. race conditions). + * to carry out operations without worrying about who + * set what lock (i.e. race conditions). * - * Note, this is the device dependent code, and my have - * to be modified for each system, but is meant to + * Note, this is the device dependent code, and may have + * to be modified for each system, but is meant to * be as "generic" as possible. * * The purpose of this code is to develop a SIMPLE Storage @@ -29,24 +29,32 @@ * Version $Id$ */ /* - Copyright (C) 2000-2005 Kern Sibbald + Bacula® - The Network Backup Solution - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of - the License, or (at your option) any later version. + Copyright (C) 2000-2007 Free Software Foundation Europe e.V. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of + 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., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. + 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. +*/ /* * Handling I/O errors and end of tape conditions are a bit tricky. @@ -54,7 +62,7 @@ * 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 - * to include ST_EOT, which is ephimeral, and ST_WEOT, which is + * to include ST_EOT, which is ephemeral, and ST_WEOT, which is * persistent. Lots of routines clear ST_EOT, but ST_WEOT is * cleared only when the problem goes away. Now when ST_WEOT * is set all calls to write_block_to_device() call the fix_up @@ -80,16 +88,14 @@ #include "bacula.h" #include "stored.h" -/* Functions in dvd.c */ -void get_filename(DEVICE *dev, char *VolName, POOL_MEM& archive_name); -int mount_dev(DEVICE* dev, int timeout); -int unmount_dev(DEVICE *dev, int timeout); -void update_free_space_dev(DEVICE* dev); - +#ifndef O_NONBLOCK +#define O_NONBLOCK 0 +#endif /* Forward referenced functions */ -void set_os_device_parameters(DEVICE *dev); +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); /* * Allocate and initialize the DEVICE structure @@ -103,55 +109,50 @@ static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat); * */ DEVICE * -init_dev(JCR *jcr, DEVICE *dev, DEVRES *device) +init_dev(JCR *jcr, DEVRES *device) { struct stat statp; - bool tape, fifo; int errstat; DCR *dcr = NULL; + DEVICE *dev; - /* Check that device is available */ - if (stat(device->device_name, &statp) < 0) { - berrno be; - if (dev) { - dev->dev_errno = errno; + + /* 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()); + return NULL; } - Jmsg2(jcr, M_ERROR, 0, _("Unable to stat device %s: ERR=%s\n"), - device->device_name, be.strerror()); - return NULL; - } - - - tape = false; - fifo = false; - if (S_ISDIR(statp.st_mode)) { - tape = false; - } else if (S_ISCHR(statp.st_mode)) { - tape = true; - } else if (S_ISFIFO(statp.st_mode)) { - fifo = true; - } else { - if (dev) { - dev->dev_errno = ENODEV; + 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 (!(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; } - 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; - } - if (!dev) { - dev = (DEVICE *)get_memory(sizeof(DEVICE)); - memset(dev, 0, sizeof(DEVICE)); - dev->state = ST_MALLOC; - } else { - memset(dev, 0, sizeof(DEVICE)); } + dev = (DEVICE *)malloc(sizeof(DEVICE)); + memset(dev, 0, sizeof(DEVICE)); + dev->Slot = -1; /* 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_block_size = device->min_block_size; dev->max_block_size = device->max_block_size; @@ -165,56 +166,53 @@ init_dev(JCR *jcr, DEVICE *dev, DEVRES *device) dev->max_spool_size = device->max_spool_size; dev->drive_index = device->drive_index; dev->autoselect = device->autoselect; - if (tape) { /* No parts on tapes */ + dev->dev_type = device->dev_type; + if (dev->is_tape()) { /* No parts on tapes */ dev->max_part_size = 0; - } - else { + } 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; } + /* Link the dev and device structures together */ dev->device = device; + device->dev = dev; - if (tape) { - dev->state |= ST_TAPE; - } else if (fifo) { - dev->state |= ST_FIFO; + if (dev->is_fifo()) { dev->capabilities |= CAP_STREAM; /* set stream device */ - } else { - dev->state |= ST_FILE; } /* If the device requires mount : * - Check that the mount point is available * - Check that (un)mount commands are defined */ - if (dev->is_file() && device->cap_bits & CAP_REQMOUNT) { - if (stat(device->mount_point, &statp) < 0) { - berrno be; - dev->dev_errno = errno; - Jmsg2(jcr, M_ERROR, 0, _("Unable to stat mount point %s: ERR=%s\n"), - device->mount_point, be.strerror()); - return NULL; + if ((dev->is_file() || dev->is_dvd()) && 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()); } + } + 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")); } - dev->state |= ST_DVD; } if (dev->max_block_size > 1000000) { 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, 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()); + dev->max_block_size, dev->print_name()); } dev->errmsg = get_pool_memory(PM_EMSG); @@ -251,9 +249,9 @@ init_dev(JCR *jcr, DEVICE *dev, DEVRES *device) Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } - dev->fd = -1; + 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->dev_name); + Dmsg2(29, "init_dev: tape=%d dev_name=%s\n", dev->is_tape(), dev->dev_name); return dev; } @@ -263,7 +261,7 @@ init_dev(JCR *jcr, DEVICE *dev, DEVRES *device) * initialize buffer pointers. * * Returns: -1 on error - * fd on success + * fd on success * * Note, for a tape, the VolName is the name we give to the * volume (not really used here), but for a file, the @@ -272,250 +270,559 @@ init_dev(JCR *jcr, DEVICE *dev, DEVRES *device) * (archive_name) with the VolName concatenated. */ int -open_dev(DEVICE *dev, char *VolName, int mode) +DEVICE::open(DCR *dcr, int omode) { - if (dev->is_open()) { - /* - * *****FIXME***** how to handle two threads wanting - * different volumes mounted???? E.g. one is waiting - * for the next volume to be mounted, and a new job - * starts and snatches up the device. - */ - if (VolName && strcmp(dev->VolCatInfo.VolCatName, VolName) != 0) { - return -1; - } - dev->use_count++; - Mmsg2(&dev->errmsg, _("WARNING!!!! device %s opened %d times!!!\n"), - dev->print_name(), dev->use_count); - Emsg1(M_WARNING, 0, "%s", dev->errmsg); - return dev->fd; - } - if (VolName) { - bstrncpy(dev->VolCatInfo.VolCatName, VolName, sizeof(dev->VolCatInfo.VolCatName)); - } - - Dmsg3(29, "open_dev: tape=%d dev_name=%s vol=%s\n", dev_is_tape(dev), - dev->dev_name, dev->VolCatInfo.VolCatName); - dev->state &= ~(ST_LABEL|ST_APPEND|ST_READ|ST_EOT|ST_WEOT|ST_EOF); - dev->label_type = B_BACULA_LABEL; - if (dev->is_tape() || dev->is_fifo()) { - dev->file_size = 0; - int timeout; - int ioerrcnt = 10; - Dmsg0(29, "open_dev: device is tape\n"); - if (mode == OPEN_READ_WRITE) { - dev->mode = O_RDWR | O_BINARY; - } else if (mode == OPEN_READ_ONLY) { - dev->mode = O_RDONLY | O_BINARY; - } else if (mode == OPEN_WRITE_ONLY) { - dev->mode = O_WRONLY | O_BINARY; + int preserve = 0; + if (is_open()) { + if (openmode == omode) { + return fd; } else { - Emsg0(M_ABORT, 0, _("Illegal mode given to open_dev.\n")); - } - timeout = dev->max_open_wait; - errno = 0; - if (dev->is_fifo() && timeout) { - /* Set open timer */ - dev->tid = start_thread_timer(pthread_self(), timeout); - } - /* If busy retry each second for max_open_wait seconds */ - while ((dev->fd = open(dev->dev_name, dev->mode, MODE_RW)) < 0) { - berrno be; - if (errno == EINTR || errno == EAGAIN) { - continue; - } - /* Busy wait for specified time (default = 5 mins) */ - if (errno == EBUSY && timeout-- > 0) { - Dmsg2(100, "Device %s busy. ERR=%s\n", dev->print_name(), be.strerror()); - bmicrosleep(1, 0); - continue; - } - /* IO error (no volume) try 10 times every 6 seconds */ - if (errno == EIO && ioerrcnt-- > 0) { - bmicrosleep(5, 0); - continue; - } - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("Unable to open device %s: ERR=%s\n"), - dev->print_name(), be.strerror(dev->dev_errno)); - /* Stop any open timer we set */ - if (dev->tid) { - stop_thread_timer(dev->tid); - dev->tid = 0; - } - Emsg0(M_FATAL, 0, dev->errmsg); - break; - } - if (dev->fd >= 0) { - dev->dev_errno = 0; - dev->state |= ST_OPENED; - dev->use_count = 1; - update_pos_dev(dev); /* update position */ - set_os_device_parameters(dev); /* do system dependent stuff */ - } - /* Stop any open() timer we started */ - if (dev->tid) { - stop_thread_timer(dev->tid); - dev->tid = 0; - } - Dmsg1(29, "open_dev: tape %d opened\n", dev->fd); + if (is_tape()) { + tape_close(fd); + } else { + ::close(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)); + } + + 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 */ + 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 { - POOL_MEM archive_name(PM_FNAME); - struct stat filestat; - /* - * Handle opening of File Archive (not a tape) - */ - if (dev->part == 0) { - dev->file_size = 0; + 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, fd); + return fd; +} + +void DEVICE::set_mode(int new_mode) +{ + switch (new_mode) { + case CREATE_READ_WRITE: + mode = O_CREAT | O_RDWR | O_BINARY; + break; + case OPEN_READ_WRITE: + mode = O_RDWR | O_BINARY; + break; + case OPEN_READ_ONLY: + mode = O_RDONLY | O_BINARY; + break; + case OPEN_WRITE_ONLY: + mode = O_WRONLY | O_BINARY; + break; + default: + Emsg0(M_ABORT, 0, _("Illegal mode given to open dev.\n")); + } +} + +/* + */ +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 ((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 */ + fd = ::open(dev_name, mode+O_NONBLOCK); + if (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; + if (ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) { + berrno be; + dev_errno = errno; /* set error status from rewind */ + ::close(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(fd); + fd = ::open(dev_name, mode); /* open normally */ + if (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 */ + } } - dev->part_size = 0; - - /* if num_parts has not been set, but VolCatInfo is available, copy - * it from the VolCatInfo.VolCatParts */ - if (dev->num_parts < dev->VolCatInfo.VolCatParts) { - dev->num_parts = dev->VolCatInfo.VolCatParts; + bmicrosleep(5, 0); + /* Exceed wait time ? */ + if (time(NULL) - start_time >= max_open_wait) { + break; /* yes, get out */ } - - if (VolName == NULL || *VolName == 0) { - Mmsg(dev->errmsg, _("Could not open file device %s. No Volume name given.\n"), - dev->print_name()); - return -1; - } - get_filename(dev, VolName, archive_name); - - if (mount_dev(dev, 1) < 0) { - Mmsg(dev->errmsg, _("Could not mount device %s.\n"), - dev->print_name()); - Emsg0(M_FATAL, 0, dev->errmsg); - dev->fd = -1; - return dev->fd; - } - - Dmsg2(29, "open_dev: device is disk %s (mode:%d)\n", archive_name.c_str(), mode); - dev->openmode = mode; - - /* - * If we are not trying to access the last part, set mode to - * OPEN_READ_ONLY as writing would be an error. - */ - if (dev->part < dev->num_parts) { - mode = OPEN_READ_ONLY; + } +#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", fd); +} + + +/* + * Open a file device + */ +void DEVICE::open_file_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) { + if (VolCatInfo.VolCatName[0] == 0) { + Mmsg(errmsg, _("Could not open file device %s. No Volume name given.\n"), + print_name()); + clear_opened(); + return; } - - if (mode == OPEN_READ_WRITE) { - dev->mode = O_CREAT | O_RDWR | O_BINARY; - } else if (mode == OPEN_READ_ONLY) { - dev->mode = O_RDONLY | O_BINARY; - } else if (mode == OPEN_WRITE_ONLY) { - dev->mode = O_WRONLY | O_BINARY; - } else { - Emsg0(M_ABORT, 0, _("Illegal mode given to open_dev.\n")); - } - /* If creating file, give 0640 permissions */ - if ((dev->fd = open(archive_name.c_str(), dev->mode, 0640)) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("Could not open: %s, ERR=%s\n"), archive_name.c_str(), be.strerror()); - Emsg0(M_FATAL, 0, dev->errmsg); - } else { - dev->dev_errno = 0; - dev->state |= ST_OPENED; - dev->use_count = 1; - update_pos_dev(dev); /* update position */ - if (fstat(dev->fd, &filestat) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("Could not fstat: %s, ERR=%s\n"), archive_name.c_str(), be.strerror()); - Emsg0(M_FATAL, 0, dev->errmsg); - } else { - dev->part_size = filestat.st_size; - } - } - Dmsg4(29, "open_dev: disk fd=%d opened, part=%d/%d, part_size=%u\n", dev->fd, dev->part, dev->num_parts, dev->part_size); - if (dev->is_dvd() && (dev->mode != OPEN_READ_ONLY) && - (dev->free_space_errno == 0 || dev->num_parts == dev->part)) { - update_free_space_dev(dev); - } - } - return dev->fd; + + 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 ((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", + fd, part, num_dvd_parts, part_size); } -#ifdef debug_tracing -#undef rewind_dev -bool _rewind_dev(char *file, int line, DEVICE *dev) +/* + * 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) { - Dmsg2(100, "rewind_dev called from %s:%d\n", file, line); - return rewind_dev(dev); + 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 ((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); + fd = ::open(archive_name.c_str(), mode, 0640); + set_mode(omode); + } + } + Dmsg1(100, "after open fd=%d\n", fd); + if (is_open()) { + if (omode == OPEN_READ_WRITE || omode == CREATE_READ_WRITE) { + set_append(); + } + /* Get size of file */ + if (fstat(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(fd); + clear_opened(); + } else { + part_size = filestat.st_size; + dev_errno = 0; + update_pos(dcr); /* update position */ + } + } } -#endif + /* * Rewind the device. * Returns: true on success - * false on failure + * false on failure */ -bool rewind_dev(DEVICE *dev) +bool DEVICE::rewind(DCR *dcr) { struct mtop mt_com; unsigned int i; + bool first = true; - Dmsg1(29, "rewind_dev %s\n", dev->print_name()); - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg1(&dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"), - dev->print_name()); - Emsg0(M_ABORT, 0, dev->errmsg); + Dmsg3(400, "rewind res=%d fd=%d %s\n", reserved_device, 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 (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; } - dev->state &= ~(ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ - dev->block_num = dev->file = 0; - dev->file_size = 0; - dev->file_addr = 0; - if (dev->is_tape()) { + 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=dev->max_rewind_wait; ; i -= 5) { - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - clrerror_dev(dev, MTREW); - if (i == dev->max_rewind_wait) { + for (i=max_rewind_wait; ; i -= 5) { + if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) { + berrno be; + clrerror(MTREW); + if (i == max_rewind_wait) { Dmsg1(200, "Rewind error, %s. retrying ...\n", be.strerror()); - } - if (dev->dev_errno == EIO && i > 0) { + } + /* + * 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(fd); + clear_opened(); + open(dcr, open_mode); + if (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; - } - Mmsg2(&dev->errmsg, _("Rewind error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; - } - break; - } - } else if (dev->is_file()) { - if (lseek_dev(dev, (off_t)0, SEEK_SET) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; + 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) +{ + lock_device(this); + block_device(this, why); + V(mutex); +} + +void DEVICE::unblock() +{ + P(mutex); + unblock_device(this); + V(mutex); +} + +const char *DEVICE::print_blocked() const +{ + switch (dev_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_eof() +void DEVICE::set_ateof() { - state |= ST_EOF; - file++; + set_eof(); + if (is_tape()) { + file++; + } file_addr = 0; file_size = 0; block_num = 0; @@ -525,64 +832,68 @@ void DEVICE::set_eof() * Called to indicate we are now at the end of the tape, and * writing is not possible. */ -void DEVICE::set_eot() +void DEVICE::set_ateot() { + /* Make tape effectively read-only */ state |= (ST_EOF|ST_EOT|ST_WEOT); - state &= ~ST_APPEND; /* make tape read-only */ + clear_append(); } /* * Position device to end of medium (end of data) * Returns: true on succes - * false on error + * false on error */ -bool -eod_dev(DEVICE *dev) +bool DEVICE::eod(DCR *dcr) { struct mtop mt_com; struct mtget mt_stat; bool ok = true; - off_t pos; + boffset_t pos; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg1(&dev->errmsg, _("Bad call to eod_dev. Device %s not open\n"), - dev->print_name()); + if (fd < 0) { + dev_errno = EBADF; + Mmsg1(errmsg, _("Bad call to eod. Device %s not open\n"), print_name()); return false; } - Dmsg0(29, "eod_dev\n"); - if (dev->at_eot()) { + +#if defined (__digital__) && defined (__unix__) + return fsf(VolCatInfo.VolCatFiles); +#endif + + Dmsg0(29, "eod\n"); + if (at_eot()) { return true; } - dev->state &= ~(ST_EOF); /* remove EOF flags */ - dev->block_num = dev->file = 0; - dev->file_size = 0; - dev->file_addr = 0; - if (dev->state & (ST_FIFO | ST_PROG)) { + clear_eof(); /* remove EOF flag */ + block_num = file = 0; + file_size = 0; + file_addr = 0; + if (is_fifo() || is_prog()) { return true; } - if (!dev->is_tape()) { - pos = lseek_dev(dev, (off_t)0, SEEK_END); + if (!is_tape()) { + pos = lseek(dcr, (boffset_t)0, SEEK_END); // Dmsg1(100, "====== Seek to %lld\n", pos); if (pos >= 0) { - update_pos_dev(dev); - dev->state |= ST_EOT; - return true; + update_pos(dcr); + set_eot(); + return true; } - dev->dev_errno = errno; + dev_errno = errno; berrno be; - Mmsg2(&dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), + print_name(), be.strerror()); return false; } #ifdef MTEOM - if (dev_cap(dev, CAP_FASTFSF) && !dev_cap(dev, CAP_EOM)) { + if (has_cap(CAP_FASTFSF) && !has_cap(CAP_EOM)) { Dmsg0(100,"Using FAST FSF for EOM\n"); /* If unknown position, rewind */ - if (!dev_get_os_pos(dev, &mt_stat)) { - if (!rewind_dev(dev)) { - return false; - } + if (!dev_get_os_pos(this, &mt_stat)) { + if (!rewind(NULL)) { + return false; + } } mt_com.mt_op = MTFSF; /* @@ -591,37 +902,37 @@ eod_dev(DEVICE *dev) */ 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 */ + mt_com.mt_count = INT16_MAX; /* brain damaged system */ } } - if (dev_cap(dev, CAP_MTIOCGET) && (dev_cap(dev, CAP_FASTFSF) || dev_cap(dev, CAP_EOM))) { - if (dev_cap(dev, CAP_EOM)) { + 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; + mt_com.mt_op = MTEOM; + mt_com.mt_count = 1; } - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - berrno be; - clrerror_dev(dev, mt_com.mt_op); + if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) { + berrno be; + clrerror(mt_com.mt_op); Dmsg1(50, "ioctl error: %s\n", be.strerror()); - update_pos_dev(dev); - Mmsg2(&dev->errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; + update_pos(dcr); + Mmsg2(errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"), + print_name(), be.strerror()); + return false; } - if (!dev_get_os_pos(dev, &mt_stat)) { - berrno be; - clrerror_dev(dev, -1); - Mmsg2(&dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; + if (!dev_get_os_pos(this, &mt_stat)) { + berrno be; + clrerror(-1); + Mmsg2(errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), + print_name(), be.strerror()); + return false; } Dmsg2(100, "EOD file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); - dev->set_eof(); - dev->file = mt_stat.mt_fileno; + set_ateof(); + file = mt_stat.mt_fileno; } else { #else { @@ -629,33 +940,32 @@ eod_dev(DEVICE *dev) /* * Rewind then use FSF until EOT reached */ - if (!rewind_dev(dev)) { - return false; + if (!rewind(NULL)) { + return false; } /* * Move file by file to the end of the tape */ int file_num; - for (file_num=dev->file; !dev->at_eot(); file_num++) { - Dmsg0(200, "eod_dev: doing fsf 1\n"); - if (!fsf_dev(dev, 1)) { - Dmsg0(200, "fsf_dev error.\n"); - return false; - } - /* - * Avoid infinite loop. ***FIXME*** possibly add code - * to set EOD or to turn off CAP_FASTFSF if on. - */ - if (file_num == (int)dev->file) { - struct mtget mt_stat; - Dmsg1(100, "fsf_dev did not advance from file %d\n", file_num); - if (dev_get_os_pos(dev, &mt_stat)) { - Dmsg2(100, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno); - dev->set_eof(); - dev->file = mt_stat.mt_fileno; - } - return false; - } + 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) { + struct mtget mt_stat; + Dmsg1(100, "fsf did not advance from file %d\n", file_num); + set_ateof(); + if (dev_get_os_pos(this, &mt_stat)) { + Dmsg2(100, "Adjust file from %d to %d\n", file_num, mt_stat.mt_fileno); + file = mt_stat.mt_fileno; + } + break; + } } } /* @@ -663,62 +973,63 @@ eod_dev(DEVICE *dev) * MTEOM, so we must backup so that appending overwrites * the second EOF. */ - if (dev_cap(dev, CAP_BSFATEOM)) { + if (has_cap(CAP_BSFATEOM)) { struct mtget mt_stat; /* Backup over EOF */ - ok = bsf_dev(dev, 1); + ok = bsf(1); /* If BSF worked and fileno is known (not -1), set file */ - if (dev_get_os_pos(dev, &mt_stat)) { - Dmsg2(100, "BSFATEOF adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno); - dev->file = mt_stat.mt_fileno; + if (dev_get_os_pos(this, &mt_stat)) { + Dmsg2(100, "BSFATEOF adjust file from %d to %d\n", file , mt_stat.mt_fileno); + file = mt_stat.mt_fileno; } else { - dev->file++; /* wing it -- not correct on all OSes */ + file++; /* wing it -- not correct on all OSes */ } } else { - update_pos_dev(dev); /* update position */ + update_pos(dcr); /* update position */ } - Dmsg1(200, "EOD dev->file=%d\n", dev->file); + Dmsg1(200, "EOD dev->file=%d\n", file); return ok; } /* - * Set the position of the device -- only for files + * Set the position of the device -- only for files and DVD * For other devices, there is no generic way to do it. * Returns: true on succes - * false on error + * false on error */ -bool update_pos_dev(DEVICE *dev) +bool DEVICE::update_pos(DCR *dcr) { - off_t pos; + boffset_t pos; bool ok = true; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad device call. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad device call. Device not open\n")); + Emsg1(M_FATAL, 0, "%s", errmsg); return false; } /* Find out where we are */ - if (dev->is_file()) { - dev->file = 0; - dev->file_addr = 0; - pos = lseek_dev(dev, (off_t)0, SEEK_CUR); + if (is_file() || is_dvd()) { + file = 0; + file_addr = 0; + pos = lseek(dcr, (boffset_t)0, SEEK_CUR); if (pos < 0) { - berrno be; - dev->dev_errno = errno; - Pmsg1(000, "Seek error: ERR=%s\n", be.strerror()); - Mmsg2(&dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - ok = false; + berrno be; + dev_errno = errno; + Pmsg1(000, _("Seek error: ERR=%s\n"), be.strerror()); + Mmsg2(errmsg, _("lseek error on %s. ERR=%s.\n"), + print_name(), be.strerror()); + ok = false; } else { - dev->file_addr = pos; + file_addr = pos; + block_num = (uint32_t)pos; + file = (uint32_t)(pos >> 32); } } return ok; } - /* * Return the status of the device. This was meant * to be a generic routine. Unfortunately, it doesn't @@ -735,67 +1046,101 @@ uint32_t status_dev(DEVICE *dev) if (dev->state & (ST_EOT | ST_WEOT)) { stat |= BMT_EOD; - Dmsg0(-20, " EOD"); + Pmsg0(-20, " EOD"); } if (dev->state & ST_EOF) { stat |= BMT_EOF; - Dmsg0(-20, " EOF"); + Pmsg0(-20, " EOF"); } if (dev->is_tape()) { stat |= BMT_TAPE; - Dmsg0(-20," Bacula status:"); - Dmsg2(-20," file=%d block=%d\n", dev->file, dev->block_num); - if (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,_(" 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; } - Dmsg0(-20, " Device status:"); + Pmsg0(-20, _(" Device status:")); #if defined(HAVE_LINUX_OS) if (GMT_EOF(mt_stat.mt_gstat)) { - stat |= BMT_EOF; - Dmsg0(-20, " EOF"); + stat |= BMT_EOF; + Pmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { - stat |= BMT_BOT; - Dmsg0(-20, " BOT"); + stat |= BMT_BOT; + Pmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { - stat |= BMT_EOT; - Dmsg0(-20, " EOT"); + stat |= BMT_EOT; + Pmsg0(-20, " EOT"); } if (GMT_SM(mt_stat.mt_gstat)) { - stat |= BMT_SM; - Dmsg0(-20, " SM"); + stat |= BMT_SM; + Pmsg0(-20, " SM"); } if (GMT_EOD(mt_stat.mt_gstat)) { - stat |= BMT_EOD; - Dmsg0(-20, " EOD"); + stat |= BMT_EOD; + Pmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { - stat |= BMT_WR_PROT; - Dmsg0(-20, " WR_PROT"); + stat |= BMT_WR_PROT; + Pmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { - stat |= BMT_ONLINE; - Dmsg0(-20, " ONLINE"); + stat |= BMT_ONLINE; + Pmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { - stat |= BMT_DR_OPEN; - Dmsg0(-20, " DR_OPEN"); + stat |= BMT_DR_OPEN; + Pmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { - stat |= BMT_IM_REP_EN; - Dmsg0(-20, " IM_REP_EN"); + 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_cap(dev, CAP_MTIOCGET)) { - Dmsg2(-20, " file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); + if (dev->has_cap(CAP_MTIOCGET)) { + Pmsg2(-20, _(" file=%d block=%d\n"), mt_stat.mt_fileno, mt_stat.mt_blkno); } else { - Dmsg2(-20, " file=%d block=%d\n", -1, -1); + Pmsg2(-20, _(" file=%d block=%d\n"), -1, -1); } } else { stat |= BMT_ONLINE | BMT_BOT; @@ -807,7 +1152,7 @@ uint32_t status_dev(DEVICE *dev) /* * Load medium in device * Returns: true on success - * false on failure + * false on failure */ bool load_dev(DEVICE *dev) { @@ -817,7 +1162,7 @@ bool load_dev(DEVICE *dev) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to load_dev. Device not open\n")); + Mmsg0(dev->errmsg, _("Bad call to load_dev. Device not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return false; } @@ -827,9 +1172,9 @@ bool load_dev(DEVICE *dev) #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()); + 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 @@ -838,11 +1183,11 @@ bool load_dev(DEVICE *dev) dev->file_addr = 0; mt_com.mt_op = MTLOAD; mt_com.mt_count = 1; - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + 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()); + Mmsg2(dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), + dev->print_name(), be.strerror()); return false; } return true; @@ -852,52 +1197,41 @@ bool load_dev(DEVICE *dev) /* * Rewind device and put it offline * Returns: true on success - * false on failure + * false on failure */ -bool offline_dev(DEVICE *dev) +bool DEVICE::offline() { struct mtop mt_com; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to offline_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return false; - } - if (!(dev->is_tape())) { - return true; + if (!is_tape()) { + return true; /* device not open */ } - dev->state &= ~(ST_APPEND|ST_READ|ST_EOT|ST_EOF|ST_WEOT); /* remove EOF/EOT flags */ - dev->block_num = dev->file = 0; - dev->file_size = 0; - dev->file_addr = 0; - dev->part = 0; -#ifdef MTUNLOCK - mt_com.mt_op = MTUNLOCK; - mt_com.mt_count = 1; - ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); -#endif + 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 (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + if (tape_ioctl(fd, MTIOCTOP, (char *)&mt_com) < 0) { berrno be; - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + 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", dev->print_name()); + Dmsg1(100, "Offlined device %s\n", print_name()); return true; } -bool offline_or_rewind_dev(DEVICE *dev) +bool DEVICE::offline_or_rewind() { - if (dev->fd < 0) { + if (fd < 0) { return false; } - if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { - return offline_dev(dev); + if (has_cap(CAP_OFFLINEUNMOUNT)) { + return offline(); } else { /* * Note, this rewind probably should not be here (it wasn't @@ -906,44 +1240,44 @@ bool offline_or_rewind_dev(DEVICE *dev) * such as backspacing after writing and EOF. If it is not * done, all future references to the drive get and I/O error. */ - clrerror_dev(dev, MTREW); - return rewind_dev(dev); + clrerror(MTREW); + return rewind(NULL); } } /* * Foward space a file * Returns: true on success - * false on failure + * false on failure */ -bool -fsf_dev(DEVICE *dev, int num) +bool DEVICE::fsf(int num) { struct mtget mt_stat; struct mtop mt_com; int stat = 0; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to fsf. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); return false; } - if (!dev->is_tape()) { + if (!is_tape()) { return true; } - if (dev->state & ST_EOT) { - dev->dev_errno = 0; - Mmsg1(dev->errmsg, _("Device %s at End of Tape.\n"), dev->print_name()); + + if (at_eot()) { + dev_errno = 0; + Mmsg1(errmsg, _("Device %s at End of Tape.\n"), print_name()); return false; } - if (dev->state & ST_EOF) { + if (at_eof()) { Dmsg0(200, "ST_EOF set on entry to FSF\n"); } - Dmsg0(100, "fsf_dev\n"); - dev->block_num = 0; + Dmsg0(100, "fsf\n"); + block_num = 0; /* * If Fast forward space file is set, then we * use MTFSF to forward space and MTIOCGET @@ -951,23 +1285,23 @@ fsf_dev(DEVICE *dev, int num) * the SCSI driver will ensure that we do not * forward space past the end of the medium. */ - if (dev_cap(dev, CAP_FSF) && dev_cap(dev, CAP_MTIOCGET) && dev_cap(dev, CAP_FASTFSF)) { + if (has_cap(CAP_FSF) && has_cap(CAP_MTIOCGET) && has_cap(CAP_FASTFSF)) { mt_com.mt_op = MTFSF; mt_com.mt_count = num; - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0 || !dev_get_os_pos(dev, &mt_stat)) { - berrno be; - dev->state |= ST_EOT; + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); + if (stat < 0 || !dev_get_os_pos(this, &mt_stat)) { + berrno be; + set_eot(); Dmsg0(200, "Set ST_EOT\n"); - clrerror_dev(dev, MTFSF); - Mmsg2(dev->errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - Dmsg1(200, "%s", dev->errmsg); - return false; + clrerror(MTFSF); + Mmsg2(errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), + print_name(), be.strerror()); + Dmsg1(200, "%s", errmsg); + return false; } Dmsg2(200, "fsf file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); - dev->set_eof(); - dev->file = mt_stat.mt_fileno; + set_ateof(); + file = mt_stat.mt_fileno; return true; /* @@ -977,65 +1311,71 @@ fsf_dev(DEVICE *dev, int num) * is the only way we can be sure that we don't read * two consecutive EOF marks, which means End of Data. */ - } else if (dev_cap(dev, CAP_FSF)) { + } else if (has_cap(CAP_FSF)) { POOLMEM *rbuf; int rbuf_len; Dmsg0(200, "FSF has cap_fsf\n"); - if (dev->max_block_size == 0) { - rbuf_len = DEFAULT_BLOCK_SIZE; + if (max_block_size == 0) { + rbuf_len = DEFAULT_BLOCK_SIZE; } else { - rbuf_len = dev->max_block_size; + rbuf_len = max_block_size; } rbuf = get_memory(rbuf_len); mt_com.mt_op = MTFSF; mt_com.mt_count = 1; - while (num-- && !(dev->state & ST_EOT)) { + while (num-- && !at_eot()) { Dmsg0(100, "Doing read before fsf\n"); - if ((stat = read(dev->fd, (char *)rbuf, rbuf_len)) < 0) { - if (errno == ENOMEM) { /* tape record exceeds buf len */ - stat = rbuf_len; /* This is OK */ - } else { - berrno be; - dev->state |= ST_EOT; - clrerror_dev(dev, -1); - Dmsg2(100, "Set ST_EOT read errno=%d. ERR=%s\n", dev->dev_errno, - be.strerror()); - Mmsg2(dev->errmsg, _("read error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - Dmsg1(100, "%s", dev->errmsg); - break; - } - } - if (stat == 0) { /* EOF */ - update_pos_dev(dev); - Dmsg1(100, "End of File mark from read. File=%d\n", dev->file+1); - /* Two reads of zero means end of tape */ - if (dev->state & ST_EOF) { - dev->state |= ST_EOT; + 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 { - dev->set_eof(); - continue; - } - } else { /* Got data */ - dev->state &= ~(ST_EOF|ST_EOT); - } + break; + } else { + set_ateof(); + continue; + } + } else { /* Got data */ + clear_eot(); + clear_eof(); + } Dmsg0(100, "Doing MTFSF\n"); - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0) { /* error => EOT */ - berrno be; - dev->state |= ST_EOT; + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); + if (stat < 0) { /* error => EOT */ + berrno be; + set_eot(); Dmsg0(100, "Set ST_EOT\n"); - clrerror_dev(dev, MTFSF); - Mmsg2(&dev->errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + 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", dev->errmsg); - } else { - dev->set_eof(); - } + Dmsg1(100, "%s", errmsg); + } else { + set_ateof(); + } } free_memory(rbuf); @@ -1044,227 +1384,258 @@ fsf_dev(DEVICE *dev, int num) */ } else { Dmsg0(200, "Doing FSR for FSF\n"); - while (num-- && !(dev->state & ST_EOT)) { - fsr_dev(dev, INT32_MAX); /* returns -1 on EOF or EOT */ + while (num-- && !at_eot()) { + fsr(INT32_MAX); /* returns -1 on EOF or EOT */ } - if (dev->state & ST_EOT) { - dev->dev_errno = 0; - Mmsg1(dev->errmsg, _("Device %s at End of Tape.\n"), dev->print_name()); - stat = -1; + if (at_eot()) { + dev_errno = 0; + Mmsg1(errmsg, _("Device %s at End of Tape.\n"), print_name()); + stat = -1; } else { - stat = 0; + stat = 0; } } - update_pos_dev(dev); Dmsg1(200, "Return %d from FSF\n", stat); - if (dev->state & ST_EOF) + if (at_eof()) { Dmsg0(200, "ST_EOF set on exit FSF\n"); - if (dev->state & ST_EOT) + } + if (at_eot()) { Dmsg0(200, "ST_EOT set on exit FSF\n"); - Dmsg1(200, "Return from FSF file=%d\n", dev->file); + } + Dmsg1(200, "Return from FSF file=%d\n", file); return stat == 0; } /* * Backward space a file * Returns: false on failure - * true on success + * true on success */ -bool -bsf_dev(DEVICE *dev, int num) +bool DEVICE::bsf(int num) { struct mtop mt_com; int stat; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to bsf_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to bsf. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); return false; } - if (!dev->is_tape()) { - Mmsg1(dev->errmsg, _("Device %s cannot BSF because it is not a tape.\n"), - dev->print_name()); + if (!is_tape()) { + Mmsg1(errmsg, _("Device %s cannot BSF because it is not a tape.\n"), + print_name()); return false; } - Dmsg0(29, "bsf_dev\n"); - dev->state &= ~(ST_EOT|ST_EOF); - dev->file -= num; - dev->file_addr = 0; - dev->file_size = 0; + + 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 = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); if (stat < 0) { berrno be; - clrerror_dev(dev, MTBSF); - Mmsg2(dev->errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + clrerror(MTBSF); + Mmsg2(errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), + print_name(), be.strerror()); } - update_pos_dev(dev); return stat == 0; } /* - * Foward space a record + * Foward space num records * Returns: false on failure - * true on success + * true on success */ -bool -fsr_dev(DEVICE *dev, int num) +bool DEVICE::fsr(int num) { struct mtop mt_com; int stat; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to fsr_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to fsr. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); return false; } - if (!dev->is_tape()) { + if (!is_tape()) { return false; } - if (!dev_cap(dev, CAP_FSR)) { - Mmsg1(dev->errmsg, _("ioctl MTFSR not permitted on %s.\n"), dev->print_name()); + + if (!has_cap(CAP_FSR)) { + Mmsg1(errmsg, _("ioctl MTFSR not permitted on %s.\n"), print_name()); return false; } - Dmsg1(29, "fsr_dev %d\n", num); + Dmsg1(29, "fsr %d\n", num); mt_com.mt_op = MTFSR; mt_com.mt_count = num; - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); if (stat == 0) { - dev->state &= ~ST_EOF; - dev->block_num += num; + clear_eof(); + block_num += num; } else { berrno be; struct mtget mt_stat; - clrerror_dev(dev, MTFSR); + clrerror(MTFSR); Dmsg1(100, "FSF fail: ERR=%s\n", be.strerror()); - if (dev_get_os_pos(dev, &mt_stat)) { - Dmsg4(100, "Adjust from %d:%d to %d:%d\n", dev->file, - dev->block_num, mt_stat.mt_fileno, mt_stat.mt_blkno); - dev->file = mt_stat.mt_fileno; - dev->block_num = mt_stat.mt_blkno; + 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 (dev->state & ST_EOF) { - dev->state |= ST_EOT; - } else { - dev->set_eof(); - } + if (at_eof()) { + set_eot(); + } else { + set_ateof(); + } } - Mmsg2(dev->errmsg, _("ioctl MTFSR error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + Mmsg3(errmsg, _("ioctl MTFSR %d error on %s. ERR=%s.\n"), + num, print_name(), be.strerror()); } - update_pos_dev(dev); return stat == 0; } /* * Backward space a record * Returns: false on failure - * true on success + * true on success */ -bool -bsr_dev(DEVICE *dev, int num) +bool DEVICE::bsr(int num) { struct mtop mt_com; int stat; - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to bsr_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to bsr_dev. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); return false; } - if (!dev->is_tape()) { + if (!is_tape()) { return false; } - if (!dev_cap(dev, CAP_BSR)) { - Mmsg1(dev->errmsg, _("ioctl MTBSR not permitted on %s.\n"), dev->print_name()); + if (!has_cap(CAP_BSR)) { + Mmsg1(errmsg, _("ioctl MTBSR not permitted on %s.\n"), print_name()); return false; } Dmsg0(29, "bsr_dev\n"); - dev->block_num -= num; - dev->state &= ~(ST_EOF|ST_EOT|ST_EOF); + block_num -= num; + clear_eof(); + clear_eot(); mt_com.mt_op = MTBSR; mt_com.mt_count = num; - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); if (stat < 0) { berrno be; - clrerror_dev(dev, MTBSR); - Mmsg2(dev->errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + clrerror(MTBSR); + Mmsg2(errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), + print_name(), be.strerror()); } - update_pos_dev(dev); return stat == 0; } +void DEVICE::lock_door() +{ +#ifdef MTLOCK + struct mtop mt_com; + mt_com.mt_op = MTLOCK; + mt_com.mt_count = 1; + tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); +#endif +} + +void DEVICE::unlock_door() +{ +#ifdef MTUNLOCK + struct mtop mt_com; + mt_com.mt_op = MTUNLOCK; + mt_com.mt_count = 1; + tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); +#endif +} + + /* * Reposition the device to file, block * Returns: false on failure - * true on success + * true on success */ -bool -reposition_dev(DEVICE *dev, uint32_t file, uint32_t block) +bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock) { - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to reposition_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to reposition. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); return false; } - if (!dev->is_tape()) { - off_t pos = (((off_t)file)<<32) + block; - Dmsg1(100, "===== lseek_dev to %d\n", (int)pos); - if (lseek_dev(dev, pos, SEEK_SET) == (off_t)-1) { - berrno be; - dev->dev_errno = errno; - Mmsg2(dev->errmsg, _("lseek_dev error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); - return false; + 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; } - dev->file = file; - dev->block_num = block; - dev->file_addr = pos; + file = rfile; + block_num = rblock; + file_addr = pos; return true; } - Dmsg4(100, "reposition_dev from %u:%u to %u:%u\n", - dev->file, dev->block_num, file, block); - if (file < dev->file) { - Dmsg0(100, "Rewind_dev\n"); - if (!rewind_dev(dev)) { - return false; + + /* 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 (file > dev->file) { - Dmsg1(100, "fsf %d\n", file-dev->file); - if (!fsf_dev(dev, file-dev->file)) { - Dmsg1(100, "fsf failed! ERR=%s\n", strerror_dev(dev)); - 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", file, dev->file); + Dmsg2(100, "wanted_file=%d at_file=%d\n", rfile, file); } - if (block < dev->block_num) { - Dmsg2(100, "wanted_blk=%d at_blk=%d\n", block, dev->block_num); - Dmsg0(100, "bsf_dev 1\n"); - bsf_dev(dev, 1); - Dmsg0(100, "fsf_dev 1\n"); - fsf_dev(dev, 1); - Dmsg2(100, "wanted_blk=%d at_blk=%d\n", block, dev->block_num); + 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 (dev_cap(dev, CAP_POSITIONBLOCKS) && block > dev->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", block-dev->block_num); - return fsr_dev(dev, block-dev->block_num); + 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); + } } return true; } @@ -1273,62 +1644,50 @@ reposition_dev(DEVICE *dev, uint32_t file, uint32_t block) /* * Write an end of file on the device - * Returns: 0 on success - * non-zero on failure + * Returns: true on success + * false on failure */ -int -weof_dev(DEVICE *dev, int num) +bool DEVICE::weof(int num) { struct mtop mt_com; int stat; - Dmsg0(29, "weof_dev\n"); + Dmsg0(129, "weof_dev\n"); - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to weof_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + if (!is_open()) { + dev_errno = EBADF; + Mmsg0(errmsg, _("Bad call to weof_dev. Device not open\n")); + Emsg0(M_FATAL, 0, errmsg); + return false; } - dev->file_size = 0; + file_size = 0; - if (!dev->is_tape()) { - return 0; + if (!is_tape()) { + return true; } - if (!dev->can_append()) { - Mmsg0(dev->errmsg, _("Attempt to WEOF on non-appendable Volume\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + if (!can_append()) { + Mmsg0(errmsg, _("Attempt to WEOF on non-appendable Volume\n")); + Emsg0(M_FATAL, 0, errmsg); + return false; } - dev->state &= ~(ST_EOT | ST_EOF); /* remove EOF/EOT flags */ + clear_eof(); + clear_eot(); mt_com.mt_op = MTWEOF; mt_com.mt_count = num; - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + stat = tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); if (stat == 0) { - dev->block_num = 0; - dev->file += num; - dev->file_addr = 0; + block_num = 0; + file += num; + file_addr = 0; } else { berrno be; - clrerror_dev(dev, MTWEOF); + clrerror(MTWEOF); if (stat == -1) { - Mmsg2(dev->errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), - dev->print_name(), be.strerror()); + Mmsg2(errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), + print_name(), be.strerror()); } } - return stat; -} - -/* - * Return string message with last error in English - * Be careful not to call this routine from within dev.c - * while editing an Mmsg() or you will end up in a recursive - * loop creating a Segmentation Violation. - */ -char * -strerror_dev(DEVICE *dev) -{ - return dev->errmsg; + return stat == 0; } @@ -1336,83 +1695,113 @@ strerror_dev(DEVICE *dev) * If implemented in system, clear the tape * error status. */ -void -clrerror_dev(DEVICE *dev, int func) +void DEVICE::clrerror(int func) { const char *msg = NULL; struct mtget mt_stat; char buf[100]; - dev->dev_errno = errno; /* save errno */ + dev_errno = errno; /* save errno */ if (errno == EIO) { - dev->VolCatInfo.VolCatErrors++; + VolCatInfo.VolCatErrors++; } - if (!dev->is_tape()) { + if (!is_tape()) { return; } + if (errno == ENOTTY || errno == ENOSYS) { /* Function not implemented */ switch (func) { case -1: - Emsg0(M_ABORT, 0, "Got ENOTTY on read/write!\n"); - break; + break; /* ignore message printed later */ case MTWEOF: msg = "WTWEOF"; - dev->capabilities &= ~CAP_EOF; /* turn off feature */ - break; + clear_cap(CAP_EOF); /* turn off feature */ + break; #ifdef MTEOM case MTEOM: msg = "WTEOM"; - dev->capabilities &= ~CAP_EOM; /* turn off feature */ - break; + clear_cap(CAP_EOM); /* turn off feature */ + break; #endif case MTFSF: msg = "MTFSF"; - dev->capabilities &= ~CAP_FSF; /* turn off feature */ - break; + clear_cap(CAP_FSF); /* turn off feature */ + break; case MTBSF: msg = "MTBSF"; - dev->capabilities &= ~CAP_BSF; /* turn off feature */ - break; + clear_cap(CAP_BSF); /* turn off feature */ + break; case MTFSR: msg = "MTFSR"; - dev->capabilities &= ~CAP_FSR; /* turn off feature */ - break; + clear_cap(CAP_FSR); /* turn off feature */ + break; case MTBSR: msg = "MTBSR"; - dev->capabilities &= ~CAP_BSR; /* turn off feature */ - break; + clear_cap(CAP_BSR); /* turn off feature */ + break; case MTREW: msg = "MTREW"; - break; + break; #ifdef MTSETBLK case MTSETBLK: msg = "MTSETBLK"; - break; + break; +#endif +#ifdef MTSETDRVBUFFER + case MTSETDRVBUFFER: + msg = "MTSETDRVBUFFER"; + break; #endif +#ifdef MTRESET + case MTRESET: + msg = "MTRESET"; + break; +#endif + #ifdef MTSETBSIZ case MTSETBSIZ: msg = "MTSETBSIZ"; - break; + break; #endif #ifdef MTSRSZ case MTSRSZ: msg = "MTSRSZ"; - break; + 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; + bsnprintf(buf, sizeof(buf), _("unknown func code %d"), func); + msg = buf; + break; } if (msg != NULL) { - dev->dev_errno = ENOSYS; - Mmsg1(dev->errmsg, _("I/O function \"%s\" not supported on this device.\n"), msg); - Emsg0(M_ERROR, 0, dev->errmsg); + dev_errno = ENOSYS; + Mmsg1(errmsg, _("I/O function \"%s\" not supported on this device.\n"), msg); + Emsg0(M_ERROR, 0, errmsg); } } + + /* + * Now we try different methods of clearing the error + * status on the drive so that it is not locked for + * further operations. + */ + /* On some systems such as NetBSD, this clears all errors */ - ioctl(dev->fd, MTIOCGET, (char *)&mt_stat); + tape_ioctl(fd, MTIOCGET, (char *)&mt_stat); /* Found on Linux */ #ifdef MTIOCLRERR @@ -1421,7 +1810,7 @@ clrerror_dev(DEVICE *dev, int func) mt_com.mt_op = MTIOCLRERR; mt_com.mt_count = 1; /* Clear any error condition on the tape */ - ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); Dmsg0(200, "Did MTIOCLRERR\n"); } #endif @@ -1429,11 +1818,12 @@ clrerror_dev(DEVICE *dev, int func) /* Typically on FreeBSD */ #ifdef MTIOCERRSTAT { + berrno be; /* Read and clear SCSI error status */ union mterrstat mt_errstat; - Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev->dev_errno, - strerror(dev->dev_errno)); - ioctl(dev->fd, MTIOCERRSTAT, (char *)&mt_errstat); + Dmsg2(200, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev_errno, + be.strerror(dev_errno)); + tape_ioctl(fd, MTIOCERRSTAT, (char *)&mt_errstat); } #endif @@ -1444,157 +1834,420 @@ clrerror_dev(DEVICE *dev, int func) mt_com.mt_op = MTCSE; mt_com.mt_count = 1; /* Clear any error condition on the tape */ - ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + tape_ioctl(fd, MTIOCTOP, (char *)&mt_com); Dmsg0(200, "Did MTCSE\n"); } #endif } /* - * Flush buffer contents - * No longer used. + * Close the device */ -int flush_dev(DEVICE *dev) -{ - return 1; -} - -static void do_close(DEVICE *dev) +void DEVICE::close() { - - Dmsg1(29, "really close_dev %s\n", dev->print_name()); - if (dev->fd >= 0) { - close(dev->fd); + Dmsg1(100, "close_dev %s\n", print_name()); + if (has_cap(CAP_OFFLINEUNMOUNT)) { + offline(); } - if (unmount_dev(dev, 1) < 0) { - Dmsg1(0, "Cannot unmount device %s.\n", dev->print_name()); + if (!is_open()) { + Dmsg2(100, "device %s already closed vol=%s\n", print_name(), + VolHdr.VolumeName); + return; /* already closed */ } - - /* Remove the last part file if it is empty */ - if (dev->can_append() && (dev->num_parts > 0)) { - struct stat statp; - POOL_MEM archive_name(PM_FNAME); - dev->part = dev->num_parts; - get_filename(dev, dev->VolCatInfo.VolCatName, archive_name); - /* Check that the part file is empty */ - if ((stat(archive_name.c_str(), &statp) == 0) && (statp.st_size == 0)) { - unlink(archive_name.c_str()); - } + + switch (dev_type) { + case B_TAPE_DEV: + unlock_door(); + tape_close(fd); + break; + default: + ::close(fd); } - + /* Clean up device packet so it can be reused */ - dev->fd = -1; - dev->state &= ~(ST_OPENED|ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF); - dev->label_type = B_BACULA_LABEL; - dev->file = dev->block_num = 0; - dev->file_size = 0; - dev->file_addr = 0; - dev->part = 0; - dev->part_size = 0; - dev->part_start = 0; - dev->EndFile = dev->EndBlock = 0; - memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo)); - memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - if (dev->tid) { - stop_thread_timer(dev->tid); - dev->tid = 0; - } - dev->use_count = 0; + clear_opened(); + state &= ~(ST_LABEL|ST_READ|ST_APPEND|ST_EOT|ST_WEOT|ST_EOF); + label_type = B_BACULA_LABEL; + file = block_num = 0; + file_size = 0; + file_addr = 0; + EndFile = EndBlock = 0; + openmode = 0; + Slot = -1; /* unknown slot */ + free_volume(this); + memset(&VolCatInfo, 0, sizeof(VolCatInfo)); + memset(&VolHdr, 0, sizeof(VolHdr)); + if (tid) { + stop_thread_timer(tid); + tid = 0; + } } /* - * Close the device + * This call closes the device, but it is used in DVD handling + * where we close one part and then open the next part. The + * 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 + * main Volume parameters are. */ -void -close_dev(DEVICE *dev) +void DEVICE::close_part(DCR *dcr) { - if (!dev) { - Mmsg0(&dev->errmsg, _("Bad call to close_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return; + VOLUME_LABEL saveVolHdr; + VOLUME_CAT_INFO saveVolCatInfo; /* Volume Catalog Information */ + + + saveVolHdr = VolHdr; /* structure assignment */ + saveVolCatInfo = VolCatInfo; /* structure assignment */ + 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(fd, (__int64)offset, whence); +#else + return ::lseek(fd, (off_t)offset, whence); +#endif } - /*if (dev->fd >= 0 && dev->use_count == 1) {*/ - /* No need to check if dev->fd >= 0: it is checked again - * in do_close, and do_close MUST be called for volumes - * splitted in parts, even if dev->fd == -1. */ - if (dev->use_count == 1) { - do_close(dev); - } else if (dev->use_count > 0) { - dev->use_count--; + 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(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; +} -#ifdef FULL_DEBUG - ASSERT(dev->use_count >= 0); -#endif +/* 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) +{ + Dmsg0(190, "Enter mount\n"); + if (is_mounted()) { + return true; + } else if (requires_mount()) { + return do_mount(1, timeout); + } + return true; } -/* - * Used when unmounting the device, ignore use_count +/* Unmount the device + * If timeout, wait until the unmount command returns 0. + * If !timeout, try to unmount the device only once. */ -void force_close_dev(DEVICE *dev) +bool DEVICE::unmount(int timeout) { - if (!dev) { - Mmsg0(&dev->errmsg, _("Bad call to force_close_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return; + Dmsg0(90, "Enter unmount\n"); + if (is_mounted()) { + return do_mount(0, timeout); } - Dmsg1(29, "Force close_dev %s\n", dev->print_name()); - do_close(dev); - -#ifdef FULL_DEBUG - ASSERT(dev->use_count >= 0); -#endif + return true; } -bool truncate_dev(DEVICE *dev) +/* (Un)mount the device */ +bool DEVICE::do_mount(int mount, int dotimeout) { - Dmsg1(100, "truncate_dev %s\n", dev->print_name()); - if (dev->is_tape()) { - return true; /* we don't really truncate tapes */ - /* maybe we should rewind and write and eof ???? */ + 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; } - /* If there is more than one part, open the first one, and then truncate it. */ - if (dev->num_parts > 0) { - dev->num_parts = 0; - dev->VolCatInfo.VolCatParts = 0; - if (open_first_part(dev) < 0) { - berrno be; - Mmsg1(&dev->errmsg, "Unable to truncate device, because I'm unable to open the first part. ERR=%s\n", be.strerror()); + 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; } - if (ftruncate(dev->fd, 0) != 0) { - berrno be; - Mmsg1(&dev->errmsg, _("Unable to truncate device. ERR=%s\n"), be.strerror()); + 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; } -bool -dev_is_tape(DEVICE *dev) +/* + * Edit codes into (Un)MountCommand, Write(First)PartCommand + * %% = % + * %a = archive device name + * %e = erase (set if cannot mount and first part) + * %n = part number + * %m = mount point + * %v = last part name + * + * omsg = edited output message + * imsg = input string containing edit codes (%x) + * + */ +void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { - return dev->is_tape() ? true : false; + const char *p; + const char *str; + char add[20]; + + POOL_MEM archive_name(PM_FNAME); + + omsg.c_str()[0] = 0; + Dmsg1(800, "edit_mount_codes: %s\n", imsg); + for (p=imsg; *p; p++) { + if (*p == '%') { + switch (*++p) { + case '%': + str = "%"; + break; + case 'a': + str = dev_name; + break; + case 'e': + if (num_dvd_parts == 0) { + if (truncating || blank_dvd) { + str = "2"; + } else { + str = "1"; + } + } else { + str = "0"; + } + break; + case 'n': + bsnprintf(add, sizeof(add), "%d", part); + str = add; + break; + 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; + add[2] = 0; + str = add; + break; + } + } else { + add[0] = *p; + add[1] = 0; + str = add; + } + Dmsg1(1900, "add_str %s\n", str); + pm_strcat(omsg, (char *)str); + Dmsg1(1800, "omsg=%s\n", omsg.c_str()); + } } +/* return the last timer interval (ms) */ +btime_t DEVICE::get_timer_count() +{ + btime_t old = last_timer; + last_timer = get_current_btime(); + return last_timer - old; +} -/* - * return 1 if the device is read for write, and 0 otherwise - * This is meant for checking at the end of a job to see - * if we still have a tape (perhaps not if at end of tape - * and the job is canceled). - */ -bool -dev_can_write(DEVICE *dev) +/* read from fd */ +ssize_t DEVICE::read(void *buf, size_t len) { - if (dev->is_open() && dev->can_append() && - dev->is_labeled() && !(dev->state & ST_WEOT)) { - return true; + ssize_t read_len ; + + get_timer_count(); + + if (this->is_tape()) { + read_len = tape_read(fd, buf, len); } else { - return false; + read_len = ::read(fd, buf, len); } + + last_tick = get_timer_count(); + + DevReadTime += last_tick; + VolCatInfo.VolReadTime += last_tick; + + if (read_len > 0) { /* skip error */ + DevReadBytes += read_len; + } + + return read_len; +} + +/* write to fd */ +ssize_t DEVICE::write(const void *buf, size_t len) +{ + ssize_t write_len ; + + get_timer_count(); + + if (this->is_tape()) { + write_len = tape_write(fd, buf, len); + } else { + write_len = ::write(fd, buf, len); + } + + last_tick = get_timer_count(); + + DevWriteTime += last_tick; + VolCatInfo.VolWriteTime += last_tick; + + if (write_len > 0) { /* skip error */ + DevWriteBytes += write_len; + } + + return write_len; } /* Return the resource name for the device */ @@ -1609,81 +2262,89 @@ dev_vol_name(DEVICE *dev) return dev->VolCatInfo.VolCatName; } -uint32_t dev_block(DEVICE *dev) -{ - update_pos_dev(dev); - return dev->block_num; -} - -uint32_t dev_file(DEVICE *dev) -{ - update_pos_dev(dev); - return dev->file; -} /* * Free memory allocated for the device */ -void -term_dev(DEVICE *dev) +void DEVICE::term(void) { - if (!dev) { - dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to term_dev. Device not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return; - } - do_close(dev); - Dmsg0(29, "term_dev\n"); - if (dev->dev_name) { - free_memory(dev->dev_name); - dev->dev_name = NULL; - } - if (dev->prt_name) { - free_memory(dev->prt_name); - dev->prt_name = NULL; - } - if (dev->errmsg) { - free_pool_memory(dev->errmsg); - dev->errmsg = NULL; - } - pthread_mutex_destroy(&dev->mutex); - pthread_cond_destroy(&dev->wait); - pthread_cond_destroy(&dev->wait_next_vol); - pthread_mutex_destroy(&dev->spool_mutex); - rwl_destroy(&dev->lock); - if (dev->attached_dcrs) { - delete dev->attached_dcrs; - dev->attached_dcrs = NULL; - } - if (dev->state & ST_MALLOC) { - free_pool_memory((POOLMEM *)dev); - } + Dmsg1(900, "term dev: %s\n", print_name()); + close(); + if (dev_name) { + free_memory(dev_name); + dev_name = NULL; + } + if (prt_name) { + free_memory(prt_name); + prt_name = NULL; + } + if (errmsg) { + free_pool_memory(errmsg); + errmsg = NULL; + } + pthread_mutex_destroy(&mutex); + pthread_cond_destroy(&wait); + pthread_cond_destroy(&wait_next_vol); + pthread_mutex_destroy(&spool_mutex); + rwl_destroy(&lock); + if (attached_dcrs) { + delete attached_dcrs; + attached_dcrs = NULL; + } + if (device) { + device->dev = NULL; + } + free((char *)this); } /* * This routine initializes the device wait timers */ -void init_dev_wait_timers(DEVICE *dev) +void init_device_wait_timers(DCR *dcr) { + DEVICE *dev = dcr->dev; + JCR *jcr = dcr->jcr; + /* ******FIXME******* put these on config variables */ dev->min_wait = 60 * 60; dev->max_wait = 24 * 60 * 60; - dev->max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */ + dev->max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */ dev->wait_sec = dev->min_wait; 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; + jcr->max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */ + jcr->wait_sec = jcr->min_wait; + jcr->rem_wait_sec = jcr->wait_sec; + jcr->num_wait = 0; + +} + +void init_jcr_device_wait_timers(JCR *jcr) +{ + /* ******FIXME******* put these on config variables */ + jcr->min_wait = 60 * 60; + jcr->max_wait = 24 * 60 * 60; + jcr->max_num_wait = 9; /* 5 waits =~ 1 day, then 1 day at a time */ + jcr->wait_sec = jcr->min_wait; + jcr->rem_wait_sec = jcr->wait_sec; + jcr->num_wait = 0; } + /* + * The dev timers are used for waiting on a particular device + * * Returns: true if time doubled - * false if max time expired + * false if max time expired */ bool double_dev_wait_time(DEVICE *dev) { - dev->wait_sec *= 2; /* double wait time */ + dev->wait_sec *= 2; /* double wait time */ if (dev->wait_sec > dev->max_wait) { /* but not longer than maxtime */ dev->wait_sec = dev->max_wait; } @@ -1695,29 +2356,42 @@ bool double_dev_wait_time(DEVICE *dev) return true; } -void set_os_device_parameters(DEVICE *dev) + +void set_os_device_parameters(DCR *dcr) { -#ifdef HAVE_LINUX_OS + DEVICE *dev = dcr->dev; + +#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; - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTSETBLK); + 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_cap(dev, CAP_TWOEOF)) { - mt_com.mt_count |= MT_ST_TWO_FM; + if (!dev->has_cap(CAP_TWOEOF)) { + mt_com.mt_count |= MT_ST_TWO_FM; } - if (dev_cap(dev, CAP_EOM)) { - mt_com.mt_count |= MT_ST_FAST_MTEOM; + if (dev->has_cap(CAP_EOM)) { + mt_com.mt_count |= MT_ST_FAST_MTEOM; } - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTSETBLK); + Dmsg0(050, "MTSETDRVBUFFER\n"); + if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + dev->clrerror(MTSETDRVBUFFER); } } +#endif return; #endif @@ -1727,14 +2401,14 @@ void set_os_device_parameters(DEVICE *dev) dev->min_block_size == 0) { /* variable block mode */ mt_com.mt_op = MTSETBSIZ; mt_com.mt_count = 0; - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTSETBSIZ); + 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 (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTEWARN); + if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + dev->clrerror(MTEWARN); } } return; @@ -1746,10 +2420,26 @@ void set_os_device_parameters(DEVICE *dev) dev->min_block_size == 0) { /* variable block mode */ mt_com.mt_op = MTSETBSIZ; mt_com.mt_count = 0; - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTSETBSIZ); + if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + dev->clrerror(MTSETBSIZ); } } +/* Turn this on later when fully tested */ +#if defined(xxxMTIOCSETEOTMODEL) + 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 @@ -1759,8 +2449,8 @@ void set_os_device_parameters(DEVICE *dev) dev->min_block_size == 0) { /* variable block mode */ mt_com.mt_op = MTSRSZ; mt_com.mt_count = 0; - if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { - clrerror_dev(dev, MTSRSZ); + if (tape_ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + dev->clrerror(MTSRSZ); } } return; @@ -1769,7 +2459,26 @@ void set_os_device_parameters(DEVICE *dev) static bool dev_get_os_pos(DEVICE *dev, struct mtget *mt_stat) { - return dev_cap(dev, CAP_MTIOCGET) && - ioctl(dev->fd, MTIOCGET, (char *)mt_stat) == 0 && - mt_stat->mt_fileno >= 0; + 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[] = { + "CREATE_READ_WRITE", + "OPEN_READ_WRITE", + "OPEN_READ_ONLY", + "OPEN_WRITE_ONLY" +}; + + +static char *mode_to_str(int mode) +{ + static char buf[100]; + if (mode < 1 || mode > 4) { + bsnprintf(buf, sizeof(buf), "BAD mode=%d", mode); + return buf; + } + return modes[mode-1]; }