1514 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1514 lines
		
	
	
		
			38 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); \
 | |
| 			  }
 | |
| 
 | |
| #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));
 | |
|   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 (GetTapeStatus (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 (GetTapeStatus (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 (GetTapeStatus (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;
 | |
| 
 | |
|   if ((tstat = GetTapeStatus (mt)) == ERROR_NO_MEDIA_IN_DRIVE
 | |
|       || get_mp (mt) == 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, int32_t options)
 | |
| {
 | |
|   int32_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 __reg3
 | |
| 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 __reg3
 | |
| 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 __reg2
 | |
| 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));
 | |
| }
 |