1531 lines
39 KiB
C++
1531 lines
39 KiB
C++
/* fhandler_tape.cc. See fhandler.h for a description of the fhandler
|
|
classes.
|
|
|
|
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 "cygtls.h"
|
|
#include <stdlib.h>
|
|
#include <sys/mtio.h>
|
|
#include <sys/param.h>
|
|
#include <devioctl.h>
|
|
#include <ntddstor.h>
|
|
#include "security.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "shared_info.h"
|
|
#include "sigproc.h"
|
|
#include "child_info.h"
|
|
|
|
/* Media changes and bus resets are sometimes reported and the function
|
|
hasn't been executed. We repeat all functions which return with one
|
|
of these error codes. */
|
|
#define TAPE_FUNC(func) while ((lasterr = (func)) == ERROR_MEDIA_CHANGED) \
|
|
{ \
|
|
initialize (drive, false); \
|
|
part (partition)->initialize (0); \
|
|
}
|
|
|
|
/* Certain tape drives (known example: QUANTUM_ULTRIUM-HH6) return
|
|
ERROR_NOT_READY rather than ERROR_NO_MEDIA_IN_DRIVE if no media
|
|
is in the drive. Convert this to the only sane error code. */
|
|
#define GET_TAPE_STATUS(_h) ({ \
|
|
int _err = GetTapeStatus (_h); \
|
|
if (_err == ERROR_NOT_READY) \
|
|
_err = ERROR_NO_MEDIA_IN_DRIVE; \
|
|
_err; \
|
|
})
|
|
|
|
#define IS_BOT(err) ((err) == ERROR_BEGINNING_OF_MEDIA)
|
|
|
|
#define IS_EOF(err) ((err) == ERROR_FILEMARK_DETECTED \
|
|
|| (err) == ERROR_SETMARK_DETECTED)
|
|
|
|
#define IS_SM(err) ((err) == ERROR_SETMARK_DETECTED)
|
|
|
|
#define IS_EOD(err) ((err) == ERROR_END_OF_MEDIA \
|
|
|| (err) == ERROR_EOM_OVERFLOW \
|
|
|| (err) == ERROR_NO_DATA_DETECTED)
|
|
|
|
#define IS_EOM(err) ((err) == ERROR_END_OF_MEDIA \
|
|
|| (err) == ERROR_EOM_OVERFLOW)
|
|
|
|
/**********************************************************************/
|
|
/* mtinfo_part */
|
|
|
|
void
|
|
mtinfo_part::initialize (int64_t nblock)
|
|
{
|
|
block = nblock;
|
|
if (block == 0)
|
|
file = fblock = 0;
|
|
else
|
|
file = fblock = -1;
|
|
smark = false;
|
|
emark = no_eof;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* mtinfo_drive */
|
|
|
|
void
|
|
mtinfo_drive::initialize (int num, bool first_time)
|
|
{
|
|
drive = num;
|
|
partition = 0;
|
|
block = -1;
|
|
lock = unlocked;
|
|
if (first_time)
|
|
{
|
|
buffer_writes (true);
|
|
async_writes (false);
|
|
two_fm (false);
|
|
fast_eom (false);
|
|
auto_lock (false);
|
|
sysv (false);
|
|
nowait (false);
|
|
}
|
|
for (int i = 0; i < MAX_PARTITION_NUM; ++i)
|
|
part (i)->initialize ();
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::get_dp (HANDLE mt)
|
|
{
|
|
DWORD len = sizeof _dp;
|
|
TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_DRIVE_INFORMATION, &len, &_dp));
|
|
return error ("get_dp");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::get_mp (HANDLE mt)
|
|
{
|
|
DWORD len = sizeof _mp;
|
|
TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_MEDIA_INFORMATION, &len, &_mp));
|
|
/* See GET_TAPE_STATUS comment. */
|
|
if (lasterr == ERROR_NOT_READY)
|
|
lasterr = ERROR_NO_MEDIA_IN_DRIVE;
|
|
return error ("get_mp");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::open (HANDLE mt)
|
|
{
|
|
/* First access after opening the device can return BUS RESET, but we
|
|
need the drive parameters, so just try again. */
|
|
while (get_dp (mt) == ERROR_BUS_RESET)
|
|
;
|
|
get_mp (mt);
|
|
get_pos (mt);
|
|
if (partition < MAX_PARTITION_NUM && part (partition)->block != block)
|
|
part (partition)->initialize (block);
|
|
/* The following rewind in position 0 solves a problem which appears
|
|
* in case of multi volume archives (at least on NT4): The last ReadFile
|
|
* on the previous medium returns ERROR_NO_DATA_DETECTED. After media
|
|
* change, all subsequent ReadFile calls return ERROR_NO_DATA_DETECTED,
|
|
* too. The call to set_pos apparently reset some internal flags.
|
|
* FIXME: Is that really true or based on a misinterpretation? */
|
|
if (!block)
|
|
{
|
|
debug_printf ("rewind in position 0");
|
|
set_pos (mt, TAPE_REWIND, 0, false);
|
|
}
|
|
return error ("open");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::close (HANDLE mt, bool rewind)
|
|
{
|
|
lasterr = 0;
|
|
if (GET_TAPE_STATUS (mt) == ERROR_NO_MEDIA_IN_DRIVE)
|
|
dirty = clean;
|
|
if (dirty >= has_written)
|
|
{
|
|
/* If an async write is still pending, wait for completion. */
|
|
if (dirty == async_write_pending)
|
|
lasterr = async_wait (mt, NULL);
|
|
if (!lasterr)
|
|
{
|
|
/* if last operation was writing, write a filemark */
|
|
debug_printf ("writing filemark");
|
|
write_marks (mt, TAPE_FILEMARKS, two_fm () ? 2 : 1);
|
|
if (two_fm () && !lasterr && !rewind) /* Backspace over 2nd fmark. */
|
|
{
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
|
|
if (!lasterr)
|
|
part (partition)->fblock = 0; /* That's obvious, isn't it? */
|
|
}
|
|
}
|
|
}
|
|
else if (dirty == has_read && !rewind)
|
|
{
|
|
if (sysv ())
|
|
{
|
|
/* Under SYSV semantics, the tape is moved past the next file mark
|
|
after read. */
|
|
if (part (partition)->emark == no_eof)
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, 1, false);
|
|
else if (part (partition)->emark == eof_hit)
|
|
part (partition)->emark = eof;
|
|
}
|
|
else
|
|
{
|
|
/* Under BSD semantics, we must check if the filemark has been
|
|
inadvertendly crossed. If so cross the filemark backwards
|
|
and position the tape right before EOF. */
|
|
if (part (partition)->emark == eof_hit)
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
|
|
}
|
|
}
|
|
if (rewind)
|
|
{
|
|
debug_printf ("rewinding");
|
|
set_pos (mt, TAPE_REWIND, 0, false);
|
|
}
|
|
if (auto_lock () && lock == auto_locked)
|
|
prepare (mt, TAPE_UNLOCK);
|
|
dirty = clean;
|
|
return error ("close");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::read (HANDLE mt, LPOVERLAPPED pov, void *ptr, size_t &ulen)
|
|
{
|
|
BOOL ret;
|
|
DWORD bytes_read = 0;
|
|
|
|
if (GET_TAPE_STATUS (mt) == ERROR_NO_MEDIA_IN_DRIVE)
|
|
return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
|
|
if (lasterr == ERROR_BUS_RESET)
|
|
{
|
|
ulen = 0;
|
|
goto out;
|
|
}
|
|
/* If an async write is still pending, wait for completion. */
|
|
if (dirty == async_write_pending)
|
|
lasterr = async_wait (mt, NULL);
|
|
dirty = clean;
|
|
if (part (partition)->emark == eof_hit)
|
|
{
|
|
part (partition)->emark = eof;
|
|
lasterr = ulen = 0;
|
|
goto out;
|
|
}
|
|
else if (part (partition)->emark == eod_hit)
|
|
{
|
|
part (partition)->emark = eod;
|
|
lasterr = ulen = 0;
|
|
goto out;
|
|
}
|
|
else if (part (partition)->emark == eod)
|
|
{
|
|
lasterr = ERROR_NO_DATA_DETECTED;
|
|
ulen = (size_t) -1;
|
|
goto out;
|
|
}
|
|
else if (part (partition)->emark == eom_hit)
|
|
{
|
|
part (partition)->emark = eom;
|
|
lasterr = ulen = 0;
|
|
goto out;
|
|
}
|
|
else if (part (partition)->emark == eom)
|
|
{
|
|
lasterr = ERROR_END_OF_MEDIA;
|
|
ulen = (size_t) -1;
|
|
goto out;
|
|
}
|
|
part (partition)->smark = false;
|
|
if (auto_lock () && lock < auto_locked)
|
|
prepare (mt, TAPE_LOCK, true);
|
|
ov = pov;
|
|
ov->Offset = ov->OffsetHigh = 0;
|
|
ret = ReadFile (mt, ptr, ulen, &bytes_read, ov);
|
|
lasterr = ret ? 0 : GetLastError ();
|
|
if (lasterr == ERROR_IO_PENDING)
|
|
lasterr = async_wait (mt, &bytes_read);
|
|
ulen = (size_t) bytes_read;
|
|
if (bytes_read > 0)
|
|
{
|
|
int32_t blocks_read = mp ()->BlockSize == 0
|
|
? 1 : howmany (bytes_read, mp ()->BlockSize);
|
|
block += blocks_read;
|
|
part (partition)->block += blocks_read;
|
|
if (part (partition)->fblock >= 0)
|
|
part (partition)->fblock += blocks_read;
|
|
}
|
|
if (IS_EOF (lasterr))
|
|
{
|
|
block++;
|
|
part (partition)->block++;
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file++;
|
|
part (partition)->fblock = 0;
|
|
part (partition)->smark = IS_SM (lasterr);
|
|
part (partition)->emark = bytes_read > 0 ? eof_hit : eof;
|
|
lasterr = 0;
|
|
}
|
|
else if (IS_EOD (lasterr))
|
|
{
|
|
if (part (partition)->emark == eof)
|
|
part (partition)->emark = IS_EOM (lasterr) ? eom : eod;
|
|
else
|
|
{
|
|
part (partition)->emark = IS_EOM (lasterr) ? eom_hit : eod_hit;
|
|
lasterr = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
part (partition)->emark = no_eof;
|
|
/* This happens if the buffer is too small when in variable block
|
|
size mode. Linux returns ENOMEM here. We're doing the same. */
|
|
if (lasterr == ERROR_MORE_DATA)
|
|
lasterr = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
if (!lasterr)
|
|
dirty = has_read;
|
|
out:
|
|
return error ("read");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::async_wait (HANDLE mt, DWORD *bytes_written)
|
|
{
|
|
DWORD written;
|
|
|
|
bool ret = GetOverlappedResult (mt, ov, &written, TRUE);
|
|
if (bytes_written)
|
|
*bytes_written = written;
|
|
return ret ? 0 : GetLastError ();
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::write (HANDLE mt, LPOVERLAPPED pov, const void *ptr, size_t &len)
|
|
{
|
|
BOOL ret;
|
|
DWORD bytes_written = 0;
|
|
int async_err = 0;
|
|
|
|
if (GET_TAPE_STATUS (mt) == ERROR_NO_MEDIA_IN_DRIVE)
|
|
return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
|
|
if (lasterr == ERROR_BUS_RESET)
|
|
{
|
|
len = 0;
|
|
return error ("write");
|
|
}
|
|
if (dirty == async_write_pending)
|
|
async_err = async_wait (mt, &bytes_written);
|
|
dirty = clean;
|
|
part (partition)->smark = false;
|
|
if (auto_lock () && lock < auto_locked)
|
|
prepare (mt, TAPE_LOCK, true);
|
|
ov = pov;
|
|
ov->Offset = ov->OffsetHigh = 0;
|
|
ret = WriteFile (mt, ptr, len, &bytes_written, ov);
|
|
lasterr = ret ? 0: GetLastError ();
|
|
if (lasterr == ERROR_IO_PENDING)
|
|
{
|
|
if (async_writes () && mp ()->BlockSize == 0)
|
|
dirty = async_write_pending;
|
|
else
|
|
/* Wait for completion if a non-async write. */
|
|
lasterr = async_wait (mt, &bytes_written);
|
|
}
|
|
len = (size_t) bytes_written;
|
|
if (bytes_written > 0)
|
|
{
|
|
int32_t blocks_written = mp ()->BlockSize == 0
|
|
? 1 : howmany (bytes_written, mp ()->BlockSize);
|
|
block += blocks_written;
|
|
part (partition)->block += blocks_written;
|
|
if (part (partition)->fblock >= 0)
|
|
part (partition)->fblock += blocks_written;
|
|
}
|
|
if (!lasterr && async_err)
|
|
lasterr = async_err;
|
|
if (lasterr == ERROR_EOM_OVERFLOW)
|
|
part (partition)->emark = eom;
|
|
else if (lasterr == ERROR_END_OF_MEDIA)
|
|
; // FIXME?: part (partition)->emark = eom_hit;
|
|
else
|
|
{
|
|
part (partition)->emark = no_eof;
|
|
if (!lasterr)
|
|
dirty = has_written;
|
|
else if (lasterr == ERROR_IO_PENDING)
|
|
dirty = async_write_pending;
|
|
}
|
|
return error ("write");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::get_pos (HANDLE mt, int32_t *ppartition, int64_t *pblock)
|
|
{
|
|
DWORD p;
|
|
ULARGE_INTEGER b;
|
|
|
|
TAPE_FUNC (GetTapePosition (mt, TAPE_LOGICAL_POSITION, &p,
|
|
&b.LowPart, &b.HighPart));
|
|
if (lasterr == ERROR_INVALID_FUNCTION)
|
|
TAPE_FUNC (GetTapePosition (mt, TAPE_ABSOLUTE_POSITION, &p,
|
|
&b.LowPart, &b.HighPart));
|
|
if (!lasterr)
|
|
{
|
|
if (p > 0)
|
|
partition = (int32_t) p - 1;
|
|
block = (int64_t) b.QuadPart;
|
|
if (ppartition)
|
|
*ppartition= partition;
|
|
if (pblock)
|
|
*pblock = block;
|
|
}
|
|
else
|
|
{
|
|
partition = 0;
|
|
block = -1;
|
|
}
|
|
return error ("get_pos");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::_set_pos (HANDLE mt, int mode, int64_t count, int partition,
|
|
BOOL dont_wait)
|
|
{
|
|
/* If an async write is still pending, wait for completion. */
|
|
if (dirty == async_write_pending)
|
|
lasterr = async_wait (mt, NULL);
|
|
dirty = clean;
|
|
LARGE_INTEGER c = { QuadPart:count };
|
|
TAPE_FUNC (SetTapePosition (mt, mode, partition, c.LowPart, c.HighPart,
|
|
dont_wait));
|
|
return lasterr;
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::set_pos (HANDLE mt, int mode, int64_t count, bool sfm_func)
|
|
{
|
|
int err = 0;
|
|
int64_t undone = count;
|
|
BOOL dont_wait = FALSE;
|
|
|
|
switch (mode)
|
|
{
|
|
case TAPE_SPACE_RELATIVE_BLOCKS:
|
|
case TAPE_SPACE_FILEMARKS:
|
|
case TAPE_SPACE_SETMARKS:
|
|
if (!count)
|
|
{
|
|
lasterr = 0;
|
|
goto out;
|
|
}
|
|
break;
|
|
case TAPE_ABSOLUTE_BLOCK:
|
|
case TAPE_LOGICAL_BLOCK:
|
|
case TAPE_REWIND:
|
|
dont_wait = nowait () ? TRUE : FALSE;
|
|
break;
|
|
}
|
|
if (mode == TAPE_SPACE_FILEMARKS)
|
|
{
|
|
while (!err && undone > 0)
|
|
if (!(err = _set_pos (mt, mode, 1, 0, FALSE)) || IS_SM (err))
|
|
--undone;
|
|
while (!err && undone < 0)
|
|
if (!(err = _set_pos (mt, mode, -1, 0, FALSE)) || IS_SM (err))
|
|
++undone;
|
|
}
|
|
else
|
|
err = _set_pos (mt, mode, count, 0, dont_wait);
|
|
switch (mode)
|
|
{
|
|
case TAPE_ABSOLUTE_BLOCK:
|
|
case TAPE_LOGICAL_BLOCK:
|
|
get_pos (mt);
|
|
part (partition)->initialize (block);
|
|
break;
|
|
case TAPE_REWIND:
|
|
if (!err)
|
|
{
|
|
block = 0;
|
|
part (partition)->initialize (0);
|
|
}
|
|
else
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->initialize (block);
|
|
}
|
|
break;
|
|
case TAPE_SPACE_END_OF_DATA:
|
|
get_pos (mt);
|
|
part (partition)->initialize (block);
|
|
part (partition)->emark = IS_EOM (err) ? eom : eod;
|
|
break;
|
|
case TAPE_SPACE_FILEMARKS:
|
|
if (!err || IS_SM (err))
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->block = block;
|
|
if (count > 0)
|
|
{
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file += count - undone;
|
|
part (partition)->fblock = 0;
|
|
part (partition)->smark = IS_SM (err);
|
|
}
|
|
else
|
|
{
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file += count - undone;
|
|
part (partition)->fblock = -1;
|
|
part (partition)->smark = false;
|
|
}
|
|
if (sfm_func)
|
|
err = set_pos (mt, mode, count > 0 ? -1 : 1, false);
|
|
else
|
|
part (partition)->emark = count > 0 ? eof : no_eof;
|
|
}
|
|
else if (IS_EOD (err))
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->block = block;
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file += count - undone;
|
|
part (partition)->fblock = -1;
|
|
part (partition)->smark = false;
|
|
part (partition)->emark = IS_EOM (err) ? eom : eod;
|
|
}
|
|
else if (IS_BOT (err))
|
|
{
|
|
block = 0;
|
|
part (partition)->initialize (0);
|
|
}
|
|
else
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->initialize (block);
|
|
}
|
|
break;
|
|
case TAPE_SPACE_RELATIVE_BLOCKS:
|
|
if (!err)
|
|
{
|
|
block += count;
|
|
part (partition)->block += count;
|
|
if (part (partition)->fblock >= 0)
|
|
part (partition)->fblock += count;
|
|
part (partition)->smark = false;
|
|
part (partition)->emark = no_eof;
|
|
}
|
|
else if (IS_EOF (err))
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->block = block;
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file += count > 0 ? 1 : -1;
|
|
part (partition)->fblock = count > 0 ? 0 : -1;
|
|
part (partition)->smark = (count > 0 && IS_SM (err));
|
|
part (partition)->emark = count > 0 ? eof : no_eof;
|
|
}
|
|
else if (IS_EOD (err))
|
|
{
|
|
get_pos (mt);
|
|
part (partition)->fblock = block - part (partition)->block;
|
|
part (partition)->block = block;
|
|
part (partition)->smark = false;
|
|
part (partition)->emark = IS_EOM (err) ? eom : eod;
|
|
}
|
|
else if (IS_BOT (err))
|
|
{
|
|
block = 0;
|
|
part (partition)->initialize (0);
|
|
}
|
|
break;
|
|
case TAPE_SPACE_SETMARKS:
|
|
get_pos (mt);
|
|
part (partition)->block = block;
|
|
if (!err)
|
|
{
|
|
part (partition)->file = -1;
|
|
part (partition)->fblock = -1;
|
|
part (partition)->smark = true;
|
|
}
|
|
break;
|
|
}
|
|
lasterr = err;
|
|
out:
|
|
return error ("set_pos");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::create_partitions (HANDLE mt, int32_t count)
|
|
{
|
|
if (dp ()->MaximumPartitionCount <= 1)
|
|
return ERROR_INVALID_PARAMETER;
|
|
if (set_pos (mt, TAPE_REWIND, 0, false))
|
|
goto out;
|
|
partition = 0;
|
|
part (partition)->initialize (0);
|
|
debug_printf ("Format tape with %s partition(s)", count <= 0 ? "one" : "two");
|
|
if (get_feature (TAPE_DRIVE_INITIATOR))
|
|
{
|
|
TAPE_FUNC (CreateTapePartition (mt, TAPE_INITIATOR_PARTITIONS,
|
|
count <= 0 ? 0 : 2, (DWORD) count));
|
|
}
|
|
else if (get_feature (TAPE_DRIVE_SELECT))
|
|
{
|
|
TAPE_FUNC (CreateTapePartition (mt, TAPE_SELECT_PARTITIONS,
|
|
count <= 0 ? 0 : 2, 0));
|
|
}
|
|
else if (get_feature (TAPE_DRIVE_FIXED))
|
|
{
|
|
/* This is supposed to work for Tandberg SLR drivers up to version
|
|
1.6 which missed to set the TAPE_DRIVE_INITIATOR flag. According
|
|
to Tandberg, CreateTapePartition(TAPE_FIXED_PARTITIONS) apparently
|
|
does not ignore the dwCount parameter. Go figure! */
|
|
TAPE_FUNC (CreateTapePartition (mt, TAPE_FIXED_PARTITIONS,
|
|
count <= 0 ? 0 : 2, (DWORD) count));
|
|
}
|
|
else
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
out:
|
|
return error ("partition");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::set_partition (HANDLE mt, int32_t count)
|
|
{
|
|
if (count < 0 || (uint32_t) count >= MAX_PARTITION_NUM)
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
else if ((DWORD) count >= dp ()->MaximumPartitionCount)
|
|
lasterr = ERROR_IO_DEVICE;
|
|
else
|
|
{
|
|
uint64_t part_block = part (count)->block >= 0 ? part (count)->block : 0;
|
|
int err = _set_pos (mt, TAPE_LOGICAL_BLOCK, part_block, count + 1, FALSE);
|
|
if (err)
|
|
{
|
|
int64_t sav_block = block;
|
|
int32_t sav_partition = partition;
|
|
get_pos (mt);
|
|
if (sav_partition != partition)
|
|
{
|
|
if (partition < MAX_PARTITION_NUM
|
|
&& part (partition)->block != block)
|
|
part (partition)->initialize (block);
|
|
}
|
|
else if (sav_block != block && partition < MAX_PARTITION_NUM)
|
|
part (partition)->initialize (block);
|
|
lasterr = err;
|
|
}
|
|
else
|
|
{
|
|
partition = count;
|
|
if (part (partition)->block == -1)
|
|
part (partition)->initialize (0);
|
|
}
|
|
}
|
|
return error ("set_partition");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::write_marks (HANDLE mt, int marktype, DWORD count)
|
|
{
|
|
/* If an async write is still pending, wait for completion. */
|
|
if (dirty == async_write_pending)
|
|
{
|
|
lasterr = async_wait (mt, NULL);
|
|
dirty = has_written;
|
|
}
|
|
if (marktype != TAPE_SETMARKS)
|
|
dirty = clean;
|
|
if (marktype == TAPE_FILEMARKS
|
|
&& !get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
|
|
{
|
|
if (get_feature (TAPE_DRIVE_WRITE_LONG_FMKS))
|
|
marktype = TAPE_LONG_FILEMARKS;
|
|
else
|
|
marktype = TAPE_SHORT_FILEMARKS;
|
|
}
|
|
TAPE_FUNC (WriteTapemark (mt, marktype, count, FALSE));
|
|
int err = lasterr;
|
|
if (!err)
|
|
{
|
|
block += count;
|
|
part (partition)->block += count;
|
|
if (part (partition)->file >= 0)
|
|
part (partition)->file += count;
|
|
part (partition)->fblock = 0;
|
|
part (partition)->emark = eof;
|
|
part (partition)->smark = (marktype == TAPE_SETMARKS);
|
|
}
|
|
else
|
|
{
|
|
int64_t sav_block = block;
|
|
int32_t sav_partition = partition;
|
|
get_pos (mt);
|
|
if (sav_partition != partition)
|
|
{
|
|
if (partition < MAX_PARTITION_NUM
|
|
&& part (partition)->block != block)
|
|
part (partition)->initialize (block);
|
|
}
|
|
else if (sav_block != block && partition < MAX_PARTITION_NUM)
|
|
part (partition)->initialize (block);
|
|
lasterr = err;
|
|
}
|
|
return error ("write_marks");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::erase (HANDLE mt, int mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case TAPE_ERASE_SHORT:
|
|
if (!get_feature (TAPE_DRIVE_ERASE_SHORT))
|
|
mode = TAPE_ERASE_LONG;
|
|
break;
|
|
case TAPE_ERASE_LONG:
|
|
if (!get_feature (TAPE_DRIVE_ERASE_LONG))
|
|
mode = TAPE_ERASE_SHORT;
|
|
break;
|
|
}
|
|
TAPE_FUNC (EraseTape (mt, mode, nowait () ? TRUE : FALSE));
|
|
part (partition)->initialize (0);
|
|
return error ("erase");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::prepare (HANDLE mt, int action, bool is_auto)
|
|
{
|
|
BOOL dont_wait = FALSE;
|
|
|
|
/* If an async write is still pending, wait for completion. */
|
|
if (dirty == async_write_pending)
|
|
lasterr = async_wait (mt, NULL);
|
|
dirty = clean;
|
|
if (action == TAPE_UNLOAD || action == TAPE_LOAD || action == TAPE_TENSION)
|
|
dont_wait = nowait () ? TRUE : FALSE;
|
|
TAPE_FUNC (PrepareTape (mt, action, dont_wait));
|
|
/* Reset buffer after all successful preparations but lock and unlock. */
|
|
switch (action)
|
|
{
|
|
case TAPE_FORMAT:
|
|
case TAPE_UNLOAD:
|
|
case TAPE_LOAD:
|
|
initialize (drive, false);
|
|
break;
|
|
case TAPE_TENSION:
|
|
part (partition)->initialize (0);
|
|
break;
|
|
case TAPE_LOCK:
|
|
lock = lasterr ? lock_error : is_auto ? auto_locked : locked;
|
|
break;
|
|
case TAPE_UNLOCK:
|
|
lock = lasterr ? lock_error : unlocked;
|
|
break;
|
|
}
|
|
return error ("prepare");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::set_compression (HANDLE mt, int32_t count)
|
|
{
|
|
if (!get_feature (TAPE_DRIVE_SET_COMPRESSION))
|
|
return ERROR_INVALID_PARAMETER;
|
|
TAPE_SET_DRIVE_PARAMETERS sdp =
|
|
{
|
|
dp ()->ECC,
|
|
(BOOLEAN) (count ? TRUE : FALSE),
|
|
dp ()->DataPadding,
|
|
dp ()->ReportSetmarks,
|
|
dp ()->EOTWarningZoneSize
|
|
};
|
|
TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
|
|
int err = lasterr;
|
|
if (!err)
|
|
dp ()->Compression = sdp.Compression;
|
|
else
|
|
get_dp (mt);
|
|
lasterr = err;
|
|
return error ("set_compression");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::set_blocksize (HANDLE mt, DWORD count)
|
|
{
|
|
TAPE_SET_MEDIA_PARAMETERS smp = {count};
|
|
TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_MEDIA_INFORMATION, &smp));
|
|
/* Make sure to update blocksize info! */
|
|
return lasterr ? error ("set_blocksize") : get_mp (mt);
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::get_status (HANDLE mt, struct mtget *get)
|
|
{
|
|
int notape = 0;
|
|
DWORD tstat;
|
|
|
|
if (!get)
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
tstat = GET_TAPE_STATUS (mt);
|
|
DWORD mpstat = (DWORD) get_mp (mt);
|
|
|
|
debug_printf ("GetTapeStatus: %u, get_mp: %d", tstat, mpstat);
|
|
|
|
if (tstat == ERROR_NO_MEDIA_IN_DRIVE || mpstat == ERROR_NO_MEDIA_IN_DRIVE)
|
|
notape = 1;
|
|
|
|
memset (get, 0, sizeof *get);
|
|
|
|
get->mt_type = MT_ISUNKNOWN;
|
|
|
|
if (!notape && get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
|
|
get->mt_dsreg = (mp ()->BlockSize << MT_ST_BLKSIZE_SHIFT)
|
|
& MT_ST_BLKSIZE_MASK;
|
|
else
|
|
get->mt_dsreg = (dp ()->DefaultBlockSize << MT_ST_BLKSIZE_SHIFT)
|
|
& MT_ST_BLKSIZE_MASK;
|
|
|
|
DWORD size = sizeof (GET_MEDIA_TYPES) + 10 * sizeof (DEVICE_MEDIA_INFO);
|
|
void *buf = alloca (size);
|
|
if (DeviceIoControl (mt, IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
|
|
NULL, 0, buf, size, &size, NULL)
|
|
|| GetLastError () == ERROR_MORE_DATA)
|
|
{
|
|
PGET_MEDIA_TYPES gmt = (PGET_MEDIA_TYPES) buf;
|
|
for (DWORD i = 0; i < gmt->MediaInfoCount; ++i)
|
|
{
|
|
PDEVICE_MEDIA_INFO dmi = &gmt->MediaInfo[i];
|
|
get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
|
|
#define TINFO DeviceSpecific.TapeInfo
|
|
if (dmi->TINFO.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED)
|
|
{
|
|
get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
|
|
if (dmi->TINFO.BusType == BusTypeScsi)
|
|
get->mt_dsreg |=
|
|
(dmi->TINFO.BusSpecificData.ScsiInformation.DensityCode
|
|
<< MT_ST_DENSITY_SHIFT)
|
|
& MT_ST_DENSITY_MASK;
|
|
break;
|
|
}
|
|
#undef TINFO
|
|
}
|
|
}
|
|
|
|
if (!notape)
|
|
{
|
|
get->mt_resid = (partition & 0xffff)
|
|
| ((mp ()->PartitionCount & 0xffff) << 16);
|
|
get->mt_fileno = part (partition)->file;
|
|
get->mt_blkno = part (partition)->fblock;
|
|
|
|
if (get->mt_blkno != 0)
|
|
/* nothing to do */;
|
|
else if (get->mt_fileno == 0)
|
|
get->mt_gstat |= GMT_BOT (-1);
|
|
else
|
|
get->mt_gstat |= GMT_EOF (-1);
|
|
if (part (partition)->emark >= eod_hit)
|
|
get->mt_gstat |= GMT_EOD (-1);
|
|
if (part (partition)->emark >= eom_hit)
|
|
get->mt_gstat |= GMT_EOT (-1);
|
|
|
|
if (part (partition)->smark)
|
|
get->mt_gstat |= GMT_SM (-1);
|
|
|
|
get->mt_gstat |= GMT_ONLINE (-1);
|
|
|
|
if (mp ()->WriteProtected)
|
|
get->mt_gstat |= GMT_WR_PROT (-1);
|
|
|
|
get->mt_capacity = mp ()->Capacity.QuadPart;
|
|
get->mt_remaining = mp ()->Remaining.QuadPart;
|
|
}
|
|
|
|
if (notape)
|
|
get->mt_gstat |= GMT_DR_OPEN (-1);
|
|
|
|
if (buffer_writes ())
|
|
get->mt_gstat |= GMT_IM_REP_EN (-1); /* TODO: Async writes */
|
|
|
|
if (tstat == ERROR_DEVICE_REQUIRES_CLEANING)
|
|
get->mt_gstat |= GMT_CLN (-1);
|
|
|
|
/* Cygwin specials: */
|
|
if (dp ()->ReportSetmarks)
|
|
get->mt_gstat |= GMT_REP_SM (-1);
|
|
if (dp ()->DataPadding)
|
|
get->mt_gstat |= GMT_PADDING (-1);
|
|
if (dp ()->ECC)
|
|
get->mt_gstat |= GMT_HW_ECC (-1);
|
|
if (dp ()->Compression)
|
|
get->mt_gstat |= GMT_HW_COMP (-1);
|
|
if (two_fm ())
|
|
get->mt_gstat |= GMT_TWO_FM (-1);
|
|
if (fast_eom ())
|
|
get->mt_gstat |= GMT_FAST_MTEOM (-1);
|
|
if (auto_lock ())
|
|
get->mt_gstat |= GMT_AUTO_LOCK (-1);
|
|
if (sysv ())
|
|
get->mt_gstat |= GMT_SYSV (-1);
|
|
if (nowait ())
|
|
get->mt_gstat |= GMT_NOWAIT (-1);
|
|
if (async_writes ())
|
|
get->mt_gstat |= GMT_ASYNC (-1);
|
|
|
|
get->mt_erreg = 0; /* FIXME: No softerr counting */
|
|
|
|
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
|
|
mtinfo_drive::set_options (HANDLE mt, uint32_t options)
|
|
{
|
|
uint32_t what = (options & MT_ST_OPTIONS);
|
|
bool call_setparams = false;
|
|
bool set;
|
|
TAPE_SET_DRIVE_PARAMETERS sdp =
|
|
{
|
|
dp ()->ECC,
|
|
dp ()->Compression,
|
|
dp ()->DataPadding,
|
|
dp ()->ReportSetmarks,
|
|
dp ()->EOTWarningZoneSize
|
|
};
|
|
|
|
lasterr = 0;
|
|
switch (what)
|
|
{
|
|
case 0:
|
|
if (options == 0 || options == 1)
|
|
{
|
|
buffer_writes ((options == 1));
|
|
}
|
|
break;
|
|
case MT_ST_BOOLEANS:
|
|
buffer_writes (!!(options & MT_ST_BUFFER_WRITES));
|
|
async_writes (!!(options & MT_ST_ASYNC_WRITES));
|
|
two_fm (!!(options & MT_ST_TWO_FM));
|
|
fast_eom (!!(options & MT_ST_FAST_MTEOM));
|
|
auto_lock (!!(options & MT_ST_AUTO_LOCK));
|
|
sysv (!!(options & MT_ST_SYSV));
|
|
nowait (!!(options & MT_ST_NOWAIT));
|
|
if (get_feature (TAPE_DRIVE_SET_ECC))
|
|
sdp.ECC = !!(options & MT_ST_ECC);
|
|
if (get_feature (TAPE_DRIVE_SET_PADDING))
|
|
sdp.DataPadding = !!(options & MT_ST_PADDING);
|
|
if (get_feature (TAPE_DRIVE_SET_REPORT_SMKS))
|
|
sdp.ReportSetmarks = !!(options & MT_ST_REPORT_SM);
|
|
if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
|
|
|| sdp.ReportSetmarks != dp ()->ReportSetmarks)
|
|
call_setparams = true;
|
|
break;
|
|
case MT_ST_SETBOOLEANS:
|
|
case MT_ST_CLEARBOOLEANS:
|
|
set = (what == MT_ST_SETBOOLEANS);
|
|
if (options & MT_ST_BUFFER_WRITES)
|
|
buffer_writes (set);
|
|
if (options & MT_ST_ASYNC_WRITES)
|
|
async_writes (set);
|
|
if (options & MT_ST_TWO_FM)
|
|
two_fm (set);
|
|
if (options & MT_ST_FAST_MTEOM)
|
|
fast_eom (set);
|
|
if (options & MT_ST_AUTO_LOCK)
|
|
auto_lock (set);
|
|
if (options & MT_ST_SYSV)
|
|
sysv (set);
|
|
if (options & MT_ST_NOWAIT)
|
|
nowait (set);
|
|
if (options & MT_ST_ECC)
|
|
sdp.ECC = set;
|
|
if (options & MT_ST_PADDING)
|
|
sdp.DataPadding = set;
|
|
if (options & MT_ST_REPORT_SM)
|
|
sdp.ReportSetmarks = set;
|
|
if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
|
|
|| sdp.ReportSetmarks != dp ()->ReportSetmarks)
|
|
call_setparams = true;
|
|
break;
|
|
case MT_ST_EOT_WZ_SIZE:
|
|
if (get_feature (TAPE_DRIVE_SET_EOT_WZ_SIZE))
|
|
{
|
|
sdp.EOTWarningZoneSize = (options & ~MT_ST_OPTIONS);
|
|
if (sdp.EOTWarningZoneSize != dp ()->EOTWarningZoneSize)
|
|
call_setparams = true;
|
|
}
|
|
break;
|
|
}
|
|
if (call_setparams)
|
|
{
|
|
TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
|
|
int err = lasterr;
|
|
if (!err)
|
|
{
|
|
dp ()->ECC = sdp.ECC;
|
|
dp ()->DataPadding = sdp.DataPadding;
|
|
dp ()->ReportSetmarks = sdp.ReportSetmarks;
|
|
}
|
|
else
|
|
get_dp (mt);
|
|
lasterr = err;
|
|
}
|
|
return error ("set_options");
|
|
}
|
|
|
|
int
|
|
mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf)
|
|
{
|
|
__try
|
|
{
|
|
if (cmd == MTIOCTOP)
|
|
{
|
|
struct mtop *op = (struct mtop *) buf;
|
|
if (lasterr == ERROR_BUS_RESET)
|
|
{
|
|
/* If a bus reset occurs, block further access to this device
|
|
until the user rewinds, unloads or in any other way tries
|
|
to maintain a well-known tape position. */
|
|
if (op->mt_op != MTREW && op->mt_op != MTOFFL
|
|
&& op->mt_op != MTRETEN && op->mt_op != MTERASE
|
|
&& op->mt_op != MTSEEK && op->mt_op != MTEOM)
|
|
return ERROR_BUS_RESET;
|
|
/* Try to maintain last lock state after bus reset. */
|
|
if (lock >= auto_locked && PrepareTape (mt, TAPE_LOCK, FALSE))
|
|
{
|
|
debug_printf ("Couldn't relock drive after bus reset.");
|
|
lock = unlocked;
|
|
}
|
|
}
|
|
switch (op->mt_op)
|
|
{
|
|
case MTRESET:
|
|
break;
|
|
case MTFSF:
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false);
|
|
break;
|
|
case MTBSF:
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false);
|
|
break;
|
|
case MTFSR:
|
|
set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false);
|
|
break;
|
|
case MTBSR:
|
|
set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false);
|
|
break;
|
|
case MTWEOF:
|
|
write_marks (mt, TAPE_FILEMARKS, op->mt_count);
|
|
break;
|
|
case MTREW:
|
|
set_pos (mt, TAPE_REWIND, 0, false);
|
|
break;
|
|
case MTOFFL:
|
|
case MTUNLOAD:
|
|
prepare (mt, TAPE_UNLOAD);
|
|
break;
|
|
case MTNOP:
|
|
lasterr = 0;
|
|
break;
|
|
case MTRETEN:
|
|
if (!get_feature (TAPE_DRIVE_TENSION))
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
else if (!set_pos (mt, TAPE_REWIND, 0, false))
|
|
prepare (mt, TAPE_TENSION);
|
|
break;
|
|
case MTBSFM:
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true);
|
|
break;
|
|
case MTFSFM:
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, true);
|
|
break;
|
|
case MTEOM:
|
|
if (fast_eom () && get_feature (TAPE_DRIVE_END_OF_DATA))
|
|
set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false);
|
|
else
|
|
set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false);
|
|
break;
|
|
case MTERASE:
|
|
erase (mt, TAPE_ERASE_LONG);
|
|
break;
|
|
case MTRAS1:
|
|
case MTRAS2:
|
|
case MTRAS3:
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
case MTSETBLK:
|
|
if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
|
|
{
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if ((DWORD) op->mt_count == mp ()->BlockSize)
|
|
{
|
|
/* Nothing has changed. */
|
|
lasterr = 0;
|
|
break;
|
|
}
|
|
if ((op->mt_count == 0 && !get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
|
|
|| (op->mt_count > 0
|
|
&& ((DWORD) op->mt_count < dp ()->MinimumBlockSize
|
|
|| (DWORD) op->mt_count > dp ()->MaximumBlockSize)))
|
|
{
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (set_blocksize (mt, op->mt_count)
|
|
&& lasterr == ERROR_INVALID_FUNCTION)
|
|
lasterr = ERROR_INVALID_BLOCK_LENGTH;
|
|
break;
|
|
case MTSEEK:
|
|
if (get_feature (TAPE_DRIVE_LOGICAL_BLK))
|
|
set_pos (mt, TAPE_LOGICAL_BLOCK, op->mt_count, false);
|
|
else if (!get_pos (mt))
|
|
set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS,
|
|
op->mt_count - block, false);
|
|
break;
|
|
case MTTELL:
|
|
if (!get_pos (mt))
|
|
op->mt_count = (int) block;
|
|
break;
|
|
case MTFSS:
|
|
set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false);
|
|
break;
|
|
case MTBSS:
|
|
set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false);
|
|
break;
|
|
case MTWSM:
|
|
write_marks (mt, TAPE_SETMARKS, op->mt_count);
|
|
break;
|
|
case MTLOCK:
|
|
prepare (mt, TAPE_LOCK);
|
|
break;
|
|
case MTUNLOCK:
|
|
prepare (mt, TAPE_UNLOCK);
|
|
break;
|
|
case MTLOAD:
|
|
prepare (mt, TAPE_LOAD);
|
|
break;
|
|
case MTCOMPRESSION:
|
|
set_compression (mt, op->mt_count);
|
|
break;
|
|
case MTSETPART:
|
|
set_partition (mt, op->mt_count);
|
|
break;
|
|
case MTMKPART:
|
|
create_partitions (mt, op->mt_count);
|
|
break;
|
|
case MTSETDRVBUFFER:
|
|
set_options (mt, op->mt_count);
|
|
break;
|
|
case MTSETDENSITY:
|
|
default:
|
|
lasterr = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
else if (cmd == MTIOCGET)
|
|
get_status (mt, (struct mtget *) buf);
|
|
else if (cmd == MTIOCPOS && !get_pos (mt))
|
|
((struct mtpos *) buf)->mt_blkno = (long) block;
|
|
}
|
|
__except (NO_ERROR)
|
|
{
|
|
lasterr = ERROR_NOACCESS;
|
|
}
|
|
__endtry
|
|
return lasterr;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* mtinfo */
|
|
|
|
void
|
|
mtinfo::initialize ()
|
|
{
|
|
for (unsigned i = 0; i < MAX_DRIVE_NUM; ++i)
|
|
drive (i)->initialize (i, true);
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* fhandler_dev_tape */
|
|
|
|
#define mt (cygwin_shared->mt)
|
|
|
|
#define lock(err_ret_val) if (!_lock (false)) return (err_ret_val);
|
|
|
|
inline bool
|
|
fhandler_dev_tape::_lock (bool cancelable)
|
|
{
|
|
/* O_NONBLOCK is only valid in a read or write call. Only those are
|
|
cancelable. */
|
|
DWORD timeout = cancelable && is_nonblocking () ? 0 : INFINITE;
|
|
switch (cygwait (mt_mtx, timeout,
|
|
cw_sig | cw_sig_restart | cw_cancel | cw_cancel_self))
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
return true;
|
|
case WAIT_TIMEOUT:
|
|
set_errno (EAGAIN);
|
|
return false;
|
|
default:
|
|
__seterrno ();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline int
|
|
fhandler_dev_tape::unlock (int ret)
|
|
{
|
|
ReleaseMutex (mt_mtx);
|
|
return ret;
|
|
}
|
|
|
|
fhandler_dev_tape::fhandler_dev_tape ()
|
|
: fhandler_dev_raw ()
|
|
{
|
|
debug_printf ("unit: %d", dev ().get_minor ());
|
|
}
|
|
|
|
int
|
|
fhandler_dev_tape::open (int flags, mode_t)
|
|
{
|
|
int ret;
|
|
|
|
if (driveno () >= MAX_DRIVE_NUM)
|
|
{
|
|
set_errno (ENOENT);
|
|
return 0;
|
|
}
|
|
if (!(mt_mtx = CreateMutex (&sec_all, !!(flags & O_CLOEXEC), NULL)))
|
|
{
|
|
__seterrno ();
|
|
return 0;
|
|
}
|
|
|
|
/* The O_SYNC flag is not supported by the tape driver. Use the
|
|
MT_ST_BUFFER_WRITES and MT_ST_ASYNC_WRITES flags in the drive
|
|
settings instead. In turn, the MT_ST_BUFFER_WRITES is translated
|
|
into O_SYNC, which controls the FILE_WRITE_THROUGH flag in the
|
|
NtCreateFile call in fhandler_base::open. */
|
|
flags &= ~O_SYNC;
|
|
if (!mt.drive (driveno ())->buffer_writes ())
|
|
flags |= O_SYNC;
|
|
|
|
ret = fhandler_dev_raw::open (flags);
|
|
if (ret)
|
|
{
|
|
mt.drive (driveno ())->open (get_handle ());
|
|
|
|
/* In append mode, seek to beginning of next filemark */
|
|
if (flags & O_APPEND)
|
|
mt.drive (driveno ())->set_pos (get_handle (),
|
|
TAPE_SPACE_FILEMARKS, 1, true);
|
|
|
|
if (!(flags & O_DIRECT))
|
|
{
|
|
devbufsiz = mt.drive (driveno ())->dp ()->MaximumBlockSize;
|
|
devbufalign = 1;
|
|
devbufalloc = devbuf = new char [devbufsiz];
|
|
}
|
|
}
|
|
else
|
|
ReleaseMutex (mt_mtx);
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_tape::close ()
|
|
{
|
|
int ret = 0;
|
|
int cret = 0;
|
|
|
|
if (!have_execed)
|
|
{
|
|
lock (-1);
|
|
ret = mt.drive (driveno ())->close (get_handle (), is_rewind_device ());
|
|
if (ret)
|
|
__seterrno_from_win_error (ret);
|
|
cret = fhandler_dev_raw::close ();
|
|
unlock (0);
|
|
}
|
|
if (ov.hEvent)
|
|
CloseHandle (ov.hEvent);
|
|
CloseHandle (mt_mtx);
|
|
return ret ? -1 : cret;
|
|
}
|
|
|
|
void
|
|
fhandler_dev_tape::raw_read (void *ptr, size_t &ulen)
|
|
{
|
|
char *buf = (char *) ptr;
|
|
size_t len = ulen;
|
|
size_t block_size;
|
|
size_t bytes_to_read;
|
|
size_t bytes_read = 0;
|
|
int ret = 0;
|
|
|
|
if (lastblk_to_read ())
|
|
{
|
|
lastblk_to_read (false);
|
|
ulen = 0;
|
|
return;
|
|
}
|
|
if (!_lock (true))
|
|
{
|
|
ulen = (size_t) -1;
|
|
return;
|
|
}
|
|
block_size = mt.drive (driveno ())->mp ()->BlockSize;
|
|
if (devbuf)
|
|
{
|
|
if (devbufend > devbufstart)
|
|
{
|
|
bytes_to_read = MIN (len, devbufend - devbufstart);
|
|
debug_printf ("read %lu bytes from buffer (rest %lu)",
|
|
bytes_to_read, devbufend - devbufstart - bytes_to_read);
|
|
memcpy (buf, devbuf + devbufstart, bytes_to_read);
|
|
len -= bytes_to_read;
|
|
bytes_read += bytes_to_read;
|
|
buf += bytes_to_read;
|
|
devbufstart += bytes_to_read;
|
|
if (devbufstart == devbufend)
|
|
devbufstart = devbufend = 0;
|
|
/* If a switch to variable block_size occured, just return the buffer
|
|
remains until the buffer is empty, then proceed with usual variable
|
|
block size handling (one block per read call). */
|
|
if (!block_size)
|
|
len = 0;
|
|
}
|
|
if (len > 0)
|
|
{
|
|
if (!ov.hEvent
|
|
&& !(ov.hEvent = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
|
|
debug_printf ("Creating event failed, %E");
|
|
size_t block_fit = !block_size ? len : rounddown(len, block_size);
|
|
if (block_fit)
|
|
{
|
|
debug_printf ("read %lu bytes from tape (rest %lu)",
|
|
block_fit, len - block_fit);
|
|
ret = mt.drive (driveno ())->read (get_handle (), &ov, buf,
|
|
block_fit);
|
|
if (ret)
|
|
__seterrno_from_win_error (ret);
|
|
else if (block_fit)
|
|
{
|
|
len -= block_fit;
|
|
bytes_read += block_fit;
|
|
buf += block_fit;
|
|
/* Only one block in each read call, please. */
|
|
if (!block_size)
|
|
len = 0;
|
|
}
|
|
else {
|
|
len = 0;
|
|
if (bytes_read)
|
|
lastblk_to_read (true);
|
|
}
|
|
}
|
|
if (!ret && len > 0)
|
|
{
|
|
debug_printf ("read %lu bytes from tape (one block)", block_size);
|
|
ret = mt.drive (driveno ())->read (get_handle (), &ov, devbuf,
|
|
block_size);
|
|
if (ret)
|
|
__seterrno_from_win_error (ret);
|
|
else if (block_size)
|
|
{
|
|
devbufstart = len;
|
|
devbufend = block_size;
|
|
bytes_read += len;
|
|
memcpy (buf, devbuf, len);
|
|
}
|
|
else if (bytes_read)
|
|
lastblk_to_read (true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ov.hEvent
|
|
&& !(ov.hEvent = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
|
|
debug_printf ("Creating event failed, %E");
|
|
bytes_read = ulen;
|
|
ret = mt.drive (driveno ())->read (get_handle (), &ov, ptr, bytes_read);
|
|
}
|
|
ulen = (ret ? (size_t) -1 : bytes_read);
|
|
unlock ();
|
|
}
|
|
|
|
ssize_t
|
|
fhandler_dev_tape::raw_write (const void *ptr, size_t len)
|
|
{
|
|
if (!_lock (true))
|
|
return -1;
|
|
if (!ov.hEvent && !(ov.hEvent = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
|
|
debug_printf ("Creating event failed, %E");
|
|
int ret = mt.drive (driveno ())->write (get_handle (), &ov, ptr, len);
|
|
if (ret)
|
|
__seterrno_from_win_error (ret);
|
|
return unlock (ret ? -1 : (int) len);
|
|
}
|
|
|
|
off_t
|
|
fhandler_dev_tape::lseek (off_t offset, int whence)
|
|
{
|
|
#if 1
|
|
/* On Linux lseek on tapes is a no-op. For now, let's keep the old code
|
|
intact but commented out, should incompatibilities arise. */
|
|
return 0;
|
|
#else
|
|
struct mtop op;
|
|
struct mtpos pos;
|
|
DWORD block_size;
|
|
off_t ret = ILLEGAL_SEEK;
|
|
|
|
lock (ILLEGAL_SEEK);
|
|
|
|
debug_printf ("lseek (%s, %D, %d)", get_name (), offset, whence);
|
|
|
|
block_size = mt.drive (driveno ())->mp ()->BlockSize;
|
|
if (block_size == 0)
|
|
{
|
|
set_errno (EIO);
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl (MTIOCPOS, &pos))
|
|
goto out;
|
|
|
|
switch (whence)
|
|
{
|
|
case SEEK_END:
|
|
op.mt_op = MTFSF;
|
|
op.mt_count = 1;
|
|
if (ioctl (MTIOCTOP, &op))
|
|
goto out;
|
|
break;
|
|
case SEEK_SET:
|
|
if (whence == SEEK_SET && offset < 0)
|
|
{
|
|
set_errno (EINVAL);
|
|
goto out;
|
|
}
|
|
break;
|
|
case SEEK_CUR:
|
|
break;
|
|
default:
|
|
set_errno (EINVAL);
|
|
goto out;
|
|
}
|
|
|
|
op.mt_op = MTFSR;
|
|
op.mt_count = offset / block_size
|
|
- (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))
|
|
goto out;
|
|
|
|
ret = pos.mt_blkno * block_size;
|
|
|
|
out:
|
|
return unlock (ret);
|
|
#endif
|
|
}
|
|
|
|
int
|
|
fhandler_dev_tape::fstat (struct stat *buf)
|
|
{
|
|
int ret;
|
|
|
|
if (driveno () >= MAX_DRIVE_NUM)
|
|
{
|
|
set_errno (ENOENT);
|
|
return -1;
|
|
}
|
|
if (!(ret = fhandler_base::fstat (buf)))
|
|
buf->st_blocks = 0;
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_tape::dup (fhandler_base *child, int flags)
|
|
{
|
|
lock (-1);
|
|
fhandler_dev_tape *fh = (fhandler_dev_tape *) child;
|
|
if (!DuplicateHandle (GetCurrentProcess (), mt_mtx,
|
|
GetCurrentProcess (), &fh->mt_mtx,
|
|
0, TRUE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
debug_printf ("dup(%s) failed, mutex handle %p, %E",
|
|
get_name (), mt_mtx);
|
|
__seterrno ();
|
|
return unlock (-1);
|
|
}
|
|
fh->ov.hEvent = NULL;
|
|
if (ov.hEvent &&
|
|
!DuplicateHandle (GetCurrentProcess (), ov.hEvent,
|
|
GetCurrentProcess (), &fh->ov.hEvent,
|
|
0, TRUE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
debug_printf ("dup(%s) failed, event handle %p, %E",
|
|
get_name (), ov.hEvent);
|
|
__seterrno ();
|
|
return unlock (-1);
|
|
}
|
|
return unlock (fhandler_dev_raw::dup (child, flags));
|
|
}
|
|
|
|
void
|
|
fhandler_dev_tape::fixup_after_fork (HANDLE parent)
|
|
{
|
|
fhandler_dev_raw::fixup_after_fork (parent);
|
|
fork_fixup (parent, mt_mtx, "mt_mtx");
|
|
if (ov.hEvent)
|
|
fork_fixup (parent, ov.hEvent, "ov.hEvent");
|
|
}
|
|
|
|
void
|
|
fhandler_dev_tape::set_close_on_exec (bool val)
|
|
{
|
|
fhandler_dev_raw::set_close_on_exec (val);
|
|
set_no_inheritance (mt_mtx, val);
|
|
if (ov.hEvent)
|
|
set_no_inheritance (ov.hEvent, val);
|
|
}
|
|
|
|
int
|
|
fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
|
|
{
|
|
int ret = 0;
|
|
lock (-1);
|
|
if (cmd == MTIOCTOP || cmd == MTIOCGET || cmd == MTIOCPOS)
|
|
{
|
|
ret = mt.drive (driveno ())->ioctl (get_handle (), cmd, buf);
|
|
if (ret)
|
|
__seterrno_from_win_error (ret);
|
|
return unlock (ret ? -1 : 0);
|
|
}
|
|
return unlock (fhandler_dev_raw::ioctl (cmd, buf));
|
|
}
|