OSDN Git Service

Revert erroneous checkin.
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_tape.cc
1 /* fhandler_tape.cc.  See fhandler.h for a description of the fhandler
2    classes.
3
4    Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include "cygtls.h"
14 #include <sys/termios.h>
15 #include <unistd.h>
16 #include <stdlib.h>
17 #include <sys/mtio.h>
18 #include <sys/param.h>
19 #include <ddk/ntddstor.h>
20 #include "cygerrno.h"
21 #include "perprocess.h"
22 #include "security.h"
23 #include "path.h"
24 #include "fhandler.h"
25 #include "dtable.h"
26 #include "cygheap.h"
27 #include "shared_info.h"
28 #include "sigproc.h"
29 #include "mtinfo.h"
30
31 /* Media changes and bus resets are sometimes reported and the function
32    hasn't been executed.  We repeat all functions which return with one
33    of these error codes. */
34 #define TAPE_FUNC(func) while ((lasterr = (func)) == ERROR_MEDIA_CHANGED) \
35                           { \
36                             initialize (drive, false); \
37                             part (partition)->initialize (0); \
38                           }
39
40 #define IS_BOT(err) ((err) == ERROR_BEGINNING_OF_MEDIA)
41
42 #define IS_EOF(err) ((err) == ERROR_FILEMARK_DETECTED \
43                      || (err) == ERROR_SETMARK_DETECTED)
44
45 #define IS_SM(err)  ((err) == ERROR_SETMARK_DETECTED)
46
47 #define IS_EOD(err) ((err) == ERROR_END_OF_MEDIA \
48                      || (err) == ERROR_EOM_OVERFLOW \
49                      || (err) == ERROR_NO_DATA_DETECTED)
50
51 #define IS_EOM(err) ((err) == ERROR_END_OF_MEDIA \
52                      || (err) == ERROR_EOM_OVERFLOW)
53
54 /**********************************************************************/
55 /* mtinfo_part */
56
57 void
58 mtinfo_part::initialize (long nblock)
59 {
60   block = nblock;
61   if (block == 0)
62     file = fblock = 0;
63   else
64     file = fblock = -1;
65   smark = false;
66   emark = no_eof;
67 }
68
69 /**********************************************************************/
70 /* mtinfo_drive */
71
72 void
73 mtinfo_drive::initialize (int num, bool first_time)
74 {
75   drive = num;
76   partition = 0;
77   block = -1;
78   lock = unlocked;
79   if (first_time)
80     {
81       buffer_writes (true);
82       async_writes (false);
83       two_fm (false);
84       fast_eom (false);
85       auto_lock (false);
86       sysv (false);
87       nowait (false);
88     }
89   for (int i = 0; i < MAX_PARTITION_NUM; ++i)
90     part (i)->initialize ();
91 }
92
93 int
94 mtinfo_drive::get_dp (HANDLE mt)
95 {
96   DWORD len = sizeof _dp;
97   TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_DRIVE_INFORMATION, &len, &_dp));
98   return error ("get_dp");
99 }
100
101 int
102 mtinfo_drive::get_mp (HANDLE mt)
103 {
104   DWORD len = sizeof _mp;
105   TAPE_FUNC (GetTapeParameters (mt, GET_TAPE_MEDIA_INFORMATION, &len, &_mp));
106   return error ("get_mp");
107 }
108
109 int
110 mtinfo_drive::open (HANDLE mt)
111 {
112   get_dp (mt);
113   get_mp (mt);
114   get_pos (mt);
115   if (partition < MAX_PARTITION_NUM && part (partition)->block != block)
116     part (partition)->initialize (block);
117   /* The following rewind in position 0 solves a problem which appears
118    * in case of multi volume archives (at least on NT4): The last ReadFile
119    * on the previous medium returns ERROR_NO_DATA_DETECTED.  After media
120    * change, all subsequent ReadFile calls return ERROR_NO_DATA_DETECTED,
121    * too.  The call to set_pos apparently reset some internal flags.
122    * FIXME:  Is that really true or based on a misinterpretation? */
123   if (!block)
124     {
125       debug_printf ("rewind in position 0");
126       set_pos (mt, TAPE_REWIND, 0, false);
127     }
128   return error ("open");
129 }
130
131 int
132 mtinfo_drive::close (HANDLE mt, bool rewind)
133 {
134   lasterr = 0;
135   if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
136     dirty = clean;
137   if (dirty >= has_written)
138     {
139       /* If an async write is still pending, wait for completion. */
140       if (dirty == async_write_pending)
141         lasterr = async_wait (mt, NULL);
142       if (!lasterr)
143         {
144           /* if last operation was writing, write a filemark */
145           debug_printf ("writing filemark");
146           write_marks (mt, TAPE_FILEMARKS, two_fm () ? 2 : 1);
147           if (two_fm () && !lasterr && !rewind) /* Backspace over 2nd fmark. */
148             {
149               set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
150               if (!lasterr)
151                 part (partition)->fblock = 0; /* That's obvious, isn't it? */
152             }
153         }
154     }
155   else if (dirty == has_read && !rewind)
156     {
157       if (sysv ())
158         {
159           /* Under SYSV semantics, the tape is moved past the next file mark
160              after read. */
161           if (part (partition)->emark == no_eof)
162             set_pos (mt, TAPE_SPACE_FILEMARKS, 1, false);
163           else if (part (partition)->emark == eof_hit)
164             part (partition)->emark = eof;
165         }
166       else
167         {
168           /* Under BSD semantics, we must check if the filemark has been
169              inadvertendly crossed.  If so cross the filemark backwards
170              and position the tape right before EOF. */
171           if (part (partition)->emark == eof_hit)
172             set_pos (mt, TAPE_SPACE_FILEMARKS, -1, false);
173         }
174     }
175   if (rewind)
176     {
177       debug_printf ("rewinding");
178       set_pos (mt, TAPE_REWIND, 0, false);
179     }
180   if (auto_lock () && lock == auto_locked)
181     prepare (mt, TAPE_UNLOCK);
182   dirty = clean;
183   return error ("close");
184 }
185
186 int
187 mtinfo_drive::read (HANDLE mt, HANDLE mt_evt, void *ptr, size_t &ulen)
188 {
189   BOOL ret;
190   DWORD bytes_read = 0;
191
192   if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
193     return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
194   if (lasterr == ERROR_BUS_RESET)
195     {
196       ulen = 0;
197       goto out;
198     }
199   /* If an async write is still pending, wait for completion. */
200   if (dirty == async_write_pending)
201     lasterr = async_wait (mt, NULL);
202   dirty = clean;
203   if (part (partition)->emark == eof_hit)
204     {
205       part (partition)->emark = eof;
206       lasterr = ulen = 0;
207       goto out;
208     }
209   else if (part (partition)->emark == eod_hit)
210     {
211       part (partition)->emark = eod;
212       lasterr = ulen = 0;
213       goto out;
214     }
215   else if (part (partition)->emark == eod)
216     {
217       lasterr = ERROR_NO_DATA_DETECTED;
218       ulen = (size_t) -1;
219       goto out;
220     }
221   else if (part (partition)->emark == eom_hit)
222     {
223       part (partition)->emark = eom;
224       lasterr = ulen = 0;
225       goto out;
226     }
227   else if (part (partition)->emark == eom)
228     {
229       lasterr = ERROR_END_OF_MEDIA;
230       ulen = (size_t) -1;
231       goto out;
232     }
233   part (partition)->smark = false;
234   if (auto_lock () && lock < auto_locked)
235     prepare (mt, TAPE_LOCK, true);
236   ov.Offset = ov.OffsetHigh = 0;
237   ov.hEvent = mt_evt;
238   ret = ReadFile (mt, ptr, ulen, &bytes_read, &ov);
239   lasterr = ret ? 0 : GetLastError ();
240   if (lasterr == ERROR_IO_PENDING)
241     lasterr = async_wait (mt, &bytes_read);
242   ulen = (size_t) bytes_read;
243   if (bytes_read > 0)
244     {
245       long blocks_read = mp ()->BlockSize == 0
246                          ? 1 : howmany (bytes_read, mp ()->BlockSize);
247       block += blocks_read;
248       part (partition)->block += blocks_read;
249       if (part (partition)->fblock >= 0)
250         part (partition)->fblock += blocks_read;
251     }
252   if (IS_EOF (lasterr))
253     {
254       block++;
255       part (partition)->block++;
256       if (part (partition)->file >= 0)
257         part (partition)->file++;
258       part (partition)->fblock = 0;
259       part (partition)->smark = IS_SM (lasterr);
260       part (partition)->emark = bytes_read > 0 ? eof_hit : eof;
261       lasterr = 0;
262     }
263   else if (IS_EOD (lasterr))
264     {
265       if (part (partition)->emark == eof)
266         part (partition)->emark = IS_EOM (lasterr) ? eom : eod;
267       else
268         {
269           part (partition)->emark = IS_EOM (lasterr) ? eom_hit : eod_hit;
270           lasterr = 0;
271         }
272     }
273   else
274     {
275       part (partition)->emark = no_eof;
276       /* This happens if the buffer is too small when in variable block
277          size mode.  Linux returns ENOMEM here.  We're doing the same. */
278       if (lasterr == ERROR_MORE_DATA)
279         lasterr = ERROR_NOT_ENOUGH_MEMORY;
280     }
281   if (!lasterr)
282     dirty = has_read;
283 out:
284   return error ("read");
285 }
286
287 int
288 mtinfo_drive::async_wait (HANDLE mt, DWORD *bytes_written)
289 {
290   DWORD written;
291
292   bool ret = GetOverlappedResult (mt, &ov, &written, TRUE);
293   if (bytes_written)
294     *bytes_written = written;
295   return ret ? 0 : GetLastError ();
296 }
297
298 int
299 mtinfo_drive::write (HANDLE mt, HANDLE mt_evt, const void *ptr, size_t &len)
300 {
301   BOOL ret;
302   DWORD bytes_written = 0;
303   int async_err = 0;
304
305   if (GetTapeStatus (mt) == ERROR_NO_MEDIA_IN_DRIVE)
306     return lasterr = ERROR_NO_MEDIA_IN_DRIVE;
307   if (lasterr == ERROR_BUS_RESET)
308     {
309       len = 0;
310       return error ("write");
311     }
312   if (dirty == async_write_pending)
313     async_err = async_wait (mt, &bytes_written);
314   dirty = clean;
315   part (partition)->smark = false;
316   if (auto_lock () && lock < auto_locked)
317     prepare (mt, TAPE_LOCK, true);
318   ov.Offset = ov.OffsetHigh = 0;
319   ov.hEvent = mt_evt;
320   ret = WriteFile (mt, ptr, len, &bytes_written, &ov);
321   lasterr = ret ? 0: GetLastError ();
322   if (lasterr == ERROR_IO_PENDING)
323     {
324       if (async_writes () && mp ()->BlockSize == 0)
325         dirty = async_write_pending;
326       else
327         /* Wait for completion if a non-async write. */
328         lasterr = async_wait (mt, &bytes_written);
329     }
330   len = (size_t) bytes_written;
331   if (bytes_written > 0)
332     {
333       long blocks_written = mp ()->BlockSize == 0
334                          ? 1 : howmany (bytes_written, mp ()->BlockSize);
335       block += blocks_written;
336       part (partition)->block += blocks_written;
337       if (part (partition)->fblock >= 0)
338         part (partition)->fblock += blocks_written;
339     }
340   if (!lasterr && async_err)
341     lasterr = async_err;
342   if (lasterr == ERROR_EOM_OVERFLOW)
343     part (partition)->emark = eom;
344   else if (lasterr == ERROR_END_OF_MEDIA)
345     ; // FIXME?: part (partition)->emark = eom_hit;
346   else
347     {
348       part (partition)->emark = no_eof;
349       if (!lasterr)
350         dirty = has_written;
351       else if (lasterr == ERROR_IO_PENDING)
352         dirty = async_write_pending;
353     }
354   return error ("write");
355 }
356
357 int
358 mtinfo_drive::get_pos (HANDLE mt, long *ppartition, long *pblock)
359 {
360   DWORD p, low, high;
361
362   TAPE_FUNC (GetTapePosition (mt, TAPE_LOGICAL_POSITION, &p, &low, &high));
363   if (lasterr == ERROR_INVALID_FUNCTION)
364     TAPE_FUNC (GetTapePosition (mt, TAPE_ABSOLUTE_POSITION, &p, &low, &high));
365   if (!lasterr)
366     {
367       if (p > 0)
368         partition = (long) p - 1;
369       block = (long) low;
370       if (ppartition)
371         *ppartition= partition;
372       if (pblock)
373         *pblock = block;
374     }
375   else
376     {
377       partition = 0;
378       block = -1;
379     }
380   return error ("get_pos");
381 }
382
383 int
384 mtinfo_drive::_set_pos (HANDLE mt, int mode, long count, int partition,
385                         BOOL dont_wait)
386 {
387   /* If an async write is still pending, wait for completion. */
388   if (dirty == async_write_pending)
389     lasterr = async_wait (mt, NULL);
390   dirty = clean;
391   TAPE_FUNC (SetTapePosition (mt, mode, partition, count, count < 0 ? -1 : 0,
392                               dont_wait));
393   return lasterr;
394 }
395
396 int
397 mtinfo_drive::set_pos (HANDLE mt, int mode, long count,
398                        bool sfm_func)
399 {
400   int err = 0;
401   long undone = count;
402   BOOL dont_wait = FALSE;
403
404   switch (mode)
405     {
406       case TAPE_SPACE_RELATIVE_BLOCKS:
407       case TAPE_SPACE_FILEMARKS:
408       case TAPE_SPACE_SETMARKS:
409         if (!count)
410           {
411             lasterr = 0;
412             goto out;
413           }
414         break;
415       case TAPE_ABSOLUTE_BLOCK:
416       case TAPE_LOGICAL_BLOCK:
417       case TAPE_REWIND:
418         dont_wait = nowait () ? TRUE : FALSE;
419         break;
420     }
421   if (mode == TAPE_SPACE_FILEMARKS)
422     {
423       while (!err && undone > 0)
424         if (!(err = _set_pos (mt, mode, 1, 0, FALSE)) || IS_SM (err))
425           --undone;
426       while (!err && undone < 0)
427         if (!(err = _set_pos (mt, mode, -1, 0, FALSE)) || IS_SM (err))
428           ++undone;
429     }
430   else
431     err = _set_pos (mt, mode, count, 0, dont_wait);
432   switch (mode)
433     {
434       case TAPE_ABSOLUTE_BLOCK:
435       case TAPE_LOGICAL_BLOCK:
436         get_pos (mt);
437         part (partition)->initialize (block);
438         break;
439       case TAPE_REWIND:
440         if (!err)
441           {
442             block = 0;
443             part (partition)->initialize (0);
444           }
445         else
446           {
447             get_pos (mt);
448             part (partition)->initialize (block);
449           }
450         break;
451       case TAPE_SPACE_END_OF_DATA:
452         get_pos (mt);
453         part (partition)->initialize (block);
454         part (partition)->emark = IS_EOM (err) ? eom : eod;
455         break;
456       case TAPE_SPACE_FILEMARKS:
457         if (!err || IS_SM (err))
458           {
459             get_pos (mt);
460             part (partition)->block = block;
461             if (count > 0)
462               {
463                 if (part (partition)->file >= 0)
464                   part (partition)->file += count - undone;
465                 part (partition)->fblock = 0;
466                 part (partition)->smark = IS_SM (err);
467               }
468             else
469               {
470                 if (part (partition)->file >= 0)
471                   part (partition)->file += count - undone;
472                 part (partition)->fblock = -1;
473                 part (partition)->smark = false;
474               }
475             if (sfm_func)
476               err = set_pos (mt, mode, count > 0 ? -1 : 1, false);
477             else
478               part (partition)->emark = count > 0 ? eof : no_eof;
479           }
480         else if (IS_EOD (err))
481           {
482             get_pos (mt);
483             part (partition)->block = block;
484             if (part (partition)->file >= 0)
485               part (partition)->file += count - undone;
486             part (partition)->fblock = -1;
487             part (partition)->smark = false;
488             part (partition)->emark = IS_EOM (err) ? eom : eod;
489           }
490         else if (IS_BOT (err))
491           {
492             block = 0;
493             part (partition)->initialize (0);
494           }
495         else
496           {
497             get_pos (mt);
498             part (partition)->initialize (block);
499           }
500         break;
501       case TAPE_SPACE_RELATIVE_BLOCKS:
502         if (!err)
503           {
504             block += count;
505             part (partition)->block += count;
506             if (part (partition)->fblock >= 0)
507               part (partition)->fblock += count;
508             part (partition)->smark = false;
509             part (partition)->emark = no_eof;
510           }
511         else if (IS_EOF (err))
512           {
513             get_pos (mt);
514             part (partition)->block = block;
515             if (part (partition)->file >= 0)
516               part (partition)->file += count > 0 ? 1 : -1;
517             part (partition)->fblock = count > 0 ? 0 : -1;
518             part (partition)->smark = (count > 0 && IS_SM (err));
519             part (partition)->emark = count > 0 ? eof : no_eof;
520           }
521         else if (IS_EOD (err))
522           {
523             get_pos (mt);
524             part (partition)->fblock = block - part (partition)->block;
525             part (partition)->block = block;
526             part (partition)->smark = false;
527             part (partition)->emark = IS_EOM (err) ? eom : eod;
528           }
529         else if (IS_BOT (err))
530           {
531             block = 0;
532             part (partition)->initialize (0);
533           }
534         break;
535       case TAPE_SPACE_SETMARKS:
536         get_pos (mt);
537         part (partition)->block = block;
538         if (!err)
539           {
540             part (partition)->file = -1;
541             part (partition)->fblock = -1;
542             part (partition)->smark = true;
543           }
544         break;
545     }
546   lasterr = err;
547 out:
548   return error ("set_pos");
549 }
550
551 int
552 mtinfo_drive::create_partitions (HANDLE mt, long count)
553 {
554   if (dp ()->MaximumPartitionCount <= 1)
555     return ERROR_INVALID_PARAMETER;
556   if (set_pos (mt, TAPE_REWIND, 0, false))
557     goto out;
558   partition = 0;
559   part (partition)->initialize (0);
560   debug_printf ("Format tape with %s partition(s)", count <= 0 ? "one" : "two");
561   if (get_feature (TAPE_DRIVE_INITIATOR))
562     {
563       if (count <= 0)
564         TAPE_FUNC (CreateTapePartition (mt, TAPE_INITIATOR_PARTITIONS,
565                                         count <= 0 ? 0 : 2, (DWORD) count));
566     }
567   else if (get_feature (TAPE_DRIVE_FIXED))
568     {
569       /* This is supposed to work for Tandberg SLR drivers up to version
570          1.6 which missed to set the TAPE_DRIVE_INITIATOR flag.  According
571          to Tandberg, CreateTapePartition(TAPE_FIXED_PARTITIONS) apparently
572          does not ignore the dwCount parameter.  Go figure! */
573       TAPE_FUNC (CreateTapePartition (mt, TAPE_FIXED_PARTITIONS,
574                                       count <= 0 ? 0 : 2, (DWORD) count));
575     }
576   else
577     lasterr = ERROR_INVALID_PARAMETER;
578 out:
579   return error ("partition");
580 }
581
582 int
583 mtinfo_drive::set_partition (HANDLE mt, long count)
584 {
585   if (count < 0 || (unsigned long) count >= MAX_PARTITION_NUM)
586     lasterr = ERROR_INVALID_PARAMETER;
587   else if ((DWORD) count >= dp ()->MaximumPartitionCount)
588     lasterr = ERROR_IO_DEVICE;
589   else
590     {
591       int part_block = part (count)->block >= 0 ? part (count)->block : 0;
592       int err = _set_pos (mt, TAPE_LOGICAL_BLOCK, part_block, count + 1, FALSE);
593       if (err)
594         {
595           int sav_block = block;
596           int sav_partition = partition;
597           get_pos (mt);
598           if (sav_partition != partition)
599             {
600               if (partition < MAX_PARTITION_NUM
601                   && part (partition)->block != block)
602                 part (partition)->initialize (block);
603             }
604           else if (sav_block != block && partition < MAX_PARTITION_NUM)
605             part (partition)->initialize (block);
606           lasterr = err;
607         }
608       else
609         {
610           partition = count;
611           if (part (partition)->block == -1)
612             part (partition)->initialize (0);
613         }
614     }
615   return error ("set_partition");
616 }
617
618 int
619 mtinfo_drive::write_marks (HANDLE mt, int marktype, DWORD count)
620 {
621   /* If an async write is still pending, wait for completion. */
622   if (dirty == async_write_pending)
623     {
624       lasterr = async_wait (mt, NULL);
625       dirty = has_written;
626     }
627   if (marktype != TAPE_SETMARKS)
628     dirty = clean;
629   if (marktype == TAPE_FILEMARKS
630       && !get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
631     {
632       if (get_feature (TAPE_DRIVE_WRITE_LONG_FMKS))
633         marktype = TAPE_LONG_FILEMARKS;
634       else
635         marktype = TAPE_SHORT_FILEMARKS;
636     }
637   TAPE_FUNC (WriteTapemark (mt, marktype, count, FALSE));
638   int err = lasterr;
639   if (!err)
640     {
641       block += count;
642       part (partition)->block += count;
643       if (part (partition)->file >= 0)
644         part (partition)->file += count;
645       part (partition)->fblock = 0;
646       part (partition)->emark = eof;
647       part (partition)->smark = (marktype == TAPE_SETMARKS);
648     }
649   else
650     {
651       int sav_block = block;
652       int sav_partition = partition;
653       get_pos (mt);
654       if (sav_partition != partition)
655         {
656           if (partition < MAX_PARTITION_NUM
657               && part (partition)->block != block)
658             part (partition)->initialize (block);
659         }
660       else if (sav_block != block && partition < MAX_PARTITION_NUM)
661         part (partition)->initialize (block);
662       lasterr = err;
663     }
664   return error ("write_marks");
665 }
666
667 int
668 mtinfo_drive::erase (HANDLE mt, int mode)
669 {
670   switch (mode)
671     {
672       case TAPE_ERASE_SHORT:
673         if (!get_feature (TAPE_DRIVE_ERASE_SHORT))
674           mode = TAPE_ERASE_LONG;
675         break;
676       case TAPE_ERASE_LONG:
677         if (!get_feature (TAPE_DRIVE_ERASE_LONG))
678           mode = TAPE_ERASE_SHORT;
679         break;
680     }
681   TAPE_FUNC (EraseTape (mt, mode, nowait () ? TRUE : FALSE));
682   part (partition)->initialize (0);
683   return error ("erase");
684 }
685
686 int
687 mtinfo_drive::prepare (HANDLE mt, int action, bool is_auto)
688 {
689   BOOL dont_wait = FALSE;
690
691   /* If an async write is still pending, wait for completion. */
692   if (dirty == async_write_pending)
693     lasterr = async_wait (mt, NULL);
694   dirty = clean;
695   if (action == TAPE_UNLOAD || action == TAPE_LOAD || action == TAPE_TENSION)
696     dont_wait = nowait () ? TRUE : FALSE;
697   TAPE_FUNC (PrepareTape (mt, action, dont_wait));
698   /* Reset buffer after all successful preparations but lock and unlock. */
699   switch (action)
700     {
701       case TAPE_FORMAT:
702       case TAPE_UNLOAD:
703       case TAPE_LOAD:
704         initialize (drive, false);
705         break;
706       case TAPE_TENSION:
707         part (partition)->initialize (0);
708         break;
709       case TAPE_LOCK:
710         lock = lasterr ? lock_error : is_auto ? auto_locked : locked;
711         break;
712       case TAPE_UNLOCK:
713         lock = lasterr ? lock_error : unlocked;
714         break;
715     }
716   return error ("prepare");
717 }
718
719 int
720 mtinfo_drive::set_compression (HANDLE mt, long count)
721 {
722   if (!get_feature (TAPE_DRIVE_SET_COMPRESSION))
723     return ERROR_INVALID_PARAMETER;
724   TAPE_SET_DRIVE_PARAMETERS sdp =
725     {
726       dp ()->ECC,
727       count ? TRUE : FALSE,
728       dp ()->DataPadding,
729       dp ()->ReportSetmarks,
730       dp ()->EOTWarningZoneSize
731     };
732   TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
733   int err = lasterr;
734   if (!err)
735     dp ()->Compression = sdp.Compression;
736   else
737     get_dp (mt);
738   lasterr = err;
739   return error ("set_compression");
740 }
741
742 int
743 mtinfo_drive::set_blocksize (HANDLE mt, long count)
744 {
745   TAPE_SET_MEDIA_PARAMETERS smp = {count};
746   TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_MEDIA_INFORMATION, &smp));
747   return error ("set_blocksize");
748 }
749
750 int
751 mtinfo_drive::get_status (HANDLE mt, struct mtget *get)
752 {
753   int notape = 0;
754   DWORD tstat;
755
756   if (!get)
757     return ERROR_INVALID_PARAMETER;
758
759   if ((tstat = GetTapeStatus (mt)) == ERROR_NO_MEDIA_IN_DRIVE)
760     notape = 1;
761
762   memset (get, 0, sizeof *get);
763
764   get->mt_type = MT_ISUNKNOWN;
765
766   if (!notape && get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
767     get->mt_dsreg = (mp ()->BlockSize << MT_ST_BLKSIZE_SHIFT)
768                     & MT_ST_BLKSIZE_MASK;
769   else
770     get->mt_dsreg = (dp ()->DefaultBlockSize << MT_ST_BLKSIZE_SHIFT)
771                     & MT_ST_BLKSIZE_MASK;
772
773   if (wincap.has_ioctl_storage_get_media_types_ex ())
774     {
775       DWORD size = sizeof (GET_MEDIA_TYPES) + 10 * sizeof (DEVICE_MEDIA_INFO);
776       void *buf = alloca (size);
777       if (DeviceIoControl (mt, IOCTL_STORAGE_GET_MEDIA_TYPES_EX,
778                            NULL, 0, buf, size, &size, NULL)
779           || GetLastError () == ERROR_MORE_DATA)
780         {
781           PGET_MEDIA_TYPES gmt = (PGET_MEDIA_TYPES) buf;
782           for (DWORD i = 0; i < gmt->MediaInfoCount; ++i)
783             {
784               PDEVICE_MEDIA_INFO dmi = &gmt->MediaInfo[i];
785               get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
786 #define TINFO DeviceSpecific.TapeInfo
787               if (dmi->TINFO.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED)
788                 {
789                   get->mt_type = dmi->DeviceSpecific.TapeInfo.MediaType;
790                   if (dmi->TINFO.BusType == BusTypeScsi)
791                     get->mt_dsreg |=
792                       (dmi->TINFO.BusSpecificData.ScsiInformation.DensityCode
793                        << MT_ST_DENSITY_SHIFT)
794                       & MT_ST_DENSITY_MASK;
795                   break;
796                 }
797 #undef TINFO
798             }
799         }
800     }
801
802   if (!notape)
803     {
804       get->mt_resid = partition;
805       get->mt_fileno = part (partition)->file;
806       get->mt_blkno = part (partition)->fblock;
807
808       if (get->mt_blkno == 0)
809         if (get->mt_fileno == 0)
810           get->mt_gstat |= GMT_BOT (-1);
811         else
812           get->mt_gstat |= GMT_EOF (-1);
813       if (part (partition)->emark >= eod_hit)
814         get->mt_gstat |= GMT_EOD (-1);
815       if (part (partition)->emark >= eom_hit)
816         get->mt_gstat |= GMT_EOT (-1);
817
818       if (part (partition)->smark)
819         get->mt_gstat |= GMT_SM (-1);
820
821       get->mt_gstat |= GMT_ONLINE (-1);
822
823       if (mp ()->WriteProtected)
824         get->mt_gstat |= GMT_WR_PROT (-1);
825
826       get->mt_capacity = get_ll (mp ()->Capacity);
827       get->mt_remaining = get_ll (mp ()->Remaining);
828   }
829
830   if (notape)
831     get->mt_gstat |= GMT_DR_OPEN (-1);
832
833   if (buffer_writes ())
834     get->mt_gstat |= GMT_IM_REP_EN (-1);        /* TODO: Async writes */
835
836   if (tstat == ERROR_DEVICE_REQUIRES_CLEANING)
837     get->mt_gstat |= GMT_CLN (-1);
838
839   /* Cygwin specials: */
840   if (dp ()->ReportSetmarks)
841     get->mt_gstat |= GMT_REP_SM (-1);
842   if (dp ()->DataPadding)
843     get->mt_gstat |= GMT_PADDING (-1);
844   if (dp ()->ECC)
845     get->mt_gstat |= GMT_HW_ECC (-1);
846   if (dp ()->Compression)
847     get->mt_gstat |= GMT_HW_COMP (-1);
848   if (two_fm ())
849     get->mt_gstat |= GMT_TWO_FM (-1);
850   if (fast_eom ())
851     get->mt_gstat |= GMT_FAST_MTEOM (-1);
852   if (auto_lock ())
853     get->mt_gstat |= GMT_AUTO_LOCK (-1);
854   if (sysv ())
855     get->mt_gstat |= GMT_SYSV (-1);
856   if (nowait ())
857     get->mt_gstat |= GMT_NOWAIT (-1);
858   if (async_writes ())
859     get->mt_gstat |= GMT_ASYNC (-1);
860
861   get->mt_erreg = 0;                            /* FIXME: No softerr counting */
862
863   get->mt_minblksize = dp ()->MinimumBlockSize;
864   get->mt_maxblksize = dp ()->MaximumBlockSize;
865   get->mt_defblksize = dp ()->DefaultBlockSize;
866   get->mt_featureslow = dp ()->FeaturesLow;
867   get->mt_featureshigh = dp ()->FeaturesHigh;
868   get->mt_eotwarningzonesize = dp ()->EOTWarningZoneSize;
869
870   return 0;
871 }
872
873 int
874 mtinfo_drive::set_options (HANDLE mt, long options)
875 {
876   long what = (options & MT_ST_OPTIONS);
877   bool call_setparams = false;
878   bool set;
879   TAPE_SET_DRIVE_PARAMETERS sdp =
880     {
881       dp ()->ECC,
882       dp ()->Compression,
883       dp ()->DataPadding,
884       dp ()->ReportSetmarks,
885       dp ()->EOTWarningZoneSize
886     };
887
888   lasterr = 0;
889   switch (what)
890     {
891       case 0:
892         if (options == 0 || options == 1)
893           {
894             buffer_writes ((options == 1));
895           }
896         break;
897       case MT_ST_BOOLEANS:
898         buffer_writes (!!(options & MT_ST_BUFFER_WRITES));
899         async_writes (!!(options & MT_ST_ASYNC_WRITES));
900         two_fm (!!(options & MT_ST_TWO_FM));
901         fast_eom (!!(options & MT_ST_FAST_MTEOM));
902         auto_lock (!!(options & MT_ST_AUTO_LOCK));
903         sysv (!!(options & MT_ST_SYSV));
904         nowait (!!(options & MT_ST_NOWAIT));
905         if (get_feature (TAPE_DRIVE_SET_ECC))
906           sdp.ECC = !!(options & MT_ST_ECC);
907         if (get_feature (TAPE_DRIVE_SET_PADDING))
908           sdp.DataPadding = !!(options & MT_ST_PADDING);
909         if (get_feature (TAPE_DRIVE_SET_REPORT_SMKS))
910           sdp.ReportSetmarks = !!(options & MT_ST_REPORT_SM);
911         if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
912             || sdp.ReportSetmarks != dp ()->ReportSetmarks)
913           call_setparams = true;
914         break;
915       case MT_ST_SETBOOLEANS:
916       case MT_ST_CLEARBOOLEANS:
917         set = (what == MT_ST_SETBOOLEANS);
918         if (options & MT_ST_BUFFER_WRITES)
919           buffer_writes (set);
920         if (options & MT_ST_ASYNC_WRITES)
921           async_writes (set);
922         if (options & MT_ST_TWO_FM)
923           two_fm (set);
924         if (options & MT_ST_FAST_MTEOM)
925           fast_eom (set);
926         if (options & MT_ST_AUTO_LOCK)
927           auto_lock (set);
928         if (options & MT_ST_SYSV)
929           sysv (set);
930         if (options & MT_ST_NOWAIT)
931           nowait (set);
932         if (options & MT_ST_ECC)
933           sdp.ECC = set;
934         if (options & MT_ST_PADDING)
935           sdp.DataPadding = set;
936         if (options & MT_ST_REPORT_SM)
937           sdp.ReportSetmarks = set;
938         if (sdp.ECC != dp ()->ECC || sdp.DataPadding != dp ()->DataPadding
939             || sdp.ReportSetmarks != dp ()->ReportSetmarks)
940           call_setparams = true;
941         break;
942       case MT_ST_EOT_WZ_SIZE:
943         if (get_feature (TAPE_DRIVE_SET_EOT_WZ_SIZE))
944           {
945             sdp.EOTWarningZoneSize = (options & ~MT_ST_OPTIONS);
946             if (sdp.EOTWarningZoneSize != dp ()->EOTWarningZoneSize)
947               call_setparams = true;
948           }
949         break;
950     }
951   if (call_setparams)
952     {
953       TAPE_FUNC (SetTapeParameters (mt, SET_TAPE_DRIVE_INFORMATION, &sdp));
954       int err = lasterr;
955       if (!err)
956         {
957           dp ()->ECC = sdp.ECC;
958           dp ()->DataPadding = sdp.DataPadding;
959           dp ()->ReportSetmarks = sdp.ReportSetmarks;
960         }
961       else
962         get_dp (mt);
963       lasterr = err;
964     }
965   return error ("set_options");
966 }
967
968 int
969 mtinfo_drive::ioctl (HANDLE mt, unsigned int cmd, void *buf)
970 {
971   myfault efault;
972   if (efault.faulted ())
973     return ERROR_NOACCESS;
974   if (cmd == MTIOCTOP)
975     {
976       struct mtop *op = (struct mtop *) buf;
977       if (lasterr == ERROR_BUS_RESET)
978         {
979           /* If a bus reset occurs, block further access to this device
980              until the user rewinds, unloads or in any other way tries
981              to maintain a well-known tape position. */
982           if (op->mt_op != MTREW && op->mt_op != MTOFFL
983               && op->mt_op != MTRETEN && op->mt_op != MTERASE
984               && op->mt_op != MTSEEK && op->mt_op != MTEOM)
985             return ERROR_BUS_RESET;
986           /* Try to maintain last lock state after bus reset. */
987           if (lock >= auto_locked && PrepareTape (mt, TAPE_LOCK, FALSE))
988             {
989               debug_printf ("Couldn't relock drive after bus reset.");
990               lock = unlocked;
991             }
992         }
993       switch (op->mt_op)
994         {
995           case MTRESET:
996             break;
997           case MTFSF:
998             set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, false);
999             break;
1000           case MTBSF:
1001             set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, false);
1002             break;
1003           case MTFSR:
1004             set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count, false);
1005             break;
1006           case MTBSR:
1007             set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count, false);
1008             break;
1009           case MTWEOF:
1010             write_marks (mt, TAPE_FILEMARKS, op->mt_count);
1011             break;
1012           case MTREW:
1013             set_pos (mt, TAPE_REWIND, 0, false);
1014             break;
1015           case MTOFFL:
1016           case MTUNLOAD:
1017             prepare (mt, TAPE_UNLOAD);
1018             break;
1019           case MTNOP:
1020             lasterr = 0;
1021             break;
1022           case MTRETEN:
1023             if (!get_feature (TAPE_DRIVE_TENSION))
1024               lasterr = ERROR_INVALID_PARAMETER;
1025             else if (!set_pos (mt, TAPE_REWIND, 0, false))
1026               prepare (mt, TAPE_TENSION);
1027             break;
1028           case MTBSFM:
1029             set_pos (mt, TAPE_SPACE_FILEMARKS, -op->mt_count, true);
1030             break;
1031           case MTFSFM:
1032             set_pos (mt, TAPE_SPACE_FILEMARKS, op->mt_count, true);
1033             break;
1034           case MTEOM:
1035             if (fast_eom () && get_feature (TAPE_DRIVE_END_OF_DATA))
1036               set_pos (mt, TAPE_SPACE_END_OF_DATA, 0, false);
1037             else
1038               set_pos (mt, TAPE_SPACE_FILEMARKS, 32767, false);
1039             break;
1040           case MTERASE:
1041             erase (mt, TAPE_ERASE_LONG);
1042             break;
1043           case MTRAS1:
1044           case MTRAS2:
1045           case MTRAS3:
1046             lasterr = ERROR_INVALID_PARAMETER;
1047             break;
1048           case MTSETBLK:
1049             if (!get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
1050               {
1051                 lasterr = ERROR_INVALID_PARAMETER;
1052                 break;
1053               }
1054             if ((DWORD) op->mt_count == mp ()->BlockSize)
1055               {
1056                 /* Nothing has changed. */
1057                 lasterr = 0;
1058                 break;
1059               }
1060             if ((op->mt_count == 0 && !get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
1061                 || (op->mt_count > 0
1062                     && ((DWORD) op->mt_count < dp ()->MinimumBlockSize
1063                         || (DWORD) op->mt_count > dp ()->MaximumBlockSize)))
1064               {
1065                 lasterr = ERROR_INVALID_PARAMETER;
1066                 break;
1067               }
1068             if (set_blocksize (mt, op->mt_count)
1069                 && lasterr == ERROR_INVALID_FUNCTION)
1070               lasterr = ERROR_INVALID_BLOCK_LENGTH;
1071             break;
1072           case MTSEEK:
1073             if (get_feature (TAPE_DRIVE_LOGICAL_BLK))
1074               set_pos (mt, TAPE_LOGICAL_BLOCK, op->mt_count, false);
1075             else if (!get_pos (mt))
1076               set_pos (mt, TAPE_SPACE_RELATIVE_BLOCKS,
1077                        op->mt_count - block, false);
1078             break;
1079           case MTTELL:
1080             if (!get_pos (mt))
1081               op->mt_count = block;
1082             break;
1083           case MTFSS:
1084             set_pos (mt, TAPE_SPACE_SETMARKS, op->mt_count, false);
1085             break;
1086           case MTBSS:
1087             set_pos (mt, TAPE_SPACE_SETMARKS, -op->mt_count, false);
1088             break;
1089           case MTWSM:
1090             write_marks (mt, TAPE_SETMARKS, op->mt_count);
1091             break;
1092           case MTLOCK:
1093             prepare (mt, TAPE_LOCK);
1094             break;
1095           case MTUNLOCK:
1096             prepare (mt, TAPE_UNLOCK);
1097             break;
1098           case MTLOAD:
1099             prepare (mt, TAPE_LOAD);
1100             break;
1101           case MTCOMPRESSION:
1102             set_compression (mt, op->mt_count);
1103             break;
1104           case MTSETPART:
1105             set_partition (mt, op->mt_count);
1106             break;
1107           case MTMKPART:
1108             create_partitions (mt, op->mt_count);
1109             break;
1110           case MTSETDRVBUFFER:
1111             set_options (mt, op->mt_count);
1112             break;
1113           case MTSETDENSITY:
1114           default:
1115             lasterr = ERROR_INVALID_PARAMETER;
1116             break;
1117         }
1118     }
1119   else if (cmd == MTIOCGET)
1120     get_status (mt, (struct mtget *) buf);
1121   else if (cmd == MTIOCPOS && !get_pos (mt))
1122     ((struct mtpos *) buf)->mt_blkno = block;
1123
1124   return lasterr;
1125 }
1126
1127 /**********************************************************************/
1128 /* mtinfo */
1129
1130 void
1131 mtinfo::initialize ()
1132 {
1133   char name[CYG_MAX_PATH];
1134   HANDLE mtx;
1135
1136   shared_name (name, "mtinfo_mutex", 0);
1137   if (!(mtx = CreateMutex (&sec_all_nih, FALSE, name)))
1138     api_fatal ("CreateMutex '%s', %E.  Terminating.", name);
1139   WaitForSingleObject (mtx, INFINITE);
1140   if (!magic)
1141     {
1142       magic = MTINFO_MAGIC;
1143       version = MTINFO_VERSION;
1144       for (unsigned i = 0; i < MAX_DRIVE_NUM; ++i)
1145         drive (i)->initialize (i, true);
1146       ReleaseMutex (mtx);
1147       CloseHandle (mtx);
1148     }
1149   else
1150     {
1151       ReleaseMutex (mtx);
1152       CloseHandle (mtx);
1153       if (magic != MTINFO_MAGIC)
1154         api_fatal ("MT magic number screwed up: %lu, should be %lu",
1155                    magic, MTINFO_MAGIC);
1156       if (version != MTINFO_VERSION)
1157         system_printf ("MT version number mismatch: %lu, should be %lu",
1158                        version, MTINFO_VERSION);
1159     }
1160 }
1161
1162 mtinfo *mt;
1163
1164 void __stdcall
1165 mtinfo_init ()
1166 {
1167   shared_locations sh_mtinfo = SH_MTINFO;
1168   mt = (mtinfo *) open_shared ("mtinfo", MTINFO_VERSION, cygheap->mt_h, sizeof (mtinfo), sh_mtinfo);
1169   ProtectHandleINH (cygheap->mt_h);
1170   mt->initialize ();
1171 }
1172
1173 /**********************************************************************/
1174 /* fhandler_dev_tape */
1175
1176 #define lock(err_ret_val) if (!_lock ()) return err_ret_val;
1177
1178 inline bool
1179 fhandler_dev_tape::_lock ()
1180 {
1181   HANDLE obj[2] = { mt_mtx, signal_arrived };
1182   BOOL ret = WaitForMultipleObjects (2, obj, FALSE, INFINITE) == WAIT_OBJECT_0;
1183   if (!ret)
1184     {
1185       debug_printf ("signal_arrived"); \
1186       set_errno (EINTR);
1187     }
1188   return ret;
1189 }
1190
1191 inline int
1192 fhandler_dev_tape::unlock (int ret)
1193 {
1194   ReleaseMutex (mt_mtx);
1195   return ret;
1196 }
1197
1198 fhandler_dev_tape::fhandler_dev_tape ()
1199   : fhandler_dev_raw ()
1200 {
1201   debug_printf ("unit: %d", dev ().minor);
1202 }
1203
1204 int
1205 fhandler_dev_tape::open (int flags, mode_t)
1206 {
1207   int ret;
1208
1209   if (driveno () >= MAX_DRIVE_NUM)
1210     {
1211       set_errno (ENOENT);
1212       return 0;
1213     }
1214   if (!(mt_mtx = CreateMutex (&sec_all, TRUE, NULL)))
1215     {
1216       __seterrno ();
1217       return 0;
1218     }
1219   /* The O_TEXT flag is used to indicate write-through (non buffered writes)
1220      to the underlying fhandler_dev_raw::open call. */
1221   flags &= ~O_TEXT;
1222   if (!mt->drive (driveno ())->buffer_writes ())
1223     flags |= O_TEXT;
1224   ret = fhandler_dev_raw::open (flags);
1225   if (ret)
1226     {
1227       mt->drive (driveno ())->open (get_handle ());
1228
1229       /* In append mode, seek to beginning of next filemark */
1230       if (flags & O_APPEND)
1231         mt->drive (driveno ())->set_pos (get_handle (),
1232                                          TAPE_SPACE_FILEMARKS, 1, true);
1233
1234       devbufsiz = mt->drive (driveno ())->dp ()->MaximumBlockSize;
1235       devbuf = new char [devbufsiz];
1236       devbufstart = devbufend = 0;
1237     }
1238   else
1239     ReleaseMutex (mt_mtx);
1240   return ret;
1241 }
1242
1243 int
1244 fhandler_dev_tape::close ()
1245 {
1246   int ret = 0;
1247   int cret = 0;
1248
1249   if (!hExeced)
1250     {
1251       lock (-1);
1252       ret = mt->drive (driveno ())->close (get_handle (), is_rewind_device ());
1253       if (ret)
1254         __seterrno_from_win_error (ret);
1255       cret = fhandler_dev_raw::close ();
1256       unlock (0);
1257     }
1258   if (mt_evt)
1259     CloseHandle (mt_evt);
1260   CloseHandle (mt_mtx);
1261   return ret ? -1 : cret;
1262 }
1263
1264 void
1265 fhandler_dev_tape::raw_read (void *ptr, size_t &ulen)
1266 {
1267   char *buf = (char *) ptr;
1268   size_t len = ulen;
1269   size_t block_size;
1270   size_t bytes_to_read;
1271   size_t bytes_read = 0;
1272   int ret = 0;
1273
1274   if (lastblk_to_read ())
1275     {
1276       lastblk_to_read (false);
1277       ulen = 0;
1278       return;
1279     }
1280   if (!_lock ())
1281     {
1282       ulen = (size_t) -1;
1283       return;
1284     }
1285   block_size = mt->drive (driveno ())->mp ()->BlockSize;
1286   if (devbufend > devbufstart)
1287     {
1288       bytes_to_read = min (len, devbufend - devbufstart);
1289       debug_printf ("read %d bytes from buffer (rest %d)",
1290                     bytes_to_read, devbufend - devbufstart - bytes_to_read);
1291       memcpy (buf, devbuf + devbufstart, bytes_to_read);
1292       len -= bytes_to_read;
1293       bytes_read += bytes_to_read;
1294       buf += bytes_to_read;
1295       devbufstart += bytes_to_read;
1296       if (devbufstart == devbufend)
1297         devbufstart = devbufend = 0;
1298       /* If a switch to variable block_size occured, just return the buffer
1299          remains until the buffer is empty, then proceed with usual variable
1300          block size handling (one block per read call). */
1301       if (!block_size)
1302         len = 0;
1303     }
1304   if (len > 0)
1305     {
1306       if (!mt_evt && !(mt_evt = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
1307         debug_printf ("Creating event failed, %E");
1308       size_t block_fit = !block_size ? len : rounddown(len,  block_size);
1309       if (block_fit)
1310         {
1311           debug_printf ("read %d bytes from tape (rest %d)",
1312                         block_fit, len - block_fit);
1313           ret = mt->drive (driveno ())->read (get_handle (), mt_evt, buf,
1314                                               block_fit);
1315           if (ret)
1316             __seterrno_from_win_error (ret);
1317           else if (block_fit)
1318             {
1319               len -= block_fit;
1320               bytes_read += block_fit;
1321               buf += block_fit;
1322               /* Only one block in each read call, please. */
1323               if (!block_size)
1324                 len = 0;
1325             }
1326           else {
1327             len = 0;
1328             if (bytes_read)
1329               lastblk_to_read (true);
1330           }
1331         }
1332       if (!ret && len > 0)
1333         {
1334           debug_printf ("read %d bytes from tape (one block)", block_size);
1335           ret = mt->drive (driveno ())->read (get_handle (), mt_evt, devbuf,
1336                                               block_size);
1337           if (ret)
1338             __seterrno_from_win_error (ret);
1339           else if (block_size)
1340             {
1341               devbufstart = len;
1342               devbufend = block_size;
1343               bytes_read += len;
1344               memcpy (buf, devbuf, len);
1345             }
1346           else if (bytes_read)
1347             lastblk_to_read (true);
1348         }
1349     }
1350   ulen = (ret ? (size_t) -1 : bytes_read);
1351   unlock ();
1352 }
1353
1354 int
1355 fhandler_dev_tape::raw_write (const void *ptr, size_t len)
1356 {
1357   lock (-1);
1358   if (!mt_evt && !(mt_evt = CreateEvent (&sec_none, TRUE, FALSE, NULL)))
1359     debug_printf ("Creating event failed, %E");
1360   int ret = mt->drive (driveno ())->write (get_handle (), mt_evt, ptr, len);
1361   if (ret)
1362     __seterrno_from_win_error (ret);
1363   return unlock (ret ? -1 : (int) len);
1364 }
1365
1366 _off64_t
1367 fhandler_dev_tape::lseek (_off64_t offset, int whence)
1368 {
1369   struct mtop op;
1370   struct mtpos pos;
1371   DWORD block_size;
1372   _off64_t ret = ILLEGAL_SEEK;
1373
1374   lock (ILLEGAL_SEEK);
1375
1376   debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
1377
1378   block_size = mt->drive (driveno ())->mp ()->BlockSize;
1379   if (block_size == 0)
1380     {
1381       set_errno (EIO);
1382       goto out;
1383     }
1384
1385   if (ioctl (MTIOCPOS, &pos))
1386     goto out;
1387
1388   switch (whence)
1389     {
1390       case SEEK_END:
1391         op.mt_op = MTFSF;
1392         op.mt_count = 1;
1393         if (ioctl (MTIOCTOP, &op))
1394           goto out;
1395         break;
1396       case SEEK_SET:
1397         if (whence == SEEK_SET && offset < 0)
1398           {
1399             set_errno (EINVAL);
1400             goto out;
1401           }
1402         break;
1403       case SEEK_CUR:
1404         break;
1405       default:
1406         set_errno (EINVAL);
1407         goto out;
1408     }
1409
1410   op.mt_op = MTFSR;
1411   op.mt_count = offset / block_size
1412                 - (whence == SEEK_SET ? pos.mt_blkno : 0);
1413
1414   if (op.mt_count < 0)
1415     {
1416       op.mt_op = MTBSR;
1417       op.mt_count = -op.mt_count;
1418     }
1419
1420   if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
1421     goto out;
1422
1423   ret = pos.mt_blkno * block_size;
1424
1425 out:
1426   return unlock (ret);
1427 }
1428
1429 int
1430 fhandler_dev_tape::fstat (struct __stat64 *buf)
1431 {
1432   int ret;
1433
1434   if (driveno () >= MAX_DRIVE_NUM)
1435     {
1436       set_errno (ENOENT);
1437       return -1;
1438     }
1439   if (!(ret = fhandler_base::fstat (buf)))
1440     buf->st_blocks = 0;
1441   return ret;
1442 }
1443
1444 int
1445 fhandler_dev_tape::dup (fhandler_base *child)
1446 {
1447   lock (-1);
1448   fhandler_dev_tape *fh = (fhandler_dev_tape *) child;
1449   if (!DuplicateHandle (hMainProc, mt_mtx, hMainProc, &fh->mt_mtx, 0, TRUE,
1450                         DUPLICATE_SAME_ACCESS))
1451     {
1452       debug_printf ("dup(%s) failed, mutex handle %x, %E",
1453                     get_name (), mt_mtx);
1454       __seterrno ();
1455       return unlock (-1);
1456     }
1457   fh->mt_evt = NULL;
1458   if (mt_evt &&
1459       !DuplicateHandle (hMainProc, mt_evt, hMainProc, &fh->mt_evt, 0, TRUE,
1460                         DUPLICATE_SAME_ACCESS))
1461     {
1462       debug_printf ("dup(%s) failed, event handle %x, %E",
1463                     get_name (), mt_evt);
1464       __seterrno ();
1465       return unlock (-1);
1466     }
1467   return unlock (fhandler_dev_raw::dup (child));
1468 }
1469
1470 void
1471 fhandler_dev_tape::fixup_after_fork (HANDLE parent)
1472 {
1473   fhandler_dev_raw::fixup_after_fork (parent);
1474   fork_fixup (parent, mt_mtx, "mt_mtx");
1475   if (mt_evt)
1476     fork_fixup (parent, mt_evt, "mt_evt");
1477 }
1478
1479 void
1480 fhandler_dev_tape::set_close_on_exec (bool val)
1481 {
1482   fhandler_dev_raw::set_close_on_exec (val);
1483   set_no_inheritance (mt_mtx, val);
1484   if (mt_evt)
1485     set_no_inheritance (mt_evt, val);
1486 }
1487
1488 int
1489 fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
1490 {
1491   int ret = 0;
1492   lock (-1);
1493   if (cmd == MTIOCTOP || cmd == MTIOCGET || cmd == MTIOCPOS)
1494     {
1495       ret = mt->drive (driveno ())->ioctl (get_handle (), cmd, buf);
1496       if (ret)
1497         __seterrno_from_win_error (ret);
1498       return unlock (ret ? -1 : 0);
1499     }
1500   return unlock (fhandler_dev_raw::ioctl (cmd, buf));
1501 }