summaryrefslogtreecommitdiffstats
path: root/sys/cam
diff options
context:
space:
mode:
authormjacob <mjacob@FreeBSD.org>2006-01-14 14:32:41 +0000
committermjacob <mjacob@FreeBSD.org>2006-01-14 14:32:41 +0000
commitc70a5f466800680dccb571a0ee4ac3a9f335c52b (patch)
tree438a8530c052da1d4e0fbdb9de794d77880d45a5 /sys/cam
parent906a70beb368a5d2654fcbe3f96cf0b2807b3f30 (diff)
downloadFreeBSD-src-c70a5f466800680dccb571a0ee4ac3a9f335c52b.zip
FreeBSD-src-c70a5f466800680dccb571a0ee4ac3a9f335c52b.tar.gz
Incorporate the O_NONBLOCK open semantics of Linux and Solaris. This allows
an application to upon a tape (yea, even the non-control device) even if it cannot establish a mount session. If the open cannot establish a mount session and O_NONBLOCK was specified, the tape becomes 'open pending mount'. All I/O operations that would require access to a tape thereafter until a close attempt to initiate the mount session. If the mount session succeeds, the tape driver transitions to full open state, else returns an appropriate I/O error (ENXIO). At the same time, add a change that remembers whether tape is being opened read-only. If so, disallow 'write' operations like writing filemarks that bypass the normal read-only filtering operations that happen in the write(2) syscall. Reviewed by: ken, justin, grog MFC after: 2 weeks Suggested by: The Bacula Team
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/scsi/scsi_sa.c93
1 files changed, 87 insertions, 6 deletions
diff --git a/sys/cam/scsi/scsi_sa.c b/sys/cam/scsi/scsi_sa.c
index f970cf9..d3260b6 100644
--- a/sys/cam/scsi/scsi_sa.c
+++ b/sys/cam/scsi/scsi_sa.c
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
#ifdef _KERNEL
#include <sys/conf.h>
#endif
+#include <sys/fcntl.h>
#include <sys/devicestat.h>
#ifndef _KERNEL
@@ -255,8 +256,10 @@ struct sa_softc {
* Misc other flags/state
*/
u_int32_t
- : 31,
- ctrl_mode : 1; /* control device open */
+ : 29,
+ open_rdonly : 1, /* open read-only */
+ open_pending_mount : 1, /* open pending mount */
+ ctrl_mode : 1; /* control device open */
};
struct sa_quirk_entry {
@@ -468,23 +471,37 @@ saopen(struct cdev *dev, int flags, int fmt, struct thread *td)
cam_periph_unlock(periph);
return (ENXIO);
}
+
if (SA_IS_CTRL(dev)) {
softc->ctrl_mode = 1;
cam_periph_unlock(periph);
return (0);
}
-
if (softc->flags & SA_FLAG_OPEN) {
error = EBUSY;
} else if (softc->flags & SA_FLAG_INVALID) {
error = ENXIO;
} else {
/*
+ * Preserve whether this is a read_only open.
+ */
+ softc->open_rdonly = (flags & O_RDWR) == O_RDONLY;
+
+ /*
* The function samount ensures media is loaded and ready.
* It also does a device RESERVE if the tape isn't yet mounted.
+ *
+ * If the mount fails and this was a non-blocking open,
+ * make this a 'open_pending_mount' action.
*/
error = samount(periph, flags, dev);
+ if (error && (flags & O_NONBLOCK)) {
+ softc->flags |= SA_FLAG_OPEN;
+ softc->open_pending_mount = 1;
+ cam_periph_unlock(periph);
+ return (0);
+ }
}
if (error) {
@@ -521,6 +538,7 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td)
return (error);
}
+ softc->open_rdonly = 0;
if (SA_IS_CTRL(dev)) {
softc->ctrl_mode = 0;
cam_periph_release(periph);
@@ -528,6 +546,14 @@ saclose(struct cdev *dev, int flag, int fmt, struct thread *td)
return (0);
}
+ if (softc->open_pending_mount) {
+ softc->flags &= ~SA_FLAG_OPEN;
+ softc->open_pending_mount = 0;
+ cam_periph_release(periph);
+ cam_periph_unlock(periph);
+ return (0);
+ }
+
/*
* Were we writing the tape?
*/
@@ -681,10 +707,32 @@ sastrategy(struct bio *bp)
return;
}
+ /*
+ * This should actually never occur as the write(2)
+ * system call traps attempts to write to a read-only
+ * file descriptor.
+ */
+ if (bp->bio_cmd == BIO_WRITE && softc->open_rdonly) {
+ splx(s);
+ biofinish(bp, NULL, EBADF);
+ return;
+ }
+
splx(s);
+ if (softc->open_pending_mount) {
+ int error = samount(periph, 0, bp->bio_dev);
+ if (error) {
+ biofinish(bp, NULL, ENXIO);
+ return;
+ }
+ saprevent(periph, PR_PREVENT);
+ softc->open_pending_mount = 0;
+ }
+
+
/*
- * If it's a null transfer, return immediatly
+ * If it's a null transfer, return immediately
*/
if (bp->bio_bcount == 0) {
biodone(bp);
@@ -756,6 +804,17 @@ sastrategy(struct bio *bp)
return;
}
+
+#define PENDING_MOUNT_CHECK(softc, periph, dev) \
+ if (softc->open_pending_mount) { \
+ error = samount(periph, 0, dev); \
+ if (error) { \
+ break; \
+ } \
+ saprevent(periph, PR_PREVENT); \
+ softc->open_pending_mount = 0; \
+ }
+
static int
saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
{
@@ -865,7 +924,7 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
* If this isn't the control mode device, actually go out
* and ask the drive again what it's set to.
*/
- if (!SA_IS_CTRL(dev)) {
+ if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) {
u_int8_t write_protect;
int comp_enabled, comp_supported;
error = sagetparams(periph, SA_PARAM_ALL,
@@ -962,7 +1021,8 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
sizeof (sep->ctl_cdb));
- if (SA_IS_CTRL(dev) == 0 || didlockperiph)
+ if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) ||
+ didlockperiph)
bzero((caddr_t) &softc->errinfo,
sizeof (softc->errinfo));
error = 0;
@@ -973,8 +1033,11 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
struct mtop *mt;
int count;
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
mt = (struct mtop *)arg;
+
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
("saioctl: op=0x%x count=0x%x\n",
mt->mt_op, mt->mt_count));
@@ -1067,6 +1130,7 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
break;
}
case MTREW: /* rewind */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
(void) sacheckeod(periph);
error = sarewind(periph);
/* see above */
@@ -1076,12 +1140,14 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
softc->filemarks = 0;
break;
case MTERASE: /* erase */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = saerase(periph, count);
softc->flags &=
~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
softc->flags &= ~SA_FLAG_ERR_PENDING;
break;
case MTRETENS: /* re-tension tape */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = saretension(periph);
softc->flags &=
~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
@@ -1089,6 +1155,8 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
break;
case MTOFFL: /* rewind and put the drive offline */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
(void) sacheckeod(periph);
/* see above */
softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
@@ -1119,6 +1187,8 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
case MTSETBSIZ: /* Set block size for device */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
0, 0, 0);
if (error == 0) {
@@ -1161,6 +1231,8 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
}
break;
case MTSETDNSTY: /* Set density for device and mode */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
+
if (count > UCHAR_MAX) {
error = EINVAL;
break;
@@ -1170,6 +1242,7 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
}
break;
case MTCOMP: /* enable compression */
+ PENDING_MOUNT_CHECK(softc, periph, dev);
/*
* Some devices don't support compression, and
* don't like it if you ask them for the
@@ -1193,15 +1266,19 @@ saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
error = 0;
break;
case MTIOCRDSPOS:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sardpos(periph, 0, (u_int32_t *) arg);
break;
case MTIOCRDHPOS:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sardpos(periph, 1, (u_int32_t *) arg);
break;
case MTIOCSLOCATE:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sasetpos(periph, 0, (u_int32_t *) arg);
break;
case MTIOCHLOCATE:
+ PENDING_MOUNT_CHECK(softc, periph, dev);
error = sasetpos(periph, 1, (u_int32_t *) arg);
break;
case MTIOCGETEOTMODEL:
@@ -3147,6 +3224,8 @@ sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)
int error, nwm = 0;
softc = (struct sa_softc *)periph->softc;
+ if (softc->open_rdonly)
+ return (EBADF);
ccb = cam_periph_getccb(periph, 1);
/*
@@ -3364,6 +3443,8 @@ saerase(struct cam_periph *periph, int longerase)
int error;
softc = (struct sa_softc *)periph->softc;
+ if (softc->open_rdonly)
+ return (EBADF);
ccb = cam_periph_getccb(periph, 1);
OpenPOWER on IntegriCloud