OSDN Git Service

* errno.cc (errmap): Map ERROR_SHARING_VIOLATION to EBUSY,
authorcorinna <corinna>
Fri, 26 Mar 2004 21:43:48 +0000 (21:43 +0000)
committercorinna <corinna>
Fri, 26 Mar 2004 21:43:48 +0000 (21:43 +0000)
ERROR_EOM_OVERFLOW and ERROR_NO_DATA_DETECTED to EIO.  Add mappings
for ERROR_NO_MEDIA_IN_DRIVE, ERROR_DEVICE_REQUIRES_CLEANING and
ERROR_DEVICE_DOOR_OPEN.
* fhandler.h (class fhandler_dev_raw): Drop varblkop member.
(fhandler_dev_raw::is_eom): De-virtualize.
(fhandler_dev_raw::is_eof): Ditto.
(class fhandler_dev_tape): Drop lasterr and dp member.  Add mt_mtx
member.  Drop all private methods formerly used by ioctl.
(fhandler_dev_tape::is_rewind_device): Use get_minor for clarity.
(fhandler_dev_tape::driveno): New method.
(fhandler_dev_tape::drive_init): New method.
(fhandler_dev_tape::clear): Remove method.
(fhandler_dev_tape::is_eom): Ditto.
(fhandler_dev_tape::is_eof): Ditto.
(fhandler_dev_tape::write_file): Ditto.
(fhandler_dev_tape::read_file): Ditto.
(fhandler_dev_tape::_lock): New method.
(fhandler_dev_tape::unlock): New method.
(fhandler_dev_tape::raw_read): New method.
(fhandler_dev_tape::raw_write): New method.
* fhandler_raw.cc (fhandler_dev_raw::is_eom): New method.
(fhandler_dev_raw::is_eof): New method.
(fhandler_dev_raw::open): Allow setting write through option by
using the O_TEXT flag as ... flag.
(fhandler_dev_raw::writebuf): Remove usage of varblkop and other
tape specific code.
(fhandler_dev_raw::raw_read): Ditto.
(fhandler_dev_raw::dup): Ditto.
* fhandler_tape.cc: Rewrite tape operations entirely.  Implement
new tape driver classes mtinfo, mtinfo_drive and mtinfo_part.
Reduce fhandler_dev_tape methods to mostly just calling appropriate
mtinfo_drive methods.
(mtinfo_init): New function adding the mtinfo shared memory area.
* mtinfo.h: New file, containing the definition of the new tape
driver classes.
* shared.cc: Include mtinfo.h.
(offsets): Add entry for mtinfo shared memory area.
(memory_init): Call mtinfo_init.
* shared_info.h (shared_locations): Add SH_MTINFO shared location.
* include/cygwin/mtio.h: Change and add various comments.  Add GMT_xxx
macros for new generic flags.  Add MT_ST_xxx bitfield definitions
for MTSETDRVBUFFER ioctl.
* include/cygwin/version.h: Bump API minor version number.

winsup/cygwin/ChangeLog
winsup/cygwin/errno.cc
winsup/cygwin/fhandler.h
winsup/cygwin/fhandler_raw.cc
winsup/cygwin/fhandler_tape.cc
winsup/cygwin/include/cygwin/mtio.h
winsup/cygwin/include/cygwin/version.h
winsup/cygwin/mtinfo.h [new file with mode: 0644]
winsup/cygwin/shared.cc
winsup/cygwin/shared_info.h

index c0f44a1..e6c6a4b 100644 (file)
@@ -1,3 +1,50 @@
+2004-03-26  Corinna Vinschen  <corinna@vinschen.de>
+
+       * errno.cc (errmap): Map ERROR_SHARING_VIOLATION to EBUSY,
+       ERROR_EOM_OVERFLOW and ERROR_NO_DATA_DETECTED to EIO.  Add mappings
+       for ERROR_NO_MEDIA_IN_DRIVE, ERROR_DEVICE_REQUIRES_CLEANING and
+       ERROR_DEVICE_DOOR_OPEN.
+       * fhandler.h (class fhandler_dev_raw): Drop varblkop member.
+       (fhandler_dev_raw::is_eom): De-virtualize.
+       (fhandler_dev_raw::is_eof): Ditto.
+       (class fhandler_dev_tape): Drop lasterr and dp member.  Add mt_mtx
+       member.  Drop all private methods formerly used by ioctl.
+       (fhandler_dev_tape::is_rewind_device): Use get_minor for clarity.
+       (fhandler_dev_tape::driveno): New method.
+       (fhandler_dev_tape::drive_init): New method.
+       (fhandler_dev_tape::clear): Remove method.
+       (fhandler_dev_tape::is_eom): Ditto.
+       (fhandler_dev_tape::is_eof): Ditto.
+       (fhandler_dev_tape::write_file): Ditto.
+       (fhandler_dev_tape::read_file): Ditto.
+       (fhandler_dev_tape::_lock): New method.
+       (fhandler_dev_tape::unlock): New method.
+       (fhandler_dev_tape::raw_read): New method.
+       (fhandler_dev_tape::raw_write): New method.
+       * fhandler_raw.cc (fhandler_dev_raw::is_eom): New method.
+       (fhandler_dev_raw::is_eof): New method.
+       (fhandler_dev_raw::open): Allow setting write through option by
+       using the O_TEXT flag as ... flag.
+       (fhandler_dev_raw::writebuf): Remove usage of varblkop and other
+       tape specific code.
+       (fhandler_dev_raw::raw_read): Ditto.
+       (fhandler_dev_raw::dup): Ditto.
+       * fhandler_tape.cc: Rewrite tape operations entirely.  Implement
+       new tape driver classes mtinfo, mtinfo_drive and mtinfo_part.
+       Reduce fhandler_dev_tape methods to mostly just calling appropriate
+       mtinfo_drive methods.
+       (mtinfo_init): New function adding the mtinfo shared memory area.
+       * mtinfo.h: New file, containing the definition of the new tape
+       driver classes.
+       * shared.cc: Include mtinfo.h.
+       (offsets): Add entry for mtinfo shared memory area.
+       (memory_init): Call mtinfo_init.
+       * shared_info.h (shared_locations): Add SH_MTINFO shared location.
+       * include/cygwin/mtio.h: Change and add various comments.  Add GMT_xxx
+       macros for new generic flags.  Add MT_ST_xxx bitfield definitions
+       for MTSETDRVBUFFER ioctl.
+       * include/cygwin/version.h: Bump API minor version number.
+
 2004-03-26  Christopher Faylor  <cgf@redhat.com>
 
        * path.cc (path_conv::check): Use 'strchr' rather than 'strrchr' to
index 2c7915c..475164a 100644 (file)
@@ -48,7 +48,7 @@ static NO_COPY struct
   X (NO_MORE_FILES,            ENMFILE),
   X (WRITE_PROTECT,            EROFS),
   X (BAD_UNIT,                 ENODEV),
-  X (SHARING_VIOLATION,                EACCES),
+  X (SHARING_VIOLATION,                EBUSY),
   X (LOCK_VIOLATION,           EACCES),
   X (SHARING_BUFFER_EXCEEDED,  ENOLCK),
   X (HANDLE_EOF,               ENODATA),
@@ -101,10 +101,10 @@ static NO_COPY struct
   X (IO_DEVICE,                        EIO),
   X (NOT_OWNER,                        EPERM),
   X (END_OF_MEDIA,             ENOSPC),
-  X (EOM_OVERFLOW,             ENOSPC),
+  X (EOM_OVERFLOW,             EIO),
   X (BEGINNING_OF_MEDIA,       EIO),
   X (SETMARK_DETECTED,         EIO),
-  X (NO_DATA_DETECTED,         ENOSPC),
+  X (NO_DATA_DETECTED,         EIO),
   X (POSSIBLE_DEADLOCK,                EDEADLOCK),
   X (CRC,                      EIO),
   X (NEGATIVE_SEEK,            EINVAL),
@@ -116,6 +116,9 @@ static NO_COPY struct
   X (INVALID_BLOCK_LENGTH,     EIO),
   X (BUS_RESET,                        EIO),
   X (FILEMARK_DETECTED,                EIO),
