diff -ru ..\release\mt-st-0.9b/Makefile ./Makefile --- ..\release\mt-st-0.9b/Makefile 2005-08-16 12:16:28.000000000 -0700 +++ ./Makefile 2006-08-09 03:26:58.292856500 -0700 @@ -1,29 +1,27 @@ +CC= mingw32-gcc CFLAGS= -Wall -O2 -SBINDIR= /sbin -BINDIR= /bin -MANDIR= /usr/share/man +PREFIX= +SBINDIR= $(PREFIX)/sbin +BINDIR= $(PREFIX)/bin +MANDIR= $(PREFIX)/man -all: mt stinit +all: mt.exe -mt: mt.c - $(CC) $(CFLAGS) -o mt mt.c +mt.exe: mt.c + $(CC) $(CFLAGS) -o mt.exe mt.c mtops.c -stinit: stinit.c +stinit.exe: stinit.c $(CC) $(CFLAGS) -o stinit stinit.c -install: mt stinit - install -s mt $(BINDIR) +install: mt.exe + install mt.exe $(BINDIR) install -c -m 444 mt.1 $(MANDIR)/man1 (if [ -f $(MANDIR)/man1/mt.1.gz ] ; then \ rm -f $(MANDIR)/man1/mt.1.gz; gzip $(MANDIR)/man1/mt.1; fi) - install -s stinit $(SBINDIR) - install -c -m 444 stinit.8 $(MANDIR)/man8 - (if [ -f $(MANDIR)/man8/stinit.8.gz ] ; then \ - rm -f $(MANDIR)/man8/stinit.8.gz; gzip $(MANDIR)/man8/stinit.8; fi) dist: clean (mydir=`basename \`pwd\``;\ cd .. && tar cvvf - $$mydir | gzip -9 > $${mydir}.tar.gz) clean: - rm -f *~ \#*\# *.o mt stinit + rm -f *~ \#*\# *.o mt.exe stinit.exe diff -ru ..\release\mt-st-0.9b/mt.1 ./mt.1 --- ..\release\mt-st-0.9b/mt.1 2005-08-21 11:53:50.000000000 -0700 +++ ./mt.1 2006-08-09 03:26:58.302871100 -0700 @@ -48,20 +48,22 @@ files. The tape is positioned on the first block of the next file. .IP fsfm -Forward space +Forward space past .I count -files. -The tape is positioned on the last block of the previous file. +file marks, then backward space one file record. +This leaves the tape positioned on the last block of the file that is count-1 +files past the current file. .IP bsf Backward space .I count files. The tape is positioned on the last block of the previous file. .IP bsfm -Backward space +Backward space past .I count -files. -The tape is positioned on the first block of the next file. +file marks, then forward space one file record. +This leaves the tape positioned on the first block of the file that is count-1 +files before the current file. .IP asf The tape is positioned at the beginning of the .I count diff -ru ..\release\mt-st-0.9b/mt.c ./mt.c --- ..\release\mt-st-0.9b/mt.c 2005-08-21 11:48:06.000000000 -0700 +++ ./mt.c 2006-08-09 04:00:01.093525100 -0700 @@ -11,25 +11,35 @@ Last Modified: Sun Aug 21 21:48:06 2005 by kai.makisara */ +#include +#include +#include + +#define O_NONBLOCK 0 + #include +#if !defined(_MSC_VER) #include +#endif #include #include #include #include #include #include -#include +#include "mtops.h" #include "mtio.h" +#define ioctl tape_ioctl + #ifndef DEFTAPE -#define DEFTAPE "/dev/tape" /* default tape device */ +#define DEFTAPE "Tape0" /* default tape device */ #endif /* DEFTAPE */ -#define VERSION "0.9b" +#define VERSION "0.9b-bacula" -typedef int (* cmdfunc)(/* int, struct cmdef_tr *, int, char ** */); +typedef int (* cmdfunc)(int, struct cmdef_tr *, int, char **); typedef struct cmdef_tr { char *cmd_name; @@ -143,12 +153,14 @@ FD_RDONLY, ONE_ARG, 0}, { "defcompression", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_COMPRESSION, FD_RDONLY, ONE_ARG, 0}, +#if 0 { "stsetcln", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_CLN, FD_RDONLY, ONE_ARG, 0}, { "sttimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_TIMEOUT, FD_RDONLY, ONE_ARG, 0}, { "stlongtimeout", MTSETDRVBUFFER, do_drvbuffer, MT_ST_SET_LONG_TIMEOUT, FD_RDONLY, ONE_ARG, 0}, +#endif { "densities", 0, print_densities, 0, NO_FD, NO_ARGS, 0 }, { "setpartition", MTSETPART, do_standard, 0, FD_RDONLY, ONE_ARG, @@ -211,13 +223,19 @@ {0x30, "AIT-1 or MLR3"}, {0x31, "AIT-2"}, {0x32, "AIT-3"}, - {0x33, "SLR6"}, + {0x33, "AIT-4 or SLR6"}, {0x34, "SLR100"}, + {0x38, "AIT-E Turbo"}, + {0x39, "AIT-1 Turbo"}, + {0x3A, "AIT-2 Turbo"}, + {0x3B, "AIT-3Ex"}, {0x40, "DLT1 40 GB, or Ultrium"}, {0x41, "DLT 40GB, or Ultrium2"}, {0x42, "LTO-2"}, {0x45, "QIC-3095-MC (TR-4)"}, {0x47, "TR-5"}, + {0x48, "Quantum SDLT220"}, + {0x49, "Quantum SDLT320"}, {0x80, "DLT 15GB uncomp. or Ecrix"}, {0x81, "DLT 15GB compressed"}, {0x82, "DLT 20GB uncompressed"}, @@ -254,20 +272,25 @@ {"no-blklimits", MT_ST_NO_BLKLIMS, "drive doesn't support read block limits"}, {"can-partitions",MT_ST_CAN_PARTITIONS,"drive can handle partitioned tapes"}, {"scsi2logical", MT_ST_SCSI2LOGICAL, "logical block addresses used with SCSI-2"}, +#if 0 {"no-wait", MT_ST_NOWAIT, "immediate mode for rewind, etc."}, +#endif #ifdef MT_ST_SYSV {"sysv", MT_ST_SYSV, "enable the SystemV semantics"}, #endif +#if 0 {"cleaning", MT_ST_SET_CLN, "set the cleaning bit location and mask"}, +#endif {NULL, 0}}; static char *tape_name; /* The tape name for messages */ - int +int main(int argc, char **argv) { - int mtfd, cmd_code, i, argn, len, oflags; + int mtfd, cmd_code, i, argn, oflags; + unsigned int len; char *cmdstr; cmdef_tr *comp, *comp2; @@ -344,7 +367,7 @@ oflags = comp->cmd_fdtype == FD_RDONLY ? O_RDONLY : O_RDWR; if ((comp->error_tests & ET_ONLINE) == 0) oflags |= O_NONBLOCK; - if ((mtfd = open(tape_name, oflags)) < 0) { + if ((mtfd = tape_open(tape_name, oflags, 0)) < 0) { perror(tape_name); exit(1); } @@ -368,7 +391,7 @@ } if (mtfd >= 0) - close(mtfd); + tape_close(mtfd); return i; } @@ -409,9 +432,9 @@ do_standard(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtop mt_com; - char *endp; + char *endp = NULL; - mt_com.mt_op = cmd->cmd_code; + mt_com.mt_op = (short)cmd->cmd_code; mt_com.mt_count = (argc > 0 ? strtol(*argv, &endp, 0) : 1); if (argc > 0 && endp != *argv) { if (*endp == 'k') @@ -464,7 +487,8 @@ static int do_options(int mtfd, cmdef_tr *cmd, int argc, char **argv) { - int i, an, len; + int i, an; + unsigned int len; struct mtop mt_com; mt_com.mt_op = MTSETDRVBUFFER; @@ -596,8 +620,10 @@ type = "SCSI 1"; else if (status.mt_type == MT_ISSCSI2) type = "SCSI 2"; +#if 0 else if (status.mt_type == MT_ISONSTREAM_SC) type = "OnStream SC-, DI-, DP-, or USB"; +#endif else type = NULL; if (type == NULL) { @@ -607,7 +633,7 @@ printf("IDE-Tape (type code 0) ?\n"); else printf("Unknown tape drive type (type code %ld)\n", status.mt_type); - printf("File number=%d, block number=%d.\n", + printf("File number=%ld, block number=%ld.\n", status.mt_fileno, status.mt_blkno); printf("mt_resid: %ld, mt_erreg: 0x%lx\n", status.mt_resid, status.mt_erreg); @@ -617,14 +643,17 @@ else { printf("%s tape drive:\n", type); if (status.mt_type == MT_ISSCSI2) - printf("File number=%d, block number=%d, partition=%ld.\n", + printf("File number=%ld, block number=%ld, partition=%ld.\n", status.mt_fileno, status.mt_blkno, (status.mt_resid & 0xff)); else - printf("File number=%d, block number=%d.\n", + printf("File number=%ld, block number=%ld.\n", status.mt_fileno, status.mt_blkno); - if (status.mt_type == MT_ISSCSI1 || - status.mt_type == MT_ISSCSI2 || - status.mt_type == MT_ISONSTREAM_SC) { + if (status.mt_type == MT_ISSCSI1 + || status.mt_type == MT_ISSCSI2 +#if 0 + || status.mt_type == MT_ISONSTREAM_SC +#endif + ) { dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT; density = "no translation"; for (i=0; i < NBR_DENSITIES; i++) @@ -666,8 +695,10 @@ printf(" DR_OPEN"); if (GMT_IM_REP_EN(status.mt_gstat)) printf(" IM_REP_EN"); +#if 0 if (GMT_CLN(status.mt_gstat)) printf(" CLN"); +#endif printf("\n"); return 0; } diff -ru ..\release\mt-st-0.9b/mtio.h ./mtio.h --- ..\release\mt-st-0.9b/mtio.h 2005-08-16 12:16:28.000000000 -0700 +++ ./mtio.h 2006-08-09 03:26:58.352944100 -0700 @@ -8,9 +8,7 @@ #ifndef _LINUX_MTIO_H #define _LINUX_MTIO_H -#include -#include -#include +#include /* * Structures and definitions for mag tape io control commands @@ -150,6 +148,7 @@ }; +#ifdef USE_QIC02 /* structure for MTIOCGETCONFIG/MTIOCSETCONFIG primarily intended * as an interim solution for QIC-02 until DDI is fully implemented. */ @@ -281,6 +280,7 @@ * command */ }; +#endif /* mag tape io control commands */ #define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */ diff -ru ..\release\mt-st-0.9b/stinit.def.examples ./stinit.def.examples --- ..\release\mt-st-0.9b/stinit.def.examples 2005-08-16 12:16:28.000000000 -0700 +++ ./stinit.def.examples 2006-08-09 03:26:58.362958700 -0700 @@ -56,3 +56,169 @@ mode3 blocksize=0 density=1 # 800 bpi } +# DLT2000 / 2000XT +manufacturer="QUANTUM" model = "DLT2000" { +scsi2logical=1 +can-bsr +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +mode1 blocksize=0 density=0x81 # 10GB + compression on DLTtape III, 15+ with DLTtape IIIXT in 2000XT +mode2 blocksize=0 density=0x80 # 10GB, no compression on DLTtape III, 15 with DLTtape IIIXT in 2000XT +mode3 blocksize=0 density=0x18 # 6GB, compression not available, on DLTtape III +mode4 blocksize=0 density=0x17 #2.6GB, compression not available, on DLTtape III +} + +# DLT4000 +manufacturer="QUANTUM" model = "DLT4000" { +scsi2logical=1 +can-bsr +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards compatible, use older modes (e.g. from above) as required +mode1 blocksize=0 density=0x83 # 20GB + compression +mode2 blocksize=0 density=0x82 # 20GB, no compression +mode3 blocksize=0 density=0x81 # 10GB + compression (DLT2000 mode) with DLTtape III, 15+ with DLTtape IIIXT in 2000XT +mode4 blocksize=0 density=0x80 # 10GB, no compression (DLT2000 mode) with DLTtape III, 15 with DLTtape IIIXT in 2000XT +} + +# DLT7000 +manufacturer="QUANTUM" model = "DLT7000" { +scsi2logical=1 +can-bsr +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards compatible, use older modes (e.g. from above) as required. +mode1 blocksize=0 density=0x85 # 35GB + compression +mode2 blocksize=0 density=0x84 # 35GB, no compression +mode3 blocksize=0 density=0x83 # 20GB + compression (DLT4000 mode) +mode4 blocksize=0 density=0x82 # 20GB, no compression (DLT4000 mode) +} + +# DLT8000 +manufacturer="QUANTUM" model = "DLT8000" { +scsi2logical=1 +can-bsr=1 +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards compatible to DLT7000, use older modes (e.g. from above) as required. Modes <10GB (<0x19) not supported! +mode1 blocksize=0 density=0x89 # 40GB + compression +mode2 blocksize=0 density=0x88 # 40GB, no compression +mode3 blocksize=0 density=0x85 # 35GB + compression (DLT7000 mode) +mode4 blocksize=0 density=0x84 # 35GB, no compression (DLT7000 mode) +} + + +# SDLT220 +manufacturer="QUANTUM" model = "SuperDLT1" { +scsi2logical=1 +can-bsr=1 +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so no need to define any other modes here. +mode1 blocksize=0 density=0x48 compression=1 # 110 GB + compression +mode2 blocksize=0 density=0x48 compression=0 # 110 GB, no ompression +} + +# SDLT320 +manufacturer="QUANTUM" model = "SDLT320" { +scsi2logical=1 +can-bsr=1 +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards write compatible to SDLT220 and read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so we need only the SDL220/320 modes here +mode1 blocksize=0 density=0x49 compression=1 # 160 GB + compression +mode2 blocksize=0 density=0x49 compression=0 # 160 GB, no ompression +mode3 blocksize=0 density=0x48 compression=1 # 110 GB + compression +mode4 blocksize=0 density=0x48 compression=0 # 110 GB, no ompression +} + +# SDLT600 +manufacturer="QUANTUM" model = "SDLT600" { +scsi2logical=1 +can-bsr=1 +auto-lock=0 +two-fms=0 +drive-buffering=1 +buffer-writes +read-ahead=1 +async-writes=1 +can-partitions=0 +fast-mteom=1 +# +# If your stinit supports the timeouts: +timeout=3600 # 1 hour +long-timeout=14400 # 4 hours +# +# Drive is backwards read compatible to SDLT220/320 and VS160. Mode settings are only required for writing, so we need only the native SDLT600 mode here. +mode1 blocksize=0 density=0x4a compression=1 # 300 GB + compression +mode2 blocksize=0 density=0x4a compression=0 # 300 GB, no ompression +mode3 blocksize=0 density=0x4a compression=1 # 300 GB + compression +mode4 blocksize=0 density=0x4a compression=0 # 300 GB, no ompression +} + --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ mtops.c 2006-08-09 04:03:09.307917500 -0700 @@ -0,0 +1,1163 @@ +/* + * mtops.cpp - Emulate the Linux st (scsi tape) driver on Microsoft Windows. + * + * Author: Robert Nelson, May, 2006 + * + * Version $Id: mt.patch 3802 2006-12-14 11:41:02Z kerns $ + * + * Copyright (C) 2006 Free Software Foundation Europe e.V. + * + * This file was contributed to the Bacula project by Robert Nelson. + * + * Robert Nelson has been granted a perpetual, worldwide, + * non-exclusive, no-charge, royalty-free, irrevocable copyright + * license to reproduce, prepare derivative works of, publicly + * display, publicly perform, sublicense, and distribute the original + * work contributed by Robert Nelson to the Bacula project in source + * or object form. + * + * If you wish to license contributions from Robert Nelson + * under an alternate open source license please contact + * Robert Nelson . + */ +/* + Copyright (C) 2006 Free Software Foundation Europe e.V. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + version 2 as amended with additional clauses defined in the + file LICENSE in the main source directory. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + the file LICENSE for additional details. + + */ + +#include +#include + +#include +#include + +#include "mtops.h" +#include "mtio.h" +#if defined(_MSC_VER) +#include +#include +#else +#include +#include +#endif + +#ifndef __cplusplus +typedef char bool; +#define true 1 +#define false 0 +#endif + +// +// SCSI bus status codes. +// + +#define SCSISTAT_GOOD 0x00 +#define SCSISTAT_CHECK_CONDITION 0x02 +#define SCSISTAT_CONDITION_MET 0x04 +#define SCSISTAT_BUSY 0x08 +#define SCSISTAT_INTERMEDIATE 0x10 +#define SCSISTAT_INTERMEDIATE_COND_MET 0x14 +#define SCSISTAT_RESERVATION_CONFLICT 0x18 +#define SCSISTAT_COMMAND_TERMINATED 0x22 +#define SCSISTAT_QUEUE_FULL 0x28 + +/* Forward referenced functions */ + +extern char my_name[]; +extern int debug_level; + +inline SHORT Read16BitSigned(const unsigned char *pValue) +{ + return (SHORT)(((USHORT)pValue[0] << 8) | (USHORT)pValue[1]); +} + +inline USHORT Read16BitUnsigned(const unsigned char *pValue) +{ + return (((USHORT)pValue[0] << 8) | (USHORT)pValue[1]); +} + +inline LONG Read24BitSigned(const unsigned char *pValue) +{ + return ((LONG)(((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | + (ULONG)pValue[2])) << 8 >> 8; +} + +inline ULONG Read24BitUnsigned(const unsigned char *pValue) +{ + return ((ULONG)pValue[0] << 16) | ((ULONG)pValue[1] << 8) | (ULONG)pValue[2]; +} + +inline LONG Read32BitSigned(const unsigned char *pValue) +{ + return (LONG)(((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) | + ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]); +} + +inline ULONG Read32BitUnsigned(const unsigned char *pValue) +{ + return (((ULONG)pValue[0] << 24) | ((ULONG)pValue[1] << 16) | + ((ULONG)pValue[2] << 8) | (ULONG)pValue[3]); +} + +inline LONGLONG Read64BitSigned(const unsigned char *pValue) +{ + return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) | + ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) | + ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) | + ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]); +} + +inline ULONGLONG Read64BitUnsigned(const unsigned char *pValue) +{ + return (LONGLONG)(((ULONGLONG)pValue[0] << 56) | ((ULONGLONG)pValue[1] << 48) | + ((ULONGLONG)pValue[2] << 40) | ((ULONGLONG)pValue[3] << 32) | + ((ULONGLONG)pValue[4] << 24) | ((ULONGLONG)pValue[5] << 16) | + ((ULONGLONG)pValue[6] << 8) | (ULONGLONG)pValue[7]); +} + +typedef struct _TAPE_POSITION_INFO +{ + UCHAR AtPartitionStart:1; + UCHAR AtPartitionEnd:1; + UCHAR PartitionBlockValid:1; + UCHAR FileSetValid:1; + UCHAR :4; + UCHAR Reserved1[3]; + ULONG Partition; + ULONGLONG BlockNumber; + ULONGLONG FileNumber; + ULONGLONG SetNumber; +} TAPE_POSITION_INFO, *PTAPE_POSITION_INFO; + +typedef struct _TAPE_HANDLE_INFO +{ + HANDLE OSHandle; + bool bEOD; + bool bEOF; + bool bEOT; + bool bBlockValid; + ULONG FeaturesLow; + ULONG FeaturesHigh; + ULONG ulFile; + ULONGLONG ullFileStart; + +} TAPE_HANDLE_INFO, *PTAPE_HANDLE_INFO; + +TAPE_HANDLE_INFO TapeHandleTable[] = +{ + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE }, + { INVALID_HANDLE_VALUE } +}; + +#define NUMBER_HANDLE_ENTRIES (sizeof(TapeHandleTable) / sizeof(TapeHandleTable[0])) + +DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo); +DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize); + +int tape_get(int fd, struct mtget *mt_get); +int tape_op(int fd, struct mtop *mt_com); +int tape_pos(int fd, struct mtpos *mt_pos); + +int +tape_open(const char *file, int flags, int mode) +{ + HANDLE hDevice = INVALID_HANDLE_VALUE; + char szDeviceName[256] = "\\\\.\\"; + int idxFile; + DWORD dwResult; + + for (idxFile = 0; idxFile < (int)NUMBER_HANDLE_ENTRIES; idxFile++) { + if (TapeHandleTable[idxFile].OSHandle == INVALID_HANDLE_VALUE) { + break; + } + } + + if (idxFile >= (int)NUMBER_HANDLE_ENTRIES) { + return EMFILE; + } + + memset(&TapeHandleTable[idxFile], 0, sizeof(TapeHandleTable[idxFile])); + + if (file[0] != '\\' && file[0] != '/') { + strncpy(&szDeviceName[4], file, sizeof(szDeviceName) - 4); + } else { + strncpy(&szDeviceName[0], file, sizeof(szDeviceName)); + } + + szDeviceName[sizeof(szDeviceName) - 1] = '\0'; + + hDevice = CreateFile(szDeviceName, FILE_ALL_ACCESS, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (hDevice != INVALID_HANDLE_VALUE) { + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[idxFile]; + + memset(pHandleInfo, 0, sizeof(*pHandleInfo)); + + pHandleInfo->OSHandle = hDevice; + + TAPE_GET_DRIVE_PARAMETERS TapeDriveParameters; + DWORD dwSize = sizeof(TapeDriveParameters); + + dwResult = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &dwSize, &TapeDriveParameters); + if (dwResult == NO_ERROR) { + pHandleInfo->FeaturesLow = TapeDriveParameters.FeaturesLow; + pHandleInfo->FeaturesHigh = TapeDriveParameters.FeaturesHigh; + } + + TAPE_POSITION_INFO TapePositionInfo; + + dwResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo); + + if (dwResult == NO_ERROR) { + if (TapePositionInfo.AtPartitionStart || TapePositionInfo.AtPartitionEnd || + (TapePositionInfo.PartitionBlockValid && TapePositionInfo.BlockNumber == 0)) { + pHandleInfo->ulFile = 0; + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = 0; + } else if (TapePositionInfo.FileSetValid) { + pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber; + } + } + } else { + DWORD dwError = GetLastError(); + + switch (dwError) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + errno = ENOENT; + break; + + case ERROR_TOO_MANY_OPEN_FILES: + errno = EMFILE; + break; + + default: + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_INVALID_NAME: + errno = EACCES; + break; + + case ERROR_FILE_EXISTS: + errno = EEXIST; + break; + + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + } + + return(int) -1; + } + + return (int)idxFile + 3; +} + +int +tape_read(int fd, void *buffer, unsigned int count) +{ + if (buffer == NULL) { + errno = EINVAL; + return -1; + } + + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || + TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + DWORD bytes_read; + BOOL bResult; + + bResult = ReadFile(pHandleInfo->OSHandle, buffer, count, &bytes_read, NULL); + + if (bResult) { + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + return bytes_read; + } else { + int iReturnValue = 0; + DWORD last_error = GetLastError(); + + switch (last_error) { + + case ERROR_FILEMARK_DETECTED: + pHandleInfo->bEOF = true; + break; + + case ERROR_END_OF_MEDIA: + pHandleInfo->bEOT = true; + break; + + case ERROR_NO_MEDIA_IN_DRIVE: + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + errno = ENOMEDIUM; + iReturnValue = -1; + break; + + case ERROR_NO_DATA_DETECTED: + pHandleInfo->bEOD = true; + break; + + case ERROR_INVALID_HANDLE: + case ERROR_ACCESS_DENIED: + case ERROR_LOCK_VIOLATION: + errno = EBADF; + iReturnValue = -1; + break; + + default: + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + errno = EIO; + iReturnValue = -1; + } + + return iReturnValue; + } +} + +int +tape_write(int fd, const void *buffer, unsigned int count) +{ + if (buffer == NULL) { + errno = EINVAL; + return -1; + } + + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + DWORD bytes_written; + BOOL bResult; + + bResult = WriteFile(pHandleInfo->OSHandle, buffer, count, &bytes_written, NULL); + + if (bResult) { + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + return bytes_written; + } else { + DWORD last_error = GetLastError(); + + switch (last_error) { + case ERROR_END_OF_MEDIA: + case ERROR_DISK_FULL: + pHandleInfo->bEOT = true; + errno = ENOSPC; + break; + + case ERROR_NO_MEDIA_IN_DRIVE: + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + errno = ENOMEDIUM; + break; + + case ERROR_INVALID_HANDLE: + case ERROR_ACCESS_DENIED: + errno = EBADF; + break; + + default: + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + errno = EIO; + break; + } + return -1; + } +} + +int +tape_close(int fd) +{ + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || + TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + if (!CloseHandle(pHandleInfo->OSHandle)) { + pHandleInfo->OSHandle = INVALID_HANDLE_VALUE; + errno = EBADF; + return -1; + } + + pHandleInfo->OSHandle = INVALID_HANDLE_VALUE; + + return 0; +} + +int +tape_ioctl(int fd, unsigned long int request, ...) +{ + va_list argp; + int result; + + va_start(argp, request); + + switch (request) { + case MTIOCTOP: + result = tape_op(fd, va_arg(argp, struct mtop *)); + break; + + case MTIOCGET: + result = tape_get(fd, va_arg(argp, struct mtget *)); + break; + + case MTIOCPOS: + result = tape_pos(fd, va_arg(argp, struct mtpos *)); + break; + + default: + errno = ENOTTY; + result = -1; + break; + } + + va_end(argp); + + return result; +} + +int tape_op(int fd, struct mtop *mt_com) +{ + DWORD result = NO_ERROR; + int index; + + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || + TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + switch (mt_com->mt_op) + { + case MTRESET: + case MTNOP: + case MTSETDRVBUFFER: + break; + + default: + case MTRAS1: + case MTRAS2: + case MTRAS3: + case MTSETDENSITY: + errno = ENOTTY; + result = (DWORD)-1; + break; + + case MTFSF: + for (index = 0; index < mt_com->mt_count; index++) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE); + if (result == NO_ERROR) { + pHandleInfo->ulFile++; + pHandleInfo->bEOF = true; + pHandleInfo->bEOT = false; + } + } + break; + + case MTBSF: + for (index = 0; index < mt_com->mt_count; index++) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE); + if (result == NO_ERROR) { + pHandleInfo->ulFile--; + pHandleInfo->bBlockValid = false; + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } + } + break; + + case MTFSR: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, mt_com->mt_count, 0, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } else if (result == ERROR_FILEMARK_DETECTED) { + pHandleInfo->bEOF = true; + } + break; + + case MTBSR: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_RELATIVE_BLOCKS, 0, -mt_com->mt_count, ~0, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } else if (result == ERROR_FILEMARK_DETECTED) { + pHandleInfo->ulFile--; + pHandleInfo->bBlockValid = false; + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } + break; + + case MTWEOF: + result = WriteTapemark(pHandleInfo->OSHandle, TAPE_FILEMARKS, mt_com->mt_count, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOF = true; + pHandleInfo->bEOT = false; + pHandleInfo->ulFile += mt_com->mt_count; + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = 0; + } + break; + + case MTREW: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_REWIND, 0, 0, 0, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->ulFile = 0; + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = 0; + } + break; + + case MTOFFL: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->ulFile = 0; + pHandleInfo->ullFileStart = 0; + } + break; + + case MTRETEN: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_TENSION, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->ulFile = 0; + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = 0; + } + break; + + case MTBSFM: + for (index = 0; index < mt_com->mt_count; index++) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE); + if (result == NO_ERROR) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE); + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } + } + break; + + case MTFSFM: + for (index = 0; index < mt_com->mt_count; index++) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, mt_com->mt_count, 0, FALSE); + if (result == NO_ERROR) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, (DWORD)-1, ~0, FALSE); + pHandleInfo->bEOD = false; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + } + } + break; + + case MTEOM: + for ( ; ; ) { + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_FILEMARKS, 0, 1, 0, FALSE); + if (result != NO_ERROR) { + pHandleInfo->bEOF = false; + + if (result == ERROR_END_OF_MEDIA) { + pHandleInfo->bEOD = true; + pHandleInfo->bEOT = true; + return 0; + } + if (result == ERROR_NO_DATA_DETECTED) { + pHandleInfo->bEOD = true; + pHandleInfo->bEOT = false; + return 0; + } + break; + } else { + pHandleInfo->bEOF = true; + pHandleInfo->ulFile++; + } + } + break; + + case MTERASE: + result = EraseTape(pHandleInfo->OSHandle, TAPE_ERASE_LONG, FALSE); + if (result == NO_ERROR) { + pHandleInfo->bEOD = true; + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->ulFile = 0; + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = 0; + } + break; + + case MTSETBLK: + { + TAPE_SET_MEDIA_PARAMETERS SetMediaParameters; + + SetMediaParameters.BlockSize = mt_com->mt_count; + result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_MEDIA_INFORMATION, &SetMediaParameters); + } + break; + + case MTSEEK: + { + TAPE_POSITION_INFO TapePositionInfo; + + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, 0, mt_com->mt_count, 0, FALSE); + + memset(&TapePositionInfo, 0, sizeof(TapePositionInfo)); + DWORD dwPosResult = GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo); + if (dwPosResult == NO_ERROR && TapePositionInfo.FileSetValid) { + pHandleInfo->ulFile = (ULONG)TapePositionInfo.FileNumber; + } else { + pHandleInfo->ulFile = ~0U; + } + } + break; + + case MTTELL: + { + DWORD partition; + DWORD offset; + DWORD offsetHi; + + result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi); + if (result == NO_ERROR) { + return offset; + } + } + break; + + case MTFSS: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, mt_com->mt_count, 0, FALSE); + break; + + case MTBSS: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_SPACE_SETMARKS, 0, -mt_com->mt_count, ~0, FALSE); + break; + + case MTWSM: + result = WriteTapemark(pHandleInfo->OSHandle, TAPE_SETMARKS, mt_com->mt_count, FALSE); + break; + + case MTLOCK: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOCK, FALSE); + break; + + case MTUNLOCK: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOCK, FALSE); + break; + + case MTLOAD: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_LOAD, FALSE); + break; + + case MTUNLOAD: + result = PrepareTape(pHandleInfo->OSHandle, TAPE_UNLOAD, FALSE); + break; + + case MTCOMPRESSION: + { + TAPE_GET_DRIVE_PARAMETERS GetDriveParameters; + TAPE_SET_DRIVE_PARAMETERS SetDriveParameters; + DWORD size; + + size = sizeof(GetDriveParameters); + + result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &GetDriveParameters); + + if (result == NO_ERROR) + { + SetDriveParameters.ECC = GetDriveParameters.ECC; + SetDriveParameters.Compression = (BOOLEAN)mt_com->mt_count; + SetDriveParameters.DataPadding = GetDriveParameters.DataPadding; + SetDriveParameters.ReportSetmarks = GetDriveParameters.ReportSetmarks; + SetDriveParameters.EOTWarningZoneSize = GetDriveParameters.EOTWarningZoneSize; + + result = SetTapeParameters(pHandleInfo->OSHandle, SET_TAPE_DRIVE_INFORMATION, &SetDriveParameters); + } + } + break; + + case MTSETPART: + result = SetTapePosition(pHandleInfo->OSHandle, TAPE_LOGICAL_BLOCK, mt_com->mt_count, 0, 0, FALSE); + break; + + case MTMKPART: + if (mt_com->mt_count == 0) + { + result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 1, 0); + } + else + { + result = CreateTapePartition(pHandleInfo->OSHandle, TAPE_INITIATOR_PARTITIONS, 2, mt_com->mt_count); + } + break; + } + + if ((result == NO_ERROR && pHandleInfo->bEOF) || + (result == ERROR_FILEMARK_DETECTED && mt_com->mt_op == MTFSR)) { + + TAPE_POSITION_INFO TapePositionInfo; + + if (GetTapePositionInfo(pHandleInfo->OSHandle, &TapePositionInfo) == NO_ERROR) { + pHandleInfo->bBlockValid = true; + pHandleInfo->ullFileStart = TapePositionInfo.BlockNumber; + } + } + + switch (result) { + case NO_ERROR: + case (DWORD)-1: /* Error has already been translated into errno */ + break; + + default: + case ERROR_FILEMARK_DETECTED: + errno = EIO; + break; + + case ERROR_END_OF_MEDIA: + pHandleInfo->bEOT = true; + errno = EIO; + break; + + case ERROR_NO_DATA_DETECTED: + pHandleInfo->bEOD = true; + errno = EIO; + break; + + case ERROR_NO_MEDIA_IN_DRIVE: + pHandleInfo->bEOF = false; + pHandleInfo->bEOT = false; + pHandleInfo->bEOD = false; + errno = ENOMEDIUM; + break; + + case ERROR_INVALID_HANDLE: + case ERROR_ACCESS_DENIED: + case ERROR_LOCK_VIOLATION: + errno = EBADF; + break; + } + + return result == NO_ERROR ? 0 : -1; +} + +int tape_get(int fd, struct mtget *mt_get) +{ + TAPE_POSITION_INFO pos_info; + BOOL result; + + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || + TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + if (GetTapePositionInfo(pHandleInfo->OSHandle, &pos_info) != NO_ERROR) { + return -1; + } + + DWORD density = 0; + DWORD blocksize = 0; + + result = GetDensityBlockSize(pHandleInfo->OSHandle, &density, &blocksize); + + if (result != NO_ERROR) { + TAPE_GET_DRIVE_PARAMETERS drive_params; + DWORD size; + + size = sizeof(drive_params); + + result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_DRIVE_INFORMATION, &size, &drive_params); + + if (result == NO_ERROR) { + blocksize = drive_params.DefaultBlockSize; + } + } + + mt_get->mt_type = MT_ISSCSI2; + + // Partition # + mt_get->mt_resid = pos_info.PartitionBlockValid ? pos_info.Partition : (ULONG)-1; + + // Density / Block Size + mt_get->mt_dsreg = ((density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK) | + ((blocksize << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK); + + mt_get->mt_gstat = 0x00010000; /* Immediate report mode.*/ + + if (pHandleInfo->bEOF) { + mt_get->mt_gstat |= 0x80000000; // GMT_EOF + } + + if (pos_info.PartitionBlockValid && pos_info.BlockNumber == 0) { + mt_get->mt_gstat |= 0x40000000; // GMT_BOT + } + + if (pHandleInfo->bEOT) { + mt_get->mt_gstat |= 0x20000000; // GMT_EOT + } + + if (pHandleInfo->bEOD) { + mt_get->mt_gstat |= 0x08000000; // GMT_EOD + } + + TAPE_GET_MEDIA_PARAMETERS media_params; + DWORD size = sizeof(media_params); + + result = GetTapeParameters(pHandleInfo->OSHandle, GET_TAPE_MEDIA_INFORMATION, &size, &media_params); + + if (result == NO_ERROR && media_params.WriteProtected) { + mt_get->mt_gstat |= 0x04000000; // GMT_WR_PROT + } + + result = GetTapeStatus(pHandleInfo->OSHandle); + + if (result != NO_ERROR) { + if (result == ERROR_NO_MEDIA_IN_DRIVE) { + mt_get->mt_gstat |= 0x00040000; // GMT_DR_OPEN + } + } else { + mt_get->mt_gstat |= 0x01000000; // GMT_ONLINE + } + + // Recovered Error Count + mt_get->mt_erreg = 0; + + // File Number + mt_get->mt_fileno = (__kernel_daddr_t)pHandleInfo->ulFile; + + // Block Number + mt_get->mt_blkno = (__kernel_daddr_t)(pHandleInfo->bBlockValid ? pos_info.BlockNumber - pHandleInfo->ullFileStart : (ULONGLONG)-1); + + return 0; +} + +#define SERVICEACTION_SHORT_FORM_BLOCKID 0 +#define SERVICEACTION_SHORT_FORM_VENDOR_SPECIFIC 1 +#define SERVICEACTION_LONG_FORM 6 +#define SERVICEACTION_EXTENDED_FORM 8 + + +typedef struct _SCSI_READ_POSITION_SHORT_BUFFER +{ + UCHAR :1; + UCHAR PERR:1; + UCHAR BPU:1; + UCHAR :1; + UCHAR BYCU:1; + UCHAR BCU:1; + UCHAR EOP:1; + UCHAR BOP:1; + UCHAR Partition; + UCHAR Reserved1[2]; + UCHAR FirstBlock[4]; + UCHAR LastBlock[4]; + UCHAR Reserved2; + UCHAR NumberBufferBlocks[3]; + UCHAR NumberBufferBytes[4]; +} SCSI_READ_POSITION_SHORT_BUFFER, *PSCSI_READ_POSITION_SHORT_BUFFER; + +typedef struct _SCSI_READ_POSITION_LONG_BUFFER +{ + UCHAR :2; + UCHAR BPU:1; + UCHAR MPU:1; + UCHAR :2; + UCHAR EOP:1; + UCHAR BOP:1; + UCHAR Reserved3[3]; + UCHAR Partition[4]; + UCHAR BlockNumber[8]; + UCHAR FileNumber[8]; + UCHAR SetNumber[8]; +} SCSI_READ_POSITION_LONG_BUFFER, *PSCSI_READ_POSITION_LONG_BUFFER; + +typedef struct _SCSI_READ_POSITION_EXTENDED_BUFFER +{ + UCHAR :1; + UCHAR PERR:1; + UCHAR LOPU:1; + UCHAR :1; + UCHAR BYCU:1; + UCHAR LOCU:1; + UCHAR EOP:1; + UCHAR BOP:1; + UCHAR Partition; + UCHAR AdditionalLength[2]; + UCHAR Reserved1; + UCHAR NumberBufferObjects[3]; + UCHAR FirstLogicalObject[8]; + UCHAR LastLogicalObject[8]; + UCHAR NumberBufferObjectBytes[8]; +} SCSI_READ_POSITION_EXTENDED_BUFFER, *PSCSI_READ_POSITION_EXTENDED_BUFFER; + +typedef union _READ_POSITION_RESULT { + SCSI_READ_POSITION_SHORT_BUFFER ShortBuffer; + SCSI_READ_POSITION_LONG_BUFFER LongBuffer; + SCSI_READ_POSITION_EXTENDED_BUFFER ExtendedBuffer; +} READ_POSITION_RESULT, *PREAD_POSITION_RESULT; + +DWORD GetTapePositionInfo(HANDLE hDevice, PTAPE_POSITION_INFO TapePositionInfo) +{ + PSCSI_PASS_THROUGH ScsiPassThrough; + BOOL bResult; + DWORD dwBytesReturned; + int pass; + + const DWORD dwBufferSize = sizeof(SCSI_PASS_THROUGH) + sizeof(READ_POSITION_RESULT) + 28; + + memset(TapePositionInfo, 0, sizeof(*TapePositionInfo)); + + ScsiPassThrough = (PSCSI_PASS_THROUGH)malloc(dwBufferSize); + + for (pass = 0; pass < 2; pass++) + { + memset(ScsiPassThrough, 0, dwBufferSize); + + ScsiPassThrough->Length = sizeof(SCSI_PASS_THROUGH); + + ScsiPassThrough->CdbLength = 10; + ScsiPassThrough->SenseInfoLength = 28; + ScsiPassThrough->DataIn = 1; + ScsiPassThrough->DataTransferLength = sizeof(SCSI_READ_POSITION_LONG_BUFFER); + ScsiPassThrough->TimeOutValue = 1000; + ScsiPassThrough->DataBufferOffset = sizeof(SCSI_PASS_THROUGH) + 28; + ScsiPassThrough->SenseInfoOffset = sizeof(SCSI_PASS_THROUGH); + + ScsiPassThrough->Cdb[0] = 0x34; // READ POSITION + + switch (pass) + { + case 0: + ScsiPassThrough->Cdb[1] = SERVICEACTION_LONG_FORM; + break; + + case 1: + ScsiPassThrough->Cdb[1] = SERVICEACTION_SHORT_FORM_BLOCKID; + break; + } + + bResult = DeviceIoControl( hDevice, + IOCTL_SCSI_PASS_THROUGH, + ScsiPassThrough, sizeof(SCSI_PASS_THROUGH), + ScsiPassThrough, dwBufferSize, + &dwBytesReturned, + NULL); + + if (bResult && dwBytesReturned >= (offsetof(SCSI_PASS_THROUGH, ScsiStatus) + sizeof(ScsiPassThrough->ScsiStatus))) { + if (ScsiPassThrough->ScsiStatus == SCSISTAT_GOOD) { + PREAD_POSITION_RESULT pPosResult = (PREAD_POSITION_RESULT)((PUCHAR)ScsiPassThrough + ScsiPassThrough->DataBufferOffset); + + switch (pass) + { + case 0: // SERVICEACTION_LONG_FORM + { + TapePositionInfo->AtPartitionStart = pPosResult->LongBuffer.BOP; + TapePositionInfo->AtPartitionEnd = pPosResult->LongBuffer.EOP; + + if (!TapePositionInfo->PartitionBlockValid) { + TapePositionInfo->PartitionBlockValid = !pPosResult->LongBuffer.BPU; + + if (TapePositionInfo->PartitionBlockValid) { + TapePositionInfo->Partition = Read32BitUnsigned(pPosResult->LongBuffer.Partition); + TapePositionInfo->BlockNumber = Read64BitUnsigned(pPosResult->LongBuffer.BlockNumber); + } + } + + TapePositionInfo->FileSetValid = !pPosResult->LongBuffer.MPU; + if (TapePositionInfo->FileSetValid) { + TapePositionInfo->FileNumber = Read64BitUnsigned(pPosResult->LongBuffer.FileNumber); + TapePositionInfo->SetNumber = Read64BitUnsigned(pPosResult->LongBuffer.SetNumber); + } + } + break; + + case 1: // SERVICEACTION_SHORT_FORM_BLOCKID + { + // pPosResult->ShortBuffer.PERR; + // pPosResult->ShortBuffer.BYCU; + // pPosResult->ShortBuffer.BCU; + TapePositionInfo->AtPartitionStart = pPosResult->ShortBuffer.BOP; + TapePositionInfo->AtPartitionEnd = pPosResult->ShortBuffer.EOP; + + if (!TapePositionInfo->PartitionBlockValid) { + TapePositionInfo->PartitionBlockValid = !pPosResult->ShortBuffer.BPU; + + if (TapePositionInfo->PartitionBlockValid) { + TapePositionInfo->Partition = pPosResult->ShortBuffer.Partition; + TapePositionInfo->BlockNumber = Read32BitUnsigned(pPosResult->ShortBuffer.FirstBlock); + } + } + // Read32BitsUnsigned(pPosResult->ShortBuffer.LastBlock); + // Read24BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBlocks); + // Read32BitsUnsigned(pPosResult->ShortBuffer.NumberBufferBytes); + } + break; + } + } + } + } + free(ScsiPassThrough); + + return NO_ERROR; +} + +DWORD GetDensityBlockSize(HANDLE hDevice, DWORD *pdwDensity, DWORD *pdwBlockSize) +{ + DWORD dwBufferSize = sizeof(GET_MEDIA_TYPES) + 5 * sizeof(DEVICE_MEDIA_INFO); + GET_MEDIA_TYPES * pGetMediaTypes = (GET_MEDIA_TYPES *)malloc(dwBufferSize); + BOOL bResult; + DWORD dwResult; + DWORD idxMedia; + + if (pGetMediaTypes == NULL) { + return ERROR_OUTOFMEMORY; + } + + do { + DWORD dwBytesReturned; + + bResult = DeviceIoControl( hDevice, + IOCTL_STORAGE_GET_MEDIA_TYPES_EX, + NULL, 0, + (LPVOID)pGetMediaTypes, dwBufferSize, + &dwBytesReturned, + NULL); + + if (!bResult) { + dwResult = GetLastError(); + + if (dwResult != ERROR_INSUFFICIENT_BUFFER) { + free(pGetMediaTypes); + return dwResult; + } + + dwBufferSize += 6 * sizeof(DEVICE_MEDIA_INFO); + + GET_MEDIA_TYPES * pNewBuffer = (GET_MEDIA_TYPES *)realloc(pGetMediaTypes, dwBufferSize); + + if (pNewBuffer != pGetMediaTypes) { + free(pGetMediaTypes); + + if (pNewBuffer == NULL) { + return ERROR_OUTOFMEMORY; + } + + pGetMediaTypes = pNewBuffer; + } + } + } while (!bResult); + + if (pGetMediaTypes->DeviceType != FILE_DEVICE_TAPE) { + free(pGetMediaTypes); + return ERROR_BAD_DEVICE; + } + + for (idxMedia = 0; idxMedia < pGetMediaTypes->MediaInfoCount; idxMedia++) { + + if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.MediaCharacteristics & MEDIA_CURRENTLY_MOUNTED) { + + if (pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusType == BusTypeScsi) { + *pdwDensity = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.BusSpecificData.ScsiInformation.DensityCode; + } else { + *pdwDensity = 0; + } + + *pdwBlockSize = pGetMediaTypes->MediaInfo[idxMedia].DeviceSpecific.TapeInfo.CurrentBlockSize; + + free(pGetMediaTypes); + + return NO_ERROR; + } + } + + free(pGetMediaTypes); + + return ERROR_NO_MEDIA_IN_DRIVE; +} + +int tape_pos(int fd, struct mtpos *mt_pos) +{ + DWORD partition; + DWORD offset; + DWORD offsetHi; + BOOL result; + + if (fd < 3 || fd >= (int)(NUMBER_HANDLE_ENTRIES + 3) || + TapeHandleTable[fd - 3].OSHandle == INVALID_HANDLE_VALUE) { + errno = EBADF; + return -1; + } + + PTAPE_HANDLE_INFO pHandleInfo = &TapeHandleTable[fd - 3]; + + result = GetTapePosition(pHandleInfo->OSHandle, TAPE_ABSOLUTE_BLOCK, &partition, &offset, &offsetHi); + if (result == NO_ERROR) { + mt_pos->mt_blkno = offset; + return 0; + } + + return -1; +} --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ mtops.h 2006-08-09 03:26:58.372973300 -0700 @@ -0,0 +1,15 @@ +int tape_open(const char *file, int flags, int mode); +int tape_read(int fd, void *buffer, unsigned int count); +int tape_write(int fd, const void *buffer, unsigned int count); +int tape_ioctl(int fd, unsigned long int request, ...); +int tape_close(int fd); + +typedef unsigned long __kernel_daddr_t; + +#ifndef ENOMEDIUM +#define ENOMEDIUM 123 +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif --- /dev/null 1969-12-31 16:00:00.000000000 -0800 +++ Makefile.msc 2006-08-09 04:00:53.970613100 -0700 @@ -0,0 +1,20 @@ +CC= cl +CFLAGS= /nologo /Ox /Gy /Zi /W3 /TP \ + /D_CRT_SECURE_NO_DEPRECATE +LDFLAGS= /link /DEBUG /INCREMENTAL:NO /OPT:NOREF /PDB:$*.pdb /OUT:$@ +PREFIX= C:\ + +all: mt.exe + +mt.exe: mt.c + $(CC) $(CFLAGS) mt.c mtops.c $(LDFLAGS) + +stinit.exe: stinit.c + $(CC) $(CFLAGS) stinit.c $(LDFLAGS) + +install: mt.exe + if not exist $(PREFIX)\bin\nul mkdir $(PREFIX)\bin + !copy /y $** $(PREFIX)\bin + +clean: + del /f *~ *.obj mt.exe stinit.exe