|
Supported Operations
|
The SCSItape driver supports the following operations on tape
devices:
It doesn't support random access to records using lseek() ,
but allows positioning via ioctl() .
|
open()
|
int open(const char *path, int flags, .../*mode*/);
|
The driver performs the following actions when it receives a request to
open a tape device. All of these must succeed in order for the device
to be opened.
- The driver checks if the device is available.
- It queries the device to find out if it is ready to take commands
(e.g., whether a medium is present).
- It finds the device's block size limits and selects an initial block
size. The driver initially attempts to use variable-block mode,
unless the device does not support it. The block size can be modified
using
ioctl() .
- If the logical device name indicates a partition other than the one
currently used by the drive, the driver tries to change the drive's
active partition to the one selected by the device name.
- The driver attempts to establish a set of device configuration options
which it expects to use (namely, reporting of setmarks, behavior on
encountering the early-warning condition, and data compression).
- It checks the medium's write protection status and ensures that the
flags agree with it. SCSItape only evaluates the
read-write mode (O_RDONLY , O_WRONLY ,
O_RDWR ) portion of the flags argument and
ignores both all other flags (O_CREAT , O_APPEND
etc.) and the optional mode .
If the operation succeeds, the driver attempts to prevent removal of the
recording medium.
If the device is not ready (e.g., when no medium is loaded), the
open() succeeds, but subsequent operations on the file
will fail, unless the device has become ready in the meantime.
The only exception is the ioctl()
status inquiry request (MTIOCGET ),
which always succeeds and which will report that the device
is off-line.
|
close()
|
int close(int fd);
|
The driver's behavior upon closing a tape device depends on various
factors:
- The logical device name which was used to open the device.
- Whether or not any
write() operation was performed
on the device.
- Whether or not the tape has been repositioned after writing.
The intent behind all this is to make it simple to write programs
which read from and write to tape in a linear (streaming) fashion,
while still allowing programs which perform more complex operations
to control exactly what gets written.
- If the device has been written to:
- If the device has not been repositioned after writing:
- If it is a QIC device, one filemark is written to the medium.
- Otherwise, two filemarks are written to the medium.
- If the logical device name selects an auto-rewind device, the tape is
rewound; otherwise it is repositioned after the (first) filemark just
written.
- If the device has not been written to, or if it has been repositioned
after writing:
- If the logical device name selects an auto-rewind device, the tape is
rewound.
- If the logical device name selects an AT&T-style no-auto-rewind
device, and if the tape is not currently positioned at BOT, EOD, EOT,
or after a filemark or setmark, the tape is repositioned after the
next filemark following the current position.
- If the logical device name selects a Berkeley-style no-auto-rewind
device, the tape is not repositioned in any way.
After these operations have been performed, the driver releases the device
and allows the medium to be removed.
NOTES:
- Portable programs should not reposition the tape immediately after
writing records. UNIX operating systems are not consistent in
their behavior in this situation. The user program should write
an appropriate number of filemarks before positioning.
- When a tape device is opened for writing and immediately closed,
the SCSItape driver does not create an empty file,
as some UNIX versions do.
|
read()
|
ssize_t read(int fd, void *buf, size_t count);
|
The read() operation reads successive records from the tape.
Its effects depend on the currently selected block size; when the block
size is zero, the driver operates in variable-block
mode, otherwise in fixed-block mode.
Independent of the block size, the read() fails if the passed
file descriptor fd doesn't refer to a device which has been
opened for reading, or which is not ready (e.g., because no medium is
present).
NOTES:
- The details of end-of-file and error handling may differ between
operating systems. Portable programs should not expect to be able
to continue reading into the next file after end-of-file has been
encountered.
- Some operating systems require
count to be an exact
multiple of the block size when operating in fixed-block mode.
Portable programs should not rely on the rounding behavior of
SCSItape.
- The detection of incorrect block lengths in variable-block mode
seems to be unreliable. I have not been able to determine if this
is a problem in the driver, in the
BeOS
SCSI interface layer, or in my tape drive. Tape operations should
always be performed with consistent block sizes, for the time being.
|
Variable-Block Mode Reading
|
In variable-block mode, each call to read() attempts to read
a single record from the medium. The count argument gives the
expected record length to be read. It is an error if count
is smaller than the smallest record length supported by the device.
If count is larger than the device's largest supported block
size, it is silently rounded down.
The value returned from read() indicates that an error occurred,
or else it contains the number of bytes which were transferred to buf .
Even if no error occurs, a number of exceptional situations are indicated
by the combination of the returned value and the status information obtained
via MTIOCGET :
- If
read() returns zero, a filemark or setmark has been
encountered. The tape is positioned after the filemark or setmark.
MTIOCGET can be used to find out
which kind of tape mark was present on the medium.
- If the returned value is smaller than
count , a record
of less than the expected length was read. No data has been lost.
- If the returned value is equal to
count , but the residual
count reported by MTIOCGET
is less than zero, a record of more than the expected number of bytes
was encountered.
The residual count indicates how much data was lost. To recover, the
user program must back up and reread the record with a larger
count .
|
Fixed-Block Mode Reading
|
In fixed-block mode, the count argument is rounded to the next
smaller integral multiple of the selected block size. The driver then
attempts to read as many records of this size as necessary to transfer that
many bytes.
The return value indicates that an error occurred, or else it contains
the number of bytes which were transferred to buf .
Even if no error occurs, a number of exceptional situations are indicated
by the combination of the returned value and the status information obtained
via MTIOCGET :
- If
read() returns zero, the driver has encountered
a filemark, a setmark, or a record of a length other than the current
block size. The tape is positioned after the filemark, setmark or
record. MTIOCGET can be used
to determine the exact status. To recover from reading
an incorrect-length record, the user program must back up, change
the block size, and reread the record.
- If the returned value is smaller than
count , the request
was terminated prematurely. The next call to read will
indicate the cause of the problem. If the next tape operation is not
a read() , this information will be lost.
|
write()
|
ssize_t write(int fd, const void *buf, size_t count);
|
The write() operation writes new records to the medium.
Like read() , its effects depend
on the current block size; when the user selected zero-size blocks,
the driver operates in variable-block mode,
otherwise in fixed-block mode.
Independent of the block size, the write() fails if the passed
file descriptor fd doesn't refer to a device which has been
opened for writing, or which is not ready (e.g., because no medium is
present).
If the driver encounters logical end-of-tape ("early warning") during
a write() , it signals this with an EIO
(B_IO_ERROR ) error code, or, in fixed-block
mode, by returning a value smaller than the requested count
(see below). All further calls to write()
will be rejected. This write barrier can be crossed by issuing
an MTIOCGET status request, after which
the driver will accept all writes until physical EOT is encountered.
If the latter happens, data may be lost, the tape may come off the reel,
the sky may fall on the user's head, or other general nastiness may ensue,
depending on the tape drive.
NOTES:
- The details of end-of-medium and error handling may differ between
operating systems. Portable programs should not expect to be able
to continue writing after logical EOT has been encountered.
- Some operating systems require
count to be an exact
multiple of the block size when operating in fixed-block mode.
Portable programs should not rely on the rounding behavior of
SCSItape.
- Most drives will buffer data, allowing the user program to set up the
next record while past ones are still being written out. This makes it
much easier to keep the tape in motion, which is important for
drives of the "streaming" type. To force the drive to flush all
buffered data to tape, the user program can issue an
MTIOCTOP command
MTNOP or MTWEOF with an mt_count
of zero.
|
Variable-Block Mode Writing
|
In variable-block mode, each call to write() attempts to write
a single record to the medium. The count argument contains the
number of bytes to be written. It is an error if count
is smaller than the smallest record length supported by the device.
If count is larger than the device's largest supported block
size, it is silently rounded down.
The value returned from write() indicates that an error
occurred, or else it contains the number of bytes written.
|
Fixed-Block Mode Writing
|
In fixed-block mode, the count argument is rounded to the next
smaller integral multiple of the selected block size. The driver then
attempts to write as many records of this size as necessary to transfer that
many bytes.
The return value indicates that an error occurred, or else it contains
the number of bytes written.
This value may be smaller than the requested count , even
absent any rounding, if the drive encounters logical EOT before all
records could be written.
|
ioctl()
|
int ioctl(int fd, int op, .../*argument*/);
|
The ioctl() operation allows arbitrary commands (encoded as an
integer op code) to be passed to a tape device.
It is inherently non-portable, but the SCSItape driver attempts to
support a set of operations which is found on many UNIX systems.
A program which restricts itself to these commands should work if it
doesn't rely on specific encoded values or data structure formats.
The command opcodes and corresponding data types and macros are defined
in the header files mtio.h (for tape-specific commands) and
<Drivers.h> (for standard
BeOS
commands).
The driver supports the following commands:
-
MTIOCGET
- This command can be used to inquire the status of a tape device.
-
MTIOCTOP
- This command is used to perform tape operations like repositioning
or erasing.
-
MTIOCPOS
- This command is used to inquire the current tape position.
-
MTIOCSETPOS
- This command allows repositioning of the tape to a position
previously returned by the
MTIOCPOS command.
-
B_EJECT_DEVICE
- This is a common
BeOS
device control opcode. The tape driver implements it by issuing
an
MTIOCTOP MTOFFL command, which rewinds and unloads
the tape. Wether or not it is actually ejected depends on the drive
mechanism.
-
B_GET_ICON
- This is a common
BeOS
device control opcode. It is recognized but not implemented yet.
Does anybody have a nice tape drive or cartridge icon?
|
MTIOCGET
|
The argument to the MTIOCGET command
must be a pointer to a struct mtget , which is defined in
mtio.h as follows:
struct mtget {
int32 mt_resid; /* residual count */
uint32 mt_gstat; /* generic status */
uint32 mt_bsize; /* current block size */
};
The fields of the structure are filled in by the ioctl() call
and contain the following information:
-
mt_resid
- The residual count left by the previous tape operation.
After a
read()
or write() , this field contains
the number of bytes which were not transferred. When a user program
reads a tape with blocks of unknown size, this information may be
used to detect if data was lost.
After an MTIOCTOP command
MTWEOF or MTWSS , this field contains
the number of tape marks which were not written.
After a tape positioning command, this field contains the number of
records or tape marks not skipped (e.g., because BOT or EOD was
encountered).
If the residual count is non-zero, the mt_gstat field
should be consulted to determine the cause of the problem.
-
mt_gstat
- This field contains encoded status information. Several macros are
defined in
mtio.h to help decode this information:
-
GMT_BOT(x)
- Non-zero if the previous tape operation encountered BOT.
-
GMT_EOD(x)
- Non-zero if the previous tape operation encountered EOD.
-
GMT_EOT(x)
- Non-zero if the previous tape operation encountered (logical) EOT.
As explained
above , when this
condition arises during a write() ,
as a side effect the MTIOCGET command lifts the
"write barrier" so that writing past this point is allowed.
-
GMT_EOF(x)
- Non-zero if the previous tape operation encountered a filemark.
-
GMT_SM(x)
- Non-zero if the previous tape operation encountered a setmark.
-
GMT_ONLINE(x)
- Non-zero if the device is ready (medium present etc.).
-
GMT_WR_PROT(x)
- Non-zero if a write-protected volume is loaded.
-
GMT_COMPRESS(x)
- Non-zero if the device will perform data compression on writing.
(Data compression is always active when reading, as long as the
drive supports it.)
-
GMT_DENSITY(x)
- Returns the desity of the loaded medium. Various symbolic
constants for common densities are defined in
mtio.h .
-
mt_bsize
- This field contains the current block size. If it is zero, the driver
operates in variable-block mode, otherwise it operates in fixed-block
mode. The mode and block size influence the behavior of the
read()
and write() operations.
|
MTIOCTOP
|
The argument to the MTIOCTOP command
must be a pointer to a struct mtop , which is defined in
mtio.h as follows:
struct mtop {
uint32 mt_op; /* operation */
int32 mt_count; /* numeric parameter */
};
The mt_op field specifies one of several possible operations
to be performed; mt_count is used by some of them.
The SCSItape driver can handle the following tape commands
(all codes are define in mtio.h ):
-
MTWEOF (Write EOF), MTWSS (Write Setmark)
- The device must be open for writing; the value of
mt_count
must be non-negative. The driver writes mt_count filemarks
(for MTWEOF ) or setmarks (for MTWSS ).
If the count is zero, this command forces the drive to write all
buffered data to the tape.
-
MTFSR , MTBSR (Forward/Backward Space Record)
- These commands reposition the tape by skipping
mt_count
records, filemarks and/or setmarks in the indicated direction
(forward for MTFSR , backward for MTBSR ).
If mt_count is negative, the direction of motion is
reversed.
-
MTFSF , MTBSF (Forward/Backward Space File)
- These commands reposition the tape by skipping
mt_count
filemarks and/or setmarks in the indicated direction
(forward for MTFSF , backward for MTBSF ).
If mt_count is negative, the direction of motion is
reversed.
-
MTFSS , MTBSS (Forward/Backward Space Setmark)
- These commands reposition the tape by skipping
mt_count
setmarks in the indicated direction
(forward for MTFSS , backward for MTBSS ).
If mt_count is negative, the direction of motion is
reversed.
-
MTEOD (space to EOD)
- This command repositions the tape to the end of recorded data. The
next write operation will append to the tape.
-
MTREW (Rewind)
- This command rewinds the tape.
-
MTOFFL (Off-line)
- This command rewinds the tape and unloads the medium. It may cause
the drive to eject the medium (if the hardware supports this).
Even if the medium is not ejected, some drives (notably QIC drives)
may require physical intervention before another
open() can succeed.
-
MTRETEN (Retension)
- This command is primarily intended for QIC media. It causes the drive
to take the necessary steps to ensure the tape is correctly tensioned
(typically, the entire tape is wound from the supply to the take-up
reel and back again). This operation should be applied when a medium
has not been used for some time.
-
MTERASE (Erase)
- This command performs a "long" erase operation, writing over the
entire medium. The device must be open for writing.
-
MTSETBSIZ (Set Block Size)
- This command sets the block (record) size used by the driver.
The value of
mt_count must be non-negative. If it is zero,
subsequent read()
and write() operations will use
variable-block mode; otherwise they will use fixed-block mode.
If the tape drive doesn't accept the given block size, the operation
will return an error code; MTIOCGET
can be used to check if the drive replaced it by an acceptable
alternative size.
-
MTNOP (No Operation)
- Does nothing. Wait, I lied: If the device is opened for writing, this
command forces the drive to write all buffered data to the medium.
-
MTCACHE , MTNOCACHE
- These operations are defined for compatibility reasons; they succeed,
but don't do anything.
NOTES:
- The behavior of the spacing commands
MTFSR ,
MTBSR , MTFSF and MTBSF
is not consistent between operating systems. Some UNIX systems
will terminate a spacing command when a hierarchically superior tape
mark is encountered, instead of including it in the count.
Portable programs should check the tape status
using MTIOCGET after these
commands.
- Spacing, retensioning and erasing operations can take a long time.
Some QIC drives will require hours for certain commands.
- The
MTERASE command may not be safe for data security
purposes. Check your tape drive hardware manual before entrusting
military secrects or Scientology scriptures to it.
|
MTIOCPOS
|
The argument to the MTIOCPOS
command must be a pointer to a struct mtpos , which is defined
in mtio.h as follows:
struct mtpos {
uint32 mt_blkno; /* logical block number */
};
If the operation is successful, a value indicating the current tape
position is stored in the mt_blkno field.
NOTE: This command may not be supported by the tape drive.
|
MTIOCSETPOS
|
The argument to the MTIOCSETPOS
command must be a pointer to a struct mtpos , which is defined
in mtio.h as follows:
struct mtpos {
uint32 mt_blkno; /* logical block number */
};
The mt_blkno field should contain a value obtained from the
MTIOCPOS command.
If the operation succeeds, the tape will be repositioned accordingly.
NOTE: This command may not be supported by the tape drive.
|