+  X (NO_MEDIA_IN_DRIVE,                ENOMEDIUM),
+  X (DEVICE_REQUIRES_CLEANING, EIO),
+  X (DEVICE_DOOR_OPEN,         EIO),
   { 0, NULL, 0}
 };
 
index c7d7568..0b5c712 100644 (file)
@@ -509,7 +509,6 @@ class fhandler_dev_raw: public fhandler_base
   int lastblk_to_read : 1;
   int is_writing      : 1;
   int has_written     : 1;
-  int varblkop       : 1;
 
   virtual void clear (void);
   virtual BOOL write_file (const void *buf, DWORD to_write,
@@ -518,9 +517,9 @@ class fhandler_dev_raw: public fhandler_base
   virtual int writebuf (void);
 
   /* returns not null, if `win_error' determines an end of media condition */
-  virtual int is_eom(int win_error) = 0;
+  virtual int is_eom(int win_error);
   /* returns not null, if `win_error' determines an end of file condition */
-  virtual int is_eof(int win_error) = 0;
+  virtual int is_eof(int win_error);
 
   fhandler_dev_raw ();
 
@@ -569,18 +568,14 @@ class fhandler_dev_floppy: public fhandler_dev_raw
 
 class fhandler_dev_tape: public fhandler_dev_raw
 {
-  int lasterr;
-  TAPE_GET_DRIVE_PARAMETERS dp;
+  HANDLE mt_mtx;
 
-  bool is_rewind_device () { return get_unit () < 128; }
+  bool is_rewind_device () { return get_minor () < 128; }
+  unsigned int driveno () { return (unsigned int) get_minor () & 0x7f; }
+  void drive_init (void);
 
- protected:
-  virtual void clear (void);
-  virtual int is_eom (int win_error);
-  virtual int is_eof (int win_error);
-  virtual BOOL write_file (const void *buf, DWORD to_write,
-                          DWORD *written, int *err);
-  virtual BOOL read_file (void *buf, DWORD to_read, DWORD *read, int *err);
+  inline bool _lock ();
+  inline int unlock (int ret = 0);
 
  public:
   fhandler_dev_tape ();
@@ -588,33 +583,15 @@ class fhandler_dev_tape: public fhandler_dev_raw
   virtual int open (int flags, mode_t mode = 0);
   virtual int close (void);
 
+  void raw_read (void *ptr, size_t& ulen);
+  int raw_write (const void *ptr, size_t ulen);
+
   virtual _off64_t lseek (_off64_t offset, int whence);
 
   virtual int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2)));
 
   virtual int dup (fhandler_base *child);
-
   virtual int ioctl (unsigned int cmd, void *buf);
-
- private:
-  inline bool tape_get_feature (DWORD parm)
-    {
-      return ((parm & TAPE_DRIVE_HIGH_FEATURES)
-             ? ((dp.FeaturesHigh & parm) != 0)
-             : ((dp.FeaturesLow & parm) != 0));
-    }
-  int tape_error (const char *txt);
-  int tape_write_marks (int marktype, DWORD len);
-  int tape_get_pos (unsigned long *block, unsigned long *partition = NULL);
-  int tape_set_pos (int mode, long count, bool sfm_func = false);
-  int _tape_set_pos (int mode, long count, int partition = 0);
-  int tape_erase (int mode);
-  int tape_prepare (int action);
-  int tape_set_blocksize (long count);
-  int tape_status (struct mtget *get);
-  int tape_compression (long count);
-  int tape_partition (long count);
-  int tape_set_partition (long count);
 };
 
 /* Standard disk file */
index 93356ab..44c4495 100644 (file)
@@ -37,9 +37,21 @@ fhandler_dev_raw::clear (void)
   eom_detected = 0;
   eof_detected = 0;
   lastblk_to_read = 0;
-  varblkop = 0;
 }
 
+int
+fhandler_dev_raw::is_eom (int win_error)
+{
+  return 0;
+}
+
+int
+fhandler_dev_raw::is_eof (int)
+{
+  return 0;
+}
+
+
 /* Wrapper functions to allow fhandler_dev_tape to detect and care for
    media changes and bus resets. */
 
@@ -76,16 +88,13 @@ fhandler_dev_raw::writebuf (void)
   DWORD written;
   int ret = 0;
 
-  if (!varblkop && is_writing && devbuf && devbufend)
+  if (is_writing && devbuf && devbufend)
     {
       DWORD to_write;
       int ret = 0;
 
       memset (devbuf + devbufend, 0, devbufsiz - devbufend);
-      if (get_major () != DEV_TAPE_MAJOR)
-       to_write = ((devbufend - 1) / 512 + 1) * 512;
-      else
-       to_write = devbufsiz;
+      to_write = ((devbufend - 1) / 512 + 1) * 512;
       if (!write_file (devbuf, to_write, &written, &ret)
          && is_eom (ret))
        eom_detected = 1;
@@ -141,7 +150,7 @@ fhandler_dev_raw::open (int flags, mode_t)
     }
 
   /* Check for illegal flags. */
-  if (flags & (O_APPEND | O_EXCL))
+  if (get_major () != DEV_TAPE_MAJOR && (flags & (O_APPEND | O_EXCL)))
     {
       set_errno (EINVAL);
       return 0;
@@ -162,11 +171,16 @@ fhandler_dev_raw::open (int flags, mode_t)
   WCHAR devname[CYG_MAX_PATH + 1];
   str2buf2uni (dev, devname, get_win32_name ());
   OBJECT_ATTRIBUTES attr;
+  ULONG options = FILE_SYNCHRONOUS_IO_NONALERT;
+  /* The O_TEXT flag is used to indicate write-through on tape devices */
+  if (get_major () == DEV_TAPE_MAJOR && (flags & O_TEXT))
+    options |= FILE_WRITE_THROUGH;
+  flags &= ~O_TEXT;
   InitializeObjectAttributes (&attr, &dev, OBJ_CASE_INSENSITIVE, NULL, NULL);
 
   HANDLE h;
   IO_STATUS_BLOCK io;
-  NTSTATUS status = NtOpenFile (&h, access, &attr, &io, wincap.shared (),
+  NTSTATUS status = NtOpenFile (&h, access, &attr, &io, 0 /* excl. access */,
                                FILE_SYNCHRONOUS_IO_NONALERT);
   if (!NT_SUCCESS (status))
     {
@@ -234,7 +248,8 @@ fhandler_dev_raw::raw_read (void *ptr, size_t& ulen)
            {
              bytes_to_read = min (len, devbufend - devbufstart);
              debug_printf ("read %d bytes from buffer (rest %d)",
-                           bytes_to_read, devbufend - devbufend);
+                           bytes_to_read,
+                           devbufend - devbufstart - bytes_to_read);
              memcpy (p, devbuf + devbufstart, bytes_to_read);
              len -= bytes_to_read;
              p += bytes_to_read;
@@ -251,10 +266,7 @@ fhandler_dev_raw::raw_read (void *ptr, size_t& ulen)
            {
              if (len >= devbufsiz)
                {
-                 if (get_major () == DEV_TAPE_MAJOR)
-                   bytes_to_read = (len / devbufsiz) * devbufsiz;
-                 else
-                   bytes_to_read = (len / 512) * 512;
+                 bytes_to_read = (len / 512) * 512;
                  tgt = p;
                  debug_printf ("read %d bytes direct from file",bytes_to_read);
                }
@@ -310,11 +322,6 @@ fhandler_dev_raw::raw_read (void *ptr, size_t& ulen)
     {
       if (!is_eof (ret) && !is_eom (ret))
        {
-         if (varblkop && ret == ERROR_MORE_DATA)
-           /* *ulen < blocksize.  Linux returns ENOMEM here
-              when reading with variable blocksize . */
-           set_errno (ENOMEM);
-         else
          __seterrno ();
          goto err;
        }
@@ -470,7 +477,6 @@ fhandler_dev_raw::dup (fhandler_base *child)
       fhc->eom_detected = eom_detected;
       fhc->eof_detected = eof_detected;
       fhc->lastblk_to_read = 0;
-      fhc->varblkop = varblkop;
     }
   return ret;
 }
index a99e4a2..bbcc670 100644 (file)
@@ -14,6 +14,7 @@ details. */
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/mtio.h>
+#include <sys/param.h>
 #include <ddk/ntddstor.h>
 #include "cygerrno.h"
 #include "perprocess.h"
@@ -22,742 +23,1358 @@ details. */
 #include "fhandler.h"
 #include "dtable.h"
 #include "cygheap.h"
+#include "shared_info.h"
+#include "sigproc.h"
+#include "mtinfo.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. */
-/* FIXME: Note that this is wrong!  The correct behaviour after getting
-   an ERROR_BUS_RESET is to raise a flag and then to block any access,
-   except for MTREW, MTOFFL, MT_RETEN, MTERASE, MTSEEK and MTEOM, and
-   to set errno to EIO in all other cases. */
-#define TAPE_FUNC(func) do { \
-                         lasterr = (func); \
-                       } while (lasterr == ERROR_MEDIA_CHANGED \
-                                || lasterr == ERROR_BUS_RESET)
+#define TAPE_FUNC(func) while ((lasterr = (func)) == ERROR_MEDIA_CHANGED) \
+                         { \
+                           initialize (drive, false); \
+                           part (partition)->initialize (0); \
+                         }
 
 /* Convert LARGE_INTEGER into long long */
 #define get_ll(pl)  (((long long) (pl).HighPart << 32) | (pl).LowPart)
 
-#define IS_EOM(err) ((err) == ERROR_END_OF_MEDIA \
-                    || (err) == ERROR_EOM_OVERFLOW \
-                    || (err) == ERROR_NO_DATA_DETECTED)
+#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)
+
 /**********************************************************************/
-/* fhandler_dev_tape */
+/* mtinfo_part */
 
 void
-fhandler_dev_tape::clear (void)
+mtinfo_part::initialize (long nblock)
 {
-  lasterr = 0;
-  fhandler_dev_raw::clear ();
+  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;
+      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
-fhandler_dev_tape::is_eom (int win_error)
+mtinfo_drive::get_dp (HANDLE mt)
 {
-  int ret = IS_EOM (win_error);
-  if (ret)
-    debug_printf ("end of medium");
-  return ret;
+  DWORD len = sizeof _dp;
+  TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_DRIVE_INFORMATION, &len, &_dp));
+  return error ("get_dp");
 }
 
 int
