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
This commit is contained in:
Matt Jacob 2006-01-14 14:32:41 +00:00
parent 6b5ac2b675
commit de8fa52e52

View File

@ -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);