X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fstored%2Fdev.c;h=4d770d3726ef763379130eda81985a92bb675277;hb=c60355e991ba92ec5d7eb3c887b24291f635e16c;hp=86799f5f538fb98f41543f258f7f6c685c0a6f2d;hpb=b71e8dd48707b2d91d86d3bb348f7e0580d12394;p=bacula%2Fbacula diff --git a/bacula/src/stored/dev.c b/bacula/src/stored/dev.c index 86799f5f53..4d770d3726 100644 --- a/bacula/src/stored/dev.c +++ b/bacula/src/stored/dev.c @@ -2,7 +2,7 @@ * * dev.c -- low level operations on device (storage device) * - * Kern Sibbald + * Kern Sibbald, MM * * NOTE!!!! None of these routines are reentrant. You must * use lock_device() and unlock_device() at a higher level, @@ -29,7 +29,7 @@ * Version $Id$ */ /* - Copyright (C) 2000-2003 Kern Sibbald and John Walker + Copyright (C) 2000-2004 Kern Sibbald and John Walker This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -57,15 +57,13 @@ * to include ST_EOT, which is ephimeral, 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_dev() are handled as usual. However, - * in write_block() instead of attempting to write the block to - * the physical device, it is chained into a list of blocks written - * after the EOT condition. In addition, all threads are blocked - * from writing on the tape by calling lock(), and thread other + * is set all calls to write_block_to_device() call the fix_up + * routine. In addition, all threads are blocked + * from writing on the tape by calling lock_dev(), and thread other * than the first thread to hit the EOT will block on a condition * variable. The first thread to hit the EOT will continue to * be able to read and write the tape (he sort of tunnels through - * the locking mechanism -- see lock() for details). + * 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 @@ -83,11 +81,7 @@ #include "stored.h" /* Forward referenced functions */ -int dev_is_tape(DEVICE *dev); -void clrerror_dev(DEVICE *dev, int func); -int fsr_dev(DEVICE *dev, int num); - -extern int debug_level; +void set_os_device_parameters(DEVICE *dev); /* * Allocate and initialize the DEVICE structure @@ -104,32 +98,34 @@ DEVICE * init_dev(DEVICE *dev, DEVRES *device) { struct stat statp; - int tape, fifo; + bool tape, fifo; int errstat; + DCR *dcr = NULL; /* 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, - strerror(errno)); + be.strerror()); return NULL; } - tape = FALSE; - fifo = FALSE; + tape = false; + fifo = false; if (S_ISDIR(statp.st_mode)) { - tape = FALSE; + tape = false; } else if (S_ISCHR(statp.st_mode)) { - tape = TRUE; + tape = true; } else if (S_ISFIFO(statp.st_mode)) { - fifo = TRUE; + fifo = true; } else { if (dev) { dev->dev_errno = ENODEV; } Emsg2(M_FATAL, 0, _("%s is an unknown device type. Must be tape or directory. st_mode=%x\n"), - dev_name, statp.st_mode); + device->device_name, statp.st_mode); return NULL; } if (!dev) { @@ -152,6 +148,13 @@ init_dev(DEVICE *dev, DEVRES *device) dev->max_rewind_wait = device->max_rewind_wait; dev->max_open_wait = device->max_open_wait; dev->max_open_vols = device->max_open_vols; + dev->vol_poll_interval = device->vol_poll_interval; + dev->max_spool_size = device->max_spool_size; + dev->drive_index = device->drive_index; + /* Sanity check */ + if (dev->vol_poll_interval && dev->vol_poll_interval < 60) { + dev->vol_poll_interval = 60; + } dev->device = device; if (tape) { @@ -177,22 +180,40 @@ init_dev(DEVICE *dev, DEVRES *device) *dev->errmsg = 0; if ((errstat = pthread_mutex_init(&dev->mutex, NULL)) != 0) { + berrno be; dev->dev_errno = errstat; - Mmsg1(&dev->errmsg, _("Unable to init mutex: ERR=%s\n"), strerror(errstat)); + Mmsg1(dev->errmsg, _("Unable to init mutex: ERR=%s\n"), be.strerror(errstat)); Emsg0(M_FATAL, 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"), strerror(errstat)); + Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.strerror(errstat)); Emsg0(M_FATAL, 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"), strerror(errstat)); + Mmsg1(dev->errmsg, _("Unable to init cond variable: ERR=%s\n"), be.strerror(errstat)); Emsg0(M_FATAL, 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); + } + 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); + } + dev->fd = -1; + 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); + return dev; } @@ -227,7 +248,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) return dev->fd; } if (VolName) { - strcpy(dev->VolCatInfo.VolCatName, 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), @@ -253,7 +274,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) } /* If busy retry each second for max_open_wait seconds */ while ((dev->fd = open(dev->dev_name, dev->mode, MODE_RW)) < 0) { - if (errno == EAGAIN) { + if (errno == EINTR || errno == EAGAIN) { continue; } if (errno == EBUSY && timeout-- > 0) { @@ -275,8 +296,9 @@ open_dev(DEVICE *dev, char *VolName, int mode) if (dev->fd >= 0) { dev->dev_errno = 0; dev->state |= ST_OPENED; - dev->use_count++; + 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) { @@ -288,12 +310,17 @@ open_dev(DEVICE *dev, char *VolName, int mode) /* * 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); + pm_strcpy(archive_name, dev->dev_name); if (archive_name[strlen(archive_name)] != '/') { - pm_strcat(&archive_name, "/"); + pm_strcat(archive_name, "/"); } - pm_strcat(&archive_name, VolName); + 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; @@ -312,7 +339,7 @@ open_dev(DEVICE *dev, char *VolName, int mode) } else { dev->dev_errno = 0; dev->state |= ST_OPENED; - dev->use_count++; + dev->use_count = 1; update_pos_dev(dev); /* update position */ } Dmsg1(29, "open_dev: disk fd=%d opened\n", dev->fd); @@ -321,6 +348,16 @@ open_dev(DEVICE *dev, char *VolName, int mode) return dev->fd; } +#ifdef debug_tracing +#undef rewind_dev +int _rewind_dev(char *file, int line, DEVICE *dev) +{ + Dmsg2(100, "rewind_dev called from %s:%d\n", file, line); + return rewind_dev(dev); +} +#endif + + /* * Rewind the device. * Returns: 1 on success @@ -331,7 +368,7 @@ int rewind_dev(DEVICE *dev) struct mtop mt_com; unsigned int i; - Dmsg0(29, "rewind_dev\n"); + 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"), @@ -339,7 +376,7 @@ int rewind_dev(DEVICE *dev) Emsg0(M_FATAL, 0, dev->errmsg); return 0; } - dev->state &= ~(ST_APPEND|ST_READ|ST_EOT | ST_EOF | ST_WEOT); /* remove EOF/EOT flags */ + 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; if (dev->state & ST_TAPE) { @@ -402,17 +439,44 @@ eod_dev(DEVICE *dev) } if (!(dev->state & ST_TAPE)) { pos = lseek(dev->fd, (off_t)0, SEEK_END); -// Dmsg1(000, "====== Seek to %lld\n", pos); +// Dmsg1(100, "====== Seek to %lld\n", pos); if (pos >= 0) { update_pos_dev(dev); dev->state |= ST_EOT; return 1; } + dev->dev_errno = errno; + Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"), + dev->dev_name, strerror(dev->dev_errno)); return 0; } +#ifdef MTEOM + + if (dev_cap(dev, CAP_FASTFSF) && !dev_cap(dev, CAP_EOM)) { + struct mtget mt_stat; + 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; + } + } + mt_com.mt_op = MTFSF; + /* + * ***FIXEM*** fix code to handle case that INT16_MAX is + * not large enough. + */ + mt_com.mt_count = INT16_MAX; /* use big positive number */ + if (mt_com.mt_count < 0) { + mt_com.mt_count = INT16_MAX; /* brain damaged system */ + } + } + if (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) { Dmsg1(50, "ioctl error: %s\n", strerror(dev->dev_errno)); clrerror_dev(dev, mt_com.mt_op); @@ -421,28 +485,51 @@ eod_dev(DEVICE *dev) dev->dev_name, strerror(dev->dev_errno)); return 0; } + if (ioctl(dev->fd, MTIOCGET, (char *)&mt_stat) < 0) { dev->dev_errno = errno; Mmsg2(&dev->errmsg, _("ioctl MTIOCGET error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); return 0; } - Dmsg2(200, "EOD file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); + 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 */ } else { +#else + { +#endif if (!rewind_dev(dev)) { return 0; } - while (!(dev->state & ST_EOT)) { - Dmsg0(200, "Do fsf 1\n"); + /* + * 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 */ + } } } /* @@ -451,8 +538,16 @@ eod_dev(DEVICE *dev) * the second EOF. */ if (dev_cap(dev, CAP_BSFATEOM)) { - stat = (bsf_dev(dev, 1) == 0); - dev->file++; /* keep same file */ + struct mtget mt_stat; + /* Backup over EOF */ + stat = bsf_dev(dev, 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; + } else { + dev->file++; /* wing it -- not correct on all OSes */ + } } else { update_pos_dev(dev); /* update position */ stat = 1; @@ -464,19 +559,19 @@ eod_dev(DEVICE *dev) /* * Set the position of the device -- only for files * For other devices, there is no generic way to do it. - * Returns: 1 on succes - * 0 on error + * Returns: true on succes + * false on error */ -int update_pos_dev(DEVICE *dev) +bool update_pos_dev(DEVICE *dev) { off_t pos; - int stat = 0; + 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); - return 0; + return false; } /* Find out where we are */ @@ -489,13 +584,12 @@ int update_pos_dev(DEVICE *dev) dev->dev_errno = errno; Mmsg2(&dev->errmsg, _("lseek error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); + ok = false; } else { - stat = 1; dev->file_addr = pos; } - return stat; } - return 1; + return ok; } @@ -506,26 +600,24 @@ int update_pos_dev(DEVICE *dev) * currently), which means that for the moment, this * routine has very little value. * - * Returns: 1 on success - * 0 on error + * Returns: status */ -int -status_dev(DEVICE *dev, uint32_t *status) +uint32_t status_dev(DEVICE *dev) { struct mtget mt_stat; uint32_t stat = 0; if (dev->state & (ST_EOT | ST_WEOT)) { - stat |= MT_EOD; + stat |= BMT_EOD; Dmsg0(-20, " EOD"); } if (dev->state & ST_EOF) { - stat |= MT_EOF; + stat |= BMT_EOF; Dmsg0(-20, " EOF"); } if (dev->state & ST_TAPE) { - stat |= MT_TAPE; - Dmsg0(-20," Driver status:"); + 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) { dev->dev_errno = errno; @@ -537,57 +629,56 @@ status_dev(DEVICE *dev, uint32_t *status) #if defined(HAVE_LINUX_OS) if (GMT_EOF(mt_stat.mt_gstat)) { - stat |= MT_EOF; + stat |= BMT_EOF; Dmsg0(-20, " EOF"); } if (GMT_BOT(mt_stat.mt_gstat)) { - stat |= MT_BOT; + stat |= BMT_BOT; Dmsg0(-20, " BOT"); } if (GMT_EOT(mt_stat.mt_gstat)) { - stat |= MT_EOT; + stat |= BMT_EOT; Dmsg0(-20, " EOT"); } if (GMT_SM(mt_stat.mt_gstat)) { - stat |= MT_SM; + stat |= BMT_SM; Dmsg0(-20, " SM"); } if (GMT_EOD(mt_stat.mt_gstat)) { - stat |= MT_EOD; + stat |= BMT_EOD; Dmsg0(-20, " EOD"); } if (GMT_WR_PROT(mt_stat.mt_gstat)) { - stat |= MT_WR_PROT; + stat |= BMT_WR_PROT; Dmsg0(-20, " WR_PROT"); } if (GMT_ONLINE(mt_stat.mt_gstat)) { - stat |= MT_ONLINE; + stat |= BMT_ONLINE; Dmsg0(-20, " ONLINE"); } if (GMT_DR_OPEN(mt_stat.mt_gstat)) { - stat |= MT_DR_OPEN; + stat |= BMT_DR_OPEN; Dmsg0(-20, " DR_OPEN"); } if (GMT_IM_REP_EN(mt_stat.mt_gstat)) { - stat |= MT_IM_REP_EN; + stat |= BMT_IM_REP_EN; Dmsg0(-20, " IM_REP_EN"); } #endif /* !SunOS && !OSF */ Dmsg2(-20, " file=%d block=%d\n", mt_stat.mt_fileno, mt_stat.mt_blkno); } else { - stat |= MT_ONLINE | MT_BOT; + stat |= BMT_ONLINE | BMT_BOT; } - *status = stat; - return 1; + return stat; } /* * Load medium in device - * Returns: 1 on success - * 0 on failure + * Returns: true on success + * false on failure */ -int load_dev(DEVICE *dev) +bool load_dev(DEVICE *dev) { #ifdef MTLOAD struct mtop mt_com; @@ -597,17 +688,17 @@ int load_dev(DEVICE *dev) dev->dev_errno = EBADF; Mmsg0(&dev->errmsg, _("Bad call to load_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return 0; + return false; } if (!(dev->state & ST_TAPE)) { - return 1; + return true; } #ifndef MTLOAD Dmsg0(200, "stored: MTLOAD command not available\n"); dev->dev_errno = ENOTTY; /* function not available */ Mmsg2(&dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); return 0; - return 0; + return false; #else dev->block_num = dev->file = 0; @@ -618,30 +709,32 @@ int load_dev(DEVICE *dev) dev->dev_errno = errno; Mmsg2(&dev->errmsg, _("ioctl MTLOAD error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); return 0; + return false; } - return 1; + return true; #endif } /* * Rewind device and put it offline - * Returns: 1 on success - * 0 on failure + * Returns: true on success + * false on failure */ -int offline_dev(DEVICE *dev) +bool offline_dev(DEVICE *dev) { struct mtop mt_com; 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 offline_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return 0; + return false; } if (!(dev->state & ST_TAPE)) { - return 1; + return true; } + 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 @@ -655,21 +748,37 @@ int offline_dev(DEVICE *dev) dev->dev_errno = errno; Mmsg2(&dev->errmsg, _("ioctl MTOFFL error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); - return 0; + return false; } Dmsg1(100, "Offlined device %s\n", dev->dev_name); - return 1; + return true; } +int offline_or_rewind_dev(DEVICE *dev) +{ + if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) { + return offline_dev(dev); + } 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. + */ + return rewind_dev(dev); + } +} /* * Foward space a file - * Returns: 1 on success - * 0 on failure + * Returns: true on success + * false on failure */ -int +bool fsf_dev(DEVICE *dev, int num) { + struct mtget mt_stat; struct mtop mt_com; int stat = 0; @@ -677,27 +786,58 @@ fsf_dev(DEVICE *dev, int num) dev->dev_errno = EBADF; Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return 0; + return false; } if (!(dev->state & ST_TAPE)) { - return 1; + return true; } if (dev->state & ST_EOT) { dev->dev_errno = 0; - Mmsg1(&dev->errmsg, _("Device %s at End of Tape.\n"), dev->dev_name); - return 0; + Mmsg1(dev->errmsg, _("Device %s at End of Tape.\n"), dev->dev_name); + return false; } if (dev->state & ST_EOF) { Dmsg0(200, "ST_EOF set on entry to FSF\n"); } - if (dev->state & ST_EOT) { - Dmsg0(200, "ST_EOT set on entry to FSF\n"); - } Dmsg0(29, "fsf_dev\n"); dev->block_num = 0; - if (dev_cap(dev, CAP_FSF)) { + /* + * If Fast forward space file is set, then we + * use MTFSF to forward space and MTIOCGET + * to get the file position. We assume that + * the SCSI driver will ensure that we do not + * forward space over the end of data mark. + */ + if (dev_cap(dev, CAP_FSF) && dev_cap(dev, 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; + 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; + } + 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; + 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)) { POOLMEM *rbuf; int rbuf_len; Dmsg0(200, "FSF has cap_fsf\n"); @@ -715,12 +855,13 @@ fsf_dev(DEVICE *dev, int num) 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, - strerror(dev->dev_errno)); - Mmsg2(&dev->errmsg, _("read error on %s. ERR=%s.\n"), - dev->dev_name, strerror(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; } @@ -743,7 +884,7 @@ fsf_dev(DEVICE *dev, int num) dev->state &= ~(ST_EOF|ST_EOT); } - Dmsg0(200, "Doing MT_FSF\n"); + Dmsg0(200, "Doing MTFSF\n"); stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat < 0) { /* error => EOT */ dev->state |= ST_EOT; @@ -751,7 +892,7 @@ fsf_dev(DEVICE *dev, int num) clrerror_dev(dev, MTFSF); Mmsg2(&dev->errmsg, _("ioctl MTFSF error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); - Dmsg0(200, "Got < 0 for MT_FSF\n"); + Dmsg0(200, "Got < 0 for MTFSF\n"); Dmsg1(200, "%s", dev->errmsg); } else { dev->state |= ST_EOF; /* just read EOF */ @@ -771,7 +912,7 @@ fsf_dev(DEVICE *dev, int num) } if (dev->state & ST_EOT) { dev->dev_errno = 0; - Mmsg1(&dev->errmsg, _("Device %s at End of Tape.\n"), dev->dev_name); + Mmsg1(dev->errmsg, _("Device %s at End of Tape.\n"), dev->dev_name); stat = -1; } else { stat = 0; @@ -784,13 +925,15 @@ fsf_dev(DEVICE *dev, int num) if (dev->state & ST_EOT) Dmsg0(200, "ST_EOT set on exit FSF\n"); Dmsg1(200, "Return from FSF file=%d\n", dev->file); - return stat == 0 ? 1 : 0; + return stat == 0; } /* * Backward space a file + * Returns: false on failure + * true on success */ -int +bool bsf_dev(DEVICE *dev, int num) { struct mtop mt_com; @@ -798,13 +941,15 @@ bsf_dev(DEVICE *dev, int num) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(dev->errmsg, _("Bad call to bsf_dev. Archive device not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + return false; } - if (!(dev->state & ST_TAPE)) { - return 0; + if (!(dev_state(dev, ST_TAPE))) { + Mmsg1(dev->errmsg, _("Device %s cannot BSF because it is not a tape.\n"), + dev->dev_name); + return false; } Dmsg0(29, "bsf_dev\n"); dev->state &= ~(ST_EOT|ST_EOF); @@ -815,18 +960,20 @@ bsf_dev(DEVICE *dev, int num) stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat < 0) { clrerror_dev(dev, MTBSF); - Mmsg2(&dev->errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), + Mmsg2(dev->errmsg, _("ioctl MTBSF error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); } update_pos_dev(dev); - return stat; + return stat == 0; } /* * Foward space a record + * Returns: false on failure + * true on success */ -int +bool fsr_dev(DEVICE *dev, int num) { struct mtop mt_com; @@ -834,43 +981,57 @@ fsr_dev(DEVICE *dev, int num) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(dev->errmsg, _("Bad call to fsr_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + return false; } - if (!(dev->state & ST_TAPE)) { - return 0; + if (!(dev_state(dev, ST_TAPE))) { + return false; } + if (!dev_cap(dev, CAP_FSR)) { + Mmsg1(dev->errmsg, _("ioctl MTFSR not permitted on %s.\n"), dev->dev_name); + return false; + } + Dmsg0(29, "fsr_dev\n"); - dev->block_num += num; mt_com.mt_op = MTFSR; mt_com.mt_count = num; stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat == 0) { dev->state &= ~ST_EOF; + dev->block_num += num; } else { - if (dev->state & ST_EOF) { - dev->state |= ST_EOT; + 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; } else { - dev->state |= ST_EOF; /* assume EOF */ - dev->file++; - dev->file_addr = 0; + 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"), + Mmsg2(dev->errmsg, _("ioctl MTFSR error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); } update_pos_dev(dev); - return stat; + return stat == 0; } /* * Backward space a record - * Returns: 0 on success - * -1 on failure + * Returns: false on failure + * true on success */ -int +bool bsr_dev(DEVICE *dev, int num) { struct mtop mt_com; @@ -878,14 +1039,20 @@ bsr_dev(DEVICE *dev, int num) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(dev->errmsg, _("Bad call to bsr_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); - return -1; + return false; } if (!(dev->state & ST_TAPE)) { - return 0; + return false; + } + + if (!dev_cap(dev, CAP_BSR)) { + Mmsg1(dev->errmsg, _("ioctl MTBSR not permitted on %s.\n"), dev->dev_name); + return false; } + Dmsg0(29, "bsr_dev\n"); dev->block_num -= num; dev->state &= ~(ST_EOF|ST_EOT|ST_EOF); @@ -894,11 +1061,66 @@ bsr_dev(DEVICE *dev, int num) stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat < 0) { clrerror_dev(dev, MTBSR); - Mmsg2(&dev->errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), + Mmsg2(dev->errmsg, _("ioctl MTBSR error on %s. ERR=%s.\n"), dev->dev_name, strerror(dev->dev_errno)); } update_pos_dev(dev); - return stat; + return stat == 0; +} + +/* + * Reposition the device to file, block + * Returns: false on failure + * 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); + return false; + } + + if (!(dev_state(dev, ST_TAPE))) { + off_t pos = (((off_t)file)<<32) + block; + 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; + 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; + } + } + if (file > dev->file) { + Dmsg1(100, "fsf %d\n", file-dev->file); + if (!fsf_dev(dev, file-dev->file)) { + return false; + } + } + if (block < dev->block_num) { + bsf_dev(dev, 1); + fsf_dev(dev, 1); + } + if (dev_cap(dev, CAP_POSITIONBLOCKS) && block > dev->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); + } + return true; } @@ -916,12 +1138,12 @@ weof_dev(DEVICE *dev, int num) if (dev->fd < 0) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(dev->errmsg, _("Bad call to weof_dev. Archive drive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return -1; } - if (!(dev->state & ST_TAPE)) { + if (!(dev_state(dev, ST_TAPE))) { return 0; } dev->state &= ~(ST_EOT | ST_EOF); /* remove EOF/EOT flags */ @@ -931,12 +1153,15 @@ weof_dev(DEVICE *dev, int num) mt_com.mt_count = num; stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); if (stat == 0) { - dev->file++; + dev->file += num; dev->file_addr = 0; } else { + berrno be; clrerror_dev(dev, MTWEOF); - Mmsg2(&dev->errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), - dev->dev_name, strerror(dev->dev_errno)); + if (stat == -1) { + Mmsg2(dev->errmsg, _("ioctl MTWEOF error on %s. ERR=%s.\n"), + dev->dev_name, be.strerror()); + } } return stat; } @@ -944,7 +1169,7 @@ weof_dev(DEVICE *dev, int num) /* * 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 + * while editing an Mmsg() or you will end up in a recursive * loop creating a Segmentation Violation. */ char * @@ -961,7 +1186,8 @@ strerror_dev(DEVICE *dev) void clrerror_dev(DEVICE *dev, int func) { - char *msg = NULL; + const char *msg = NULL; + struct mtget mt_stat; dev->dev_errno = errno; /* save errno */ if (errno == EIO) { @@ -973,36 +1199,38 @@ clrerror_dev(DEVICE *dev, int func) } if (errno == ENOTTY || errno == ENOSYS) { /* Function not implemented */ switch (func) { - case -1: - Emsg0(M_ABORT, 0, "Got ENOTTY on read/write!\n"); - break; - case MTWEOF: - msg = "WTWEOF"; - dev->capabilities &= ~CAP_EOF; /* turn off feature */ - break; - case MTEOM: - msg = "WTEOM"; - dev->capabilities &= ~CAP_EOM; /* turn off feature */ - break; - case MTFSF: - msg = "MTFSF"; - dev->capabilities &= ~CAP_FSF; /* turn off feature */ - break; - case MTBSF: - msg = "MTBSF"; - dev->capabilities &= ~CAP_BSF; /* turn off feature */ - break; - case MTFSR: - msg = "MTFSR"; - dev->capabilities &= ~CAP_FSR; /* turn off feature */ - break; - case MTBSR: - msg = "MTBSR"; - dev->capabilities &= ~CAP_BSR; /* turn off feature */ - break; - default: - msg = "Unknown"; - break; + case -1: + Emsg0(M_ABORT, 0, "Got ENOTTY on read/write!\n"); + break; + case MTWEOF: + msg = "WTWEOF"; + dev->capabilities &= ~CAP_EOF; /* turn off feature */ + break; +#ifdef MTEOM + case MTEOM: + msg = "WTEOM"; + dev->capabilities &= ~CAP_EOM; /* turn off feature */ + break; +#endif + case MTFSF: + msg = "MTFSF"; + dev->capabilities &= ~CAP_FSF; /* turn off feature */ + break; + case MTBSF: + msg = "MTBSF"; + dev->capabilities &= ~CAP_BSF; /* turn off feature */ + break; + case MTFSR: + msg = "MTFSR"; + dev->capabilities &= ~CAP_FSR; /* turn off feature */ + break; + case MTBSR: + msg = "MTBSR"; + dev->capabilities &= ~CAP_BSR; /* turn off feature */ + break; + default: + msg = "Unknown"; + break; } if (msg != NULL) { dev->dev_errno = ENOSYS; @@ -1010,16 +1238,43 @@ clrerror_dev(DEVICE *dev, int func) Emsg0(M_ERROR, 0, dev->errmsg); } } + /* On some systems such as NetBSD, this clears all errors */ + ioctl(dev->fd, MTIOCGET, (char *)&mt_stat); + +/* Found on Linux */ #ifdef MTIOCLRERR { struct mtop mt_com; - int stat; mt_com.mt_op = MTIOCLRERR; mt_com.mt_count = 1; - stat = ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + /* Clear any error condition on the tape */ + ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); Dmsg0(200, "Did MTIOCLRERR\n"); } #endif + +/* Typically on FreeBSD */ +#ifdef MTIOCERRSTAT +{ + /* 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); +} +#endif + +/* Clear Subsystem Exception OSF1 */ +#ifdef MTCSE +{ + struct mtop mt_com; + mt_com.mt_op = MTCSE; + mt_com.mt_count = 1; + /* Clear any error condition on the tape */ + ioctl(dev->fd, MTIOCTOP, (char *)&mt_com); + Dmsg0(200, "Did MTCSE\n"); +} +#endif } /* @@ -1034,8 +1289,10 @@ int flush_dev(DEVICE *dev) static void do_close(DEVICE *dev) { - Dmsg0(29, "really close_dev\n"); - close(dev->fd); + Dmsg1(29, "really close_dev %s\n", dev->dev_name); + if (dev->fd >= 0) { + close(dev->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); @@ -1044,11 +1301,11 @@ static void do_close(DEVICE *dev) dev->EndFile = dev->EndBlock = 0; memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo)); memset(&dev->VolHdr, 0, sizeof(dev->VolHdr)); - dev->use_count--; if (dev->tid) { stop_thread_timer(dev->tid); dev->tid = 0; } + dev->use_count = 0; } /* @@ -1058,48 +1315,74 @@ void close_dev(DEVICE *dev) { if (!dev) { - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(&dev->errmsg, _("Bad call to close_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return; } if (dev->fd >= 0 && dev->use_count == 1) { do_close(dev); - } else { - Dmsg0(29, "close_dev but in use so leave open.\n"); + } else if (dev->use_count > 0) { dev->use_count--; } + +#ifdef FULL_DEBUG + ASSERT(dev->use_count >= 0); +#endif } /* - * Used when unmounting the device + * Used when unmounting the device, ignore use_count */ void force_close_dev(DEVICE *dev) { if (!dev) { - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(&dev->errmsg, _("Bad call to force_close_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return; } - Dmsg0(29, "really close_dev\n"); + Dmsg1(29, "Force close_dev %s\n", dev->dev_name); do_close(dev); + +#ifdef FULL_DEBUG + ASSERT(dev->use_count >= 0); +#endif } -int truncate_dev(DEVICE *dev) +bool truncate_dev(DEVICE *dev) { if (dev->state & ST_TAPE) { - return 1; + return true; /* we don't really truncate tapes */ + /* maybe we should rewind and write and eof ???? */ } if (ftruncate(dev->fd, 0) != 0) { Mmsg1(&dev->errmsg, _("Unable to truncate device. ERR=%s\n"), strerror(errno)); - return 0; + return false; } - return 1; + return true; } -int +bool dev_is_tape(DEVICE *dev) { - return (dev->state & ST_TAPE) ? 1 : 0; + 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). + */ +bool +dev_can_write(DEVICE *dev) +{ + if ((dev->state & ST_OPENED) && (dev->state & ST_APPEND) && + (dev->state & ST_LABEL) && !(dev->state & ST_WEOT)) { + return true; + } else { + return false; + } } char * @@ -1134,7 +1417,7 @@ term_dev(DEVICE *dev) { if (!dev) { dev->dev_errno = EBADF; - Mmsg0(&dev->errmsg, _("Bad call to fsf_dev. Archive not open\n")); + Mmsg0(&dev->errmsg, _("Bad call to term_dev. Archive not open\n")); Emsg0(M_FATAL, 0, dev->errmsg); return; } @@ -1151,45 +1434,120 @@ term_dev(DEVICE *dev) 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); } } - -/* To make following two functions more readable */ - -#define attached_jcrs ((JCR *)(dev->attached_jcrs)) - -void attach_jcr_to_device(DEVICE *dev, JCR *jcr) +/* + * This routine initializes the device wait timers + */ +void init_dev_wait_timers(DEVICE *dev) { - jcr->prev_dev = NULL; - jcr->next_dev = attached_jcrs; - if (attached_jcrs) { - attached_jcrs->prev_dev = jcr; - } - attached_jcrs = jcr; - Dmsg1(100, "Attached Job %s\n", jcr->Job); + /* ******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->wait_sec = dev->min_wait; + dev->rem_wait_sec = dev->wait_sec; + dev->num_wait = 0; + dev->poll = false; + dev->BadVolName[0] = 0; } -void detach_jcr_from_device(DEVICE *dev, JCR *jcr) +/* + * Returns: true if time doubled + * false if max time expired + */ +bool double_dev_wait_time(DEVICE *dev) { - if (!jcr->prev_dev) { - attached_jcrs = jcr->next_dev; - } else { - jcr->prev_dev->next_dev = jcr->next_dev; + 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; } - if (jcr->next_dev) { - jcr->next_dev->prev_dev = jcr->prev_dev; + dev->num_wait++; + dev->rem_wait_sec = dev->wait_sec; + if (dev->num_wait >= dev->max_num_wait) { + return false; } - jcr->next_dev = jcr->prev_dev = NULL; - Dmsg1(100, "Detached Job %s\n", jcr->Job); + return true; } -JCR *next_attached_jcr(DEVICE *dev, JCR *jcr) +void set_os_device_parameters(DEVICE *dev) { - if (jcr == NULL) { - return attached_jcrs; +#ifdef HAVE_LINUX_OS + struct mtop mt_com; + if (dev->min_block_size == dev->max_block_size && + dev->min_block_size == 0) { /* variable block mode */ + mt_com.mt_op = MTSETBLK; + mt_com.mt_count = 0; + if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + clrerror_dev(dev, MTSETBLK); + } + 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_cap(dev, CAP_EOM)) { + mt_com.mt_count |= MT_ST_FAST_MTEOM; + } + if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + clrerror_dev(dev, MTSETBLK); + } + } + return; +#endif + +#ifdef HAVE_NETBSD_OS + struct mtop mt_com; + if (dev->min_block_size == dev->max_block_size && + dev->min_block_size == 0) { /* variable block mode */ + mt_com.mt_op = MTSETBSIZ; + mt_com.mt_count = 0; + if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + clrerror_dev(dev, 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); + } } - return jcr->next_dev; + return; +#endif + +#if HAVE_FREEBSD_OS || HAVE_OPENBSD_OS + struct mtop mt_com; + if (dev->min_block_size == dev->max_block_size && + dev->min_block_size == 0) { /* variable block mode */ + mt_com.mt_op = MTSETBSIZ; + mt_com.mt_count = 0; + if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + clrerror_dev(dev, MTSETBSIZ); + } + } + return; +#endif + +#ifdef HAVE_SUN_OS + struct mtop mt_com; + if (dev->min_block_size == dev->max_block_size && + dev->min_block_size == 0) { /* variable block mode */ + mt_com.mt_op = MTSRSZ; + mt_com.mt_count = 0; + if (ioctl(dev->fd, MTIOCTOP, (char *)&mt_com) < 0) { + clrerror_dev(dev, MTSRSZ); + } + } + return; +#endif + }