-fhandler_dev_tape::is_eof (int win_error)
+mtinfo_drive::get_mp (HANDLE mt)
 {
-  int ret = IS_EOF (win_error);
-  if (ret)
-    debug_printf ("end of file");
-  return ret;
+  DWORD len = sizeof _mp;
+  TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_MEDIA_INFORMATION, &len, &_mp));
+  return error ("get_mp");
 }
 
-BOOL
-fhandler_dev_tape::write_file (const void *buf, DWORD to_write,
-                              DWORD *written, int *err)
+int
+mtinfo_drive::open (HANDLE mt)
 {
-  BOOL ret;
+  get_dp (mt);
+  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");
+}
 
-  do
+int
+mtinfo_drive::close (HANDLE mt, bool rewind)
+{
+  lasterr = 0;
+  if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
+    dirty = clean;
+  if (dirty == has_written)
     {
-      *err = 0;
-      if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
-       *err = GetLastError ();
+      /* 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 the 2nd filemark. */
+        {
+         set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
+         if (!lasterr)
+           part (partition)->fblock = 0; /* That's obvious, isn't it? */
+       }
     }
-  while (*err == ERROR_MEDIA_CHANGED || *err == ERROR_BUS_RESET);
-  syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)",
-                 ret, *err, get_handle (), buf, to_write, *written);
-  return ret;
+  else if (dirty == has_read && sysv && !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");
 }
 
-BOOL
-fhandler_dev_tape::read_file (void *buf, DWORD to_read, DWORD *read, int *err)
+int
+mtinfo_drive::read (HANDLE mt, void *ptr, size_t &ulen)
 {
   BOOL ret;
+  DWORD bytes_read = 0;
 
-  do
+  if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
+    return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
+  if (lasterr == ERROR_BUS_RESET)
     {
-      *err = 0;
-      if (!(ret = ReadFile (get_handle (), buf, to_read, read, 0)))
-       *err = GetLastError ();
+      ulen = 0;
+      goto out;
     }
-  while (*err == ERROR_MEDIA_CHANGED || *err == ERROR_BUS_RESET);
-  syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)",
-                 ret, *err, get_handle (), buf, to_read, *read);
-  return ret;
+  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);
+  ret = ReadFile (mt, ptr, ulen, &bytes_read, 0);
+  lasterr = ret ? 0 : GetLastError ();
+  ulen = (size_t) bytes_read;
+  if (bytes_read > 0)
+    {
+      long 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");
 }
 
-fhandler_dev_tape::fhandler_dev_tape ()
-  : fhandler_dev_raw ()
+int
+mtinfo_drive::write (HANDLE mt, const void *ptr, size_t &len)
 {
-  debug_printf ("unit: %d", dev ().minor);
+  BOOL ret;
+  DWORD bytes_written = 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");
+    }
+  dirty = clean;
+  part (partition)->smark = false;
+  if (auto_lock && lock < auto_locked)
+    prepare (mt, TAPE_LOCK, true);
+  ret = WriteFile (mt, ptr, len, &bytes_written, 0);
+  lasterr = ret ? 0: GetLastError ();
+  len = (size_t) bytes_written;
+  if (bytes_written > 0)
+    {
+      long 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 == 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;
+    }
+  return error ("write");
 }
 
 int
-fhandler_dev_tape::open (int flags, mode_t)
+mtinfo_drive::get_pos (HANDLE mt, long *ppartition, long *pblock)
 {
-  int ret;
-
-  devbufsiz = 1L;
+  DWORD p, low, high;
 
-  ret = fhandler_dev_raw::open (flags);
-  if (ret)
+  TAPE_FUNC (GetTapePosition (mt, TAPE_LOGICAL_POSITION, &p, &low, &high));
+  if (lasterr == ERROR_INVALID_FUNCTION)
+    TAPE_FUNC (GetTapePosition (mt, TAPE_ABSOLUTE_POSITION, &p, &low, &high));
+  if (!lasterr)
     {
-      DWORD varlen;
-      struct mtget get;
-      unsigned long block;
+      partition = (long) (p > 0 ? p - 1 : p);
+      block = (long) low;
+      if (ppartition)
+        *ppartition= partition;
+      if (pblock)
+        *pblock = block;
+    }
+  else
+    {
+      partition = 0;
+      block = -1;
+    }
+  return error ("get_pos");
+}
 
-      TAPE_FUNC (GetTapeParameters (get_handle (), GET_TAPE_DRIVE_INFORMATION,
-                                   (varlen = sizeof dp, &varlen), &dp));
+int
+mtinfo_drive::_set_pos (HANDLE mt, int mode, long count,
+                       int partition)
+{
+  TAPE_FUNC (SetTapePosition (mt, mode, partition, count,
+                             count < 0 ? -1 : 0, FALSE));
+  dirty = clean;
+  return lasterr;
+}
 
-      if (!tape_status (&get))
-        {
-         long blksize = (get.mt_dsreg & MT_ST_BLKSIZE_MASK)
-                        >> MT_ST_BLKSIZE_SHIFT;
-         /* Tape drive supports and is set to variable block size. */
-         if (blksize == 0)
-           devbufsiz = get.mt_maxblksize;
-         else
-           devbufsiz = blksize;
-         varblkop = blksize == 0;
-       }
+int
+mtinfo_drive::set_pos (HANDLE mt, int mode, long count,
+                      bool sfm_func)
+{
+  int err = 0;
+  long undone = count;
+  BOOL dont_wait = FALSE;
 
-      if (devbufsiz > 1L)
-       devbuf = new char [devbufsiz];
+  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)) || IS_SM (err))
+         --undone;
+      while (!err && undone < 0)
+       if (!(err = _set_pos (mt, mode, -1, 0)) || IS_SM (err))
+         ++undone;
+    }
+  else
+    err = _set_pos (mt, mode, count, 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");
+}
 
