📄 st.c
字号:
/* * If we are opening the tape for writing, check * to make sure that the tape can be written. */ if (flag & FWRITE) { err = 0; if (un->un_mspl->wp) { un->un_status = KEY_WRITE_PROTECT; un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; /* restore resid for status reporting */ un->un_err_resid = resid; return (EACCES); } else { un->un_read_only = 0; } } else { un->un_read_only = 1; } /* * If we're opening the tape write-only, we need to * write 2 filemarks on the HP 1/2 inch drive, to * create a null file. */ if (flag == FWRITE && un->un_dp->options & ST_REEL) un->un_fmneeded = 2; else if (flag == FWRITE) un->un_fmneeded = 1; else un->un_fmneeded = 0; /* * Now this is sort of stupid, but we need to check * whether or not, independent of doing any I/O, * whether or not we can set the requested density * unless the device being opened is the 'generic' * device open (the 'lowest' density) device. * * If we can, fine. If not, we have to bounce the * open with EIO. We use the B_WRITE flag here * becuase in this case we are not searching for * the right density- we are taking the user * specified density and seeing whether or not * setting that density (for writing) would * work. * * Later on this might get done again. */ if ((un->un_fileno < 0 || (un->un_fileno == 0 && un->un_blkno == 0)) && MT_DENSITY(dev) != 0) { if (st_determine_density(devp, B_WRITE)) { /* restore resid for status reporting */ un->un_err_resid = resid; un->un_status = KEY_ILLEGAL_REQUEST; un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; return (EIO); } /* * Destroy the knowledge that we have 'determined' * density so that a later read at BOT comes along * does the right density determination. */ un->un_density_known = 0; } /* restore resid for status reporting */ un->un_err_resid = resid; /* * Okay, the tape is loaded and either at BOT or somewhere past. * Mark the state such that any I/O or tape space operations * will get/set the right density, etc.. */ un->un_laststate = un->un_state; un->un_state = ST_STATE_OPEN_PENDING_IO; /* * Set test append flag if writing. * First write must check that tape is positioned correctly. */ un->un_test_append = (flag & FWRITE); return (0);}intstclose(dev, flag)dev_t dev;int flag;{ int err = 0; register struct scsi_device *devp; register struct scsi_tape *un; int unit, norew, count; unit = MTUNIT(dev); if (DEBUGGING_ALL) { printf("st%d:\tstclose dev 0x%x flag %d\n", unit, dev, flag); } /* * validate arguments */ if ((unit = MTUNIT(dev)) >= ST_MAXUNIT) { return (ENXIO); } if (!(devp = stunits[unit]) || !(un = UPTR)) { return (ENXIO); } /* * a close causes a silent span to the next file if we've hit * an EOF (but not yet read across it). */ if (un->un_eof == ST_EOF) { if (un->un_fileno >= 0) { un->un_fileno++; un->un_blkno = 0; } un->un_eof = ST_NO_EOF; } /* * set state to indicate that we are in process of closing */ un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSING; /* * rewinding? */ norew = (minor(dev) & MT_NOREWIND); /* * For performance reasons (HP 88780), the driver should * postpone writing the second tape mark until just before a file * positioning ioctl is issued (e.g., rewind). This means that * the user must not manually rewind the tape because the tape will * be missing the second tape mark which marks EOM. * However, this small performance improvement is not worth the risk. */ /* * We need to back up over the filemark we inadvertently popped * over doing a read in between the two filemarks that constitute * logical eot for 1/2" tapes. Note that ST_EOT_PENDING is only * set while reading. * * If we happen to be at physical eot (ST_EOM) (writing case), * the writing of filemark(s) will clear the ST_EOM state, which * we don't want, so we save this state and restore it later. */ if (un->un_eof == ST_EOT_PENDING) { if (norew) { if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { err = EIO; } else { un->un_blkno = 0; un->un_eof = ST_EOT; } } else { un->un_eof = ST_NO_EOF; } } else if (((flag & FWRITE) && (un->un_lastop == ST_OP_WRITE || un->un_fmneeded > 0)) || ((flag == FWRITE) && (un->un_lastop == ST_OP_NIL))) { /* save ST_EOM state */ int was_at_eom = (un->un_eof == ST_EOM)? 1: 0; /* * Do we need to write a file mark? * * Note that we will write a filemark if we had opened * the tape write only and no data was written, thus * creating a null file. * * For HP (1/2 inch tape), un_fmneeded tells us how many * filemarks need to be written out. If the user * already wrote one, we only have to write one more. * If they wrote two, we don't have to write any. */ count = (un->un_dp->options & ST_REEL) ? un->un_fmneeded : 1; if (count > 0) { if (stcmd(devp, SCMD_WRITE_FILE_MARK, count, SYNC_CMD)) { err = EIO; } if ((un->un_dp->options & ST_REEL) && norew) { if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { err = EIO; } un->un_eof = ST_NO_EOF; /* fix up block number */ un->un_blkno = 0; } } /* * If we aren't going to be rewinding, and we were at * physical eot, restore the state that indicates we * are at physical eot. Once you have reached physical * eot, and you close the tape, the only thing you can * do on the next open is to rewind. Access to trailer * records is only allowed without closing the device. */ if (norew == 0 && was_at_eom) un->un_eof = ST_EOM; } /* * Do we need to rewind? Can we rewind? */ if (norew == 0 && un->un_fileno >= 0 && err == 0) { /* * We'd like to rewind with the * 'immediate' bit set, but this * causes problems on some drives * where subsequent opens get a * 'NOT READY' error condition * back while the tape is rewinding, * which is impossible to distinguish * from the condition of 'no tape loaded'. * * Also, for some targets, if you disconnect * with the 'immediate' bit set, you don't * actually return right away, i.e., the * target ignores your request for immediate * return. * * Instead, we'll fire off an async rewind * command. We'll mark the device as closed, * and any subsequent open will stall on * the first TEST_UNIT_READY until the rewind * completes. * */ (void) stcmd(devp, SCMD_REWIND, 0, ASYNC_CMD); } /* * clear up state */ un->un_laststate = un->un_state; un->un_state = ST_STATE_CLOSED; un->un_lastop = ST_OP_NIL; /* * any kind of error on closing causes all state to be tossed */ if (err && un->un_status != KEY_ILLEGAL_REQUEST) { un->un_density_known = 0; /* stintr already set un_fileno to -1 */ } return (err);}/* * These routines perform raw i/o operations. */intstread(dev, uio)dev_t dev;struct uio *uio;{ return (strw(dev, uio, B_READ));}intstwrite(dev, uio)dev_t dev;struct uio *uio;{ return (strw(dev, uio, B_WRITE));}/* * Perform max. record blocking. For variable-length devices: * if greater than 64KB -1, block into 64 KB -2 requests; otherwise, * let it through unmodified. * For fixed-length record devices: 63K is max (default minphys). */static voidstminphys(bp)struct buf *bp;{ struct scsi_device *devp = stunits[MTUNIT(bp->b_dev)]; if (UPTR->un_dp->options & ST_VARIABLE) { if (bp->b_bcount > ST_MAXRECSIZE_VARIABLE) bp->b_bcount = ST_MAXRECSIZE_VARIABLE_LIMIT; } else { if (bp->b_bcount > ST_MAXRECSIZE_FIXED) bp->b_bcount = ST_MAXRECSIZE_FIXED; }}static intstrw(dev, uio, flag)dev_t dev;struct uio *uio;int flag;{ struct scsi_device *devp; struct scsi_tape *un; register int rval, len = uio->uio_iov->iov_len; if (MTUNIT(dev) >= ST_MAXUNIT) { return (ENXIO); } devp = stunits[MTUNIT(dev)]; un = UPTR; if ((un->un_dp->options & ST_VARIABLE) == 0) { if (uio->uio_iov->iov_len & (un->un_dp->bsize-1)) { printf("%s%d: %s: not modulo %d block size\n", DNAME, DUNIT, (flag == B_WRITE) ? "write": "read", un->un_dp->bsize); return (EINVAL); } } rval = physio(ststrategy, un->un_rbufp, dev, flag, stminphys, uio); /* * if we hit logical EOT during this xfer and there is not a * full residue, then set un_eof back to ST_EOM to make sure that * the user will see at least one zero write * after this short write */ if (un->un_eof == ST_WRITE_AFTER_EOM && uio->uio_resid != len) un->un_eof = ST_EOM; return (rval);}intststrategy(bp)register struct buf *bp;{ register s; register struct scsi_device *devp; register struct scsi_tape *un; int unit = MTUNIT(bp->b_dev); /* * validate arguments */ devp = stunits[unit]; if (!devp || !(un = UPTR)) { bp->b_flags |= B_ERROR; bp->b_resid = bp->b_bcount; bp->b_error = ENXIO; iodone(bp); return; } if (bp != un->un_sbufp) { char reading = bp->b_flags & B_READ; int wasopening = 0; /* * Check for legal operations */ if (un->un_fileno < 0) { DPRINTF(devp, "strategy with un->un_fileno < 0"); goto b_done_err; } /* * Process this first. If we were reading, and we're pending * logical eot, that means we've bumped one file mark too far. */ if (un->un_eof == ST_EOT_PENDING) { if (stcmd(devp, SCMD_SPACE, Fmk((-1)), SYNC_CMD)) { un->un_fileno = -1; un->un_density_known = 0; goto b_done_err; } un->un_blkno = 0; /* fix up block number.. */ un->un_eof = ST_EOT; } /* * If we are in the process of opening, we may have to * determine/set the correct density. We also may have * to do a test_append (if QIC) to see whether we are * in a position to append to the end of the tape. * * If we're already at logical eot, we transition * to ST_NO_EOF. If we're at physical eot, we punt * to the switch statement below to handle. */ if ((un->un_state == ST_STATE_OPEN_PENDING_IO) || (un->un_test_append && (un->un_dp->options & ST_QIC))) { if (un->un_state == ST_STATE_OPEN_PENDING_IO) { if (st_determine_density(devp, (int) reading)) { goto b_done_err; } } DPRINTF(devp, "pending_io@fileno %d rw %d qic %d eof %d", un->un_fileno, (int) reading, (un->un_dp->options & ST_QIC)? 1: 0, un->un_eof); if (!reading && un->un_eof != ST_EOM) { if (un->un_eof == ST_EOT) { un->un_eof = ST_NO_EOF; } else if (un->un_fileno > 0 && (un->un_dp->options & ST_QIC)) { /* * st_test_append() will do it all */ st_test_append(bp); return; } } if (un->un_state == ST_STATE_OPEN_PENDING_IO) wasopening = 1; un->un_laststate = un->un_state; un->un_state = ST_STATE_OPEN; } /* * Process rest of END OF FILE and END OF TAPE conditions */ DPRINTF(devp, "un_eof=%x, wasopening=%x", un->un_eof, wasopening); switch (un->un_eof) { case ST_EOM: /* * This allows writes to proceed past physical * eot. We'll *really* be in trouble if the * user continues blindly writing data too * much past this point (unwind the tape). * Physical eot really means 'early warning * eot' in this context. * * Every other write from now on will succeed * (if sufficient tape left). * This write will return with resid == count * but the next one should be successful * * Note that we only transition to logical EOT * if the last state wasn't the OPENING state. * We explicitly prohibit running up to physical * eot, closing the device, and then re-opening * to proceed. Trailer records may only be gotten * at by keeping the tape open after hitting eot. * * Also note that ST_EOM cannot be set by reading- * this can only be set during writing. Reading * up to the end of the tape gets a blank check * or a double-filemark indication (ST_EOT_PENDING), * and we prohibit reading after that point. * */ DPRINTF(devp, "EOM"); if (wasopening == 0) /* * this allows strw() to reset it back to * ST_EOM to make sure that the application * will see a zero write */ un->un_eof = ST_WRITE_AFTER_EOM; un->un_status = SUN_KEY_EOT; goto b_done; case ST_WRITE_AFTER_EOM: case ST_EOT: DPRINTF(devp, "EOT"); un->un_status = SUN_KEY_EOT; if (reading) { goto b_done; } un->un_eof = ST_NO_EOF; break; case ST_EOF_PENDING: DPRINTF(devp, "EOF PENDING"); case ST_EOF: DPRINTF(devp, "EOF"); un->un_status = SUN_KEY_EOF; un->un_eof = ST_NO_EOF; un->un_fileno += 1; un->un_blkno = 0; if (reading) { DPRINTF(devp, "now file %d (read)", un->un_fileno); goto b_done; } DPRINTF(devp, "now file %d (write)", un->un_fileno); break; default: un->un_status = 0; break; } } bp->b_flags &= ~(B_DONE|B_ERROR); bp->av_forw = 0; bp->b_resid = 0; if (bp != un->un_sbufp) { BP_PKT(bp) = 0; } s = splr(stpri); if (un->un_quef) { un->un_quel->av_forw = bp; } else un->un_quef = bp; un->un_quel = bp; if (un->un_runq == NULL) ststart(devp); (void) splx(s); return;b_done_err: bp->b_flags |= B_ERROR; bp->b_error = EIO;b_done: un->un_err_resid = bp->b_resid = bp->b_bcount; iodone(bp);}/* * this routine spaces forward over filemarks * XXX space_fmks and find_eom should be merged */static intspace_fmks(dev, count)dev_t dev;int count;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -