#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;
/*
* Allocate and initialize the DEVICE structure
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),
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 */
}
/* Stop any open() timer we started */
/*
* 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)] != '/') {
} 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);
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) {
return 1;
}
+ 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
return 1;
}
+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
if (!(dev->state & ST_TAPE)) {
return 0;
}
+
+ if (!dev_cap(dev, CAP_BSR)) {
+ Mmsg1(&dev->errmsg, _("ioctl MTBSR not permitted on %s.\n"),
+ dev->dev_name);
+ return 0;
+ }
+
Dmsg0(29, "bsr_dev\n");
dev->block_num -= num;
dev->state &= ~(ST_EOF|ST_EOT|ST_EOF);
}
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 -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;
+ 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;
+ 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;
Emsg0(M_ERROR, 0, dev->errmsg);
}
}
+/* 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;
+ Pmsg2(000, "Doing MTIOCERRSTAT errno=%d ERR=%s\n", dev->dev_errno,
+ strerror(dev->dev_errno));
+ ioctl(dev->fd, MTIOCERRSTAT, (char *)&mt_errstat);
+}
+#endif
}
/*
static void do_close(DEVICE *dev)
{
- Dmsg0(29, "really close_dev\n");
+ Dmsg1(29, "really close_dev %s\n", dev->dev_name);
close(dev->fd);
/* Clean up device packet so it can be reused */
dev->fd = -1;
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;
}
/*
}
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
}
/*
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)
return (dev->state & ST_TAPE) ? 1 : 0;
}
+
+/*
+ * 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).
+ */
+int
+dev_can_write(DEVICE *dev)
+{
+ if ((dev->state & ST_OPENED) && (dev->state & ST_APPEND) &&
+ (dev->state & ST_LABEL) && !(dev->state & ST_WEOT)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
char *
dev_name(DEVICE *dev)
{
}
}
-
-/* To make following two functions more readable */
-
-#define attached_jcrs ((JCR *)(dev->attached_jcrs))
-
+/*
+ * We attach a jcr to the device so that when
+ * the Volume is full during writing, a
+ * JobMedia record will be created for this
+ * Job.
+ */
void attach_jcr_to_device(DEVICE *dev, JCR *jcr)
{
- jcr->prev_dev = NULL;
- jcr->next_dev = attached_jcrs;
- if (attached_jcrs) {
- attached_jcrs->prev_dev = jcr;
+ jcr->prev_dev = (JCR *)NULL;
+ jcr->next_dev = dev->attached_jcrs;
+ if (dev->attached_jcrs) {
+ dev->attached_jcrs->prev_dev = jcr;
}
- attached_jcrs = jcr;
+ dev->attached_jcrs = jcr;
Dmsg1(100, "Attached Job %s\n", jcr->Job);
}
void detach_jcr_from_device(DEVICE *dev, JCR *jcr)
{
if (!jcr->prev_dev) {
- attached_jcrs = jcr->next_dev;
+ dev->attached_jcrs = jcr->next_dev;
} else {
jcr->prev_dev->next_dev = jcr->next_dev;
}
JCR *next_attached_jcr(DEVICE *dev, JCR *jcr)
{
- if (jcr == NULL) {
- return attached_jcrs;
+ if (jcr == (JCR *)NULL) {
+ return dev->attached_jcrs;
}
return jcr->next_dev;
}