-      /* 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 (!tape_get_pos (&block) && !block)
-       {
-         debug_printf ("rewinding");
-         tape_set_pos (TAPE_REWIND, 0);
-       }
+int
+mtinfo_drive::create_partitions (HANDLE mt, long count)
+{
+  if (dp ()->MaximumPartitionCount <= 1)
+    return ERROR_INVALID_PARAMETER;
+  if (set_pos (mt, TAPE_REWIND, 0, false))
+    goto out;
+  debug_printf ("Format tape with %s partition(s)", count <= 0 ? "one" : "two");
+  TAPE_FUNC (CreateTapePartition (mt, TAPE_SELECT_PARTITIONS,
+                                 count <= 0 ? 1 : 2, 0));
+out:
+  return error ("partition");
+}
 
-      if (flags & O_APPEND)
+int
+mtinfo_drive::set_partition (HANDLE mt, long count)
+{
+  if (count < 0 || (unsigned long) count >= MAX_PARTITION_NUM)
+    lasterr = ERROR_INVALID_PARAMETER;
+  else if ((DWORD) count >= dp ()->MaximumPartitionCount)
+    lasterr = ERROR_IO_DEVICE;
+  else
+    {
+      int part_block = part (count)->block >= 0 ? part (count)->block : 0;
+      int err = _set_pos (mt, TAPE_LOGICAL_BLOCK, part_block, count + 1);
+      if (err)
        {
-         /* In append mode, seek to beginning of next filemark */
-         tape_set_pos (TAPE_SPACE_FILEMARKS, 1, true);
+         int sav_block = block;
+         int 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;
     }
-
-  return ret;
+  return error ("set_partition");
 }
 
 int
-fhandler_dev_tape::close (void)
+mtinfo_drive::write_marks (HANDLE mt, int marktype, DWORD count)
 {
-  struct mtop op;
-  int ret = 0;
-
-  if (is_writing)
+  if (marktype != TAPE_SETMARKS)
+    dirty = clean;
+  if (marktype == TAPE_FILEMARKS
+      && !get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
     {
-      ret = writebuf ();
-      if (has_written && !eom_detected)
+      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
+    {
+      int sav_block = block;
+      int sav_partition = partition;
+      get_pos (mt);
+      if (sav_partition != partition)
        {
-         /* if last operation was writing, write a filemark */
-         debug_printf ("writing filemark");
-         op.mt_op = MTWEOF;
-         op.mt_count = 1;
-         ioctl (MTIOCTOP, &op);
+         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");
+}
 
-  // To protected reads on signaling (e.g. Ctrl-C)
-  eof_detected = 1;
-
-  if (is_rewind_device ())
+int
+mtinfo_drive::erase (HANDLE mt, int mode)
+{
+  switch (mode)
     {
-      debug_printf ("rewinding");
-      tape_set_pos (TAPE_REWIND, 0);
+      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");
+}
 
-  if (ret)
+int
+mtinfo_drive::prepare (HANDLE mt, int action, bool is_auto)
+{
+  BOOL dont_wait = FALSE;
+
+  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)
     {
-      fhandler_dev_raw::close ();
-      return ret;
+      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 fhandler_dev_raw::close ();
+  return error ("prepare");
 }
 
 int
-fhandler_dev_tape::fstat (struct __stat64 *buf)
+mtinfo_drive::set_compression (HANDLE mt, long count)
 {
-  int ret;
-
-  if (!(ret = fhandler_base::fstat (buf)))
+  if (!get_feature (TAPE_DRIVE_SET_COMPRESSION))
+    return ERROR_INVALID_PARAMETER;
+  TAPE_SET_DRIVE_PARAMETERS sdp =
     {
-      struct mtget get;
-
-      if (!ioctl (MTIOCGET, &get))
-       buf->st_blocks = get.mt_capacity / buf->st_blksize;
-    }
+      dp ()->ECC,
+      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");
+}
 
-  return ret;
+int
+mtinfo_drive::set_blocksize (HANDLE mt, long count)
+{
+  TAPE_SET_MEDIA_PARAMETERS smp = {count};
+  TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_MEDIA_INFORMATION, &smp));
+  return error ("set_blocksize");
 }
 
-_off64_t
-fhandler_dev_tape::lseek (_off64_t offset, int whence)
+int
+mtinfo_drive::status (HANDLE mt, struct mtget *get)
 {
-  struct mtop op;
-  struct mtpos pos;
+  int notape = 0;
+  DWORD tstat;
 
-  debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
+  if (!get)
+    return ERROR_INVALID_PARAMETER;
 
-  writebuf ();
+  if ((tstat = GetTapeStatus (mt)) == ERROR_NO_MEDIA_IN_DRIVE)
+    notape = 1;
 
-  if (ioctl (MTIOCPOS, &pos))
-    {
-      return ILLEGAL_SEEK;
-    }
+  memset (get, 0, sizeof *get);
 
-  switch (whence)
+  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;
+
+  if (wincap.has_ioctl_storage_get_media_types_ex ())
     {
-      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;
+      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
+           }
+       }
     }
 
-  op.mt_op = MTFSR;
-  op.mt_count = offset / devbufsiz
-               - (whence == SEEK_SET ? pos.mt_blkno : 0);
-
-  if (op.mt_count < 0)
+  if (!notape)
     {
-      op.mt_op = MTBSR;
-      op.mt_count = -op.mt_count;
-    }
+      get->mt_fileno = part (partition)->file;
+      get->mt_blkno = part (partition)->fblock;
+
+      if (get->mt_blkno == 0)
+       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);
 
-  if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
-    return -1;
+      get->mt_gstat |= GMT_ONLINE (-1);
+
+      if (mp ()->WriteProtected)
+       get->mt_gstat |= GMT_WR_PROT (-1);
+
+      get->mt_capacity = get_ll (mp ()->Capacity);
+      get->mt_remaining = get_ll (mp ()->Remaining);
+  }
+
+  if (notape)
+    get->mt_gstat |= GMT_DR_OPEN (-1);
+
+  if (buffer_writes)
+    get->mt_gstat |= GMT_IM_REP_EN (-1);       /* TODO: Async writes */
+
+  else 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);
+
+  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 (pos.mt_blkno * devbufsiz);
+  return 0;
 }
 
 int
-fhandler_dev_tape::dup (fhandler_base *child)
+mtinfo_drive::set_options (HANDLE mt, long options)
 {
-  fhandler_dev_tape *fhc = (fhandler_dev_tape *) child;
+  long 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
+    };
 
-  fhc->lasterr = lasterr;
-  fhc->dp = dp;
-  return fhandler_dev_raw::dup (child);
+  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);
+       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_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
-fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
+mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf)
 {
-  int ret = NO_ERROR;
-  unsigned long block;
-
   if (cmd == MTIOCTOP)
     {
+      if (__check_invalid_read_ptr (buf, sizeof (struct mtop)))
+        return ERROR_NOACCESS;
       struct mtop *op = (struct mtop *) buf;
-
-      if (!op)
-       ret = ERROR_INVALID_PARAMETER;
-      else
-       switch (op->mt_op)
-         {
+      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:
-           ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count);
+           set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false);
            break;
          case MTBSF:
-           ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count);
+           set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false);
            break;
          case MTFSR:
-           ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count);
+           set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false);
            break;
          case MTBSR:
-           ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count);
+           set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false);
            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);
+           write_marks (mt, TAPE_FILEMARKS, op->mt_count);
            break;
          case MTREW:
-           ret = tape_set_pos (TAPE_REWIND, 0);
+           set_pos (mt, TAPE_REWIND, 0, false);
            break;
          case MTOFFL:
          case MTUNLOAD:
-           ret = tape_prepare (TAPE_UNLOAD);
+           prepare (mt, TAPE_UNLOAD);
            break;
          case MTNOP:
-           reset_devbuf ();
+           lasterr = 0;
            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);
+           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:
-           ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count, true);
+           set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true);
            break;
          case MTFSFM:
-           ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count, true);
+           set_pos (mt, 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);
+           if (fast_eom && get_feature (TAPE_DRIVE_END_OF_DATA))
+             set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false);
            else
-             ret = tape_set_pos (TAPE_SPACE_FILEMARKS, 32767);
+             set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false);
            break;
          case MTERASE:
-           ret = tape_erase (TAPE_ERASE_LONG);
+           erase (mt, TAPE_ERASE_LONG);
            break;
          case MTRAS1:
          case MTRAS2:
          case MTRAS3:
