From dee563095deb2bbdba5d4e04e48c99694061e302 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Fri, 26 Mar 2004 21:43:49 +0000 Subject: [PATCH] * 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. --- winsup/cygwin/ChangeLog | 47 + winsup/cygwin/errno.cc | 9 +- winsup/cygwin/fhandler.h | 45 +- winsup/cygwin/fhandler_raw.cc | 44 +- winsup/cygwin/fhandler_tape.cc | 1743 ++++++++++++++++-------- winsup/cygwin/include/cygwin/mtio.h | 56 +- winsup/cygwin/include/cygwin/version.h | 4 +- winsup/cygwin/mtinfo.h | 133 ++ winsup/cygwin/shared.cc | 8 + winsup/cygwin/shared_info.h | 1 + 10 files changed, 1460 insertions(+), 630 deletions(-) create mode 100644 winsup/cygwin/mtinfo.h diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index c0f44a17e..e6c6a4bb6 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,50 @@ +2004-03-26 Corinna Vinschen + + * 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 * path.cc (path_conv::check): Use 'strchr' rather than 'strrchr' to diff --git a/winsup/cygwin/errno.cc b/winsup/cygwin/errno.cc index 2c7915c59..475164a02 100644 --- a/winsup/cygwin/errno.cc +++ b/winsup/cygwin/errno.cc @@ -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} }; diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index c7d7568ed..0b5c71299 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -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 */ diff --git a/winsup/cygwin/fhandler_raw.cc b/winsup/cygwin/fhandler_raw.cc index 93356ab26..44c449561 100644 --- a/winsup/cygwin/fhandler_raw.cc +++ b/winsup/cygwin/fhandler_raw.cc @@ -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; } diff --git a/winsup/cygwin/fhandler_tape.cc b/winsup/cygwin/fhandler_tape.cc index a99e4a2aa..bbcc67063 100644 --- a/winsup/cygwin/fhandler_tape.cc +++ b/winsup/cygwin/fhandler_tape.cc @@ -14,6 +14,7 @@ details. */ #include #include #include +#include #include #include "cygerrno.h" #include "perprocess.h" @@ -22,628 +23,684 @@ 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) +{ + 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 +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) +{ + 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"); +} + +int +mtinfo_drive::close (HANDLE mt, bool rewind) { lasterr = 0; - fhandler_dev_raw::clear (); -} - -int -fhandler_dev_tape::is_eom (int win_error) -{ - int ret = IS_EOM (win_error); - if (ret) - debug_printf ("end of medium"); - return ret; -} - -int -fhandler_dev_tape::is_eof (int win_error) -{ - int ret = IS_EOF (win_error); - if (ret) - debug_printf ("end of file"); - return ret; -} - -BOOL -fhandler_dev_tape::write_file (const void *buf, DWORD to_write, - DWORD *written, int *err) -{ - BOOL ret; - - do + 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 (); - } - 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; -} - -BOOL -fhandler_dev_tape::read_file (void *buf, DWORD to_read, DWORD *read, int *err) -{ - BOOL ret; - - do - { - *err = 0; - if (!(ret = ReadFile (get_handle (), buf, to_read, read, 0))) - *err = GetLastError (); - } - 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; -} - -fhandler_dev_tape::fhandler_dev_tape () - : fhandler_dev_raw () -{ - debug_printf ("unit: %d", dev ().minor); -} - -int -fhandler_dev_tape::open (int flags, mode_t) -{ - int ret; - - devbufsiz = 1L; - - ret = fhandler_dev_raw::open (flags); - if (ret) - { - DWORD varlen; - struct mtget get; - unsigned long block; - - TAPE_FUNC (GetTapeParameters (get_handle (), GET_TAPE_DRIVE_INFORMATION, - (varlen = sizeof dp, &varlen), &dp)); - - if (!tape_status (&get)) + /* 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. */ { - 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; - } - - if (devbufsiz > 1L) - devbuf = new char [devbufsiz]; - - /* The following rewind in position 0 solves a problem which appears - * in case of multi volume archives: The last ReadFile on first medium - * returns ERROR_NO_DATA_DETECTED. After media change, all subsequent - * ReadFile calls return ERROR_NO_DATA_DETECTED, too. - * The call to tape_set_pos seems to reset some internal flags. */ - if (!tape_get_pos (&block) && !block) - { - debug_printf ("rewinding"); - tape_set_pos (TAPE_REWIND, 0); - } - - if (flags & O_APPEND) - { - /* In append mode, seek to beginning of next filemark */ - tape_set_pos (TAPE_SPACE_FILEMARKS, 1, true); + set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false); + if (!lasterr) + part (partition)->fblock = 0; /* That's obvious, isn't it? */ } } - - return ret; -} - -int -fhandler_dev_tape::close (void) -{ - struct mtop op; - int ret = 0; - - if (is_writing) + else if (dirty == has_read && sysv && !rewind) { - ret = writebuf (); - if (has_written && !eom_detected) - { - /* if last operation was writing, write a filemark */ - debug_printf ("writing filemark"); - op.mt_op = MTWEOF; - op.mt_count = 1; - ioctl (MTIOCTOP, &op); + 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); } } - - // To protected reads on signaling (e.g. Ctrl-C) - eof_detected = 1; - - if (is_rewind_device ()) + if (rewind) { debug_printf ("rewinding"); - tape_set_pos (TAPE_REWIND, 0); + set_pos (mt, TAPE_REWIND, 0, false); } - - if (ret) - { - fhandler_dev_raw::close (); - return ret; - } - - return fhandler_dev_raw::close (); + if (auto_lock && lock == auto_locked) + prepare (mt, TAPE_UNLOCK); + dirty = clean; + return error ("close"); } int -fhandler_dev_tape::fstat (struct __stat64 *buf) +mtinfo_drive::read (HANDLE mt, void *ptr, size_t &ulen) { - int ret; + BOOL ret; + DWORD bytes_read = 0; - if (!(ret = fhandler_base::fstat (buf))) + if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE) + return lasterr = ERROR_NO_MEDIA_IN_DRIVE; + if (lasterr == ERROR_BUS_RESET) { - struct mtget get; - - if (!ioctl (MTIOCGET, &get)) - buf->st_blocks = get.mt_capacity / buf->st_blksize; + ulen = 0; + goto out; } - - return ret; -} - -_off64_t -fhandler_dev_tape::lseek (_off64_t offset, int whence) -{ - struct mtop op; - struct mtpos pos; - - debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence); - - writebuf (); - - if (ioctl (MTIOCPOS, &pos)) + dirty = clean; + if (part (partition)->emark == eof_hit) { - return ILLEGAL_SEEK; + part (partition)->emark = eof; + lasterr = ulen = 0; + goto out; } - - switch (whence) + else if (part (partition)->emark == eod_hit) { - 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; + part (partition)->emark = eod; + lasterr = ulen = 0; + goto out; } - - op.mt_op = MTFSR; - op.mt_count = offset / devbufsiz - - (whence == SEEK_SET ? pos.mt_blkno : 0); - - if (op.mt_count < 0) + else if (part (partition)->emark == eod) { - op.mt_op = MTBSR; - op.mt_count = -op.mt_count; + lasterr = ERROR_NO_DATA_DETECTED; + ulen = (size_t) -1; + goto out; } - - if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos)) - return -1; - - return (pos.mt_blkno * devbufsiz); -} - -int -fhandler_dev_tape::dup (fhandler_base *child) -{ - fhandler_dev_tape *fhc = (fhandler_dev_tape *) child; - - fhc->lasterr = lasterr; - fhc->dp = dp; - return fhandler_dev_raw::dup (child); -} - -int -fhandler_dev_tape::ioctl (unsigned int cmd, void *buf) -{ - int ret = NO_ERROR; - unsigned long block; - - if (cmd == MTIOCTOP) + else if (part (partition)->emark == eom_hit) { - struct mtop *op = (struct mtop *) buf; - - if (!op) - ret = ERROR_INVALID_PARAMETER; + 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 - switch (op->mt_op) - { - case MTRESET: - break; - case MTFSF: - ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count); - break; - case MTBSF: - ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count); - break; - case MTFSR: - ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count); - break; - case MTBSR: - ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count); - break; - case MTWEOF: - if (tape_get_feature (TAPE_DRIVE_WRITE_FILEMARKS)) - ret = tape_write_marks (TAPE_FILEMARKS, op->mt_count); - else if (tape_get_feature (TAPE_DRIVE_WRITE_LONG_FMKS)) - ret = tape_write_marks (TAPE_LONG_FILEMARKS, op->mt_count); - else - ret = tape_write_marks (TAPE_SHORT_FILEMARKS, op->mt_count); - break; - case MTREW: - ret = tape_set_pos (TAPE_REWIND, 0); - break; - case MTOFFL: - case MTUNLOAD: - ret = tape_prepare (TAPE_UNLOAD); - break; - case MTNOP: - reset_devbuf (); - break; - case MTRETEN: - if (!tape_get_feature (TAPE_DRIVE_END_OF_DATA)) - ret = ERROR_INVALID_PARAMETER; - else if (!(ret = tape_set_pos (TAPE_REWIND, 0, false))) - ret = tape_prepare (TAPE_TENSION); - break; - case MTBSFM: - ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count, true); - break; - case MTFSFM: - ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count, true); - break; - case MTEOM: - if (tape_get_feature (TAPE_DRIVE_END_OF_DATA)) - ret = tape_set_pos (TAPE_SPACE_END_OF_DATA, 0); - else - ret = tape_set_pos (TAPE_SPACE_FILEMARKS, 32767); - break; - case MTERASE: - ret = tape_erase (TAPE_ERASE_LONG); - break; - case MTRAS1: - case MTRAS2: - case MTRAS3: - ret = ERROR_INVALID_PARAMETER; - break; - case MTSETBLK: - if (!tape_get_feature (TAPE_DRIVE_SET_BLOCK_SIZE)) - { - ret = ERROR_INVALID_PARAMETER; - break; - } - if ((devbuf && (size_t) op->mt_count == devbufsiz) - || (!devbuf && op->mt_count == 0)) - { - /* Nothing has changed. */ - ret = 0; - break; - } - if ((op->mt_count == 0 - && !tape_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))) - { - /* Not allowed if still data in devbuf. */ - ret = ERROR_INVALID_BLOCK_LENGTH; /* EIO */ - 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 (); - 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); - break; - case MTTELL: - if (!(ret = tape_get_pos (&block))) - op->mt_count = block; - break; - case MTFSS: - ret = tape_set_pos (TAPE_SPACE_SETMARKS, op->mt_count); - break; - case MTBSS: - ret = tape_set_pos (TAPE_SPACE_SETMARKS, -op->mt_count); - break; - case MTWSM: - ret = tape_write_marks (TAPE_SETMARKS, op->mt_count); - reset_devbuf (); - break; - case MTLOCK: - ret = tape_prepare (TAPE_LOCK); - break; - case MTUNLOCK: - ret = tape_prepare (TAPE_UNLOCK); - break; - case MTLOAD: - ret = tape_prepare (TAPE_LOAD); - break; - case MTCOMPRESSION: - ret = tape_compression (op->mt_count); - break; - case MTSETPART: - ret = tape_set_partition (op->mt_count); - reset_devbuf (); - break; - case MTMKPART: - ret = tape_partition (op->mt_count); - reset_devbuf (); - break; - case MTSETDENSITY: - case MTSETDRVBUFFER: - reset_devbuf (); - default: - ret = ERROR_INVALID_PARAMETER; - break; - } - } - else if (cmd == MTIOCGET) - ret = tape_status ((struct mtget *) buf); - else if (cmd == MTIOCPOS) - { - ret = ERROR_INVALID_PARAMETER; - if (buf && (ret = tape_get_pos (&block))) - ((struct mtpos *) buf)->mt_blkno = block; + { + part (partition)->emark = IS_EOM (lasterr) ? eom_hit : eod_hit; + lasterr = 0; + } } else - return fhandler_dev_raw::ioctl (cmd, buf); - - if (ret != NO_ERROR) { - SetLastError (ret); - __seterrno (); - return -1; + 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; } - - return 0; -} - -/* ------------------------------------------------------------------ */ -/* Private functions used by `ioctl' */ -/* ------------------------------------------------------------------ */ - -int -fhandler_dev_tape::tape_error (const char *txt) -{ - if (lasterr) - debug_printf ("%s: error: %d", txt, lasterr); - return lasterr; + if (!lasterr) + dirty = has_read; +out: + return error ("read"); } int -fhandler_dev_tape::tape_write_marks (int marktype, DWORD len) +mtinfo_drive::write (HANDLE mt, const void *ptr, size_t &len) { - syscall_printf ("write_tapemark"); - TAPE_FUNC (WriteTapemark (get_handle (), marktype, len, FALSE)); - return tape_error ("tape_write_marks"); -} + BOOL ret; + DWORD bytes_written = 0; -int -fhandler_dev_tape::tape_get_pos (unsigned long *block, - unsigned long *partition) -{ - DWORD part, low, high; - - 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)); - - if (!tape_error ("tape_get_pos")) + if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE) + return lasterr = ERROR_NO_MEDIA_IN_DRIVE; + if (lasterr == ERROR_BUS_RESET) { - if (block) - *block = low; - if (partition) - *partition = part > 0 ? part - 1 : part; + len = 0; + return error ("write"); } - - return lasterr; + 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::_tape_set_pos (int mode, long count, int partition) +mtinfo_drive::get_pos (HANDLE mt, long *ppartition, long *pblock) { - TAPE_FUNC (SetTapePosition (get_handle (), mode, partition, count, + DWORD p, low, high; + + 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) + { + 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"); +} + +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)); - /* 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); - } + dirty = clean; return lasterr; } int -fhandler_dev_tape::tape_set_pos (int mode, long count, bool sfm_func) +mtinfo_drive::set_pos (HANDLE mt, int mode, long count, + bool sfm_func) { + int err = 0; + long 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; - return tape_error ("tape_set_pos"); + goto out; } break; - } - _tape_set_pos (mode, count); - switch (mode) - { - case TAPE_SPACE_FILEMARKS: - if (!lasterr && sfm_func) - return tape_set_pos (mode, count > 0 ? -1 : 1, false); + case TAPE_ABSOLUTE_BLOCK: + case TAPE_LOGICAL_BLOCK: + case TAPE_REWIND: + dont_wait = nowait ? TRUE : FALSE; break; } - return tape_error ("tape_set_pos"); + 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"); } int -fhandler_dev_tape::tape_erase (int mode) +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"); +} + +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) + { + 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 error ("set_partition"); +} + +int +mtinfo_drive::write_marks (HANDLE mt, int marktype, DWORD count) +{ + 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 + { + 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; + } + return error ("write_marks"); +} + +int +mtinfo_drive::erase (HANDLE mt, int mode) { - if (tape_set_pos (TAPE_REWIND, 0)) - return lasterr; switch (mode) { case TAPE_ERASE_SHORT: - if (!tape_get_feature (TAPE_DRIVE_ERASE_SHORT)) + if (!get_feature (TAPE_DRIVE_ERASE_SHORT)) mode = TAPE_ERASE_LONG; break; case TAPE_ERASE_LONG: - if (!tape_get_feature (TAPE_DRIVE_ERASE_LONG)) + if (!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"); + TAPE_FUNC (EraseTape (mt, mode, nowait ? TRUE : FALSE)); + part (partition)->initialize (0); + return error ("erase"); } int -fhandler_dev_tape::tape_prepare (int action) +mtinfo_drive::prepare (HANDLE mt, int action, bool is_auto) { - TAPE_FUNC (PrepareTape (get_handle (), action, FALSE)); + 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. */ - if (!lasterr && action != TAPE_LOCK && action != TAPE_UNLOCK) - reset_devbuf (); - return tape_error ("tape_prepare"); + 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 -fhandler_dev_tape::tape_set_blocksize (long count) +mtinfo_drive::set_compression (HANDLE mt, long count) { - TAPE_SET_MEDIA_PARAMETERS mp; - - mp.BlockSize = count; - TAPE_FUNC (SetTapeParameters (get_handle (), SET_TAPE_MEDIA_INFORMATION, - &mp)); - return tape_error ("tape_set_blocksize"); + if (!get_feature (TAPE_DRIVE_SET_COMPRESSION)) + return ERROR_INVALID_PARAMETER; + TAPE_SET_DRIVE_PARAMETERS sdp = + { + 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"); } int -fhandler_dev_tape::tape_status (struct mtget *get) +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"); +} + +int +mtinfo_drive::status (HANDLE mt, struct mtget *get) { - DWORD varlen; - TAPE_GET_MEDIA_PARAMETERS mp; int notape = 0; + DWORD tstat; 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) + if ((tstat = GetTapeStatus (mt)) == ERROR_NO_MEDIA_IN_DRIVE) 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) + 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) + get->mt_dsreg = (dp ()->DefaultBlockSize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK; if (wincap.has_ioctl_storage_get_media_types_ex ()) { 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, + if (DeviceIoControl (mt, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, NULL, 0, buf, size, &size, NULL) || GetLastError () == ERROR_MORE_DATA) { @@ -667,97 +724,657 @@ fhandler_dev_tape::tape_status (struct mtget *get) } } } - 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); + get->mt_fileno = part (partition)->file; + get->mt_blkno = part (partition)->fblock; - if (!get->mt_blkno) - get->mt_gstat |= GMT_BOT (-1); + 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); get->mt_gstat |= GMT_ONLINE (-1); - if (tape_get_feature (TAPE_DRIVE_WRITE_PROTECT) && mp.WriteProtected) + if (mp ()->WriteProtected) get->mt_gstat |= GMT_WR_PROT (-1); - if (tape_get_feature (TAPE_DRIVE_TAPE_CAPACITY)) - get->mt_capacity = get_ll (mp.Capacity); + get->mt_capacity = get_ll (mp ()->Capacity); + get->mt_remaining = get_ll (mp ()->Remaining); + } - if (tape_get_feature (TAPE_DRIVE_TAPE_REMAINING)) - get->mt_remaining = get_ll (mp.Remaining); - } + if (notape) + get->mt_gstat |= GMT_DR_OPEN (-1); - if (tape_get_feature (TAPE_DRIVE_COMPRESSION) && dp.Compression) - get->mt_gstat |= GMT_HW_COMP (-1); + if (buffer_writes) + get->mt_gstat |= GMT_IM_REP_EN (-1); /* TODO: Async writes */ - if (tape_get_feature (TAPE_DRIVE_ECC) && dp.ECC) - get->mt_gstat |= GMT_HW_ECC (-1); + else if (tstat == ERROR_DEVICE_REQUIRES_CLEANING) + get->mt_gstat |= GMT_CLN (-1); - if (tape_get_feature (TAPE_DRIVE_PADDING) && dp.DataPadding) + /* 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 (tape_get_feature (TAPE_DRIVE_REPORT_SMKS) && dp.ReportSetmarks) - get->mt_gstat |= GMT_IM_REP_EN (-1); + get->mt_erreg = 0; /* FIXME: No softerr counting */ - get->mt_erreg = lasterr; - - get->mt_minblksize = dp.MinimumBlockSize; - get->mt_maxblksize = dp.MaximumBlockSize; - get->mt_defblksize = dp.DefaultBlockSize; - get->mt_featureslow = dp.FeaturesLow; - get->mt_featureshigh = dp.FeaturesHigh; - get->mt_eotwarningzonesize = dp.EOTWarningZoneSize; + get->mt_minblksize = dp ()->MinimumBlockSize; + get->mt_maxblksize = dp ()->MaximumBlockSize; + get->mt_defblksize = dp ()->DefaultBlockSize; + get->mt_featureslow = dp ()->FeaturesLow; + get->mt_featureshigh = dp ()->FeaturesHigh; + get->mt_eotwarningzonesize = dp ()->EOTWarningZoneSize; return 0; } int -fhandler_dev_tape::tape_compression (long count) +mtinfo_drive::set_options (HANDLE mt, long options) { - TAPE_SET_DRIVE_PARAMETERS dps; + 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 + }; - if (!tape_get_feature (TAPE_DRIVE_COMPRESSION)) - return ERROR_INVALID_PARAMETER; - - 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"); + 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::tape_partition (long count) +mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf) { - 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"); + if (cmd == MTIOCTOP) + { + if (__check_invalid_read_ptr (buf, sizeof (struct mtop))) + return ERROR_NOACCESS; + 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 = 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) + { + if (__check_null_invalid_struct (buf, sizeof (struct mtget))) + return ERROR_NOACCESS; + status (mt, (struct mtget *) buf); + } + else if (cmd == MTIOCPOS) + { + if (__check_null_invalid_struct (buf, sizeof (struct mtpos))) + return ERROR_NOACCESS; + if (!get_pos (mt)) + ((struct mtpos *) buf)->mt_blkno = block; + } + + return lasterr; +} + +/**********************************************************************/ +/* mtinfo */ + +void +mtinfo::initialize (void) +{ + 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 - 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"); + { + 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); + } +} + +HANDLE mt_h; +mtinfo *mt; + +void __stdcall +mtinfo_init () +{ + mt = (mtinfo *) open_shared ("mtinfo", MTINFO_VERSION, mt_h, sizeof (mtinfo), SH_MTINFO); + mt->initialize (); +} + +/**********************************************************************/ +/* fhandler_dev_tape */ + +#define lock(err_ret_val) if (!_lock ()) return err_ret_val; + +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) + { + debug_printf ("signal_arrived"); \ + set_errno (EINTR); + } + return ret; +} + +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 ().minor); } int -fhandler_dev_tape::tape_set_partition (long count) +fhandler_dev_tape::open (int flags, mode_t) { - if (count < 0 || (unsigned long) count >= dp.MaximumPartitionCount) - lasterr = ERROR_INVALID_PARAMETER; + int ret; + + if (driveno () >= MAX_DRIVE_NUM) + { + set_errno (ENOENT); + return 0; + } + if (!(mt_mtx = CreateMutex (&sec_all, FALSE, NULL))) + { + __seterrno (); + return 0; + } + /* 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) + { + 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); + + devbufsiz = mt->drive (driveno ())->dp ()->MaximumBlockSize; + devbuf = new char (devbufsiz); + devbufstart = devbufend = 0; + } else - lasterr = _tape_set_pos (TAPE_LOGICAL_BLOCK, 0, count + 1); - return tape_error ("tape_set_partition"); + ReleaseMutex (mt_mtx); + return ret; +} + +int +fhandler_dev_tape::close (void) +{ + int ret, cret; + + 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); +} + +void +fhandler_dev_tape::raw_read (void *ptr, size_t &ulen) +{ + char *buf = (char *) ptr; + size_t len = ulen; + size_t block_size; + size_t bytes_to_read; + size_t bytes_read = 0; + int ret = 0; + + if (lastblk_to_read) + { + lastblk_to_read = 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) + { + 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 (ret) + ulen = (size_t) -1; + else + ulen = bytes_read; + unlock (); +} + +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); +} + +_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; + + 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); +} + +int +fhandler_dev_tape::fstat (struct __stat64 *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) +{ + lock (-1); + return unlock (fhandler_dev_raw::dup (child)); +} + +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)); } diff --git a/winsup/cygwin/include/cygwin/mtio.h b/winsup/cygwin/include/cygwin/mtio.h index 6aab34095..11e09f038 100644 --- a/winsup/cygwin/include/cygwin/mtio.h +++ b/winsup/cygwin/include/cygwin/mtio.h @@ -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. diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index 04b7a007d..2db2e9bdc 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -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 index 000000000..31260d17f --- /dev/null +++ b/winsup/cygwin/mtinfo.h @@ -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 (); diff --git a/winsup/cygwin/shared.cc b/winsup/cygwin/shared.cc index 9a91a1c08..42beb54f9 100644 --- a/winsup/cygwin/shared.cc +++ b/winsup/cygwin/shared.cc @@ -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; @@ -58,11 +59,17 @@ static char *offsets[] = + pround (sizeof (shared_info)) + pround (sizeof (user_info)) + pround (sizeof (console_state)), + (char *) cygwin_shared_address + + 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 diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h index 4264d5eeb..114cf0bc6 100644 --- a/winsup/cygwin/shared_info.h +++ b/winsup/cygwin/shared_info.h @@ -176,6 +176,7 @@ enum shared_locations SH_USER_SHARED, SH_SHARED_CONSOLE, SH_MYSELF, + SH_MTINFO, SH_TOTAL_SIZE }; void __stdcall memory_init ();