X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fdev.c;h=474fa45d411518191f24eb9df0370aa50aec828f;hb=8bc2739483be88049421740913d40929de7e118b;hp=79b3fc3ad426b9257206a45db6b6992eb351b631;hpb=eecae5bbee34f428e4909d90fd1d01365c404515;p=bacula%2Fbacula diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 79b3fc3ad4..474fa45d41 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -2,20 +2,20 @@ * * 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 - * the higher level routines to "seize" the device and - * to carry out operations without worrying about who - * set what lock (i.e. race conditions). + * 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). * - * 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 * daemon. More complicated coding (double buffering, writer * thread, ...) is left for a later version. @@ -29,24 +29,32 @@ * Version $Id$ */ /* - Copyright (C) 2000-2004 Kern Sibbald and John Walker + Bacula® - The Network Backup Solution + + Copyright (C) 2000-2007 Free Software Foundation Europe e.V. - 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. + 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 + 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 @@ -65,14 +73,14 @@ * be able to read and write the tape (he sort of tunnels through * the locking mechanism -- see lock_dev() for details). * - * Now presumably somewhere higher in the chain of command - * (device.c), someone will notice the EOT condition and - * get a new tape up, get the tape label read, and mark - * the label for rewriting. Then this higher level routine + * Now presumably somewhere higher in the chain of command + * (device.c), someone will notice the EOT condition and + * get a new tape up, get the tape label read, and mark + * the label for rewriting. Then this higher level routine * will write the unwritten buffer to the new volume. * Finally, he will release * any blocked threads by doing a broadcast on the condition - * variable. At that point, we should be totally back in + * variable. At that point, we should be totally back in * business with no lost data. */ @@ -80,10 +88,16 @@ #include "bacula.h" #include "stored.h" +#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 * Note, if dev is non-NULL, it is already allocated, * thus we neither allocate it nor free it. This allows @@ -95,50 +109,50 @@ void set_os_device_parameters(DEVICE *dev); * */ DEVICE * -init_dev(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; - } - Emsg2(M_FATAL, 0, "Unable to stat device %s : %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 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; + } + 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; } - Emsg2(M_FATAL, 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); - strcpy(dev->dev_name, device->device_name); + 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; @@ -151,31 +165,56 @@ init_dev(DEVICE *dev, DEVRES *device) dev->vol_poll_interval = device->vol_poll_interval; dev->max_spool_size = device->max_spool_size; dev->drive_index = device->drive_index; + dev->autoselect = device->autoselect; + dev->dev_type = device->dev_type; + if (dev->is_tape()) { /* No parts on tapes */ + dev->max_part_size = 0; + } else { + dev->max_part_size = device->max_part_size; + } /* Sanity check */ if (dev->vol_poll_interval && dev->vol_poll_interval < 60) { dev->vol_poll_interval = 60; } + /* 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() || 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")); + } } if (dev->max_block_size > 1000000) { - Emsg3(M_ERROR, 0, _("Block size %u on device %s is too large, using default %u\n"), - dev->max_block_size, dev->dev_name, DEFAULT_BLOCK_SIZE); + 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) { - Emsg2(M_WARNING, 0, _("Max block size %u not multiple of device %s block size.\n"), - dev->max_block_size, dev->dev_name); - } - + 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->errmsg = get_pool_memory(PM_EMSG); *dev->errmsg = 0; @@ -183,43 +222,47 @@ init_dev(DEVICE *dev, DEVRES *device) berrno be; dev->dev_errno = errstat; Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.strerror(errstat)); - Emsg0(M_FATAL, 0, dev->errmsg); + 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)); - Emsg0(M_FATAL, 0, dev->errmsg); + 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)); - Emsg0(M_FATAL, 0, dev->errmsg); + 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)); - Emsg0(M_FATAL, 0, dev->errmsg); + Jmsg0(jcr, M_ERROR_TERM, 0, dev->errmsg); } 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)); - Emsg0(M_FATAL, 0, dev->errmsg); + 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; } -/* Open the device with the operating system and +/* + * Open the device with the operating system and * initialize buffer pointers. * + * Returns: -1 on error + * 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 * VolName represents the name of the file to be created/opened. @@ -227,314 +270,702 @@ init_dev(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) { - POOLMEM *archive_name; - - if (dev->state & ST_OPENED) { - /* - * *****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->dev_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); - if (dev->state & (ST_TAPE|ST_FIFO)) { - int timeout; - 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->state & ST_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) { - if (errno == EINTR || errno == EAGAIN) { - continue; - } - if (errno == EBUSY && timeout-- > 0) { - Dmsg2(100, "Device %s busy. ERR=%s\n", dev->dev_name, strerror(errno)); - bmicrosleep(1, 0); - continue; - } - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("stored: unable to open device %s: ERR=%s\n"), - dev->dev_name, 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); - } else { - /* - * Handle opening of File Archive (not a tape) - */ - if (VolName == NULL || *VolName == 0) { - Mmsg(dev->errmsg, _("Could not open file device %s. No Volume name given.\n"), - dev->dev_name); - return -1; - } - archive_name = get_pool_memory(PM_FNAME); - pm_strcpy(archive_name, dev->dev_name); - if (archive_name[strlen(archive_name)] != '/') { - pm_strcat(archive_name, "/"); + 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); } - pm_strcat(archive_name, VolName); - Dmsg1(29, "open_dev: device is disk %s\n", archive_name); - 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; + } + 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 { + 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 { - Emsg0(M_ABORT, 0, _("Illegal mode given to open_dev.\n")); + /* 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 */ + } } - /* If creating file, give 0640 permissions */ - if ((dev->fd = open(archive_name, dev->mode, 0640)) < 0) { - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("Could not open: %s, ERR=%s\n"), archive_name, strerror(dev->dev_errno)); - 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 */ + bmicrosleep(5, 0); + /* Exceed wait time ? */ + if (time(NULL) - start_time >= max_open_wait) { + break; /* yes, get out */ } - Dmsg1(29, "open_dev: disk fd=%d opened\n", dev->fd); - free_pool_memory(archive_name); } - return dev->fd; +#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); } -#ifdef debug_tracing -#undef rewind_dev -int _rewind_dev(char *file, int line, DEVICE *dev) + +/* + * Open a file device + */ +void DEVICE::open_file_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); + + 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 (!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); +} + +/* + * 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 ((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; - - Dmsg1(29, "rewind_dev %s\n", dev->dev_name); - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg1(&dev->errmsg, _("Bad call to rewind_dev. Device %s not open\n"), - dev->dev_name); - Emsg0(M_FATAL, 0, dev->errmsg); + bool first = true; + + 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_addr = 0; - if (dev->state & ST_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; - if (i == dev->max_rewind_wait) { - Dmsg1(200, "Rewind error, %s. retrying ...\n", strerror(errno)); - } - clrerror_dev(dev, MTREW); - if (dev->dev_errno == EIO && i > 0) { + 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()); + } + /* + * 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->dev_name, be.strerror(dev->dev_errno)); - return false; - } - break; - } - } else if (dev->state & ST_FILE) { - if (lseek(dev->fd, (off_t)0, SEEK_SET) < 0) { - berrno be; - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - 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_ateof() +{ + set_eof(); + if (is_tape()) { + file++; + } + file_addr = 0; + file_size = 0; + block_num = 0; +} + +/* + * Called to indicate we are now at the end of the tape, and + * writing is not possible. + */ +void DEVICE::set_ateot() +{ + /* Make tape effectively read-only */ + state |= (ST_EOF|ST_EOT|ST_WEOT); + clear_append(); +} + +/* * Position device to end of medium (end of data) - * Returns: 1 on succes - * 0 on error + * Returns: true on succes + * false on error */ -int -eod_dev(DEVICE *dev) +bool DEVICE::eod(DCR *dcr) { struct mtop mt_com; struct mtget mt_stat; - int stat = 0; - off_t pos; + bool ok = true; + boffset_t pos; - Dmsg0(29, "eod_dev\n"); - if (dev->state & ST_EOT) { - return 1; + if (fd < 0) { + dev_errno = EBADF; + Mmsg1(errmsg, _("Bad call to eod. Device %s not open\n"), print_name()); + return false; } - dev->state &= ~(ST_EOF); /* remove EOF flags */ - dev->block_num = dev->file = 0; - dev->file_addr = 0; - if (dev->state & (ST_FIFO | ST_PROG)) { - return 1; + +#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 (!(dev->state & ST_TAPE)) { - pos = lseek(dev->fd, (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 1; + update_pos(dcr); + set_eot(); + return true; } - dev->dev_errno = errno; + dev_errno = errno; berrno be; - Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - return 0; + 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)) { - struct mtget mt_stat; + if (has_cap(CAP_FASTFSF) && !has_cap(CAP_EOM)) { Dmsg0(100,"Using FAST FSF for EOM\n"); - if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno <= 0) { - if (!rewind_dev(dev)) { - return 0; - } + /* If unknown position, rewind */ + if (!dev_get_os_pos(this, &mt_stat)) { + if (!rewind(NULL)) { + return false; + } } mt_com.mt_op = MTFSF; /* - * ***FIXEM*** fix code to handle case that INT16_MAX is + * ***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 */ + mt_com.mt_count = INT16_MAX; /* brain damaged system */ } } - if (dev_cap(dev, CAP_EOM)) { - Dmsg0(100,"Using EOM for EOM\n"); - mt_com.mt_op = MTEOM; - mt_com.mt_count = 1; - } - if (dev_cap(dev, CAP_FASTFSF) || dev_cap(dev, CAP_EOM)) { - if ((stat=ioctl(dev->fd, MTIOCTOP, (char *)&mt_com)) < 0) { - berrno be; - clrerror_dev(dev, mt_com.mt_op); - Dmsg1(50, "ioctl error: %s\n", be.strerror(dev->dev_errno)); - update_pos_dev(dev); - Mmsg2(&dev->errmsg, _("ioctl MTEOM error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - return 0; + 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 (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) { - berrno be; - clrerror_dev(dev, -1); - Mmsg2(&dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - return 0; + 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(dcr); + Mmsg2(errmsg, _("ioctl MTEOM 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->file = mt_stat.mt_fileno; - /* - * Rewind then use FSF until EOT reached - */ + 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); + set_ateof(); + file = mt_stat.mt_fileno; } else { #else { #endif - if (!rewind_dev(dev)) { - return 0; + /* + * 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=dev->file; !(dev->state & ST_EOT); file_num++) { - Dmsg0(200, "eod_dev: doing fsf 1\n"); - if (!fsf_dev(dev, 1)) { - Dmsg0(200, "fsf_dev error.\n"); - return 0; - } - /* - * 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 (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && - mt_stat.mt_fileno >= 0) { - Dmsg2(100, "Adjust file from %d to %d\n", dev->file , mt_stat.mt_fileno); - dev->file = mt_stat.mt_fileno; - } - stat = 0; - break; /* we are not progressing, bail out */ - } + 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; + } } } /* @@ -542,63 +973,64 @@ 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 */ - stat = bsf_dev(dev, 1); + ok = bsf(1); /* If BSF worked and fileno is known (not -1), set file */ - if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) { - Dmsg2(100, "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 */ - stat = 1; + update_pos(dcr); /* update position */ } - Dmsg1(200, "EOD dev->file=%d\n", dev->file); - return stat; + 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. Archive 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->state & ST_FILE) { - dev->file = 0; - dev->file_addr = 0; - pos = lseek(dev->fd, (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) { - Pmsg1(000, "Seek error: ERR=%s\n", strerror(dev->dev_errno)); - dev->dev_errno = errno; - Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"), - dev->dev_name, strerror(dev->dev_errno)); - 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 * seem possible (at least I do not know how to do it @@ -614,64 +1046,102 @@ 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->state & ST_TAPE) { + 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->dev_name, be.strerror(dev->dev_errno)); - 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 */ - 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 { + Pmsg2(-20, _(" file=%d block=%d\n"), -1, -1); + } } else { stat |= BMT_ONLINE | BMT_BOT; } @@ -682,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) { @@ -692,31 +1162,32 @@ bool load_dev(DEVICE *dev) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to load_dev. Archive not open\n")); + Mmsg0(dev->errmsg, _("Bad call to load_dev. Device not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return false; } - if (!(dev->state & ST_TAPE)) { + 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->dev_name, be.strerror(dev->dev_errno)); return 0; + 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 (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->dev_name, be.strerror(dev->dev_errno)); return 0; + Mmsg2(dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), + dev->print_name(), be.strerror()); return false; } return true; @@ -726,413 +1197,445 @@ 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. Archive not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return false; - } - if (!(dev->state & ST_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_addr = 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->dev_name, be.strerror(dev->dev_errno)); + 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->dev_name); + Dmsg1(100, "Offlined device %s\n", print_name()); return true; } -int offline_or_rewind_dev(DEVICE *dev) +bool DEVICE::offline_or_rewind() { - if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { - return offline_dev(dev); + if (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_dev(dev, MTREW); - return rewind_dev(dev); + clrerror(MTREW); + return rewind(NULL); } } -/* - * Foward space a file +/* + * 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. Archive 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->state & ST_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->dev_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(29, "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 - * to get the file position. We assume that + * to get the file position. We assume that * the SCSI driver will ensure that we do not - * forward space over the end of data mark. + * forward space past the end of the medium. */ - if (dev_cap(dev, CAP_FSF) && 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 || ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) { - 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->dev_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->file = mt_stat.mt_fileno; - dev->state |= ST_EOF; /* just read EOF */ - dev->file_addr = 0; + set_ateof(); + file = mt_stat.mt_fileno; 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 (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)) { - Dmsg0(200, "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(200, "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->dev_name, be.strerror(dev->dev_errno)); - Dmsg1(200, "%s", dev->errmsg); - break; - } - } - if (stat == 0) { /* EOF */ - update_pos_dev(dev); - Dmsg1(200, "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; - Dmsg0(200, "Set ST_EOT\n"); - break; - } else { - dev->state |= ST_EOF; - dev->file++; - dev->file_addr = 0; - continue; - } - } else { /* Got data */ - dev->state &= ~(ST_EOF|ST_EOT); - } - - Dmsg0(200, "Doing MTFSF\n"); - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); - if (stat < 0) { /* error => EOT */ - berrno be; - dev->state |= ST_EOT; - Dmsg0(200, "Set ST_EOT\n"); - clrerror_dev(dev, MTFSF); - Mmsg2(&dev->errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - Dmsg0(200, "Got < 0 for MTFSF\n"); - Dmsg1(200, "%s", dev->errmsg); - } else { - dev->state |= ST_EOF; /* just read EOF */ - dev->file++; - dev->file_addr = 0; - } + 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(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-- && !(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->dev_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 +/* + * 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. Archive 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_state(dev, ST_TAPE))) { - Mmsg1(dev->errmsg, _("Device %s cannot BSF because it is not a tape.\n"), - dev->dev_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; + + 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->dev_name, be.strerror(dev->dev_errno)); + 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. Archive 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_state(dev, ST_TAPE))) { + if (!is_tape()) { return false; } - if (!dev_cap(dev, CAP_FSR)) { - Mmsg1(dev->errmsg, _("ioctl MTFSR not permitted on %s.\n"), dev->dev_name); + + if (!has_cap(CAP_FSR)) { + Mmsg1(errmsg, _("ioctl MTFSR not permitted on %s.\n"), print_name()); return false; } - Dmsg0(29, "fsr_dev\n"); + 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; - if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) == 0 && mt_stat.mt_fileno >= 0) { - 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; + 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 (dev->state & ST_EOF) { - dev->state |= ST_EOT; - } else { - dev->state |= ST_EOF; /* assume EOF */ - dev->file++; - dev->block_num = 0; - dev->file_addr = 0; - } - } - clrerror_dev(dev, MTFSR); - Mmsg2(dev->errmsg, _("ioctl MTFSR error on %s. ERR=%s.\n"), - dev->dev_name, be.strerror(dev->dev_errno)); - } - update_pos_dev(dev); + if (at_eof()) { + set_eot(); + } else { + set_ateof(); + } + } + Mmsg3(errmsg, _("ioctl MTFSR %d error on %s. ERR=%s.\n"), + num, print_name(), be.strerror()); + } 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. Archive 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->state & ST_TAPE)) { + if (!is_tape()) { return false; } - if (!dev_cap(dev, CAP_BSR)) { - Mmsg1(dev->errmsg, _("ioctl MTBSR not permitted on %s.\n"), dev->dev_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->dev_name, be.strerror(dev->dev_errno)); + 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) -{ - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to reposition_dev. Archive not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); +bool DEVICE::reposition(DCR *dcr, uint32_t rfile, uint32_t rblock) +{ + 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_state(dev, ST_TAPE))) { - off_t pos = (((off_t)file)<<32) + block; + if (!is_tape()) { + boffset_t pos = (((boffset_t)rfile)<<32) | rblock; Dmsg1(100, "===== lseek to %d\n", (int)pos); - if (lseek(dev->fd, pos, SEEK_SET) == (off_t)-1) { - dev->dev_errno = errno; - Mmsg2(dev->errmsg, _("lseek error on %s. ERR=%s.\n"), - dev->dev_name, strerror(dev->dev_errno)); - return false; - } - dev->file = file; - dev->block_num = block; - dev->file_addr = 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; 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)) { - 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 (block < dev->block_num) { - bsf_dev(dev, 1); - fsf_dev(dev, 1); + 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; } @@ -1141,55 +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; - - if (dev->fd < 0) { - dev->dev_errno = EBADF; - Mmsg0(dev->errmsg, _("Bad call to weof_dev. Archive drive not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + 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; } + file_size = 0; - if (!(dev_state(dev, ST_TAPE))) { - return 0; + if (!is_tape()) { + return true; + } + 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 */ - Dmsg0(29, "weof_dev\n"); + + 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->dev_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; } @@ -1197,63 +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->state & ST_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; -#endif + 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; +#ifdef MTSETBLK + case MTSETBLK: + msg = "MTSETBLK"; + 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; +#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: - msg = "Unknown"; - break; + bsnprintf(buf, sizeof(buf), _("unknown func code %d"), func); + msg = buf; + break; } if (msg != NULL) { - dev->dev_errno = ENOSYS; - Mmsg1(&dev->errmsg, _("This device does not support %s.\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 @@ -1262,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 @@ -1270,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 @@ -1285,203 +1834,517 @@ 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) +void DEVICE::close() { - return 1; -} + Dmsg1(100, "close_dev %s\n", print_name()); + if (has_cap(CAP_OFFLINEUNMOUNT)) { + offline(); + } -static void do_close(DEVICE *dev) -{ + if (!is_open()) { + Dmsg2(100, "device %s already closed vol=%s\n", print_name(), + VolHdr.VolumeName); + return; /* already closed */ + } - Dmsg1(29, "really close_dev %s\n", dev->dev_name); - if (dev->fd >= 0) { - close(dev->fd); + 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->file = dev->block_num = 0; - dev->file_addr = 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. Archive 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) { - 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; } - -#ifdef FULL_DEBUG - ASSERT(dev->use_count >= 0); -#endif + return false; } -/* - * Used when unmounting the device, ignore use_count +/* Mount the device. + * If timeout, wait until the mount command returns 0. + * If !timeout, try to mount the device only once. */ -void force_close_dev(DEVICE *dev) +bool DEVICE::mount(int timeout) { - if (!dev) { - Mmsg0(&dev->errmsg, _("Bad call to force_close_dev. Archive not open\n")); - Emsg0(M_FATAL, 0, dev->errmsg); - return; - } - Dmsg1(29, "Force close_dev %s\n", dev->dev_name); - do_close(dev); + Dmsg0(190, "Enter mount\n"); + if (is_mounted()) { + return true; + } else if (requires_mount()) { + return do_mount(1, timeout); + } + return true; +} -#ifdef FULL_DEBUG - ASSERT(dev->use_count >= 0); -#endif +/* 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) +{ + Dmsg0(90, "Enter unmount\n"); + if (is_mounted()) { + return do_mount(0, timeout); + } + return true; } -bool truncate_dev(DEVICE *dev) +/* (Un)mount the device */ +bool DEVICE::do_mount(int mount, int dotimeout) { - if (dev->state & ST_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 (ftruncate(dev->fd, 0) != 0) { - Mmsg1(&dev->errmsg, _("Unable to truncate device. ERR=%s\n"), strerror(errno)); + + 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; } -bool -dev_is_tape(DEVICE *dev) -{ - return (dev->state & ST_TAPE) ? true : false; -} - - /* - * 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). + * 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) + * */ -bool -dev_can_write(DEVICE *dev) +void DEVICE::edit_mount_codes(POOL_MEM &omsg, const char *imsg) { - if ((dev->state & ST_OPENED) && (dev->state & ST_APPEND) && - (dev->state & ST_LABEL) && !(dev->state & ST_WEOT)) { - return true; - } else { - return 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()); } } -char * -dev_name(DEVICE *dev) +/* return the last timer interval (ms) */ +btime_t DEVICE::get_timer_count() { - return dev->dev_name; + btime_t old = last_timer; + last_timer = get_current_btime(); + return last_timer - old; } -char * -dev_vol_name(DEVICE *dev) +/* read from fd */ +ssize_t DEVICE::read(void *buf, size_t len) { - return dev->VolCatInfo.VolCatName; + ssize_t read_len ; + + get_timer_count(); + + if (this->is_tape()) { + read_len = tape_read(fd, buf, len); + } else { + 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; } -uint32_t dev_block(DEVICE *dev) +/* Return the resource name for the device */ +const char *DEVICE::name() const { - update_pos_dev(dev); - return dev->block_num; + return device->hdr.name; } -uint32_t dev_file(DEVICE *dev) +char * +dev_vol_name(DEVICE *dev) { - update_pos_dev(dev); - return dev->file; + return dev->VolCatInfo.VolCatName; } -/* + +/* * 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. Archive 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->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; } @@ -1493,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 @@ -1525,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; @@ -1544,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 @@ -1557,11 +2449,36 @@ 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; #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[] = { + "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]; }