mt-st-1.1/0000755001152000001440000000000011005143040012106 5ustar makisarausersmt-st-1.1/mtio.h0000644001152000001440000002101210757100045013236 0ustar makisarausers/* * linux/mtio.h header file for Linux. Written by H. Bergman * * Sanitized version for mt/stinit (definitions not used by these * programs have been removed) 7 Oct 2007/Kai Mäkisara * */ #ifndef _LINUX_MTIO_H #define _LINUX_MTIO_H #include #include /* * Structures and definitions for mag tape io control commands */ /* structure for MTIOCTOP - mag tape op command */ struct mtop { short mt_op; /* operations defined below */ int mt_count; /* how many of them */ }; /* Magnetic Tape operations [Not all operations supported by all drivers]: */ #define MTRESET 0 /* +reset drive in case of problems */ #define MTFSF 1 /* forward space over FileMark, * position at first record of next file */ #define MTBSF 2 /* backward space FileMark (position before FM) */ #define MTFSR 3 /* forward space record */ #define MTBSR 4 /* backward space record */ #define MTWEOF 5 /* write an end-of-file record (mark) */ #define MTREW 6 /* rewind */ #define MTOFFL 7 /* rewind and put the drive offline (eject?) */ #define MTNOP 8 /* no op, set status only (read with MTIOCGET) */ #define MTRETEN 9 /* retension tape */ #define MTBSFM 10 /* +backward space FileMark, position at FM */ #define MTFSFM 11 /* +forward space FileMark, position at FM */ #define MTEOM 12 /* goto end of recorded media (for appending files). * MTEOM positions after the last FM, ready for * appending another file. */ #define MTERASE 13 /* erase tape -- be careful! */ #define MTRAS1 14 /* run self test 1 (nondestructive) */ #define MTRAS2 15 /* run self test 2 (destructive) */ #define MTRAS3 16 /* reserved for self test 3 */ #define MTSETBLK 20 /* set block length (SCSI) */ #define MTSETDENSITY 21 /* set tape density (SCSI) */ #define MTSEEK 22 /* seek to block (Tandberg, etc.) */ #define MTTELL 23 /* tell block (Tandberg, etc.) */ #define MTSETDRVBUFFER 24 /* set the drive buffering according to SCSI-2 */ /* ordinary buffered operation with code 1 */ #define MTFSS 25 /* space forward over setmarks */ #define MTBSS 26 /* space backward over setmarks */ #define MTWSM 27 /* write setmarks */ #define MTLOCK 28 /* lock the drive door */ #define MTUNLOCK 29 /* unlock the drive door */ #define MTLOAD 30 /* execute the SCSI load command */ #define MTUNLOAD 31 /* execute the SCSI unload command */ #define MTCOMPRESSION 32/* control compression with SCSI mode page 15 */ #define MTSETPART 33 /* Change the active tape partition */ #define MTMKPART 34 /* Format the tape with one or two partitions */ /* structure for MTIOCGET - mag tape get status command */ struct mtget { long mt_type; /* type of magtape device */ long mt_resid; /* residual count: (not sure) * number of bytes ignored, or * number of files not skipped, or * number of records not skipped. */ /* the following registers are device dependent */ long mt_dsreg; /* status register */ long mt_gstat; /* generic (device independent) status */ long mt_erreg; /* error register */ /* The next two fields are not always used */ __kernel_daddr_t mt_fileno; /* number of current file on tape */ __kernel_daddr_t mt_blkno; /* current block number */ }; /* * Constants for mt_type. Not all of these are supported, * and these are not all of the ones that are supported. */ #define MT_ISUNKNOWN 0x01 #define MT_ISQIC02 0x02 /* Generic QIC-02 tape streamer */ #define MT_ISWT5150 0x03 /* Wangtek 5150EQ, QIC-150, QIC-02 */ #define MT_ISARCHIVE_5945L2 0x04 /* Archive 5945L-2, QIC-24, QIC-02? */ #define MT_ISCMSJ500 0x05 /* CMS Jumbo 500 (QIC-02?) */ #define MT_ISTDC3610 0x06 /* Tandberg 6310, QIC-24 */ #define MT_ISARCHIVE_VP60I 0x07 /* Archive VP60i, QIC-02 */ #define MT_ISARCHIVE_2150L 0x08 /* Archive Viper 2150L */ #define MT_ISARCHIVE_2060L 0x09 /* Archive Viper 2060L */ #define MT_ISARCHIVESC499 0x0A /* Archive SC-499 QIC-36 controller */ #define MT_ISQIC02_ALL_FEATURES 0x0F /* Generic QIC-02 with all features */ #define MT_ISWT5099EEN24 0x11 /* Wangtek 5099-een24, 60MB, QIC-24 */ #define MT_ISTEAC_MT2ST 0x12 /* Teac MT-2ST 155mb drive, Teac DC-1 card (Wangtek type) */ #define MT_ISEVEREX_FT40A 0x32 /* Everex FT40A (QIC-40) */ #define MT_ISDDS1 0x51 /* DDS device without partitions */ #define MT_ISDDS2 0x52 /* DDS device with partitions */ #define MT_ISONSTREAM_SC 0x61 /* OnStream SCSI tape drives (SC-x0) and SCSI emulated (DI, DP, USB) */ #define MT_ISSCSI1 0x71 /* Generic ANSI SCSI-1 tape unit */ #define MT_ISSCSI2 0x72 /* Generic ANSI SCSI-2 tape unit */ /* structure for MTIOCPOS - mag tape get position command */ struct mtpos { long mt_blkno; /* current block number */ }; /* mag tape io control commands */ #define MTIOCTOP _IOW('m', 1, struct mtop) /* do a mag tape op */ #define MTIOCGET _IOR('m', 2, struct mtget) /* get tape status */ #define MTIOCPOS _IOR('m', 3, struct mtpos) /* get tape position */ /* The next two are used by the QIC-02 driver for runtime reconfiguration. * See tpqic02.h for struct mtconfiginfo. */ #define MTIOCGETCONFIG _IOR('m', 4, struct mtconfiginfo) /* get tape config */ #define MTIOCSETCONFIG _IOW('m', 5, struct mtconfiginfo) /* set tape config */ /* the next six are used by the floppy ftape drivers and its frontends * sorry, but MTIOCTOP commands are write only. */ #define MTIOCRDFTSEG _IOWR('m', 6, struct mtftseg) /* read a segment */ #define MTIOCWRFTSEG _IOWR('m', 7, struct mtftseg) /* write a segment */ #define MTIOCVOLINFO _IOR('m', 8, struct mtvolinfo) /* info about volume */ #define MTIOCGETSIZE _IOR('m', 9, struct mttapesize)/* get cartridge size*/ #define MTIOCFTFORMAT _IOWR('m', 10, struct mtftformat) /* format ftape */ #define MTIOCFTCMD _IOWR('m', 11, struct mtftcmd) /* send QIC-117 cmd */ /* Generic Mag Tape (device independent) status macros for examining * mt_gstat -- HP-UX compatible. * There is room for more generic status bits here, but I don't * know which of them are reserved. At least three or so should * be added to make this really useful. */ #define GMT_EOF(x) ((x) & 0x80000000) #define GMT_BOT(x) ((x) & 0x40000000) #define GMT_EOT(x) ((x) & 0x20000000) #define GMT_SM(x) ((x) & 0x10000000) /* DDS setmark */ #define GMT_EOD(x) ((x) & 0x08000000) /* DDS EOD */ #define GMT_WR_PROT(x) ((x) & 0x04000000) /* #define GMT_ ? ((x) & 0x02000000) */ #define GMT_ONLINE(x) ((x) & 0x01000000) #define GMT_D_6250(x) ((x) & 0x00800000) #define GMT_D_1600(x) ((x) & 0x00400000) #define GMT_D_800(x) ((x) & 0x00200000) /* #define GMT_ ? ((x) & 0x00100000) */ /* #define GMT_ ? ((x) & 0x00080000) */ #define GMT_DR_OPEN(x) ((x) & 0x00040000) /* door open (no tape) */ /* #define GMT_ ? ((x) & 0x00020000) */ #define GMT_IM_REP_EN(x) ((x) & 0x00010000) /* immediate report mode */ #define GMT_CLN(x) ((x) & 0x00008000) /* cleaning requested */ /* 15 generic status bits unused */ /* SCSI-tape specific definitions */ /* Bitfield shifts in the status */ #define MT_ST_BLKSIZE_SHIFT 0 #define MT_ST_BLKSIZE_MASK 0xffffff #define MT_ST_DENSITY_SHIFT 24 #define MT_ST_DENSITY_MASK 0xff000000 #define MT_ST_SOFTERR_SHIFT 0 #define MT_ST_SOFTERR_MASK 0xffff /* Bitfields for the MTSETDRVBUFFER ioctl */ #define MT_ST_OPTIONS 0xf0000000 #define MT_ST_BOOLEANS 0x10000000 #define MT_ST_SETBOOLEANS 0x30000000 #define MT_ST_CLEARBOOLEANS 0x40000000 #define MT_ST_WRITE_THRESHOLD 0x20000000 #define MT_ST_DEF_BLKSIZE 0x50000000 #define MT_ST_DEF_OPTIONS 0x60000000 #define MT_ST_TIMEOUTS 0x70000000 #define MT_ST_SET_TIMEOUT (MT_ST_TIMEOUTS | 0x000000) #define MT_ST_SET_LONG_TIMEOUT (MT_ST_TIMEOUTS | 0x100000) #define MT_ST_SET_CLN 0x80000000 #define MT_ST_BUFFER_WRITES 0x1 #define MT_ST_ASYNC_WRITES 0x2 #define MT_ST_READ_AHEAD 0x4 #define MT_ST_DEBUGGING 0x8 #define MT_ST_TWO_FM 0x10 #define MT_ST_FAST_MTEOM 0x20 #define MT_ST_AUTO_LOCK 0x40 #define MT_ST_DEF_WRITES 0x80 #define MT_ST_CAN_BSR 0x100 #define MT_ST_NO_BLKLIMS 0x200 #define MT_ST_CAN_PARTITIONS 0x400 #define MT_ST_SCSI2LOGICAL 0x800 #define MT_ST_SYSV 0x1000 #define MT_ST_NOWAIT 0x2000 #define MT_ST_SILI 0x4000 /* The mode parameters to be controlled. Parameter chosen with bits 20-28 */ #define MT_ST_CLEAR_DEFAULT 0xfffff #define MT_ST_DEF_DENSITY (MT_ST_DEF_OPTIONS | 0x100000) #define MT_ST_DEF_COMPRESSION (MT_ST_DEF_OPTIONS | 0x200000) #define MT_ST_DEF_DRVBUFFER (MT_ST_DEF_OPTIONS | 0x300000) /* The offset for the arguments for the special HP changer load command. */ #define MT_ST_HPLOADER_OFFSET 10000 #endif /* _LINUX_MTIO_H */ mt-st-1.1/README.stinit0000644001152000001440000000571311005110552014307 0ustar makisarausersThe program stinit is meant for initializing of SCSI tape drive modes at system startup, when the tape driver module is loaded, or when new tape drivers are initialized using echo "scsi add-single-device x y z v" >/proc/scsi/scsi or (with 2.6 kernels) echo "y z v" > /sys/class/scsi_host/hostx/scan where x=host y=channel z=id v=lun (- is wild card for 2.6). The parameters used in initialization of a tape drive are fetched from a text file. The parameter file is indexed by the inquiry data returned by the drive, i.e., the parameters are defined by the drive manufacturer, model, etc. This means that the initialization for a drive does not depend on its hardware address. A similar method is used by most Unices either within the kernel or outside the kernel. The contents of the configuration file and the command line parameters are defined in the man page stinit.8. A sample configuration file stinit.def.examples is included in this distribution. It can be used as example when writing descriptions for the tape drives in a system. NOTE that the examples by no means specify what are the "correct" parameters for different types of devices. The program is configured for maximum of 32 tapes and 4 modes (the default Linux configuration). If the kernel is configured for different number of tape modes, the definitions MAX_TAPES and NBR_MODES in stinit.c should be configured accordingly. (With 8 bit minor numbers NBR_MODES * MAX_TAPES == 128.) The files: stinit.c - the program source code stinit.8 - the man page stinit.def.examples - a file containing example definitions for imaginary tape drives Makefile - a sample makefile for the program README.stinit - this file Changes in version 1.1: - fix mode number printing in verbose mode (from Martin Jacobs) - add support for the sili option Changes in version 0.9b: - fix back out to SCSI_IOCTL_SEND_COMMAND for 2.4 kernels (2.4 uses errno EINVAL for unsupported ioctls) Changes in version 0.9: - try first SG_IO for inquiry, if the ioctl fails, try SCSI_IOCTL_SEND_COMMAND - error checking for SG_IO is very simplistic for now Changes in version 0.8: - in devfs, /dev/tapes/tape does not match th drive after rmmoding and insmodding the st driver; fix provided by Philippe Troin - put the man page into /usr/share/man/man8 - counts can use the k, M, or G postfix Changes in version 0.7: - the directory scanning for tape devices is restricted to files with certain names in some directories to avoid triggering automatic module loading for device that don't exist (original patch from Philippe Troin) - support for devfs (/dev/tapes) added - logging bug fixes - add setting the cleaning request parameter - add setting the no-wait (immediate) bit Changes in version 0.6: - fix the bug with whitespace at the beginning of lines - use O_NONBLOCK to open the tape (anticipate kernel change) Initial version 0.5. April 27, 2008 Kai Mäkisara mt-st-1.1/stinit.def.examples0000644001152000001440000000346510460510404015733 0ustar makisarausers# This file contains example definitions for different kinds of tape # devices. If the user agrees with the definitions, they can be used # in the definition file stinit.def by changing the manufacturer and # model fields to correspond the real tape device being defined. # The common definitions that can usually be used {buffer-writes read-ahead async-writes scsi2logical=1} # A non-compressing DAT (DDS-1) # The manufacturer, model, and revision strings can be obtained, # from the file /proc/scsi/scsi (cat /proc/scsi/scsi). manufacturer=XYZ model = "UVW1" { scsi2logical=1 can-bsr can-partitions auto-lock mode1 blocksize=0 mode2 blocksize=1024 } # A compressing DAT (DDS-1-DC or DDS-[234]) manufacturer=XYZ model = "UVW2" { can-bsr can-partitions auto-lock mode1 blocksize=0 compression=1 mode2 blocksize=1024 compression=1 mode3 blocksize=0 compression=0 mode4 blocksize = 1024 compression=0 } # A QIC-150 drive manufacturer=XYZ model="UVW3" { scsi2logical=0 mode1 # blocksize=512 defined by drive } # A QIC-320/525 drive manufacturer=XYZ model="UVW4" { defs-for-writes mode1 blocksize=0 density=0x11 # QIC-320 mode2 blocksize=1024 density=0x11 # QIC-320 mode3 blocksize=512 density=0x10 # QIC-150 } # Exabyte 8505 and other similar 8 mm helical scan driver manufacturer=XYZ model = "UVW5" { can-bsr auto-lock cleaning=0x080821 mode1 blocksize=0 density=0x8c # 8500 density, compressing mode2 blocksize=0 density=0x15 # 8500 density, no compression mode3 blocksize=0 density=0x90 # 8200 density, compressing mode4 blocksize=0 density=0x14 # 8200 density, no compression } # A reel-to-reel tape with 6250/1600/800 bpi densities manufacturer=XYZ model = "UVW6" { can-bsr two-fms scsi2logical=0 mode1 blocksize=0 density=3 # 6250 bpi mode2 blocksize=0 density=11 # 1600 bpi mode3 blocksize=0 density=1 # 800 bpi } mt-st-1.1/mt.c0000644001152000001440000005466611005127174012725 0ustar makisarausers/* This file contains the source of the 'mt' program intended for Linux systems. The program supports the basic mt commands found in most Unix-like systems. In addition to this the program supports several commands designed for use with the Linux SCSI tape drive. Maintained by Kai Mäkisara (email Kai.Makisara@kolumbus.fi) Copyright by Kai Mäkisara, 1998 - 2008. The program may be distributed according to the GNU Public License Last Modified: Sun Apr 27 19:49:00 2008 by kai.makisara */ #include #include #include #include #include #include #include #include #include #include #include #include "mtio.h" #ifndef DEFTAPE #define DEFTAPE "/dev/tape" /* default tape device */ #endif /* DEFTAPE */ #define VERSION "1.1" typedef int (* cmdfunc)(/* int, struct cmdef_tr *, int, char ** */); typedef struct cmdef_tr { char *cmd_name; int cmd_code; cmdfunc cmd_function; int cmd_count_bits; unsigned char cmd_fdtype; unsigned char arg_cnt; int error_tests; } cmdef_tr; #define NO_FD 0 #define FD_RDONLY 1 #define FD_RDWR 2 #define NO_ARGS 0 #define ONE_ARG 1 #define TWO_ARGS 2 #define MULTIPLE_ARGS 255 #define DO_BOOLEANS 1002 #define SET_BOOLEANS 1003 #define CLEAR_BOOLEANS 1004 #define ET_ONLINE 1 #define ET_WPROT 2 static void usage(int); static int do_standard(int, cmdef_tr *, int, char **); static int do_drvbuffer(int, cmdef_tr *, int, char **); static int do_options(int, cmdef_tr *, int, char **); static int do_tell(int, cmdef_tr *, int, char **); static int do_partseek(int, cmdef_tr *, int, char **); static int do_status(int, cmdef_tr *, int, char **); static int print_densities(int, cmdef_tr *, int, char **); static int do_asf(int, cmdef_tr *, int, char **); static int do_show_options(int, cmdef_tr *, int, char **); static void test_error(int, cmdef_tr *); static cmdef_tr cmds[] = { { "weof", MTWEOF, do_standard, 0, FD_RDWR, ONE_ARG, ET_ONLINE | ET_WPROT }, { "wset", MTWSM, do_standard, 0, FD_RDWR, ONE_ARG, ET_ONLINE | ET_WPROT }, { "eof", MTWEOF, do_standard, 0, FD_RDWR, ONE_ARG, ET_ONLINE }, { "fsf", MTFSF, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "fsfm", MTFSFM, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "bsf", MTBSF, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "bsfm", MTBSFM, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "fsr", MTFSR, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "bsr", MTBSR, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "fss", MTFSS, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "bss", MTBSS, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "rewind", MTREW, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "offline", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "rewoffl", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "eject", MTOFFL, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "retension", MTRETEN, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "eod", MTEOM, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "seod", MTEOM, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "seek", MTSEEK, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "tell", MTTELL, do_tell, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "status", MTNOP, do_status, 0, FD_RDONLY, NO_ARGS, 0 }, { "erase", MTERASE, do_standard, 0, FD_RDWR, ONE_ARG, ET_ONLINE }, { "setblk", MTSETBLK, do_standard, 0, FD_RDONLY, ONE_ARG, 0 }, { "lock", MTLOCK, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "unlock", MTUNLOCK, do_standard, 0, FD_RDONLY, NO_ARGS, ET_ONLINE }, { "load", MTLOAD, do_standard, 0, FD_RDONLY, ONE_ARG, 0 }, { "compression", MTCOMPRESSION, do_standard, 0, FD_RDONLY, ONE_ARG, 0 }, { "setdensity", MTSETDENSITY, do_standard, 0, FD_RDONLY, ONE_ARG, 0 }, { "drvbuffer", MTSETDRVBUFFER, do_drvbuffer, 0, FD_RDONLY, ONE_ARG, 0 }, { "stwrthreshold", MTSETDRVBUFFER, do_drvbuffer, MT_ST_WRITE_THRESHOLD, FD_RDONLY, ONE_ARG, 0}, { "stoptions", DO_BOOLEANS, do_options, 0, FD_RDONLY, MULTIPLE_ARGS, 0}, { "stsetoptions", SET_BOOLEANS, do_options, 0, FD_RDONLY, MULTIPLE_ARGS, 0}, { "stclearoptions", CLEAR_BOOLEANS, do_options, 0, FD_RDONLY, MULTIPLE_ARGS, 0}, { "defblksize", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_BLKSIZE, FD_RDONLY, ONE_ARG, 0}, { "defdensity", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_DENSITY, FD_RDONLY, ONE_ARG, 0}, { "defdrvbuffer", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_DRVBUFFER, FD_RDONLY, ONE_ARG, 0}, { "defcompression", MTSETDRVBUFFER, do_drvbuffer, MT_ST_DEF_COMPRESSION, FD_RDONLY, ONE_ARG, 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}, { "densities", 0, print_densities, 0, NO_FD, NO_ARGS, 0 }, { "setpartition", MTSETPART, do_standard, 0, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "mkpartition", MTMKPART, do_standard, 0, FD_RDWR, ONE_ARG, ET_ONLINE }, { "partseek", 0, do_partseek, 0, FD_RDONLY, TWO_ARGS, ET_ONLINE }, { "asf", 0, do_asf, MTREW, FD_RDONLY, ONE_ARG, ET_ONLINE }, { "stshowopt", 0, do_show_options, 0, FD_RDONLY, ONE_ARG, 0 }, { NULL, 0, 0, 0 } }; static struct densities { int code; char *name; } density_tbl[] = { {0x00, "default"}, {0x01, "NRZI (800 bpi)"}, {0x02, "PE (1600 bpi)"}, {0x03, "GCR (6250 bpi)"}, {0x04, "QIC-11"}, {0x05, "QIC-45/60 (GCR, 8000 bpi)"}, {0x06, "PE (3200 bpi)"}, {0x07, "IMFM (6400 bpi)"}, {0x08, "GCR (8000 bpi)"}, {0x09, "GCR (37871 bpi)"}, {0x0a, "MFM (6667 bpi)"}, {0x0b, "PE (1600 bpi)"}, {0x0c, "GCR (12960 bpi)"}, {0x0d, "GCR (25380 bpi)"}, {0x0f, "QIC-120 (GCR 10000 bpi)"}, {0x10, "QIC-150/250 (GCR 10000 bpi)"}, {0x11, "QIC-320/525 (GCR 16000 bpi)"}, {0x12, "QIC-1350 (RLL 51667 bpi)"}, {0x13, "DDS (61000 bpi)"}, {0x14, "EXB-8200 (RLL 43245 bpi)"}, {0x15, "EXB-8500 or QIC-1000"}, {0x16, "MFM 10000 bpi"}, {0x17, "MFM 42500 bpi"}, {0x18, "TZ86"}, {0x19, "DLT 10GB"}, {0x1a, "DLT 20GB"}, {0x1b, "DLT 35GB"}, {0x1c, "QIC-385M"}, {0x1d, "QIC-410M"}, {0x1e, "QIC-1000C"}, {0x1f, "QIC-2100C"}, {0x20, "QIC-6GB"}, {0x21, "QIC-20GB"}, {0x22, "QIC-2GB"}, {0x23, "QIC-875"}, {0x24, "DDS-2"}, {0x25, "DDS-3"}, {0x26, "DDS-4 or QIC-4GB"}, {0x27, "Exabyte Mammoth"}, {0x28, "Exabyte Mammoth-2"}, {0x29, "QIC-3080MC"}, {0x30, "AIT-1 or MLR3"}, {0x31, "AIT-2"}, {0x32, "AIT-3 or SLR7"}, {0x33, "SLR6"}, {0x34, "SLR100"}, {0x40, "DLT1 40 GB, or Ultrium"}, {0x41, "DLT 40GB, or Ultrium2"}, {0x42, "LTO-2"}, {0x44, "LTO-3"}, {0x45, "QIC-3095-MC (TR-4)"}, {0x46, "LTO-4"}, {0x47, "DDS-5 or TR-5"}, {0x51, "IBM 3592 J1A"}, {0x52, "IBM 3592 E05"}, {0x80, "DLT 15GB uncomp. or Ecrix"}, {0x81, "DLT 15GB compressed"}, {0x82, "DLT 20GB uncompressed"}, {0x83, "DLT 20GB compressed"}, {0x84, "DLT 35GB uncompressed"}, {0x85, "DLT 35GB compressed"}, {0x86, "DLT1 40 GB uncompressed"}, {0x87, "DLT1 40 GB compressed"}, {0x88, "DLT 40GB uncompressed"}, {0x89, "DLT 40GB compressed"}, {0x8c, "EXB-8505 compressed"}, {0x90, "SDLT110 uncompr/EXB-8205 compr"}, {0x91, "SDLT110 compressed"}, {0x92, "SDLT160 uncompressed"}, {0x93, "SDLT160 comprssed"} }; #define NBR_DENSITIES (sizeof(density_tbl) / sizeof(struct densities)) static struct booleans { char *name; unsigned long bitmask; char *expl; } boolean_tbl[] = { {"buffer-writes", MT_ST_BUFFER_WRITES, "buffered writes"}, {"async-writes", MT_ST_ASYNC_WRITES, "asynchronous writes"}, {"read-ahead", MT_ST_READ_AHEAD, "read-ahead for fixed block size"}, {"debug", MT_ST_DEBUGGING, "debugging (if compiled into driver)"}, {"two-fms", MT_ST_TWO_FM, "write two filemarks when file closed"}, {"fast-eod", MT_ST_FAST_MTEOM, "space directly to eod (and lose file number)"}, {"auto-lock", MT_ST_AUTO_LOCK, "automatically lock/unlock drive door"}, {"def-writes", MT_ST_DEF_WRITES, "the block size and density are for writes"}, {"can-bsr", MT_ST_CAN_BSR, "drive can space backwards well"}, {"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"}, {"no-wait", MT_ST_NOWAIT, "immediate mode for rewind, etc."}, #ifdef MT_ST_SYSV {"sysv", MT_ST_SYSV, "enable the SystemV semantics"}, #endif {"sili", MT_ST_SILI, "enable SILI for variable block mode"}, {"cleaning", MT_ST_SET_CLN, "set the cleaning bit location and mask"}, {NULL, 0}}; static char *tape_name; /* The tape name for messages */ int main(int argc, char **argv) { int mtfd, cmd_code, i, argn, len, oflags; char *cmdstr; cmdef_tr *comp, *comp2; for (argn=1; argn < argc; argn++) if (*argv[argn] == '-') switch (*(argv[argn] + 1)) { case 'f': case 't': argn += 1; if (argn >= argc) { usage(0); exit(1); } tape_name = argv[argn]; break; case 'h': usage(1); exit(0); break; case 'v': printf("mt-st v. %s\n", VERSION); exit(0); break; case '-': if (*(argv[argn] + 1) == '-' && *(argv[argn] + 2) == 'v') { printf("mt-st v. %s\n", VERSION); exit(0); } /* Fall through */ default: usage(0); exit(1); } else break; if (tape_name == NULL && (tape_name = getenv("TAPE")) == NULL) tape_name = DEFTAPE; if (argn >= argc ) { usage(0); exit(1); } cmdstr = argv[argn++]; len = strlen(cmdstr); for (comp = cmds; comp->cmd_name != NULL; comp++) if (strncmp(cmdstr, comp->cmd_name, len) == 0) break; if (comp->cmd_name == NULL) { fprintf(stderr, "mt: unknown command \"%s\"\n", cmdstr); usage(1); exit(1); } if (len != strlen(comp->cmd_name)) { for (comp2 = comp + 1; comp2->cmd_name != NULL; comp2++) if (strncmp(cmdstr, comp2->cmd_name, len) == 0) break; if (comp2->cmd_name != NULL) { fprintf(stderr, "mt: ambiguous command \"%s\"\n", cmdstr); usage(1); exit(1); } } if (comp->arg_cnt != MULTIPLE_ARGS && comp->arg_cnt < argc - argn) { fprintf(stderr, "mt: too many arguments for the command '%s'.\n", comp->cmd_name); exit(1); } cmd_code = comp->cmd_code; if (comp->cmd_fdtype != NO_FD) { 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) { perror(tape_name); exit(1); } } else mtfd = (-1); if (comp->cmd_function != NULL) { i = comp->cmd_function(mtfd, comp, argc - argn, (argc - argn > 0 ? argv + argn : NULL)); if (i) { if (errno == ENOSYS) fprintf(stderr, "mt: Command not supported by this kernel.\n"); else if (comp->error_tests != 0) test_error(mtfd, comp); } } else { fprintf(stderr, "mt: Internal error: command without function.\n"); i = 1; } if (mtfd >= 0) close(mtfd); return i; } static void usage(int explain) { int ind; char line[100]; fprintf(stderr, "usage: mt [-v] [--version] [-h] [ -f device ] command [ count ]\n"); if (explain) { for (ind=0; cmds[ind].cmd_name != NULL; ) { if (ind == 0) strcpy(line, "commands: "); else strcpy(line, " "); for ( ; cmds[ind].cmd_name != NULL; ind++) { strcat(line, cmds[ind].cmd_name); if (cmds[ind+1].cmd_name != NULL) strcat(line, ", "); else strcat(line, "."); if (strlen(line) >= 70 || cmds[ind+1].cmd_name == NULL) { fprintf(stderr, "%s\n", line); ind++; break; } } } } } /* Do a command that simply feeds an argument to the MTIOCTOP ioctl */ static int do_standard(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtop mt_com; char *endp; mt_com.mt_op = cmd->cmd_code; mt_com.mt_count = (argc > 0 ? strtol(*argv, &endp, 0) : 1); if (argc > 0 && endp != *argv) { if (*endp == 'k') mt_com.mt_count *= 1024; else if (*endp == 'M') mt_com.mt_count *= 1024 * 1024; else if (*endp == 'G') mt_com.mt_count *= 1024 * 1024 * 1024; } mt_com.mt_count |= cmd->cmd_count_bits; if (mt_com.mt_count < 0) { fprintf(stderr, "mt: negative repeat count\n"); return 1; } if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } return 0; } /* The the drive buffering and other things with this (highly overloaded) ioctl function. (See also do_options below.) */ static int do_drvbuffer(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtop mt_com; mt_com.mt_op = MTSETDRVBUFFER; mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 1); if ((cmd->cmd_count_bits & MT_ST_OPTIONS) == MT_ST_DEF_OPTIONS) mt_com.mt_count &= 0xfffff; #ifdef MT_ST_TIMEOUTS else if ((cmd->cmd_count_bits & MT_ST_OPTIONS) == MT_ST_TIMEOUTS) mt_com.mt_count &= 0x7ffffff; #endif else mt_com.mt_count &= 0xfffffff; mt_com.mt_count |= cmd->cmd_count_bits; if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } return 0; } /* Set the tape driver options */ static int do_options(int mtfd, cmdef_tr *cmd, int argc, char **argv) { int i, an, len; struct mtop mt_com; mt_com.mt_op = MTSETDRVBUFFER; if (argc == 0) mt_com.mt_count = 0; else if (isdigit(**argv)) mt_com.mt_count = strtol(*argv, NULL, 0) & ~MT_ST_OPTIONS; else for (an = 0, mt_com.mt_count = 0; an < argc; an++) { len = strlen(argv[an]); for (i=0; boolean_tbl[i].name != NULL; i++) if (!strncmp(boolean_tbl[i].name, argv[an], len)) { mt_com.mt_count |= boolean_tbl[i].bitmask; break; } if (boolean_tbl[i].name == NULL) { fprintf(stderr, "Illegal property name '%s'.\n", argv[an]); fprintf(stderr, "The implemented property names are:\n"); for (i=0; boolean_tbl[i].name != NULL; i++) fprintf(stderr, " %9s -> %s\n", boolean_tbl[i].name, boolean_tbl[i].expl); return 1; } if (len != strlen(boolean_tbl[i].name)) for (i++ ; boolean_tbl[i].name != NULL; i++) if (!strncmp(boolean_tbl[i].name, argv[an], len)) { fprintf(stderr, "Property name '%s' ambiguous.\n", argv[an]); return 1; } } switch (cmd->cmd_code) { case DO_BOOLEANS: mt_com.mt_count |= MT_ST_BOOLEANS; break; case SET_BOOLEANS: mt_com.mt_count |= MT_ST_SETBOOLEANS; break; case CLEAR_BOOLEANS: mt_com.mt_count |= MT_ST_CLEARBOOLEANS; break; } if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } return 0; } /* Tell where the tape is */ static int do_tell(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtpos mt_pos; if (ioctl(mtfd, MTIOCPOS, (char *)&mt_pos) < 0) { perror(tape_name); return 2; } printf("At block %ld.\n", mt_pos.mt_blkno); return 0; } /* Position the tape to a specific location within a specified partition */ static int do_partseek(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtop mt_com; mt_com.mt_op = MTSETPART; mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 0); if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } mt_com.mt_op = MTSEEK; mt_com.mt_count = (argc > 1 ? strtol(argv[1], NULL, 0) : 0); if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } return 0; } /* Position to start of file n. This might be implemented more intelligently some day. */ static int do_asf(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtop mt_com; mt_com.mt_op = MTREW; mt_com.mt_count = 1; if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } mt_com.mt_count = (argc > 0 ? strtol(*argv, NULL, 0) : 0); if (mt_com.mt_count > 0) { mt_com.mt_op = MTFSF; if (ioctl(mtfd, MTIOCTOP, (char *)&mt_com) < 0) { perror(tape_name); return 2; } } return 0; } /*** Decipher the status ***/ static int do_status(int mtfd, cmdef_tr *cmd, int argc, char **argv) { struct mtget status; int dens, i; char *type, *density; if (ioctl(mtfd, MTIOCGET, (char *)&status) < 0) { perror(tape_name); return 2; } if (status.mt_type == MT_ISSCSI1) type = "SCSI 1"; else if (status.mt_type == MT_ISSCSI2) type = "SCSI 2"; else if (status.mt_type == MT_ISONSTREAM_SC) type = "OnStream SC-, DI-, DP-, or USB"; else type = NULL; if (type == NULL) { if (status.mt_type & 0x800000) printf ("qic-117 drive type = 0x%05lx\n", status.mt_type & 0x1ffff); else if (status.mt_type == 0) 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", status.mt_fileno, status.mt_blkno); printf("mt_resid: %ld, mt_erreg: 0x%lx\n", status.mt_resid, status.mt_erreg); printf("mt_dsreg: 0x%lx, mt_gstat: 0x%lx\n", status.mt_dsreg, status.mt_gstat); } else { printf("%s tape drive:\n", type); if (status.mt_type == MT_ISSCSI2) printf("File number=%d, block number=%d, partition=%ld.\n", status.mt_fileno, status.mt_blkno, (status.mt_resid & 0xff)); else printf("File number=%d, block number=%d.\n", status.mt_fileno, status.mt_blkno); if (status.mt_type == MT_ISSCSI1 || status.mt_type == MT_ISSCSI2 || status.mt_type == MT_ISONSTREAM_SC) { dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT; density = "no translation"; for (i=0; i < NBR_DENSITIES; i++) if (density_tbl[i].code == dens) { density = density_tbl[i].name; break; } printf("Tape block size %ld bytes. Density code 0x%x (%s).\n", ((status.mt_dsreg & MT_ST_BLKSIZE_MASK) >> MT_ST_BLKSIZE_SHIFT), dens, density); printf("Soft error count since last status=%ld\n", (status.mt_erreg & MT_ST_SOFTERR_MASK) >> MT_ST_SOFTERR_SHIFT); } } printf("General status bits on (%lx):\n", status.mt_gstat); if (GMT_EOF(status.mt_gstat)) printf(" EOF"); if (GMT_BOT(status.mt_gstat)) printf(" BOT"); if (GMT_EOT(status.mt_gstat)) printf(" EOT"); if (GMT_SM(status.mt_gstat)) printf(" SM"); if (GMT_EOD(status.mt_gstat)) printf(" EOD"); if (GMT_WR_PROT(status.mt_gstat)) printf(" WR_PROT"); if (GMT_ONLINE(status.mt_gstat)) printf(" ONLINE"); if (GMT_D_6250(status.mt_gstat)) printf(" D_6250"); if (GMT_D_1600(status.mt_gstat)) printf(" D_1600"); if (GMT_D_800(status.mt_gstat)) printf(" D_800"); if (GMT_DR_OPEN(status.mt_gstat)) printf(" DR_OPEN"); if (GMT_IM_REP_EN(status.mt_gstat)) printf(" IM_REP_EN"); if (GMT_CLN(status.mt_gstat)) printf(" CLN"); printf("\n"); return 0; } /* From linux/drivers/scsi/st.[ch] */ #define ST_NBR_MODE_BITS 2 #define ST_NBR_MODES (1 << ST_NBR_MODE_BITS) #define ST_MODE_SHIFT (7 - ST_NBR_MODE_BITS) #define ST_MODE_MASK ((ST_NBR_MODES - 1) << ST_MODE_SHIFT) #define TAPE_NR(minor) ( (((minor) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \ ((minor) & ~(-1 << ST_MODE_SHIFT)) ) #define TAPE_MODE(minor) (((minor) & ST_MODE_MASK) >> ST_MODE_SHIFT) static const char *st_formats[] = { "", "r", "k", "s", "l", "t", "o", "u", "m", "v", "p", "x", "a", "y", "q", "z"}; /* Show the options if visible in sysfs */ static int do_show_options(int mtfd, cmdef_tr *cmd, int argc, char **argv) { int i, fd, options, tapeminor, tapeno, tapemode; struct stat stat; struct utsname uts; char fname[100], buf[20]; if (uname(&uts) < 0) { perror(tape_name); return 2; } sscanf(uts.release, "%d.%d.%d", &i, &tapeno, &tapemode); if (i < 2 || tapeno < 6 || tapemode < 26) printf("Your kernel (%d.%d.%d) may be too old for this command.\n", i, tapeno, tapemode); if (fstat(mtfd, &stat) < 0) { perror(tape_name); return 1; } if (!(stat.st_mode & S_IFCHR)) { fprintf(stderr, "mt: not a character device.\n"); return 1; } tapeminor = minor(stat.st_rdev); tapeno = TAPE_NR(tapeminor); tapemode = TAPE_MODE(tapeminor); tapemode <<= 4 - ST_NBR_MODE_BITS; /* from st.c */ sprintf(fname, "/sys/class/scsi_tape/st%d%s/options", tapeno, st_formats[tapemode]); /* printf("Trying file '%s' (st_rdev 0x%lx).\n", fname, stat.st_rdev); */ if ((fd = open(fname, O_RDONLY)) < 0 || read(fd, buf, 20) < 0) { fprintf(stderr, "Can't read the sysfs file '%s'.\n", fname); return 2; } close(fd); options = strtol(buf, NULL, 0); printf("The options set:"); for (i=0; boolean_tbl[i].name != NULL; i++) if (options & boolean_tbl[i].bitmask) printf(" %s", boolean_tbl[i].name); printf("\n"); return 0; } /* Print a list of possible density codes */ static int print_densities(int fd, cmdef_tr *cmd, int argc, char **argv) { int i, offset; printf("Some SCSI tape density codes:\ncode explanation code explanation\n"); offset = (NBR_DENSITIES + 1) / 2; for (i=0; i < offset; i++) { printf("0x%02x %-28s", density_tbl[i].code, density_tbl[i].name); if (i + offset < NBR_DENSITIES) printf(" 0x%02x %s\n", density_tbl[i + offset].code, density_tbl[i + offset].name); else printf("\n"); } return 0; } /* Try to find out why the command failed */ static void test_error(int mtfd, cmdef_tr *cmd) { struct mtget status; if (ioctl(mtfd, MTIOCGET, (char *)&status) < 0) return; if (status.mt_type != MT_ISSCSI1 && status.mt_type != MT_ISSCSI2) return; if ((cmd->error_tests & ET_ONLINE) && !GMT_ONLINE(status.mt_gstat)) fprintf(stderr, "mt: The device is offline (not powered on, no tape ?).\n"); if ((cmd->error_tests & ET_WPROT) && !GMT_WR_PROT(status.mt_gstat)) fprintf(stderr, "mt: The tape is write-protected.\n"); } mt-st-1.1/stinit.c0000644001152000001440000006070311005061140013572 0ustar makisarausers/* This program initializes Linux SCSI tape drives using the inquiry data from the devices and a text database. Copyright 1996-2008 by Kai Mäkisara (email Kai.Makisara@kolumbus.fi) Distribution of this program is allowed according to the GNU Public Licence. Last modified: Sun Apr 27 14:24:16 2008 by kai.makisara */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtio.h" #ifndef FALSE #define TRUE 1 #define FALSE 0 #endif #define SKIP_WHITE(p) for ( ; *p == ' ' || *p == '\t'; p++) #define VERSION "1.1" typedef struct _modepar_tr { int defined; int blocksize; int density; int buffer_writes; int async_writes; int read_ahead; int two_fm; int compression; int auto_lock; int fast_eod; int can_bsr; int no_blklimits; int can_partitions; int scsi2logical; int sysv; int defs_for_writes; } modepar_tr; typedef struct _devdef_tr { int do_rewind; int drive_buffering; int timeout; int long_timeout; int cleaning; int nowait; int sili; modepar_tr modedefs[4]; } devdef_tr; #define DEFMAX 2048 #define LINEMAX 256 #define MAX_TAPES 32 #define NBR_MODES 4 static int verbose = 0; /* The device directories being searched */ typedef struct { char dir[PATH_MAX]; int selective_scan; } devdir; static devdir devdirs[] = { {"/dev/scsi", 0}, {"/dev", 1}, {"", 0}}; #define DEVFS_PATH "/dev/tapes" #define DEVFS_TAPEFMT DEVFS_PATH "/tape%d" /* The partial names of the tape devices being included in the search in selective scan */ static char *tape_name_bases[] = { "st", "nst", "rmt", "nrmt", "tape", NULL}; /* The list of standard definition files being searched */ static char *std_databases[] = { "/etc/stinit.def", NULL}; static FILE * open_database(char *base) { int i; FILE *f; if (base != NULL) { if ((f = fopen(base, "r")) == NULL) fprintf(stderr, "stinit: Can't find SCSI tape database '%s'.\n", base); return f; } for (i=0; std_databases[i] != NULL; i++) { if (verbose > 1) fprintf(stderr, "Trying to open database '%s'.\n", std_databases[i]); if ((f = fopen(std_databases[i], "r")) != NULL) { if (verbose > 1) fprintf(stderr, "Open succeeded.\n"); return f; } } fprintf(stderr, "Can't find the tape characteristics database.\n"); return NULL; } static char * find_string(char *s, char *target, char *buf, int buflen) { int have_arg; char *cp, *cp2, c, *argp; if (buf != NULL && buflen > 0) *buf = '\0'; for ( ; *s != '\0'; ) { SKIP_WHITE(s); if (isalpha(*s)) { for (cp=s ; isalnum(*cp) || *cp == '-'; cp++) ; cp2 = cp; SKIP_WHITE(cp); if (*cp == '=') { cp++; SKIP_WHITE(cp); if (*cp == '"') { cp++; for (cp2=cp; *cp2 != '"' && *cp2 != '\0'; cp2++) ; } else for (cp2=cp+1; isalnum(*cp2) || *cp2 == '-'; cp2++) ; if (cp2 == '\0') return NULL; have_arg = TRUE; argp = cp; } else { have_arg = FALSE; argp = "1"; } if (!strncmp(target, s, strlen(target))) { c = *cp2; *cp2 = '\0'; if (buf == NULL) buf = strdup(argp); else { if (strlen(argp) < buflen) strcpy(buf, argp); else { strncpy(buf, argp, buflen); buf[buflen - 1] = '\0'; } } if (have_arg && c == '"') cp2++; else *cp2 = c; if (*cp2 != '\0') memmove(s, cp2, strlen(cp2) + 1); else *s = '\0'; return buf; } s = cp2; } else for ( ; *s != '\0' && *s != ' ' && *s != '\t'; s++) ; } return NULL; } static int num_arg(char *t) { int nbr; char *tt; nbr = strtol(t, &tt, 0); if (t != tt) { if (*tt == 'k') nbr *= 1024; else if (*tt == 'M') nbr *= 1024 * 1024; } return nbr; } static int next_block(FILE *dbf, char *buf, int buflen, int limiter) { int len; char *cp, *bp; static char lbuf[LINEMAX]; if (limiter == 0) { /* Restart */ rewind(dbf); lbuf[0] = '\0'; return TRUE; } for (len = 0 ; ; ) { bp = buf + len; if ((cp = strchr(lbuf, limiter)) != NULL) { *cp = '\0'; strcpy(bp, lbuf); cp++; SKIP_WHITE(cp); memmove(lbuf, cp, strlen(cp) + 1); return TRUE; } if (len + strlen(lbuf) >= DEFMAX) { fprintf(stderr, "Too long definition: '%s'\n", buf); return FALSE; } cp = lbuf; SKIP_WHITE(cp); strcpy(bp, cp); strcat(bp, " "); len += strlen(cp) + 1; if (fgets(lbuf, LINEMAX, dbf) == NULL) return FALSE; if ((cp = strchr(lbuf, '#')) != NULL) *cp = '\0'; else lbuf[strlen(lbuf) - 1] = '\0'; } } static int find_pars(FILE *dbf, char *company, char *product, char *rev, devdef_tr *defs, int parse_only) { int i, mode, modes_defined, errors; char line[LINEMAX], defstr[DEFMAX], comdef[DEFMAX]; char tmpcomp[LINEMAX], tmpprod[LINEMAX], tmprev[LINEMAX], *cp, c, *t; char *nextdef, *curdef, *comptr; static int call_nbr = 0; call_nbr++; defs->drive_buffering = (-1); defs->timeout = (-1); defs->long_timeout = (-1); defs->cleaning = (-1); defs->nowait = (-1); defs->sili = (-1); for (i=0; i < NBR_MODES; i++) { defs->modedefs[i].defined = FALSE; defs->modedefs[i].blocksize = (-1); defs->modedefs[i].density = (-1); defs->modedefs[i].buffer_writes = (-1); defs->modedefs[i].async_writes = (-1); defs->modedefs[i].read_ahead = (-1); defs->modedefs[i].two_fm = (-1); defs->modedefs[i].compression = (-1); defs->modedefs[i].auto_lock = (-1); defs->modedefs[i].fast_eod = (-1); defs->modedefs[i].can_bsr = (-1); defs->modedefs[i].no_blklimits = (-1); defs->modedefs[i].can_partitions = (-1); defs->modedefs[i].scsi2logical = (-1); defs->modedefs[i].sysv = (-1); defs->modedefs[i].defs_for_writes = (-1); } next_block(dbf, NULL, 0, 0); /* Find matching inquiry block */ for (errors=0 ; ; ) { if (!next_block(dbf, defstr, DEFMAX, '{')) break; find_string(defstr, "manuf", tmpcomp, LINEMAX); find_string(defstr, "model", tmpprod, LINEMAX); find_string(defstr, "rev", tmprev, LINEMAX); if (!next_block(dbf, defstr, DEFMAX, '}')) { fprintf(stderr, "End of definition block not found for ('%s', '%s', '%s').\n", tmpcomp, tmpprod, tmprev); return FALSE; } if (!parse_only) { if (tmpcomp[0] != '\0' && strncmp(company, tmpcomp, strlen(tmpcomp))) continue; if (tmpprod[0] != '\0' && strncmp(product, tmpprod, strlen(tmpprod))) continue; if (tmprev[0] != '\0' && strncmp(rev, tmprev, strlen(tmprev))) continue; } else if (verbose > 0) printf("\nParsing modes for ('%s', '%s', '%s').\n", tmpcomp, tmpprod, tmprev); /* Block found, get the characteristics */ for (nextdef=defstr; *nextdef != '\0' && (*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++) ; c = *nextdef; *nextdef = '\0'; strcpy(comdef, defstr); *nextdef = c; comptr = comdef; SKIP_WHITE(comptr); for ( ; *nextdef != '\0'; ) { curdef = nextdef; SKIP_WHITE(curdef); for (nextdef++ ; *nextdef != '\0' && (*nextdef != 'm' || strncmp(nextdef, "mode", 2)); nextdef++) ; c = *nextdef; *nextdef = '\0'; mode = strtol(curdef + 4, &cp, 0) - 1; if (mode < 0 || mode >= NBR_MODES) { fprintf(stderr, "Illegal mode for ('%s', '%s', '%s'):\n'%s'\n", tmpcomp, tmpprod, tmprev, curdef); *nextdef = c; errors++; continue; } strcpy(defstr, comptr); strcat(defstr, cp); *nextdef = c; if (verbose > 1) fprintf(stderr, "Mode %d definition: %s\n", mode + 1, defstr); if ((t = find_string(defstr, "disab", line, LINEMAX)) != NULL && strtol(t, NULL, 0) != 0) { defs->modedefs[mode].defined = FALSE; continue; } if ((t = find_string(defstr, "drive-", line, LINEMAX)) != NULL) defs->drive_buffering = num_arg(t); if ((t = find_string(defstr, "timeout", line, LINEMAX)) != NULL) defs->timeout = num_arg(t); if ((t = find_string(defstr, "long-time", line, LINEMAX)) != NULL) defs->long_timeout = num_arg(t); if ((t = find_string(defstr, "clean", line, LINEMAX)) != NULL) defs->cleaning = num_arg(t); if ((t = find_string(defstr, "no-w", line, LINEMAX)) != NULL) defs->nowait = num_arg(t); if ((t = find_string(defstr, "sili", line, LINEMAX)) != NULL) defs->sili = num_arg(t); defs->modedefs[mode].defined = TRUE; if ((t = find_string(defstr, "block", line, LINEMAX)) != NULL) defs->modedefs[mode].blocksize = num_arg(t); if ((t = find_string(defstr, "dens", line, LINEMAX)) != NULL) defs->modedefs[mode].density = num_arg(t); if ((t = find_string(defstr, "buff", line, LINEMAX)) != NULL) defs->modedefs[mode].buffer_writes = num_arg(t); if ((t = find_string(defstr, "async", line, LINEMAX)) != NULL) defs->modedefs[mode].async_writes = num_arg(t); if ((t = find_string(defstr, "read", line, LINEMAX)) != NULL) defs->modedefs[mode].read_ahead = num_arg(t); if ((t = find_string(defstr, "two", line, LINEMAX)) != NULL) defs->modedefs[mode].two_fm = num_arg(t); if ((t = find_string(defstr, "comp", line, LINEMAX)) != NULL) defs->modedefs[mode].compression = num_arg(t); if ((t = find_string(defstr, "auto", line, LINEMAX)) != NULL) defs->modedefs[mode].auto_lock = num_arg(t); if ((t = find_string(defstr, "fast", line, LINEMAX)) != NULL) defs->modedefs[mode].fast_eod = num_arg(t); if ((t = find_string(defstr, "can-b", line, LINEMAX)) != NULL) defs->modedefs[mode].can_bsr = num_arg(t); if ((t = find_string(defstr, "noblk", line, LINEMAX)) != NULL) defs->modedefs[mode].no_blklimits = num_arg(t); if ((t = find_string(defstr, "can-p", line, LINEMAX)) != NULL) defs->modedefs[mode].can_partitions = num_arg(t); if ((t = find_string(defstr, "scsi2", line, LINEMAX)) != NULL) defs->modedefs[mode].scsi2logical = num_arg(t); if ((t = find_string(defstr, "sysv", line, LINEMAX)) != NULL) defs->modedefs[mode].sysv = num_arg(t); if ((t = find_string(defstr, "defs-for-w", line, LINEMAX)) != NULL) defs->modedefs[mode].defs_for_writes = num_arg(t); for (t=defstr; *t == ' ' || *t == '\t'; t++) ; if (*t != '\0' && call_nbr <= 1) { fprintf(stderr, "Warning: errors in definition for ('%s', '%s', '%s'):\n%s\n", tmpcomp, tmpprod, tmprev, defstr); errors++; } } } if (parse_only) { if (verbose > 0) printf("\n"); printf("Definition parse completed. "); if (errors) { printf("Errors found!\n"); return FALSE; } else { printf("No errors found.\n"); return TRUE; } } else { for (i=modes_defined=0; i < NBR_MODES; i++) if (defs->modedefs[i].defined) modes_defined++; if (modes_defined == 0) { fprintf(stderr, "Warning: No modes in definition for ('%s', '%s', '%s').\n", tmpcomp, tmpprod, tmprev); errors++; } } if (modes_defined) return TRUE; return FALSE; } static int sg_io_errcheck(struct sg_io_hdr *hdp) { int status; status = hdp->status & 0x7e; if ((hdp->status & 0x7e) == 0 || hdp->host_status == 0 || hdp->driver_status == 0) return 0; return EIO; } #define INQUIRY 0x12 #define INQUIRY_CMDLEN 6 #define SENSE_BUFF_LEN 32 #define DEF_TIMEOUT 60000 #ifndef SCSI_IOCTL_SEND_COMMAND #define SCSI_IOCTL_SEND_COMMAND 1 #endif #define IOCTL_HEADER_LENGTH 8 static int do_inquiry(char *tname, char *company, char *product, char *rev, int print_non_found) { int fn; int result, *ip, i; #define BUFLEN 256 unsigned char buffer[BUFLEN], *cmd, *inqptr; struct sg_io_hdr io_hdr; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY, 0, 0, 0, 200, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; if ((fn = open(tname, O_RDONLY | O_NONBLOCK)) < 0) { if (print_non_found || verbose > 0) { if (errno == ENXIO) fprintf(stderr, "Device '%s' not found by kernel.\n", tname); else fprintf(stderr, "Can't open tape device '%s' (errno %d).\n", tname, errno); } return FALSE; } /* Try SG_IO first, if it is not supported, use SCSI_IOCTL_SEND_COMMAND */ memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(inqCmdBlk); io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = 200; io_hdr.dxferp = buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; inqptr = buffer; result = ioctl(fn, SG_IO, &io_hdr); if (!result) result = sg_io_errcheck(&io_hdr); if (result) { if (errno == ENOTTY || errno == EINVAL) { memset(buffer, 0, BUFLEN); ip = (int *)&(buffer[0]); *ip = 0; *(ip+1) = BUFLEN - 13; cmd = &(buffer[8]); cmd[0] = INQUIRY; cmd[4] = 200; result = ioctl(fn, SCSI_IOCTL_SEND_COMMAND, buffer); inqptr = buffer + IOCTL_HEADER_LENGTH; } if (result) { close(fn); sprintf((char *)buffer, "The SCSI INQUIRY for device '%s' failed (power off?)", tname); perror((char *)buffer); return FALSE; } } memcpy(company, inqptr + 8, 8); for (i=8; i > 0 && company[i-1] == ' '; i--) ; company[i] = '\0'; memcpy(product, inqptr + 16, 16); for (i=16; i > 0 && product[i-1] == ' '; i--) ; product[i] = '\0'; memcpy(rev, inqptr + 32, 4); for (i=4; i > 0 && rev[i-1] == ' '; i--) ; rev[i] = '\0'; close(fn); return TRUE; } static int tapenum(char *name) { int dev; struct dirent *dent; DIR *dirp; char tmpname[PATH_MAX]; const char *dn; const devdir *dvd; struct stat statbuf; if (strchr(name, '/') != NULL) { /* Complete name */ if (stat(name, &statbuf) != 0) { fprintf(stderr, "Can't stat '%s'.\n", name); return (-1); } if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) != SCSI_TAPE_MAJOR) return (-1); dev = minor(statbuf.st_rdev) & 31; return dev; } else { /* Search from the device directories */ for (dvd=devdirs; dvd->dir[0] != 0; dvd++) { dn = dvd->dir; if ((dirp = opendir(dn)) == NULL) continue; for ( ; (dent = readdir(dirp)) != NULL; ) if (!strcmp(dent->d_name, name)) { strcpy(tmpname, dn); strcat(tmpname, "/"); strcat(tmpname, dent->d_name); if (stat(tmpname, &statbuf) != 0) { fprintf(stderr, "Can't stat '%s'.\n", tmpname); continue; } if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) != SCSI_TAPE_MAJOR) continue; dev = minor(statbuf.st_rdev) & 31; closedir(dirp); return dev; } closedir(dirp); } } return (-1); } static int accept_tape_name(char *name) { char **npp; for (npp=tape_name_bases; *npp; npp++) if (!strncmp(name, *npp, strlen(*npp))) return TRUE; return FALSE; } static int find_devfiles(int tapeno, char **names) { int dev, mode, found; int non_rew[NBR_MODES]; struct dirent *dent; DIR *dirp; char tmpname[PATH_MAX]; const char *dn; static const devdir *dvd; const devdir *dvp; int tmpdevdirsindex = 0; static devdir tmpdevdirs[MAX_TAPES + 1]; struct stat statbuf; for (found=0; found < NBR_MODES; found++) { *names[found] = '\0'; non_rew[found] = FALSE; } if (dvd == NULL && !stat(DEVFS_PATH, &statbuf)) { if (S_ISDIR(statbuf.st_mode) && (dirp=opendir(DEVFS_PATH)) != NULL) { /* Assume devfs, one directory for each tape */ for ( ; (dent = readdir(dirp)) != NULL; ) { if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, "..")) continue; snprintf(tmpdevdirs[tmpdevdirsindex].dir, sizeof(tmpdevdirs[tmpdevdirsindex].dir), "%s/%s", DEVFS_PATH, dent->d_name); tmpdevdirs[tmpdevdirsindex].selective_scan = FALSE; if (++tmpdevdirsindex == MAX_TAPES) break; } tmpdevdirs[tmpdevdirsindex].dir[0] = 0; closedir(dirp); dvd = &tmpdevdirs[0]; } } if (dvd == NULL) dvd = devdirs; for (found=0, dvp=dvd; found < NBR_MODES && dvp->dir[0] != 0; dvp++) { dn = dvp->dir; if ((dirp = opendir(dn)) == NULL) continue; for ( ; (dent = readdir(dirp)) != NULL; ) { if (!strcmp (dent->d_name, ".") || !strcmp (dent->d_name, "..")) continue; /* Ignore non-tape devices to avoid loading all the modules */ if (dvp->selective_scan && !accept_tape_name(dent->d_name)) continue; strcpy(tmpname, dn); strcat(tmpname, "/"); strcat(tmpname, dent->d_name); if (stat(tmpname, &statbuf) != 0) { fprintf(stderr, "Can't stat '%s'.\n", tmpname); continue; } if (!S_ISCHR(statbuf.st_mode) || major(statbuf.st_rdev) != SCSI_TAPE_MAJOR) continue; dev = minor(statbuf.st_rdev); if ((dev & 31) != tapeno) continue; mode = (dev & 127) >> 5; if (non_rew[mode]) continue; if (*names[mode] == '\0') found++; strcpy(names[mode], tmpname); non_rew[mode] = (dev & 128) != 0; } closedir(dirp); } return (found > 0); } static int set_defs(devdef_tr *defs, char **fnames) { int i, tape; int clear_set[2]; struct mtop op; for (i=0; i < NBR_MODES; i++) { if (*fnames[i] == '\0' || !defs->modedefs[i].defined) continue; if ((tape = open(fnames[i], O_RDONLY | O_NONBLOCK)) < 0) { fprintf(stderr, "Can't open the tape device '%s' for mode %d.\n", fnames[i], i); return FALSE; } if (i == 0) { if (defs->do_rewind) { op.mt_op = MTREW; op.mt_count = 1; ioctl(tape, MTIOCTOP, &op); /* Don't worry about result */ } if (defs->drive_buffering >= 0) { op.mt_op = MTSETDRVBUFFER; op.mt_count = MT_ST_DEF_DRVBUFFER | defs->drive_buffering; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set drive buffering to %d.\n", defs->drive_buffering); } } if (defs->timeout >= 0) { op.mt_op = MTSETDRVBUFFER; op.mt_count = MT_ST_SET_TIMEOUT | defs->timeout; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set device timeout %d s.\n", defs->timeout); } } if (defs->long_timeout >= 0) { op.mt_op = MTSETDRVBUFFER; op.mt_count = MT_ST_SET_LONG_TIMEOUT | defs->long_timeout; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set device long timeout %d s.\n", defs->long_timeout); } } if (defs->cleaning >= 0) { op.mt_op = MTSETDRVBUFFER; op.mt_count = MT_ST_SET_CLN | defs->cleaning; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set cleaning request parameter to %x\n", defs->cleaning); } } } op.mt_op = MTSETDRVBUFFER; clear_set[0] = clear_set[1] = 0; if (defs->nowait >= 0) clear_set[defs->nowait != 0] |= MT_ST_NOWAIT; if (defs->sili >= 0) clear_set[defs->sili != 0] |= MT_ST_SILI; if (defs->modedefs[i].buffer_writes >= 0) clear_set[defs->modedefs[i].buffer_writes != 0] |= MT_ST_BUFFER_WRITES; if (defs->modedefs[i].async_writes >= 0) clear_set[defs->modedefs[i].async_writes != 0] |= MT_ST_ASYNC_WRITES; if (defs->modedefs[i].read_ahead >= 0) clear_set[defs->modedefs[i].read_ahead != 0] |= MT_ST_READ_AHEAD; if (defs->modedefs[i].two_fm >= 0) clear_set[defs->modedefs[i].two_fm != 0] |= MT_ST_TWO_FM; if (defs->modedefs[i].fast_eod >= 0) clear_set[defs->modedefs[i].fast_eod != 0] |= MT_ST_FAST_MTEOM; if (defs->modedefs[i].auto_lock >= 0) clear_set[defs->modedefs[i].auto_lock != 0] |= MT_ST_AUTO_LOCK; if (defs->modedefs[i].can_bsr >= 0) clear_set[defs->modedefs[i].can_bsr != 0] |= MT_ST_CAN_BSR; if (defs->modedefs[i].no_blklimits >= 0) clear_set[defs->modedefs[i].no_blklimits != 0] |= MT_ST_NO_BLKLIMS; if (defs->modedefs[i].can_partitions >= 0) clear_set[defs->modedefs[i].can_partitions != 0] |= MT_ST_CAN_PARTITIONS; if (defs->modedefs[i].scsi2logical >= 0) clear_set[defs->modedefs[i].scsi2logical != 0] |= MT_ST_SCSI2LOGICAL; if (defs->modedefs[i].sysv >= 0) clear_set[defs->modedefs[i].sysv != 0] |= MT_ST_SYSV; if (defs->modedefs[i].defs_for_writes >= 0) clear_set[defs->modedefs[i].defs_for_writes != 0] |= MT_ST_DEF_WRITES; if (clear_set[0] != 0) { op.mt_count = MT_ST_CLEARBOOLEANS | clear_set[0]; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't clear the tape options (bits 0x%x, mode %d).\n", clear_set[0], i); } } if (clear_set[1] != 0) { op.mt_count = MT_ST_SETBOOLEANS | clear_set[1]; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set the tape options (bits 0x%x, mode %d).\n", clear_set[1], i); } } if (defs->modedefs[i].blocksize >= 0) { op.mt_count = MT_ST_DEF_BLKSIZE | defs->modedefs[i].blocksize; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set blocksize %d for mode %d.\n", defs->modedefs[i].blocksize, i); } } if (defs->modedefs[i].density >= 0) { op.mt_count = MT_ST_DEF_DENSITY | defs->modedefs[i].density; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set density %x for mode %d.\n", defs->modedefs[i].density, i); } } if (defs->modedefs[i].compression >= 0) { op.mt_count = MT_ST_DEF_COMPRESSION | defs->modedefs[i].compression; if (ioctl(tape, MTIOCTOP, &op) != 0) { fprintf(stderr, "Can't set compression %d for mode %d.\n", defs->modedefs[i].compression, i); } } close(tape); } return TRUE; } static int define_tape(int tapeno, FILE *dbf, devdef_tr *defptr, int print_non_found) { int i; char company[10], product[20], rev[5], *tname, *fnames[NBR_MODES]; if (verbose > 0) printf("\nstinit, processing tape %d\n", tapeno); if ((fnames[0] = calloc(NBR_MODES, PATH_MAX)) == NULL) { fprintf(stderr, "Can't allocate name buffers.\n"); return FALSE; } for (i=1; i < NBR_MODES; i++) fnames[i] = fnames[i-1] + PATH_MAX; if (!find_devfiles(tapeno, fnames) || *fnames[0] == '\0') { if (print_non_found) fprintf(stderr, "Can't find any device files for tape %d.\n", tapeno); free(fnames[0]); return FALSE; } if (verbose > 1) for (i=0; i < NBR_MODES; i++) printf("Mode %d, name '%s'\n", i + 1, fnames[i]); tname = fnames[0]; if (!do_inquiry(tname, company, product, rev, print_non_found)) { free(fnames[0]); return FALSE; } if (verbose > 0) printf("The manufacturer is '%s', product is '%s', and revision '%s'.\n", company, product, rev); if (!find_pars(dbf, company, product, rev, defptr, FALSE)) { fprintf(stderr, "Can't find defaults for tape number %d.\n", tapeno); free(fnames[0]); return FALSE; } if (!set_defs(defptr, fnames)) { free(fnames[0]); return FALSE; } free(fnames[0]); return TRUE; } static char usage(int retval) { fprintf(stderr, "Usage: stinit [-h] [-v] [-f dbname] [-p] [drivename_or_number ...]\n"); exit(retval); } int main(int argc, char **argv) { FILE *dbf = NULL; int argn; int tapeno, parse_only = FALSE; char *dbname = NULL; devdef_tr defs; defs.do_rewind = FALSE; for (argn=1; argn < argc && *argv[argn] == '-'; argn++) { if (*(argv[argn] + 1) == 'v') verbose++; else if (*(argv[argn] + 1) == 'p') parse_only = TRUE; else if (*(argv[argn] + 1) == 'h') usage(0); else if (*(argv[argn] + 1) == 'r') defs.do_rewind = TRUE; else if (*(argv[argn] + 1) == 'f') { argn += 1; if (argn >= argc) usage(1); dbname = argv[argn]; } else if (*(argv[argn] + 1) == '-' && *(argv[argn] + 2) == 'v') { printf("stinit v. %s\n", VERSION); exit(0); break; } else usage(1); } if ((dbf = open_database(dbname)) == NULL) return 1; if (parse_only) { if (argc > argn) fprintf(stderr, "Extra arguments on command line ignored.\n"); if (!find_pars(dbf, "xyz", "xyz", "xyz", &defs, TRUE)) return 1; return 0; } if (argc > argn) { /* Initialize specific drives */ for ( ; argn < argc; argn++) { if (*argv[argn] == '-') { usage(1); return 1; /* Never executed but makes gcc happy */ } else if (isdigit(*argv[argn])) tapeno = strtol(argv[argn], NULL, 0); else if ((tapeno = tapenum(argv[argn])) < 0) { fprintf(stderr, "Can't find tape number for name '%s'.\n", argv[argn]); continue; } if (!define_tape(tapeno, dbf, &defs, TRUE)) fprintf(stderr, "Definition for '%s' failed.\n", argv[argn]); } } else { /* Initialize all SCSI tapes */ for (tapeno=0; tapeno < MAX_TAPES; tapeno++) if (!define_tape(tapeno, dbf, &defs, FALSE)) { fprintf(stderr, "Initialized %d tape device%s.\n", tapeno, (tapeno != 1 ? "s" : "")); return 0; /* Process tapes until failure */ } } return 0; } mt-st-1.1/mt.10000644001152000001440000002115611005130430012614 0ustar makisarausers.TH MT 1 "April 2008" \" -*- nroff -*- .SH NAME mt \- control magnetic tape drive operation .SH SYNOPSIS .B mt [\-h] [\-f device] operation [count] [arguments...] .SH DESCRIPTION This manual page documents the tape control program .BR mt . .B mt performs the given .IR operation , which must be one of the tape operations listed below, on a tape drive. The commands can also be listed by running the program with the .I \-h option. The version of mt is printed with the .I \-v or .I \-\-version option. The path of the tape device on which to operate can be given with the .I \-f or .I \-t option. If neither of those options is given, and the environment variable .B TAPE is set, it is used. Otherwise, a default device defined in the file .I /usr/include/sys/mtio.h is used. .PP Some operations optionally take an argument or repeat count, which can be given after the operation name and defaults to 1. The postfix .I k , .I M , or .I G can be used to give counts in units of 1024, 1024 * 1024, or 1024 * 1024 * 1024, respectively. .PP The available operations are listed below. Unique abbreviations are accepted. Not all operations are available on all systems, or work on all types of tape drives. .IP fsf Forward space .I count files. The tape is positioned on the first block of the next file. .IP fsfm Forward space .I count files. The tape is positioned on the last block of the previous file. .IP bsf Backward space .I count files. The tape is positioned on the last block of the previous file. .IP bsfm Backward space .I count files. The tape is positioned on the first block of the next file. .IP asf The tape is positioned at the beginning of the .I count file. Positioning is done by first rewinding the tape and then spacing forward over .I count filemarks. .IP fsr Forward space .I count records. .IP bsr Backward space .I count records. .IP fss (SCSI tapes) Forward space .I count setmarks. .IP bss (SCSI tapes) Backward space .I count setmarks. .IP "eod, seod" Space to end of valid data. Used on streamer tape drives to append data to the logical end of tape. .IP rewind Rewind the tape. .IP "offline, rewoffl, eject" Rewind the tape and, if applicable, unload the tape. .IP retension Rewind the tape, then wind it to the end of the reel, then rewind it again. .IP "weof, eof" Write .I count EOF marks at current position. .IP "wset" (SCSI tapes) Write .I count setmarks at current position (only SCSI tape). .IP erase Erase the tape. .IP status Print status information about the tape unit. (If the density code is "no translation" in the status output, this does not affect working of the tape drive.) .IP seek (SCSI tapes) Seek to the .I count block on the tape. This operation is available on some Tandberg and Wangtek streamers and some SCSI-2 tape drives. The block address should be obtained from a .I tell call earlier. .IP tell (SCSI tapes) Tell the current block on tape. This operation is available on some Tandberg and Wangtek streamers and some SCSI-2 tape drives. .IP setpartition (SCSI tapes) Switch to the partition determined by .I count. The default data partition of the tape is numbered zero. Switching partition is available only if enabled for the device, the device supports multiple partitions, and the tape is formatted with multiple partitions. .IP partseek (SCSI tapes) The tape position is set to block .I count in the partition given by the argument after count. The default partition is zero. .IP mkpartition (SCSI tapes) Format the tape with one (count is zero) or two partitions (count gives the size of the second partition in megabytes). The tape drive must be able to format partitioned tapes with initiator-specified partition size and partition support must be enabled for the drive. .IP load (SCSI tapes) Send the load command to the tape drive. The drives usually load the tape when a new cartridge is inserted. The argument .I count can usually be omitted. Some HP changers load tape n if the .I count 10000 + n is given (a special funtion in the Linux st driver). .IP lock (SCSI tapes) Lock the tape drive door. .IP unlock (SCSI tapes) Unlock the tape drive door. .IP setblk (SCSI tapes) Set the block size of the drive to .I count bytes per record. .IP setdensity (SCSI tapes) Set the tape density code to .I count. The proper codes to use with each drive should be looked up from the drive documentation. .IP densities (SCSI tapes) Write explanation of some common density codes to standard output. .IP drvbuffer (SCSI tapes) Set the tape drive buffer code to .I number. The proper value for unbuffered operation is zero and "normal" buffered operation one. The meanings of other values can be found in the drive documentation or, in the case of a SCSI-2 drive, from the SCSI-2 standard. .IP compression (SCSI tapes) The compression within the drive can be switched on or off using the MTCOMPRESSION ioctl. Note that this method is not supported by all drives implementing compression. For instance, the Exabyte 8 mm drives use density codes to select compression. .IP stoptions (SCSI tapes) Set the driver options bits for the device to the defined values. Allowed only for the superuser. The bits can be set either by ORing the option bits from the file /usr/include/linux/mtio.h to .I count, or by using the following keywords (as many keywords can be used on the same line as necessary, unambiguous abbreviations allowed): .RS .IP buffer-writes 15 buffered writes enabled .IP async-writes asynchronous writes enabled .IP read-ahead read-ahead for fixed block size .IP debug debugging (if compiled into driver) .IP two-fms write two filemarks when file closed .IP fast-eod space directly to eod (and lose file number) .IP no-wait don't wait until rewind, etc. complete .IP auto-lock automatically lock/unlock drive door .IP def-writes the block size and density are for writes .IP can-bsr drive can space backwards as well .IP no-blklimits drive doesn't support read block limits .IP can-partitions drive can handle partitioned tapes .IP scsi2logical seek and tell use SCSI-2 logical block addresses instead of device dependent addresses .IP sili Set the SILI bit is when reading in variable block mode. This may speed up reading blocks shorter than the read byte count. Set this option only if you know that the drive supports SILI and the HBA reliably returns transfer residual byte counts. Requires kernel version >= 2.6.26. .IP sysv enable the System V semantics .RE .IP stsetoptions (SCSI tapes) Set selected driver options bits. The methods to specify the bits to set are given above in the description of .BR stoptions. Allowed only for the superuser. .IP stclearoptions (SCSI tapes) Clear selected driver option bits. The methods to specify the bits to clear are given above in description of .BR stoptions. Allowed only for the superuser. .IP stshowoptions (SCSI tapes) Print the currently enabled options for the device. Requires kernel version >= 2.6.26 and sysfs must be mounted at /sys. .IP stwrthreshold (SCSI tapes) The write threshold for the tape device is set to .I count kilobytes. The value must be smaller than or equal to the driver buffer size. Allowed only for the superuser. .IP defblksize (SCSI tapes) Set the default block size of the device to .I count bytes. The value -1 disables the default block size. The block size set by .I setblk overrides the default until a new tape is inserted. Allowed only for the superuser. .IP defdensity (SCSI tapes) Set the default density code. The value -1 disables the default density. The density set by .I setdensity overrides the default until a new tape is inserted. Allowed only for the superuser. .IP defdrvbuffer (SCSI tapes) Set the default drive buffer code. The value -1 disables the default drive buffer code. The drive buffer code set by .I drvbuffer overrides the default until a new tape is inserted. Allowed only for the superuser. .IP defcompression (SCSI tapes) Set the default compression state. The value -1 disables the default compression. The compression state set by .I compression overrides the default until a new tape is inserted. Allowed only for the superuser. .IP sttimeout sets the normal timeout for the device. The value is given in seconds. Allowed only for the superuser. .IP stlongtimeout sets the long timeout for the device. The value is given in seconds. Allowed only for the superuser. .IP stsetcln set the cleaning request interpretation parameters. .PP .B mt exits with a status of 0 if the operation succeeded, 1 if the operation or device name given was invalid, or 2 if the operation failed. .SH AUTHOR The program is written by Kai Makisara . .SH COPYRIGHT The program and the manual page are copyrighted by Kai Makisara, 1998-2008. They can be distributed according to the GNU Copyleft. .SH SEE ALSO st(4) mt-st-1.1/mt-st-1.1.lsm0000644001152000001440000000116711005110646014177 0ustar makisarausersBegin4 Title: mt-st Version: 1.1 Entered-date: 2008-04-27 Description: Magnetic tape control tools for Linux SCSI tapes. Includes a mt-like program supporting additional commands using ioctls specific to the Linux SCSI tape driver (up to kernel 2.6.26), and the program stinit to define the SCSI tape devices in system startup scripts. Keywords: tape SCSI Author: Kai.Makisara@kolumbus.fi (Kai Makisara) Maintained-by: Kai.Makisara@kolumbus.fi (Kai Makisara) Primary-site: ftp://ftp.ibiblio.org/pub/linux/system/backup 36 kB mt-st-1.1.tar.gz 0.7 kB mt-st-1.1.lsm Copying-policy: GPL End mt-st-1.1/COPYING0000644001152000001440000004403210460510403013151 0ustar makisarausers NOTE! This copyright does *not* cover user programs that use kernel services by normal system calls - this is merely considered normal use of the kernel, and does *not* fall under the heading of "derived work". Also note that the GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the linux kernel) is copyrighted by me and others who actually wrote it. Linus Torvalds ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. mt-st-1.1/stinit.80000644001152000001440000002310711005130550013516 0ustar makisarausers.TH STINIT 8 "April 2008" \" -*- nroff -*- .SH NAME stinit \- initialize SCSI magnetic tape drives .SH SYNOPSIS .B stinit [\-f conf-file] [\-h] [-p] [-r] [-v] [devices...] .SH DESCRIPTION This manual page documents the tape control program .BR stinit can used to initialize SCSI tape drive modes at system startup, after loading the tape driver as module, or after introduction of new device to the SCSI subsystem at run-time. The initialization is performed by sending ioctl commands to the drive. The commands are defined in a text file that is indexed using the inquiry data the drive returns (manufacturer, device, revision). Values for all of the general and mode-specific SCSI tape parameters up to Linux version 2.6.0 can be initialized. .PP .SH OPTIONS .TP .I \-f conf-file Specifies the name of the text file containing the definitions for different tape drive types. By default .B stinit tries to find the definition file .I stinit.def or .I /etc/stinit.def (in this order). .TP .I \-h Print the usage information. .TP .I \-p The definition file is parsed but no tape drive initialization is attempted. This option can be used for testing the integrity of a definition file after changes have been made. .TP .I \-r Rewind every device being initialized. .TP .I \-v The more -v options (currently up to two), the more verbose output. .TP .I \-\-version Print the program version. .PP .SH THE DEVICES BEING INITIALIZED If the program is started without arguments, it tries to find all accessible SCSI tape devices and the device files for the different modes of the devices. The tape drives are searched in the scanning order of the kernel and searching is stopped at the first non-existing tape. All of the found devices are initialized if a matching description is found from the parameter file. Note that a mode for a device is not initialized if the corresponding device file is not found even if a matching description for the mode exists. The non-rewind device is preferred over the auto-rewind device for each mode. If the directory .I /dev/tapes is found, the devfs filesystem is assumed to be mounted on /dev. Otherwise, the directories .I /dev/scsi and .I /dev are scanned for device files. .PP SCSI tape drives can be initialized selectively using program arguments. A numeric argument specifies the number of the tape drive in the scanning order of the kernel. A file name specifies that the device corresponding to this name is to be initialized. If the file name is given without the directory specification, the program searches for the name in the device directories .I /dev/scsi and .I /dev. Only full path names are supported with devfs. .PP .SH THE CONFIGURATION FILE The configuration file is a simple text file that contains descriptions of tape drives and the corresponding initialization parameters. The parameter definition blocks are delimited by .I {}. Specification of the drive description is restarted after each parameter definition block. .PP The drive descriptions and the parameter definitions consist of pairs .I name = value. The value is either a numeric parameter, a string not containing blanks, or a quoted string. In case of a numeric parameter, the postfix .I k or .I M can be used to give the value in units of 1024 or 1024 * 1024, respectively. If the .I =value -part is omitted, the value .I "1" is used. If the character .I # is found from an input line, the rest of the line is discarded. This allows use of comments in the definition file. The following example contains definitions for one type of tape drives: .PP .RS .nf # The XY dat manufacturer=XY-COMPANY model = "UVW DRIVE" { scsi2logical=1 # Common definitions for all modes can-bsr can-partitions auto-lock # Definition of modes mode1 blocksize=0 compression=1 mode2 blocksize=1024 compression=1 mode3 blocksize=0 compression=0 mode4 blocksize = 1k compression=0 } .fi .RE .PP The devices are identified using zero or more of the following keywords corresponding to the data returned by the tape device as response to the SCSI INQUIRY command. The matches are case-sensitive and performed up to the length defined in the configuration file (permitting use of partial matches). .IP manufacturer= This keyword specifies the string that must match the vendor identification returned by the device. .IP model= This keyword defines the string that must match the .B product identification returned by the device. .IP revision= This keyword matched the string that must match the .B product revision level returned by the device. .PP All of the matching initializations are collected in the order they are defined in the file. This means that common parameters can be defined for all devices using zero keywords for a definition block. Another consequence is that, for instance, some parameters can be easily given different values for a specific firmware revision without repeating the parameters common to all revisions. .PP The tape parameters are defined using the following keywords. More thorough description of the parameters can be found from the .I st(4) man page (not up to date when this is written) or from the file .I drivers/scsi/README.st in the Linux kernel source tree. The keywords are matched using only the first characters. The part of the keywords not used in matching is enclosed by []. The numeric values may be specified either in decimal notation or hexadecimal notation (using the prefix 0x). .IP drive-[buffering]=value The drive's buffering parameter is set to .I value. This parameter if common for all modes. .IP cleaning The cleaning request notifying parameter is set to .I value .IP no-w[ait] The immediate mode is used with commands like rewind if .I value is non-zero (i.e., the driver does not wait for the command to finish). .IP mode=value This keyword starts definition of tape mode .I value. The number of the mode must be between 1 and 4. .IP disab[led]=value This mode is disabled for this device if .I value is non-zero. Can be used if some mode defined in a more general definition should be disabled by a more specific definition for some device (for example, for a device with buggy firmware level). .IP block[size]=value The default tape block size is set to .I value. bytes. The block size zero means variable block mode. .IP dens[ity]=value The tape density code is set to .I value. .IP buff[ering]=value The buffered writes by the driver in fixed block mode are enabled if .I value is non-zero. .IP async[-writes]=value Asynchronous writes by the driver are enabled if .I value is non-zero. .IP read[-ahead]=value Read-ahead by the driver in fixed block mode is allowed if .I value is non-zero. .IP two[-fms]=value Two filemarks are written when a file being written to is closed if .I value is non-zero. By default, one filemark is written. .IP comp[ression]=value Compression of the data by the drive is enabled if .I value is non-zero. Note that the tape driver can't enable compression for all drives that can compress data. Note also that some drives define compression using density codes. .IP auto[-lock]=value The tape drive door is locked automatically when the device file is opened if .I value is non-zero. .IP fast[-eom]=value The MTEOM command is performed using the SCSI command that spaces directly to the end of medium if .I value is non-zero. The drawback is that the file number in the status becomes invalid. By default, spacing to end of medium is performed by spacing over filemarks until end of medium is detected and the file number remains valid. .IP can-b[sr]=value Backspacing over records is used by the driver when repositioning the tape when read-ahead is enabled if .I value is non-zero. .IP noblk[limits]=value The tape driver does not use the READ BLOCK LIMITS SCSI command when the device is being opened if .I value is non-zero. This is for the drives that do not support this SCSI command. .IP can-p[artitions]=value The support for tape partitions is enabled if .I value is non-zero. .IP scsi2[logical]=value Logical block addresses are used in the MTSEEK and MTIOCPOS commands if .I value is non-zero. The default is to use the device-specific addresses. .IP sili=value If .I value is non-zero, the SILI bit is set when reading in variable block mode. This may speed up reading blocks shorter than the read byte count. Set this only if you know that the drive supports SILI and the HBA reliably returns transfer residual byte counts. Requires kernel version >= 2.6.26. .IP defs-for-w[rites]=value The parameters defining the tape format (density, block size, etc.) are forced when writing starts at the beginning of a tape if .I value is non-zero. The default is to change there parameters each time the device is opened at the beginning of a tape (or the mode is changed in the middle of a tape). .IP sysv=value The System V tape semantics are used if .I value is non-zero. Otherwise the BSD semantics are used. .IP timeout=value The normal timeout for the device is set to .I value seconds. .IP long-time[out]=value The long timeout for the device is set to .I value seconds. .SH RETURN VALUE The program exits with value one if the command line is incorrect, the definition file is not found, or option -p is given and parsing the definition file fails. In all other cases the return value is zero (i.e., failing of initialization is not currently signaled by the return value). .SH RESTRICTIONS With the exception of the -p option, the program can be used only by the superuser. This is because the program uses ioctls allowed only for the superuser. .SH AUTHOR The program is written by Kai Makisara . .SH COPYRIGHT The program and the manual page are copyrighted by Kai Makisara, 1998-2008. They can be distributed according to the GNU Copyleft. .SH SEE ALSO st(4) mt(1) mt-st-1.1/Makefile0000644001152000001440000000134110762232344013564 0ustar makisarausersCFLAGS= -Wall -O2 SBINDIR= /sbin BINDIR= /bin USRBINDIR= /usr/bin MANDIR= /usr/share/man MTDIR=$(BINDIR) all: mt stinit mt: mt.c $(CC) $(CFLAGS) -o mt mt.c stinit: stinit.c $(CC) $(CFLAGS) -o stinit stinit.c install: mt stinit install -s mt $(MTDIR) 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 mt-st-1.1/README0000644001152000001440000000712511005110515012774 0ustar makisarausersThis directory contains two programs; mt and stinit. Mt is basically a "standard" mt with additional commands to send the ioctls specific to the Linux SCSI tape driver. The source supports all SCSI tape ioctls up to kernel version 2.6.0 but it can also be compiled in kernels >= 2.0.x (and hopefully with 1.2.x). Although this mt program is tailored for SCSI tapes, it can also be used with other Linux tape drivers using the same ioctls (some of the commands may not work with all drivers). Stinit is a program to initialize the tape drive characteristics. The current version should be considered alpha. See README.stinit for more information. The files: README - This file. README.stinit - Information about the stinit program COPYING - The GNU Public License Makefile - Makefile for programs mt.c - The mt source mt.1 - The man page for mt mtio.h - The tape command definitions qic117.h - Needed by mtio.h stinit.c - The stinit source stinit.8 - The man page for stinit stinit.def.examples - example configurations for different devices Installation: - review the makefile - make - make install Changes in version 1.1: - unused defines removed from mtio.h (compiles also with distributions no having linux/qic117.h) - add support for MT_ST_SILI to mt and stinit - add command showoptions for kernels >= 2.6.26 Changes in version 0.9b: - only stinit changed Changes in version 0.9: - more density codes Changes in version 0.8: - put man page into /usr/share/man/man1 - documentation cleanup - add some density translations - counts can use the k, M, or G postfix Changes in version 0.7: - add command eject for compatibility with GNU mt (synonym for offline and rewoffl) - the load and ersae commands accept an argument - add CLN (cleaning request) to status - add command stsetcln to set the cleaning request recognition options - add the flag no-wait to the settable/clearable options - some new density codes added Changes in version 0.6: - uses local mtio.h to include support for the most recent driver features even when compiled on a system having old mtio.h - on-line and write-protect are checked after some errors and a message is printed if the probable error reason is found - the tape is opened with flag O_NONBLOCK for commands that are useful even when the device is not ready (no tape) - some new density codes added for printout - OnStream drives using the osst driver recognised - the obsolete command 'datcompression' is removed - new option --version in mt Changes in version 0.5b: - corrected the bug that caused the command argument to be ignored if option -f was used - added #include to stinit.c to enable compilation with glibc - density 0x45 (TR-4) added to known density list Changes in version 0.5: - utility stinit added to package - command asf added - command datcompression not compiled in default configuration - support added for setting timeouts - bugs in argument parsing corrected - help prints all commands - binaries not distributed any more - mt code cleaned - GNU Public License used for both programs Changes in version 0.4: - support for the ioctls for partitioned tapes - compiles also with 1.2.13 - the driver options can be specified also with keywords - floppy tape type is shown - (not working) support for other operating systems removed Changes in version 0.3: - support for new ioctls - accepts hexadecimal numbers with prefix 0x - the datcompression command improved (although it is being overrided by the command compression using a new ioctl) - bus fixes April 27, 2008 Kai Makisara (email Kai.Makisara@kolumbus.fi) mt-st-1.1/qic117.h0000644001152000001440000002727010460510404013302 0ustar makisarausers#ifndef _QIC117_H #define _QIC117_H /* * Copyright (C) 1993-1996 Bas Laarhoven, * (C) 1997 Claus-Justus Heine. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. 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 GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * * $Source: /homes/cvs/ftape-stacked/include/linux/qic117.h,v $ * $Revision: 1.2 $ * $Date: 1997/10/05 19:19:32 $ * * This file contains QIC-117 spec. related definitions for the * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. * * These data were taken from the Quarter-Inch Cartridge * Drive Standards, Inc. document titled: * `Common Command Set Interface Specification for Flexible * Disk Controller Based Minicartridge Tape Drives' * document QIC-117 Revision J, 28 Aug 96. * For more information, contact: * Quarter-Inch Cartridge Drive Standards, Inc. * 311 East Carrillo Street * Santa Barbara, California 93101 * Telephone (805) 963-3853 * Fax (805) 962-1541 * WWW http://www.qic.org * * Current QIC standard revisions (of interest) are: * QIC-40-MC, Rev. M, 2 Sep 92. * QIC-80-MC, Rev. N, 20 Mar 96. * QIC-80-MC, Rev. K, 15 Dec 94. * QIC-113, Rev. G, 15 Jun 95. * QIC-117, Rev. J, 28 Aug 96. * QIC-122, Rev. B, 6 Mar 91. * QIC-130, Rev. C, 2 Sep 92. * QIC-3010-MC, Rev. F, 14 Jun 95. * QIC-3020-MC, Rev. G, 31 Aug 95. * QIC-CRF3, Rev. B, 15 Jun 95. * */ /* * QIC-117 common command set rev. J. * These commands are sent to the tape unit * as number of pulses over the step line. */ typedef enum { QIC_NO_COMMAND = 0, QIC_RESET = 1, QIC_REPORT_NEXT_BIT = 2, QIC_PAUSE = 3, QIC_MICRO_STEP_PAUSE = 4, QIC_ALTERNATE_TIMEOUT = 5, QIC_REPORT_DRIVE_STATUS = 6, QIC_REPORT_ERROR_CODE = 7, QIC_REPORT_DRIVE_CONFIGURATION = 8, QIC_REPORT_ROM_VERSION = 9, QIC_LOGICAL_FORWARD = 10, QIC_PHYSICAL_REVERSE = 11, QIC_PHYSICAL_FORWARD = 12, QIC_SEEK_HEAD_TO_TRACK = 13, QIC_SEEK_LOAD_POINT = 14, QIC_ENTER_FORMAT_MODE = 15, QIC_WRITE_REFERENCE_BURST = 16, QIC_ENTER_VERIFY_MODE = 17, QIC_STOP_TAPE = 18, /* commands 19-20: reserved */ QIC_MICRO_STEP_HEAD_UP = 21, QIC_MICRO_STEP_HEAD_DOWN = 22, QIC_SOFT_SELECT = 23, QIC_SOFT_DESELECT = 24, QIC_SKIP_REVERSE = 25, QIC_SKIP_FORWARD = 26, QIC_SELECT_RATE = 27, /* command 27, in ccs2: Select Rate or Format */ QIC_ENTER_DIAGNOSTIC_1 = 28, QIC_ENTER_DIAGNOSTIC_2 = 29, QIC_ENTER_PRIMARY_MODE = 30, /* command 31: vendor unique */ QIC_REPORT_VENDOR_ID = 32, QIC_REPORT_TAPE_STATUS = 33, QIC_SKIP_EXTENDED_REVERSE = 34, QIC_SKIP_EXTENDED_FORWARD = 35, QIC_CALIBRATE_TAPE_LENGTH = 36, QIC_REPORT_FORMAT_SEGMENTS = 37, QIC_SET_FORMAT_SEGMENTS = 38, /* commands 39-45: reserved */ QIC_PHANTOM_SELECT = 46, QIC_PHANTOM_DESELECT = 47 } qic117_cmd_t; typedef enum { discretional = 0, required, ccs1, ccs2 } qic_compatibility; typedef enum { unused, mode, motion, report } command_types; struct qic117_command_table { char *name; __u8 mask; __u8 state; __u8 cmd_type; __u8 non_intr; __u8 level; }; #define QIC117_COMMANDS {\ /* command mask state cmd_type */\ /* | name | | | non_intr */\ /* | | | | | | level */\ /* 0*/ {NULL, 0x00, 0x00, mode, 0, discretional},\ /* 1*/ {"soft reset", 0x00, 0x00, motion, 1, required},\ /* 2*/ {"report next bit", 0x00, 0x00, report, 0, required},\ /* 3*/ {"pause", 0x36, 0x24, motion, 1, required},\ /* 4*/ {"micro step pause", 0x36, 0x24, motion, 1, required},\ /* 5*/ {"alternate command timeout", 0x00, 0x00, mode, 0, required},\ /* 6*/ {"report drive status", 0x00, 0x00, report, 0, required},\ /* 7*/ {"report error code", 0x01, 0x01, report, 0, required},\ /* 8*/ {"report drive configuration",0x00, 0x00, report, 0, required},\ /* 9*/ {"report rom version", 0x00, 0x00, report, 0, required},\ /*10*/ {"logical forward", 0x37, 0x25, motion, 0, required},\ /*11*/ {"physical reverse", 0x17, 0x05, motion, 0, required},\ /*12*/ {"physical forward", 0x17, 0x05, motion, 0, required},\ /*13*/ {"seek head to track", 0x37, 0x25, motion, 0, required},\ /*14*/ {"seek load point", 0x17, 0x05, motion, 1, required},\ /*15*/ {"enter format mode", 0x1f, 0x05, mode, 0, required},\ /*16*/ {"write reference burst", 0x1f, 0x05, motion, 1, required},\ /*17*/ {"enter verify mode", 0x37, 0x25, mode, 0, required},\ /*18*/ {"stop tape", 0x00, 0x00, motion, 1, required},\ /*19*/ {"reserved (19)", 0x00, 0x00, unused, 0, discretional},\ /*20*/ {"reserved (20)", 0x00, 0x00, unused, 0, discretional},\ /*21*/ {"micro step head up", 0x02, 0x00, motion, 0, required},\ /*22*/ {"micro step head down", 0x02, 0x00, motion, 0, required},\ /*23*/ {"soft select", 0x00, 0x00, mode, 0, discretional},\ /*24*/ {"soft deselect", 0x00, 0x00, mode, 0, discretional},\ /*25*/ {"skip segments reverse", 0x36, 0x24, motion, 1, required},\ /*26*/ {"skip segments forward", 0x36, 0x24, motion, 1, required},\ /*27*/ {"select rate or format", 0x03, 0x01, mode, 0, required /* [ccs2] */},\ /*28*/ {"enter diag mode 1", 0x00, 0x00, mode, 0, discretional},\ /*29*/ {"enter diag mode 2", 0x00, 0x00, mode, 0, discretional},\ /*30*/ {"enter primary mode", 0x00, 0x00, mode, 0, required},\ /*31*/ {"vendor unique (31)", 0x00, 0x00, unused, 0, discretional},\ /*32*/ {"report vendor id", 0x00, 0x00, report, 0, required},\ /*33*/ {"report tape status", 0x04, 0x04, report, 0, ccs1},\ /*34*/ {"skip extended reverse", 0x36, 0x24, motion, 1, ccs1},\ /*35*/ {"skip extended forward", 0x36, 0x24, motion, 1, ccs1},\ /*36*/ {"calibrate tape length", 0x17, 0x05, motion, 1, ccs2},\ /*37*/ {"report format segments", 0x17, 0x05, report, 0, ccs2},\ /*38*/ {"set format segments", 0x17, 0x05, mode, 0, ccs2},\ /*39*/ {"reserved (39)", 0x00, 0x00, unused, 0, discretional},\ /*40*/ {"vendor unique (40)", 0x00, 0x00, unused, 0, discretional},\ /*41*/ {"vendor unique (41)", 0x00, 0x00, unused, 0, discretional},\ /*42*/ {"vendor unique (42)", 0x00, 0x00, unused, 0, discretional},\ /*43*/ {"vendor unique (43)", 0x00, 0x00, unused, 0, discretional},\ /*44*/ {"vendor unique (44)", 0x00, 0x00, unused, 0, discretional},\ /*45*/ {"vendor unique (45)", 0x00, 0x00, unused, 0, discretional},\ /*46*/ {"phantom select", 0x00, 0x00, mode, 0, discretional},\ /*47*/ {"phantom deselect", 0x00, 0x00, mode, 0, discretional},\ } /* * Status bits returned by QIC_REPORT_DRIVE_STATUS */ #define QIC_STATUS_READY 0x01 /* Drive is ready or idle. */ #define QIC_STATUS_ERROR 0x02 /* Error detected, must read error code to clear this */ #define QIC_STATUS_CARTRIDGE_PRESENT 0x04 /* Tape is present */ #define QIC_STATUS_WRITE_PROTECT 0x08 /* Tape is write protected */ #define QIC_STATUS_NEW_CARTRIDGE 0x10 /* New cartridge inserted, must read error status to clear. */ #define QIC_STATUS_REFERENCED 0x20 /* Cartridge appears to have been formatted. */ #define QIC_STATUS_AT_BOT 0x40 /* Cartridge is at physical beginning of tape. */ #define QIC_STATUS_AT_EOT 0x80 /* Cartridge is at physical end of tape. */ /* * Status bits returned by QIC_REPORT_DRIVE_CONFIGURATION */ #define QIC_CONFIG_RATE_MASK 0x18 #define QIC_CONFIG_RATE_SHIFT 3 #define QIC_CONFIG_RATE_250 0 #define QIC_CONFIG_RATE_500 2 #define QIC_CONFIG_RATE_1000 3 #define QIC_CONFIG_RATE_2000 1 #define QIC_CONFIG_RATE_4000 0 /* since QIC-117 Rev. J */ #define QIC_CONFIG_LONG 0x40 /* Extra Length Tape Detected */ #define QIC_CONFIG_80 0x80 /* QIC-80 detected. */ /* * Status bits returned by QIC_REPORT_TAPE_STATUS */ #define QIC_TAPE_STD_MASK 0x0f #define QIC_TAPE_QIC40 0x01 #define QIC_TAPE_QIC80 0x02 #define QIC_TAPE_QIC3020 0x03 #define QIC_TAPE_QIC3010 0x04 #define QIC_TAPE_LEN_MASK 0x70 #define QIC_TAPE_205FT 0x10 #define QIC_TAPE_307FT 0x20 #define QIC_TAPE_VARIABLE 0x30 #define QIC_TAPE_1100FT 0x40 #define QIC_TAPE_FLEX 0x60 #define QIC_TAPE_WIDE 0x80 /* Define a value (in feet) slightly higher than * the possible maximum tape length. */ #define QIC_TOP_TAPE_LEN 1500 /* * Errors: List of error codes, and their severity. */ typedef struct { char *message; /* Text describing the error. */ unsigned int fatal:1; /* Non-zero if the error is fatal. */ } ftape_error; #define QIC117_ERRORS {\ /* 0*/ { "No error", 0, },\ /* 1*/ { "Command Received while Drive Not Ready", 0, },\ /* 2*/ { "Cartridge Not Present or Removed", 1, },\ /* 3*/ { "Motor Speed Error (not within 1%)", 1, },\ /* 4*/ { "Motor Speed Fault (jammed, or gross speed error", 1, },\ /* 5*/ { "Cartridge Write Protected", 1, },\ /* 6*/ { "Undefined or Reserved Command Code", 1, },\ /* 7*/ { "Illegal Track Address Specified for Seek", 1, },\ /* 8*/ { "Illegal Command in Report Subcontext", 0, },\ /* 9*/ { "Illegal Entry into a Diagnostic Mode", 1, },\ /*10*/ { "Broken Tape Detected (based on hole sensor)", 1, },\ /*11*/ { "Warning--Read Gain Setting Error", 1, },\ /*12*/ { "Command Received While Error Status Pending (obs)", 1, },\ /*13*/ { "Command Received While New Cartridge Pending", 1, },\ /*14*/ { "Command Illegal or Undefined in Primary Mode", 1, },\ /*15*/ { "Command Illegal or Undefined in Format Mode", 1, },\ /*16*/ { "Command Illegal or Undefined in Verify Mode", 1, },\ /*17*/ { "Logical Forward Not at Logical BOT or no Format Segments in Format Mode", 1, },\ /*18*/ { "Logical EOT Before All Segments generated", 1, },\ /*19*/ { "Command Illegal When Cartridge Not Referenced", 1, },\ /*20*/ { "Self-Diagnostic Failed (cannot be cleared)", 1, },\ /*21*/ { "Warning EEPROM Not Initialized, Defaults Set", 1, },\ /*22*/ { "EEPROM Corrupted or Hardware Failure", 1, },\ /*23*/ { "Motion Time-out Error", 1, },\ /*24*/ { "Data Segment Too Long -- Logical Forward or Pause", 1, },\ /*25*/ { "Transmit Overrun (obs)", 1, },\ /*26*/ { "Power On Reset Occurred", 0, },\ /*27*/ { "Software Reset Occurred", 0, },\ /*28*/ { "Diagnostic Mode 1 Error", 1, },\ /*29*/ { "Diagnostic Mode 2 Error", 1, },\ /*30*/ { "Command Received During Non-Interruptible Process", 1, },\ /*31*/ { "Rate or Format Selection Error", 1, },\ /*32*/ { "Illegal Command While in High Speed Mode", 1, },\ /*33*/ { "Illegal Seek Segment Value", 1, },\ /*34*/ { "Invalid Media", 1, },\ /*35*/ { "Head Positioning Failure", 1, },\ /*36*/ { "Write Reference Burst Failure", 1, },\ /*37*/ { "Prom Code Missing", 1, },\ /*38*/ { "Invalid Format", 1, },\ /*39*/ { "EOT/BOT System Failure", 1, },\ /*40*/ { "Prom A Checksum Error", 1, },\ /*41*/ { "Drive Wakeup Reset Occurred", 1, },\ /*42*/ { "Prom B Checksum Error", 1, },\ /*43*/ { "Illegal Entry into Format Mode", 1, },\ } #endif /* _QIC117_H */