-           ret = ERROR_INVALID_PARAMETER;
+           lasterr = ERROR_INVALID_PARAMETER;
            break;
          case MTSETBLK:
-           if (!tape_get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
+           if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
              {
-               ret = ERROR_INVALID_PARAMETER;
+               lasterr = ERROR_INVALID_PARAMETER;
                break;
              }
-           if ((devbuf && (size_t) op->mt_count == devbufsiz)
-               || (!devbuf && op->mt_count == 0))
+           if ((DWORD) op->mt_count == mp ()->BlockSize)
              {
                /* Nothing has changed. */
-               ret = 0;
+               lasterr = 0;
                break;
              }
-           if ((op->mt_count == 0
-                && !tape_get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
+           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)))
-             {
-               ret = ERROR_INVALID_PARAMETER;
-               break;
-             }
-           if (devbuf && devbufend - devbufstart > 0
-               && (op->mt_count == 0
-                   || (op->mt_count > 0
-                       && (size_t) op->mt_count < devbufend - devbufstart)))
+                   && ((DWORD) op->mt_count < dp ()->MinimumBlockSize
+                       || (DWORD) op->mt_count > dp ()->MaximumBlockSize)))
              {
-               /* Not allowed if still data in devbuf. */
-               ret = ERROR_INVALID_BLOCK_LENGTH; /* EIO */
+               lasterr = ERROR_INVALID_PARAMETER;
                break;
              }
-           if (!(ret = tape_set_blocksize (op->mt_count)))
-             {
-               char *buf = NULL;
-               if (op->mt_count > 1L && !(buf = new char [op->mt_count]))
-                 {
-                   ret = ERROR_OUTOFMEMORY;
-                   break;
-                 }
-               if (devbufsiz > 1L && op->mt_count > 1L)
-                 {
-                   memcpy (buf, devbuf + devbufstart,
-                           devbufend - devbufstart);
-                   devbufend -= devbufstart;
-                 }
-               else
-                 devbufend = 0;
-               devbufstart = 0;
-               delete [] devbuf;
-               devbuf = buf;
-               devbufsiz = op->mt_count;
-               varblkop = op->mt_count == 0;
-             }
-           reset_devbuf ();
+           if (set_blocksize (mt, op->mt_count)
+               && lasterr == ERROR_INVALID_FUNCTION)
+             lasterr = ERROR_INVALID_BLOCK_LENGTH;
            break;
          case MTSEEK:
-           if (tape_get_feature (TAPE_DRIVE_LOGICAL_BLK))
-             ret = tape_set_pos (TAPE_LOGICAL_BLOCK, op->mt_count);
-           else if (!(ret = tape_get_pos (&block)))
-             ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS,
-                                 op->mt_count - block);
+           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 (!(ret = tape_get_pos (&block)))
+           if (!get_pos (mt))
              op->mt_count = block;
            break;
          case MTFSS:
-           ret = tape_set_pos (TAPE_SPACE_SETMARKS, op->mt_count);
+           set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false);
            break;
          case MTBSS:
-           ret = tape_set_pos (TAPE_SPACE_SETMARKS, -op->mt_count);
+           set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false);
            break;
          case MTWSM:
-           ret = tape_write_marks (TAPE_SETMARKS, op->mt_count);
-           reset_devbuf ();
+           write_marks (mt, TAPE_SETMARKS, op->mt_count);
            break;
          case MTLOCK:
-           ret = tape_prepare (TAPE_LOCK);
+           prepare (mt, TAPE_LOCK);
            break;
          case MTUNLOCK:
-           ret = tape_prepare (TAPE_UNLOCK);
+           prepare (mt, TAPE_UNLOCK);
            break;
          case MTLOAD:
-           ret = tape_prepare (TAPE_LOAD);
+           prepare (mt, TAPE_LOAD);
            break;
          case MTCOMPRESSION:
-           ret = tape_compression (op->mt_count);
+           set_compression (mt, op->mt_count);
            break;
          case MTSETPART:
-           ret = tape_set_partition (op->mt_count);
-           reset_devbuf ();
+           set_partition (mt, op->mt_count);
            break;
          case MTMKPART:
-           ret = tape_partition (op->mt_count);
-           reset_devbuf ();
+           create_partitions (mt, op->mt_count);
            break;
-         case MTSETDENSITY:
          case MTSETDRVBUFFER:
-           reset_devbuf ();
+           set_options (mt, op->mt_count);
+           break;
+         case MTSETDENSITY:
          default:
-           ret = ERROR_INVALID_PARAMETER;
+           lasterr = 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;
+      if (__check_null_invalid_struct (buf, sizeof (struct mtget)))
+        return ERROR_NOACCESS;
+      status (mt, (struct mtget *) buf);
     }
-  else
-    return fhandler_dev_raw::ioctl (cmd, buf);
-
-  if (ret != NO_ERROR)
+  else if (cmd == MTIOCPOS)
     {
-      SetLastError (ret);
-      __seterrno ();
-      return -1;
+      if (__check_null_invalid_struct (buf, sizeof (struct mtpos)))
+        return ERROR_NOACCESS;
+      if (!get_pos (mt))
+       ((struct mtpos *) buf)->mt_blkno = block;
     }
 
-  return 0;
+  return lasterr;
 }
 
-/* ------------------------------------------------------------------ */
-/*                  Private functions used by `ioctl'                 */
-/* ------------------------------------------------------------------ */
+/**********************************************************************/
+/* mtinfo */
 
-int
-fhandler_dev_tape::tape_error (const char *txt)
+void
+mtinfo::initialize (void)
 {
-  if (lasterr)
-    debug_printf ("%s: error: %d", txt, lasterr);
-  return lasterr;
+  char name[CYG_MAX_PATH];
+  HANDLE mtx;
+
+  shared_name (name, "mtinfo_mutex", 0);
+  if (!(mtx = CreateMutex (&sec_all_nih, FALSE, name)))
+    api_fatal ("CreateMutex '%s'(%p), %E.  Terminating.", name);
+  WaitForSingleObject (mtx, INFINITE);
+  if (!magic)
+    {
+      magic = MTINFO_MAGIC;
+      version = MTINFO_VERSION;
+      for (unsigned i = 0; i < MAX_DRIVE_NUM; ++i)
+        drive (i)->initialize (i, true);
+      ReleaseMutex (mtx);
+      CloseHandle (mtx);
+    }
+  else
+    {
+      ReleaseMutex (mtx);
+      CloseHandle (mtx);
+      if (magic != MTINFO_MAGIC)
+       api_fatal ("MT magic number screwed up: %lu, should be %lu",
+                  magic, MTINFO_MAGIC);
+      if (version != MTINFO_VERSION)
+        system_printf ("MT version number mismatch: %lu, should be %lu",
+                      version, MTINFO_VERSION);
+    }
 }
 
-int
-fhandler_dev_tape::tape_write_marks (int marktype, DWORD len)
+HANDLE mt_h;
+mtinfo *mt;
+
+void __stdcall
+mtinfo_init ()
 {
-  syscall_printf ("write_tapemark");
-  TAPE_FUNC (WriteTapemark (get_handle (), marktype, len, FALSE));
-  return tape_error ("tape_write_marks");
+  mt = (mtinfo *) open_shared ("mtinfo", MTINFO_VERSION, mt_h, sizeof (mtinfo), SH_MTINFO);
+  mt->initialize ();
 }
 
