4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-23 07:27:21 +08:00
newlib-cygwin/winsup/cygwin/fhandler_tape.cc
Corinna Vinschen 7cdd029300 * fhandler_raw.cc (fhandler_dev_raw::raw_read): When reading with
variable block size, read only one block, read directly into user
	supplied buffer, return ENOMEM if user supplied buffer is smaller
	than size of next block to read.  Use read2 instead of bytes_to_read
	to count number of bytes read.
	* fhandler_tape.cc (fhandler_dev_tape::open): Add debug output.
2004-03-02 13:07:47 +00:00

840 lines
19 KiB
C++

/* fhandler_tape.cc. See fhandler.h for a description of the fhandler
classes.
Copyright 1999, 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.
This file is part of Cygwin.
This software is a copyrighted work licensed under the terms of the
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
details. */
#include "winsup.h"
#include <sys/termios.h>
#include <unistd.h>
#include <sys/mtio.h>
#include "cygerrno.h"
#include "perprocess.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "dtable.h"
#include "cygheap.h"
/**********************************************************************/
/* fhandler_dev_tape */
void
fhandler_dev_tape::clear (void)
{
lasterr = 0;
fhandler_dev_raw::clear ();
}
int
fhandler_dev_tape::is_eom (int win_error)
{
int ret = ((win_error == ERROR_END_OF_MEDIA)
|| (win_error == ERROR_EOM_OVERFLOW)
|| (win_error == ERROR_NO_DATA_DETECTED));
if (ret)
debug_printf ("end of medium");
return ret;
}
int
fhandler_dev_tape::is_eof (int win_error)
{
int ret = ((win_error == ERROR_FILEMARK_DETECTED)
|| (win_error == ERROR_SETMARK_DETECTED));
if (ret)
debug_printf ("end of file");
return ret;
}
fhandler_dev_tape::fhandler_dev_tape ()
: fhandler_dev_raw ()
{
debug_printf ("unit: %d", dev ().minor);
}
int
fhandler_dev_tape::open (int flags, mode_t)
{
int ret;
devbufsiz = 1L;
ret = fhandler_dev_raw::open (flags);
if (ret)
{
struct mtget get;
struct mtop op;
struct mtpos pos;
if (!ioctl (MTIOCGET, &get))
/* Tape drive supports and is set to variable block size. */
if (get.mt_dsreg == 0)
devbufsiz = get.mt_maxblksize;
else
devbufsiz = get.mt_dsreg;
varblkop = get.mt_dsreg == 0;
if (devbufsiz > 1L)
devbuf = new char [devbufsiz];
/* The following rewind in position 0 solves a problem which appears
* in case of multi volume archives: The last ReadFile on first medium
* returns ERROR_NO_DATA_DETECTED. After media change, all subsequent
* ReadFile calls return ERROR_NO_DATA_DETECTED, too.
* The call to tape_set_pos seems to reset some internal flags. */
if ((!ioctl (MTIOCPOS, &pos)) && (!pos.mt_blkno))
{
debug_printf ("rewinding");
op.mt_op = MTREW;
ioctl (MTIOCTOP, &op);
}
if (flags & O_APPEND)
{
/* In append mode, seek to beginning of next filemark */
op.mt_op = MTFSFM;
op.mt_count = 1;
ioctl (MTIOCTOP, &op);
}
}
return ret;
}
int
fhandler_dev_tape::close (void)
{
struct mtop op;
int ret = 0;
if (is_writing)
{
ret = writebuf ();
if ((has_written) && (! eom_detected))
{
/* if last operation was writing, write a filemark */
debug_printf ("writing filemark");
op.mt_op = MTWEOF;
op.mt_count = 1;
ioctl (MTIOCTOP, &op);
}
}
// To protected reads on signaling (e.g. Ctrl-C)
eof_detected = 1;
if (is_rewind_device ())
{
debug_printf ("rewinding");
op.mt_op = MTREW;
ioctl (MTIOCTOP, &op);
}
if (ret)
{
fhandler_dev_raw::close ();
return ret;
}
return fhandler_dev_raw::close ();
}
int
fhandler_dev_tape::fstat (struct __stat64 *buf)
{
int ret;
if (!(ret = fhandler_base::fstat (buf)))
{
struct mtget get;
if (!ioctl (MTIOCGET, &get))
buf->st_blocks = get.mt_capacity / buf->st_blksize;
}
return ret;
}
_off64_t
fhandler_dev_tape::lseek (_off64_t offset, int whence)
{
struct mtop op;
struct mtpos pos;
debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
writebuf ();
eom_detected = eof_detected = 0;
lastblk_to_read = 0;
devbufstart = devbufend = 0;
if (ioctl (MTIOCPOS, &pos))
{
return ILLEGAL_SEEK;
}
switch (whence)
{
case SEEK_END:
op.mt_op = MTFSF;
op.mt_count = 1;
if (ioctl (MTIOCTOP, &op))
return -1;
break;
case SEEK_SET:
if (whence == SEEK_SET && offset < 0)
{
set_errno (EINVAL);
return -1;
}
break;
case SEEK_CUR:
break;
default:
set_errno (EINVAL);
return -1;
}
op.mt_op = MTFSR;
op.mt_count = offset / devbufsiz
- (whence == SEEK_SET ? pos.mt_blkno : 0);
if (op.mt_count < 0)
{
op.mt_op = MTBSR;
op.mt_count = -op.mt_count;
}
if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
return -1;
return (pos.mt_blkno * devbufsiz);
}
int
fhandler_dev_tape::dup (fhandler_base *child)
{
fhandler_dev_tape *fhc = (fhandler_dev_tape *) child;
fhc->lasterr = lasterr;
return fhandler_dev_raw::dup (child);
}
int
fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
{
int ret = NO_ERROR;
unsigned long block;
if (cmd == MTIOCTOP)
{
struct mtop *op = (struct mtop *) buf;
if (! op)
ret = ERROR_INVALID_PARAMETER;
else
switch (op->mt_op)
{
case MTRESET:
break;
case MTFSF:
ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count);
break;
case MTBSF:
ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count);
break;
case MTFSR:
ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count);
break;
case MTBSR:
ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count);
break;
case MTWEOF:
if (tape_get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
ret = tape_write_marks (TAPE_FILEMARKS, op->mt_count);
else if (tape_get_feature (TAPE_DRIVE_WRITE_LONG_FMKS))
ret = tape_write_marks (TAPE_LONG_FILEMARKS, op->mt_count);
else
ret = tape_write_marks (TAPE_SHORT_FILEMARKS, op->mt_count);
break;
case MTREW:
ret = tape_set_pos (TAPE_REWIND, 0);
break;
case MTOFFL:
ret = tape_prepare (TAPE_UNLOAD);
break;
case MTNOP:
break;
case MTRETEN:
if (! tape_get_feature (TAPE_DRIVE_END_OF_DATA))
ret = ERROR_INVALID_PARAMETER;
else if (! (ret = tape_set_pos (TAPE_REWIND, 0, false)))
ret = tape_prepare (TAPE_TENSION);
break;
case MTBSFM:
ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count, true);
break;
case MTFSFM:
ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count, true);
break;
case MTEOM:
if (tape_get_feature (TAPE_DRIVE_END_OF_DATA))
ret = tape_set_pos (TAPE_SPACE_END_OF_DATA, 0);
else
ret = tape_set_pos (TAPE_SPACE_FILEMARKS, 32767);
break;
case MTERASE:
ret = tape_erase (TAPE_ERASE_SHORT);
break;
case MTRAS1:
case MTRAS2:
case MTRAS3:
ret = ERROR_INVALID_PARAMETER;
break;
case MTSETBLK:
{
long min, max;
if (! tape_get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
{
ret = ERROR_INVALID_PARAMETER;
break;
}
ret = tape_get_blocksize (&min, NULL, &max, NULL);
if (ret)
break;
if (devbuf && (size_t) op->mt_count == devbufsiz && !varblkop)
{
ret = 0;
break;
}
if ((op->mt_count == 0
&& !tape_get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
|| (op->mt_count > 0
&& (op->mt_count < min || op->mt_count > max)))
{
ret = ERROR_INVALID_PARAMETER;
break;
}
if (devbuf && op->mt_count > 0
&& (size_t) op->mt_count < devbufend - devbufstart)
{
ret = ERROR_MORE_DATA;
break;
}
if (! (ret = tape_set_blocksize (op->mt_count)))
{
size_t size = 0;
if (op->mt_count == 0)
{
struct mtget get;
if ((ret = tape_status (&get)) != NO_ERROR)
break;
size = get.mt_maxblksize;
ret = NO_ERROR;
}
char *buf = NULL;
if (size > 1L && !(buf = new char [size]))
{
ret = ERROR_OUTOFMEMORY;
break;
}
if (devbufsiz > 1L && size > 1L)
{
memcpy (buf, devbuf + devbufstart,
devbufend - devbufstart);
devbufend -= devbufstart;
}
else
devbufend = 0;
if (devbufsiz > 1L)
delete [] devbuf;
devbufstart = 0;
devbuf = buf;
devbufsiz = size;
varblkop = op->mt_count == 0;
}
}
break;
case MTSETDENSITY:
ret = ERROR_INVALID_PARAMETER;
break;
case MTSEEK:
if (tape_get_feature (TAPE_DRIVE_ABSOLUTE_BLK))
{
ret = tape_set_pos (TAPE_ABSOLUTE_BLOCK, op->mt_count);
break;
}
if (! (ret = tape_get_pos (&block)))
{
ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS,
op->mt_count - block);
}
break;
case MTTELL:
if (! (ret = tape_get_pos (&block)))
op->mt_count = block;
break;
case MTSETDRVBUFFER:
ret = ERROR_INVALID_PARAMETER;
break;
case MTFSS:
ret = tape_set_pos (TAPE_SPACE_SETMARKS, op->mt_count);
break;
case MTBSS:
ret = tape_set_pos (TAPE_SPACE_SETMARKS, -op->mt_count);
break;
case MTWSM:
ret = tape_write_marks (TAPE_SETMARKS, op->mt_count);
break;
case MTLOCK:
ret = tape_prepare (TAPE_LOCK);
break;
case MTUNLOCK:
ret = tape_prepare (TAPE_UNLOCK);
break;
case MTLOAD:
ret = tape_prepare (TAPE_LOAD);
break;
case MTUNLOAD:
ret = tape_prepare (TAPE_UNLOAD);
break;
case MTCOMPRESSION:
ret = tape_compression (op->mt_count);
break;
case MTSETPART:
case MTMKPART:
default:
ret = ERROR_INVALID_PARAMETER;
break;
}
}
else if (cmd == MTIOCGET)
ret = tape_status ((struct mtget *) buf);
else if (cmd == MTIOCPOS)
{
ret = ERROR_INVALID_PARAMETER;
if (buf && (ret = tape_get_pos (&block)))
((struct mtpos *) buf)->mt_blkno = block;
}
else
return fhandler_dev_raw::ioctl (cmd, buf);
if (ret != NO_ERROR)
{
SetLastError (ret);
__seterrno ();
return -1;
}
return 0;
}
/* ------------------------------------------------------------------ */
/* Private functions used by `ioctl' */
/* ------------------------------------------------------------------ */
static int
tape_error (DWORD lasterr, const char *txt)
{
if (lasterr)
debug_printf ("%s: error: %d", txt, lasterr);
return lasterr;
}
int
fhandler_dev_tape::tape_write_marks (int marktype, DWORD len)
{
syscall_printf ("write_tapemark");
while (((lasterr = WriteTapemark (get_handle (),
marktype,
len,
FALSE)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
return tape_error (lasterr, "tape_write_marks");
}
int
fhandler_dev_tape::tape_get_pos (unsigned long *ret)
{
DWORD part, low, high;
while (((lasterr = GetTapePosition (get_handle (),
TAPE_ABSOLUTE_POSITION,
&part,
&low,
&high)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
if (! tape_error (lasterr, "tape_get_pos") && ret)
*ret = low;
return lasterr;
}
static int _tape_set_pos (HANDLE hTape, int mode, long count)
{
int err;
while (((err = SetTapePosition (hTape,
mode,
1,
count,
count < 0 ? -1 : 0,
FALSE)) == ERROR_MEDIA_CHANGED)
|| (err == ERROR_BUS_RESET))
;
return err;
}
int
fhandler_dev_tape::tape_set_pos (int mode, long count, bool sfm_func)
{
unsigned long pos, tgtpos;
switch (mode)
{
case TAPE_SPACE_RELATIVE_BLOCKS:
lasterr = tape_get_pos (&pos);
if (lasterr)
return lasterr;
tgtpos = pos + count;
while (((lasterr = _tape_set_pos (get_handle (),
mode,
count)) == ERROR_FILEMARK_DETECTED)
|| (lasterr == ERROR_SETMARK_DETECTED))
{
lasterr = tape_get_pos (&pos);
if (lasterr)
return lasterr;
count = tgtpos - pos;
}
if (lasterr == ERROR_BEGINNING_OF_MEDIA && ! tgtpos)
lasterr = NO_ERROR;
break;
case TAPE_SPACE_FILEMARKS:
if (count < 0)
{
if (pos > 0)
{
if ((! _tape_set_pos (get_handle (),
TAPE_SPACE_RELATIVE_BLOCKS,
-1))
|| (sfm_func))
++count;
_tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, 1);
}
while (! (lasterr = _tape_set_pos (get_handle (), mode, -1))
&& count++ < 0)
;
if (lasterr == ERROR_BEGINNING_OF_MEDIA)
{
if (! count)
lasterr = NO_ERROR;
}
else if (! sfm_func)
lasterr = _tape_set_pos (get_handle (), mode, 1);
}
else
{
if (sfm_func)
{
if (_tape_set_pos (get_handle (),
TAPE_SPACE_RELATIVE_BLOCKS,
1) == ERROR_FILEMARK_DETECTED)
++count;
_tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, -1);
}
if (! (lasterr = _tape_set_pos (get_handle (), mode, count))
&& sfm_func)
lasterr = _tape_set_pos (get_handle (), mode, -1);
}
break;
case TAPE_SPACE_SETMARKS:
case TAPE_ABSOLUTE_BLOCK:
case TAPE_SPACE_END_OF_DATA:
case TAPE_REWIND:
lasterr = _tape_set_pos (get_handle (), mode, count);
break;
}
return tape_error (lasterr, "tape_set_pos");
}
int
fhandler_dev_tape::tape_erase (int mode)
{
DWORD varlen;
TAPE_GET_DRIVE_PARAMETERS dp;
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_DRIVE_INFORMATION,
(varlen = sizeof dp, &varlen),
&dp)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
switch (mode)
{
case TAPE_ERASE_SHORT:
if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_SHORT))
mode = TAPE_ERASE_LONG;
break;
case TAPE_ERASE_LONG:
if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_LONG))
mode = TAPE_ERASE_SHORT;
break;
}
return tape_error (EraseTape (get_handle (), mode, false), "tape_erase");
}
int
fhandler_dev_tape::tape_prepare (int action)
{
while (((lasterr = PrepareTape (get_handle (),
action,
FALSE)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
return tape_error (lasterr, "tape_prepare");
}
bool
fhandler_dev_tape::tape_get_feature (DWORD parm)
{
DWORD varlen;
TAPE_GET_DRIVE_PARAMETERS dp;
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_DRIVE_INFORMATION,
(varlen = sizeof dp, &varlen),
&dp)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
if (lasterr)
return false;
return ((parm & TAPE_DRIVE_HIGH_FEATURES)
? ((dp.FeaturesHigh & parm) != 0)
: ((dp.FeaturesLow & parm) != 0));
}
int
fhandler_dev_tape::tape_get_blocksize (long *min, long *def, long *max, long *cur)
{
DWORD varlen;
TAPE_GET_DRIVE_PARAMETERS dp;
TAPE_GET_MEDIA_PARAMETERS mp;
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_DRIVE_INFORMATION,
(varlen = sizeof dp, &varlen),
&dp)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
if (lasterr)
return tape_error (lasterr, "tape_get_blocksize");
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_MEDIA_INFORMATION,
(varlen = sizeof dp, &varlen),
&mp)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
if (lasterr)
return tape_error (lasterr, "tape_get_blocksize");
if (min)
*min = (long) dp.MinimumBlockSize;
if (def)
*def = (long) dp.DefaultBlockSize;
if (max)
*max = (long) dp.MaximumBlockSize;
if (cur)
*cur = (long) mp.BlockSize;
return tape_error (lasterr, "tape_get_blocksize");
}
int
fhandler_dev_tape::tape_set_blocksize (long count)
{
long min, max;
TAPE_SET_MEDIA_PARAMETERS mp;
lasterr = tape_get_blocksize (&min, NULL, &max, NULL);
if (lasterr)
return lasterr;
if (count != 0 && (count < min || count > max))
return tape_error (ERROR_INVALID_PARAMETER, "tape_set_blocksize");
mp.BlockSize = count;
return tape_error (SetTapeParameters (get_handle (),
SET_TAPE_MEDIA_INFORMATION,
&mp),
"tape_set_blocksize");
}
static long long
get_ll (PLARGE_INTEGER i)
{
long long l = 0;
l = i->HighPart;
l <<= 32;
l |= i->LowPart;
return l;
}
int
fhandler_dev_tape::tape_status (struct mtget *get)
{
DWORD varlen;
TAPE_GET_DRIVE_PARAMETERS dp;
TAPE_GET_MEDIA_PARAMETERS mp;
int notape = 0;
if (! get)
return ERROR_INVALID_PARAMETER;
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_DRIVE_INFORMATION,
(varlen = sizeof dp, &varlen),
&dp)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
/* Setting varlen to sizeof DP is by intention, actually! Never set
it to sizeof MP which seems to be more correct but results in a
ERROR_MORE_DATA error at least on W2K. */
if ((lasterr) || (lasterr = GetTapeParameters (get_handle (),
GET_TAPE_MEDIA_INFORMATION,
(varlen = sizeof dp, &varlen),
&mp)))
notape = 1;
memset (get, 0, sizeof *get);
get->mt_type = MT_ISUNKNOWN;
if (! notape && (dp.FeaturesLow & TAPE_DRIVE_TAPE_REMAINING))
{
get->mt_remaining = get_ll (&mp.Remaining);
get->mt_resid = get->mt_remaining >> 10;
}
if ((dp.FeaturesHigh & TAPE_DRIVE_SET_BLOCK_SIZE) && ! notape)
get->mt_dsreg = mp.BlockSize;
else
get->mt_dsreg = dp.DefaultBlockSize;
if (notape)
get->mt_gstat |= GMT_DR_OPEN (-1);
if (! notape)
{
if (dp.FeaturesLow & TAPE_DRIVE_GET_ABSOLUTE_BLK)
tape_get_pos ((unsigned long *) &get->mt_blkno);
if (! get->mt_blkno)
get->mt_gstat |= GMT_BOT (-1);
get->mt_gstat |= GMT_ONLINE (-1);
if ((dp.FeaturesLow & TAPE_DRIVE_WRITE_PROTECT) && mp.WriteProtected)
get->mt_gstat |= GMT_WR_PROT (-1);
if (dp.FeaturesLow & TAPE_DRIVE_TAPE_CAPACITY)
get->mt_capacity = get_ll (&mp.Capacity);
}
if ((dp.FeaturesLow & TAPE_DRIVE_COMPRESSION) && dp.Compression)
get->mt_gstat |= GMT_HW_COMP (-1);
if ((dp.FeaturesLow & TAPE_DRIVE_ECC) && dp.ECC)
get->mt_gstat |= GMT_HW_ECC (-1);
if ((dp.FeaturesLow & TAPE_DRIVE_PADDING) && dp.DataPadding)
get->mt_gstat |= GMT_PADDING (-1);
if ((dp.FeaturesLow & TAPE_DRIVE_REPORT_SMKS) && dp.ReportSetmarks)
get->mt_gstat |= GMT_IM_REP_EN (-1);
get->mt_erreg = lasterr;
get->mt_minblksize = dp.MinimumBlockSize;
get->mt_maxblksize = dp.MaximumBlockSize;
get->mt_defblksize = dp.DefaultBlockSize;
get->mt_featureslow = dp.FeaturesLow;
get->mt_featureshigh = dp.FeaturesHigh;
get->mt_eotwarningzonesize = dp.EOTWarningZoneSize;
return 0;
}
int
fhandler_dev_tape::tape_compression (long count)
{
DWORD varlen;
TAPE_GET_DRIVE_PARAMETERS dpg;
TAPE_SET_DRIVE_PARAMETERS dps;
while (((lasterr = GetTapeParameters (get_handle (),
GET_TAPE_DRIVE_INFORMATION,
(varlen = sizeof dpg, &varlen),
&dpg)) == ERROR_MEDIA_CHANGED)
|| (lasterr == ERROR_BUS_RESET))
;
if (lasterr)
return tape_error (lasterr, "tape_compression");
if (! (dpg.FeaturesLow & TAPE_DRIVE_COMPRESSION))
return ERROR_INVALID_PARAMETER;
if (count)
{
dps.ECC = dpg.ECC;
dps.Compression = count ? TRUE : FALSE;
dps.DataPadding = dpg.DataPadding;
dps.ReportSetmarks = dpg.ReportSetmarks;
dps.EOTWarningZoneSize = dpg.EOTWarningZoneSize;
lasterr = SetTapeParameters (get_handle (),
SET_TAPE_DRIVE_INFORMATION,
&dps);
if (lasterr)
return tape_error (lasterr, "tape_compression");
dpg.Compression = dps.Compression;
}
return 0;
}