-int
-fhandler_dev_tape::tape_get_pos (unsigned long *block,
-                                unsigned long *partition)
-{
-  DWORD part, low, high;
+/**********************************************************************/
+/* fhandler_dev_tape */
 
-  lasterr = ERROR_INVALID_PARAMETER;
-  if (tape_get_feature (TAPE_DRIVE_GET_LOGICAL_BLK))
-    TAPE_FUNC (GetTapePosition (get_handle (), TAPE_LOGICAL_POSITION,
-                               &part, &low, &high));
-  else if (tape_get_feature (TAPE_DRIVE_GET_ABSOLUTE_BLK))
-    TAPE_FUNC (GetTapePosition (get_handle (), TAPE_ABSOLUTE_POSITION,
-                               &part, &low, &high));
+#define lock(err_ret_val) if (!_lock ()) return err_ret_val;
 
-  if (!tape_error ("tape_get_pos"))
+inline bool
+fhandler_dev_tape::_lock ()
+{
+  HANDLE obj[2] = { mt_mtx, signal_arrived };
+  BOOL ret = WaitForMultipleObjects (2, obj, FALSE, INFINITE) == WAIT_OBJECT_0;
+  if (!ret)
     {
-      if (block)
-       *block = low;
-      if (partition)
-        *partition = part > 0 ? part - 1 : part;
+      debug_printf ("signal_arrived"); \
+      set_errno (EINTR);
     }
+  return ret;
+}
 
-  return lasterr;
+inline int
+fhandler_dev_tape::unlock (int ret)
+{
+  ReleaseMutex (mt_mtx);
+  return ret;
 }
 
-int
-fhandler_dev_tape::_tape_set_pos (int mode, long count, int partition)
+fhandler_dev_tape::fhandler_dev_tape ()
+  : fhandler_dev_raw ()
 {
-  TAPE_FUNC (SetTapePosition (get_handle (), mode, partition, count,
-                             count < 0 ? -1 : 0, FALSE));
-  /* Reset buffer after successful repositioning. */
-  if (!lasterr || IS_EOF (lasterr) || IS_EOM (lasterr))
-    {
-      reset_devbuf ();
-      eof_detected = IS_EOF (lasterr);
-      eom_detected = IS_EOM (lasterr);
-    }
-  return lasterr;
+  debug_printf ("unit: %d", dev ().minor);
 }
 
 int
-fhandler_dev_tape::tape_set_pos (int mode, long count, bool sfm_func)
+fhandler_dev_tape::open (int flags, mode_t)
 {
-  switch (mode)
+  int ret;
+
+  if (driveno () >= MAX_DRIVE_NUM)
     {
-      case TAPE_SPACE_RELATIVE_BLOCKS:
-      case TAPE_SPACE_FILEMARKS:
-        if (!count)
-         {
-           lasterr = 0;
-           return tape_error ("tape_set_pos");
-         }
-       break;
+      set_errno (ENOENT);
+      return 0;
     }
-  _tape_set_pos (mode, count);
-  switch (mode)
+  if (!(mt_mtx = CreateMutex (&sec_all, FALSE, NULL)))
     {
-      case TAPE_SPACE_FILEMARKS:
-       if (!lasterr && sfm_func)
-         return tape_set_pos (mode, count > 0 ? -1 : 1, false);
-       break;
+      __seterrno ();
+      return 0;
     }
-  return tape_error ("tape_set_pos");
-}
-
-int
-fhandler_dev_tape::tape_erase (int mode)
-{
-  if (tape_set_pos (TAPE_REWIND, 0))
-    return lasterr;
-  switch (mode)
+  /* The O_TEXT flag is used to indicate write-through (non buffered writes)
+     to the underlying fhandler_dev_raw::open call. */
+  flags &= ~O_TEXT;
+  if (!mt->drive (driveno ())->buffered_writes ())
+    flags |= O_TEXT;
+  ret = fhandler_dev_raw::open (flags);
+  if (ret)
     {
-      case TAPE_ERASE_SHORT:
-       if (!tape_get_feature (TAPE_DRIVE_ERASE_SHORT))
-         mode = TAPE_ERASE_LONG;
-       break;
-      case TAPE_ERASE_LONG:
-       if (!tape_get_feature (TAPE_DRIVE_ERASE_LONG))
-         mode = TAPE_ERASE_SHORT;
-       break;
-    }
-  TAPE_FUNC (EraseTape (get_handle (), mode, false));
-  /* Reset buffer after successful tape erasing. */
-  if (!lasterr)
-    reset_devbuf ();
-  return tape_error ("tape_erase");
-}
+      mt->drive (driveno ())->open (get_handle ());
 
-int
-fhandler_dev_tape::tape_prepare (int action)
-{
-  TAPE_FUNC (PrepareTape (get_handle (), action, FALSE));
-  /* Reset buffer after all successful preparations but lock and unlock. */
-  if (!lasterr && action != TAPE_LOCK && action != TAPE_UNLOCK)
-    reset_devbuf ();
-  return tape_error ("tape_prepare");
+      /* 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);
+
+      devbufsiz = mt->drive (driveno ())->dp ()->MaximumBlockSize;
+      devbuf = new char (devbufsiz);
+      devbufstart = devbufend = 0;
+    }
+  else
+    ReleaseMutex (mt_mtx);
+  return ret;
 }
 
 int
-fhandler_dev_tape::tape_set_blocksize (long count)
+fhandler_dev_tape::close (void)
 {
-  TAPE_SET_MEDIA_PARAMETERS mp;
+  int ret, cret;
 
-  mp.BlockSize = count;
-  TAPE_FUNC (SetTapeParameters (get_handle (), SET_TAPE_MEDIA_INFORMATION,
-                               &mp));
-  return tape_error ("tape_set_blocksize");
+  lock (-1);
+  ret = mt->drive (driveno ())->close (get_handle (), is_rewind_device ());
+  if (ret)
+    __seterrno_from_win_error (ret);
+  cret = fhandler_dev_raw::close ();
+  return unlock (ret ? -1 : cret);
 }
 
-int
-fhandler_dev_tape::tape_status (struct mtget *get)
+void
+fhandler_dev_tape::raw_read (void *ptr, size_t &ulen)
 {
-  DWORD varlen;
-  TAPE_GET_MEDIA_PARAMETERS mp;
-  int notape = 0;
-
-  if (!get)
-    return ERROR_INVALID_PARAMETER;
-
-  /* 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. */
-  TAPE_FUNC (GetTapeParameters (get_handle (), GET_TAPE_MEDIA_INFORMATION,
-                               (varlen = sizeof dp, &varlen), &mp));
-  if (lasterr)
-    notape = 1;
-
-  memset (get, 0, sizeof *get);
-
-  get->mt_type = MT_ISUNKNOWN;
-
-  if (!notape && tape_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;
+  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 (wincap.has_ioctl_storage_get_media_types_ex ())
+  if (lastblk_to_read)
     {
-      DWORD size = sizeof (GET_MEDIA_TYPES) + 10 * sizeof (DEVICE_MEDIA_INFO);
-      void *buf = alloca (size);
-      if (DeviceIoControl (get_handle (), 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)
+      lastblk_to_read = 0;
+      ulen = 0;
+      return;
+    }
+  if (!_lock ())
+    {
+      ulen = (size_t) -1;
+      return;
+    }
+  block_size = mt->drive (driveno ())->mp ()->BlockSize;
+  if (devbufend > devbufstart)
+    {
+      bytes_to_read = min (len, devbufend - devbufstart);
+      debug_printf ("read %d bytes from buffer (rest %d)",
+                   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)
+    {
+      size_t block_fit = !block_size ? len : rounddown(len,  block_size);
+      if (block_fit)
+        {
+         debug_printf ("read %d bytes from tape (rest %d)",
+                       block_fit, len - block_fit);
+         ret = mt->drive (driveno ())->read (get_handle (), buf, block_fit);
+         if (ret)
+           __seterrno_from_win_error (ret);
+         else if (block_fit)
            {
-             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
+             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 = 1;
+         }
+       }
+      if (!ret && len > 0)
+        {
+         debug_printf ("read %d bytes from tape (one block)", block_size);
+         ret = mt->drive (driveno ())->read (get_handle (), 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 = 1;
        }
     }
-  if (notape)
-    get->mt_gstat |= GMT_DR_OPEN (-1);
-
-  if (!notape)
-    {
-      if (tape_get_feature (TAPE_DRIVE_GET_LOGICAL_BLK)
-          || tape_get_feature (TAPE_DRIVE_GET_ABSOLUTE_BLK))
-       tape_get_pos ((unsigned long *) &get->mt_blkno,
-                     (unsigned long *) &get->mt_resid);
+  if (ret)
+    ulen = (size_t) -1;
+  else
+    ulen = bytes_read;
+  unlock ();
+}
 
-      if (!get->mt_blkno)
-       get->mt_gstat |= GMT_BOT (-1);
+int
+fhandler_dev_tape::raw_write (const void *ptr, size_t len)
+{
+  lock (-1);
+  int ret = mt->drive (driveno ())->write (get_handle (), ptr, len);
+    __seterrno_from_win_error (ret);
+  return unlock (len);
+}
 
-      get->mt_gstat |= GMT_ONLINE (-1);
+_off64_t
+fhandler_dev_tape::lseek (_off64_t offset, int whence)
+{
+  struct mtop op;
+  struct mtpos pos;
+  DWORD block_size;
+  _off64_t ret = ILLEGAL_SEEK;
 
-      if (tape_get_feature (TAPE_DRIVE_WRITE_PROTECT) && mp.WriteProtected)
-       get->mt_gstat |= GMT_WR_PROT (-1);
+  lock (ILLEGAL_SEEK);
 
-      if (tape_get_feature (TAPE_DRIVE_TAPE_CAPACITY))
-       get->mt_capacity = get_ll (mp.Capacity);
+  debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
 
-      if (tape_get_feature (TAPE_DRIVE_TAPE_REMAINING))
-         get->mt_remaining = get_ll (mp.Remaining);
+  block_size = mt->drive (driveno ())->mp ()->BlockSize;
+  if (block_size == 0)
+    {
+      set_errno (EIO);
+      goto out;
     }
 
-  if (tape_get_feature (TAPE_DRIVE_COMPRESSION) && dp.Compression)
-    get->mt_gstat |= GMT_HW_COMP (-1);
+  if (ioctl (MTIOCPOS, &pos))
+    goto out;
 
-  if (tape_get_feature (TAPE_DRIVE_ECC) && dp.ECC)
-    get->mt_gstat |= GMT_HW_ECC (-1);
+  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;
+    }
 
-  if (tape_get_feature (TAPE_DRIVE_PADDING) && dp.DataPadding)
-    get->mt_gstat |= GMT_PADDING (-1);
+  op.mt_op = MTFSR;
+  op.mt_count = offset / block_size
+               - (whence == SEEK_SET ? pos.mt_blkno : 0);
 
-  if (tape_get_feature (TAPE_DRIVE_REPORT_SMKS) && dp.ReportSetmarks)
-    get->mt_gstat |= GMT_IM_REP_EN (-1);
+  if (op.mt_count < 0)
+    {
+      op.mt_op = MTBSR;
+      op.mt_count = -op.mt_count;
+    }
 
-  get->mt_erreg = lasterr;
+  if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
+    goto out;
 
-  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;
+  ret = pos.mt_blkno * block_size;
 
-  return 0;
+out:
+  return unlock (ret);
 }
 
 int
-fhandler_dev_tape::tape_compression (long count)
+fhandler_dev_tape::fstat (struct __stat64 *buf)
 {
-  TAPE_SET_DRIVE_PARAMETERS dps;
-
-  if (!tape_get_feature (TAPE_DRIVE_COMPRESSION))
-    return ERROR_INVALID_PARAMETER;
+  int ret;
 
-  dps.ECC = dp.ECC;
-  dps.Compression = count ? TRUE : FALSE;
-  dps.DataPadding = dp.DataPadding;
-  dps.ReportSetmarks = dp.ReportSetmarks;
-  dps.EOTWarningZoneSize = dp.EOTWarningZoneSize;
-  TAPE_FUNC (SetTapeParameters (get_handle (), SET_TAPE_DRIVE_INFORMATION,
-                               &dps));
-  if (!lasterr)
-    dp.Compression = dps.Compression;
-  return tape_error ("tape_compression");
+  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::tape_partition (long count)
+fhandler_dev_tape::dup (fhandler_base *child)
 {
-  if (dp.MaximumPartitionCount <= 1)
-    return ERROR_INVALID_PARAMETER;
-  if (tape_set_pos (TAPE_REWIND, 0))
-    return lasterr;
-  if (count <= 0)
-    debug_printf ("Formatting tape with one partition");
-  else
-    debug_printf ("Formatting tape with two partitions");
-  TAPE_FUNC (CreateTapePartition (get_handle (), TAPE_SELECT_PARTITIONS,
-                                  count <= 0 ? 1 : 2, 0));
-  return tape_error ("tape_partition");
+  lock (-1);
+  return unlock (fhandler_dev_raw::dup (child));
 }
 
 int
-fhandler_dev_tape::tape_set_partition (long count)
+fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
 {
-  if (count < 0 || (unsigned long) count >= dp.MaximumPartitionCount)
-    lasterr = ERROR_INVALID_PARAMETER;
-  else
-    lasterr = _tape_set_pos (TAPE_LOGICAL_BLOCK, 0, count + 1);
-  return tape_error ("tape_set_partition");
+  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));
 }
index 6aab340..11e09f0 100644 (file)
@@ -35,7 +35,7 @@ struct        mtop {
 };
 
 /* Magnetic Tape operations [Not all operations supported by all drivers]: */
-#define MTRESET 0      /* +reset drive in case of problems */
+#define MTRESET 0      /* reset drive in case of problems */
 #define MTFSF  1       /* forward space over FileMark,
                         * position at first record of next file
                         */
@@ -80,15 +80,14 @@ struct      mtop {
 /* structure for MTIOCGET - mag tape get status command */
 
 struct mtget {
-       long    mt_type;        /* type of magtape device
-                                * Cygwin: MT_ISUNKNOWN */
+       long    mt_type;        /* type of magtape device */
        long    mt_resid;       /* residual count: (not sure)
                                 *      number of bytes ignored, or
                                 *      number of files not skipped, or
                                 *      number of records not skipped.
                                 *  Cygwin: remaining KB until 1.5.7.
-                                *          active partition since 1.5.8,
-                                *          same as on linux.
+                                *          active partition since 1.5.8
+                                *          (same as on GNU-Linux).
                                 */
        /* the following registers are device dependent */
        long    mt_dsreg;       /* status register, Contains blocksize and
@@ -133,17 +132,27 @@ struct    mtpos {
 #define GMT_SM(x)               ((x) & 0x10000000)  /* DDS setmark */
 #define GMT_EOD(x)              ((x) & 0x08000000)  /* DDS EOD */
 #define GMT_WR_PROT(x)          ((x) & 0x04000000)
-/* #define GMT_ ?              ((x) & 0x02000000) */
+#define GMT_REP_SM(x)           ((x) & 0x02000000)  /* Cygwin: rep. setmarks */
 #define GMT_ONLINE(x)           ((x) & 0x01000000)
 #define GMT_D_6250(x)           ((x) & 0x00800000)
 #define GMT_D_1600(x)           ((x) & 0x00400000)
 #define GMT_D_800(x)            ((x) & 0x00200000)
-#define GMT_PADDING(x)         ((x) & 0x00100000)  /* data padding */
-#define GMT_HW_ECC(x)          ((x) & 0x00080000)  /* HW error correction */
+#define GMT_PADDING(x)          ((x) & 0x00100000)  /* Cygwin: data padding */
+#define GMT_HW_ECC(x)           ((x) & 0x00080000)  /* Cygwin: HW error corr. */
 #define GMT_DR_OPEN(x)          ((x) & 0x00040000)  /* door open (no tape) */
-#define GMT_HW_COMP(x)         ((x) & 0x00020000)  /* HW compression */
+#define GMT_HW_COMP(x)          ((x) & 0x00020000)  /* Cygwin: HW compression */
 #define GMT_IM_REP_EN(x)        ((x) & 0x00010000)  /* immediate report mode */
-/* 16 generic status bits unused */
+#define GMT_CLN(x)              ((x) & 0x00008000)  /* cleaning requested */
+/* 15 generic status bits unused */
+/* Cygwin only: The below settings are also used by the GNU-Linux SCSI tape
+   driver but they aren't usually reported here.  Unfortunately, there's no
+   other official method to retrieve the values of these settings and
+   reporting them here apparently doesn't hurt. */
+#define GMT_TWO_FM(x)           ((x) & 0x00000080)  /* two fm after write */
+#define GMT_FAST_MTEOM(x)       ((x) & 0x00000040)  /* fast seek to eom */
+#define GMT_AUTO_LOCK(x)        ((x) & 0x00000020)  /* auto door lock on r/w */
+#define GMT_SYSV(x)            ((x) & 0x00000010)  /* SYSV read semantics */
+#define GMT_NOWAIT(x)          ((x) & 0x00000008)  /* don't wait for positioning commands */
 
 
 /* SCSI-tape specific definitions */
@@ -156,6 +165,33 @@ struct     mtpos {
 #define MT_ST_SOFTERR_SHIFT    0
 #define MT_ST_SOFTERR_MASK     0xffff
 
+/* Bitfields for the MTSETDRVBUFFER ioctl.  */
+#define MT_ST_OPTIONS           0xf0000000
+#define MT_ST_BOOLEANS          0x10000000
+#define MT_ST_SETBOOLEANS       0x30000000
+#define MT_ST_CLEARBOOLEANS     0x40000000
+#define MT_ST_WRITE_THRESHOLD   0x20000000     /* Not supported */
+#define MT_ST_DEF_OPTIONS       0x60000000     /* Not supported */
+#define MT_ST_EOT_WZ_SIZE      0xf0000000      /* Cygwin only */
+
+#define MT_ST_BUFFER_WRITES     0x00000001
+#define MT_ST_ASYNC_WRITES     0x00000002      /* Not supported */
+#define MT_ST_READ_AHEAD        0x00000004     /* Not supported */
+#define MT_ST_DEBUGGING         0x00000008     /* Not supported */
+#define MT_ST_TWO_FM           0x00000010
+#define MT_ST_FAST_MTEOM        0x00000020
+#define MT_ST_AUTO_LOCK                0x00000040
+#define MT_ST_DEF_WRITES        0x00000080     /* Not supported */
+#define MT_ST_CAN_BSR           0x00000100     /* Not supported */
+#define MT_ST_NO_BLKLIMS        0x00000200     /* Not supported */
+#define MT_ST_CAN_PARTITIONS    0x00000400     /* Not supported */
+#define MT_ST_SCSI2LOGICAL      0x00000800     /* Not supported */
+#define MT_ST_SYSV              0x00001000
+#define MT_ST_NOWAIT            0x00002000
+#define MT_ST_ECC              0x00010000      /* Cygwin only */
+#define MT_ST_PADDING          0x00020000      /* Cygwin only */
+#define MT_ST_REPORT_SM                0x00040000      /* Cygwin only */
+
 /*
  * Constants for mt_type. Not all of these are supported,
  * and these are not all of the ones that are supported.
index 04b7a00..2db2e9b 100644 (file)
@@ -238,12 +238,14 @@ details. */
            timer_settime
       111: Export sigqueue, sighold.
       112: Redefine some mtget fields.
+      113: Again redefine some mtget fields.  Use mt_fileno and mt_blkno as
+           on Linux.
      */
 
      /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 112
+#define CYGWIN_VERSION_API_MINOR 113
 
      /* There is also a compatibity version number associated with the
        shared memory regions.  It is incremented when incompatible
diff --git a/winsup/cygwin/mtinfo.h b/winsup/cygwin/mtinfo.h
new file mode 100644 (file)
index 0000000..31260d1
--- /dev/null
@@ -0,0 +1,133 @@
+/* mtinfo.h: Defininitions for the Cygwin tape driver class.
+
+   Copyright 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. */
+
+#define MTINFO_MAGIC           0x179b2af0
+#define MTINFO_VERSION         1
+
+/* Maximum number of supported partitions per drive. */
+#define MAX_PARTITION_NUM      64
+/* Maximum number of supported drives. */
+#define MAX_DRIVE_NUM          8
+
+/* Values for bookkeeping of the tape position relative to filemarks
+   and eod/eom.  */
+enum eom_val
+{
+  no_eof,
+  eof_hit,
+  eof,
+  eod_hit,
+  eod,
+  eom_hit,
+  eom
+};
+
+enum dirty_state
+{
+  clean,
+  has_read,
+  has_written
+};
+
+enum lock_state
+{
+  unlocked,
+  lock_error,
+  auto_locked,
+  locked
+};
+
+/* Partition specific information */
+class mtinfo_part
+{
+public:
+  long block;          /* logical block no */
+  long file;           /* current file no */
+  long fblock;         /* relative block no */
+  bool smark;          /* At setmark? */
+  eom_val emark;       /* "end-of"-mark */
+
+  void initialize (long nblock = -1);
+};
+
+class mtinfo_drive
+{
+  int drive;
+  int lasterr;
+  long partition;
+  long block;
+  dirty_state dirty;
+  lock_state lock;
+  TAPE_GET_DRIVE_PARAMETERS _dp;
+  TAPE_GET_MEDIA_PARAMETERS _mp;
+  bool buffer_writes;
+  bool two_fm;
+  bool fast_eom;
+  bool auto_lock;
+  bool sysv;
+  bool nowait;
+  mtinfo_part _part[MAX_PARTITION_NUM];
+
+  inline int error (const char *str)
+    {
+      if (lasterr)
+        debug_printf ("%s: Win32 error %d", lasterr);
+      return lasterr;
+    }
+  inline bool get_feature (DWORD parm)
+    {
+      return ((parm & TAPE_DRIVE_HIGH_FEATURES)
+             ? ((_dp.FeaturesHigh & parm) != 0)
+             : ((_dp.FeaturesLow & parm) != 0));
+    }
+  int get_pos (HANDLE mt, long *ppartition = NULL, long *pblock = NULL);
+  int _set_pos (HANDLE mt, int mode, long count, int partition);
+  int create_partitions (HANDLE mt, long count);
+  int set_partition (HANDLE mt, long count);
+  int write_marks (HANDLE mt, int marktype, DWORD count);
+  int erase (HANDLE mt, int mode);
+  int prepare (HANDLE mt, int action, bool is_auto = false);
+  int set_compression (HANDLE mt, long count);
+  int set_blocksize (HANDLE mt, long count);
+  int status (HANDLE mt, struct mtget *get);
+  int set_options (HANDLE mt, long options);
+
+public:
+  void initialize (int num, bool first_time);
+  int get_dp (HANDLE mt);
+  int get_mp (HANDLE mt);
+  int open (HANDLE mt);
+  int close (HANDLE mt, bool rewind);
+  int read (HANDLE mt, void *ptr, size_t &ulen);
+  int write (HANDLE mt, const void *ptr, size_t &len);
+  int ioctl (HANDLE mt, unsigned int cmd, void *buf);
+  int set_pos (HANDLE mt, int mode, long count, bool sfm_func);
+
+  inline bool buffered_writes (void) { return buffer_writes; }
+  PTAPE_GET_DRIVE_PARAMETERS dp (void) { return &_dp; }
+  PTAPE_GET_MEDIA_PARAMETERS mp (void) { return &_mp; }
+  mtinfo_part *part (int num) { return &_part[num]; }
+};
+
+class mtinfo
+{
+  DWORD magic;
+  DWORD version;
+  mtinfo_drive _drive[MAX_DRIVE_NUM];
+
+public:
+  void initialize (void);
+  mtinfo_drive *drive (int num) { return &_drive[num]; }
+};
+
+extern HANDLE mt_h;
+extern mtinfo *mt;
+
+extern void __stdcall mtinfo_init ();
index 9a91a1c..42beb54 100644 (file)
@@ -26,6 +26,7 @@ details. */
 #include "registry.h"
 #include "cygwin_version.h"
 #include "child_info.h"
+#include "mtinfo.h"
 
 shared_info NO_COPY *cygwin_shared;
 user_info NO_COPY *user_shared;
@@ -62,7 +63,13 @@ static char *offsets[] =
     + pround (sizeof (shared_info))
     + pround (sizeof (user_info))
     + pround (sizeof (console_state))
+    + pround (sizeof (_pinfo)),
+  (char *) cygwin_shared_address
+    + pround (sizeof (shared_info))
+    + pround (sizeof (user_info))
+    + pround (sizeof (console_state))
     + pround (sizeof (_pinfo))
+    + pround (sizeof (mtinfo))
 };
 
 void * __stdcall
@@ -243,6 +250,7 @@ memory_init ()
   ProtectHandleINH (cygheap->shared_h);
 
   user_shared_initialize (false);
+  mtinfo_init ();
 }
 
 unsigned
index 4264d5e..114cf0b 100644 (file)
@@ -176,6 +176,7 @@ enum shared_locations
   SH_USER_SHARED,
   SH_SHARED_CONSOLE,
   SH_MYSELF,
+  SH_MTINFO,
   SH_TOTAL_SIZE
 };
 void __stdcall memory_init ();