mt-st-1.8/0000775000000000000000000000000014751737673011152 5ustar rootrootmt-st-1.8/.clang-format0000644000000000000000000000261113550663046013507 0ustar rootrootAccessModifierOffset: 0 AlignEscapedNewlinesLeft: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: false BinPackParameters: false BreakBeforeBinaryOperators: false BreakBeforeBraces: Linux BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false ColumnLimit: 80 CommentPragmas: '' ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 0 ContinuationIndentWidth: 0 Cpp11BracedListStyle: false DerivePointerBinding: false IndentCaseLabels: false IndentFunctionDeclarationAfterType: false IndentWidth: 4 Language: Cpp MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 100 PenaltyBreakComment: 100 PenaltyBreakFirstLessLess: 0 PenaltyBreakString: 100 PenaltyExcessCharacter: 1 PenaltyReturnTypeOnItsOwnLine: 20 PointerBindsToType: false SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 2 SpacesInAngles: false SpacesInCStyleCastParentheses: false SpacesInContainerLiterals: true SpacesInParentheses: false Standard: Cpp11 TabWidth: 4 UseTab: Never mt-st-1.8/.dir-locals.el0000644000000000000000000000020313550663046013560 0ustar rootroot;;; Directory Local Variables ;;; For more information see (info "(emacs) Directory Variables") ((c-mode (c-basic-offset . 4))) mt-st-1.8/stinit.def.examples0000644000000000000000000001614412711510315014735 0ustar rootroot# 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 } # DLT2000 / 2000XT manufacturer="QUANTUM" model = "DLT2000" { scsi2logical=1 can-bsr auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # mode1 blocksize=0 density=0x81 # 10GB + compression on DLTtape III, 15+ with DLTtape IIIXT in 2000XT mode2 blocksize=0 density=0x80 # 10GB, no compression on DLTtape III, 15 with DLTtape IIIXT in 2000XT mode3 blocksize=0 density=0x18 # 6GB, compression not available, on DLTtape III mode4 blocksize=0 density=0x17 #2.6GB, compression not available, on DLTtape III } # DLT4000 manufacturer="QUANTUM" model = "DLT4000" { scsi2logical=1 can-bsr auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards compatible, use older modes (e.g. from above) as required mode1 blocksize=0 density=0x83 # 20GB + compression mode2 blocksize=0 density=0x82 # 20GB, no compression mode3 blocksize=0 density=0x81 # 10GB + compression (DLT2000 mode) with DLTtape III, 15+ with DLTtape IIIXT in 2000XT mode4 blocksize=0 density=0x80 # 10GB, no compression (DLT2000 mode) with DLTtape III, 15 with DLTtape IIIXT in 2000XT } # DLT7000 manufacturer="QUANTUM" model = "DLT7000" { scsi2logical=1 can-bsr auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards compatible, use older modes (e.g. from above) as required. mode1 blocksize=0 density=0x85 # 35GB + compression mode2 blocksize=0 density=0x84 # 35GB, no compression mode3 blocksize=0 density=0x83 # 20GB + compression (DLT4000 mode) mode4 blocksize=0 density=0x82 # 20GB, no compression (DLT4000 mode) } # DLT8000 manufacturer="QUANTUM" model = "DLT8000" { scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards compatible to DLT7000, use older modes (e.g. from above) as required. Modes <10GB (<0x19) not supported! mode1 blocksize=0 density=0x89 # 40GB + compression mode2 blocksize=0 density=0x88 # 40GB, no compression mode3 blocksize=0 density=0x85 # 35GB + compression (DLT7000 mode) mode4 blocksize=0 density=0x84 # 35GB, no compression (DLT7000 mode) } # SDLT220 manufacturer="QUANTUM" model = "SuperDLT1" { scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so no need to define any other modes here. mode1 blocksize=0 density=0x48 compression=1 # 110 GB + compression mode2 blocksize=0 density=0x48 compression=0 # 110 GB, no compression } # SDLT320 manufacturer="QUANTUM" model = "SDLT320" { scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards write compatible to SDLT220 and read compatible to DLT4000/7000/8000. Mode settings are only required for writing, so we need only the SDL220/320 modes here mode1 blocksize=0 density=0x49 compression=1 # 160 GB + compression mode2 blocksize=0 density=0x49 compression=0 # 160 GB, no compression mode3 blocksize=0 density=0x48 compression=1 # 110 GB + compression mode4 blocksize=0 density=0x48 compression=0 # 110 GB, no compression } # SDLT600 manufacturer="QUANTUM" model = "SDLT600" { scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-mteom=1 # # If your stinit supports the timeouts: timeout=3600 # 1 hour long-timeout=14400 # 4 hours # # Drive is backwards read compatible to SDLT220/320 and VS160. Mode settings are only required for writing, so we need only the native SDLT600 mode here. mode1 blocksize=0 density=0x4a compression=1 # 300 GB + compression mode2 blocksize=0 density=0x4a compression=0 # 300 GB, no compression mode3 blocksize=0 density=0x4a compression=1 # 300 GB + compression mode4 blocksize=0 density=0x4a compression=0 # 300 GB, no compression } # HP StorageWorks Ultrium 960/920 SAS LTO-3 manufacturer="HP" model = "Ultrium 3-SCSI" { scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-eom=1 blocksize=0 sili=1 # # If your stinit supports the timeouts: timeout=900 # 15 min long-timeout=14400 # 4 hours # # Drive is backwards write compatible to LTO-2 media and read compatible to LTO-1 media. Mode settings are only required for writing, so we need only the LTO-3/LTO-2 modes here. mode1 density=0x44 compression=0 # LTO3 400 GB mode2 density=0x44 compression=1 # LTO3 400 GB + compression mode3 density=0x42 compression=0 # LTO2 220 GB mode4 density=0x42 compression=1 # LTO2 220 GB + compression } mt-st-1.8/stinit.c0000644000000000000000000007533614751736512012633 0ustar rootroot/* This program initializes Linux SCSI tape drives using the inquiry data from the devices and a text database. Maintained by Iustin Pop (iustin@k1024.org). Copyright 1996-2008 by Kai Mäkisara (Kai.Makisara@kolumbus.fi). Distribution of this program is allowed according to the GNU Public Licence. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtio.h" #include "version.h" #ifndef FALSE #define TRUE 1 #define FALSE 0 #endif #define SKIP_WHITE(p) for (; *p == ' ' || *p == '\t'; p++) 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 weof_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 char usage(int retval) __attribute__((noreturn)); 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 { if (*cp == '\0') return NULL; 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) < (unsigned int)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, size_t buflen, int limiter) { size_t 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) >= buflen) { 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], modebuf[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->weof_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';) { int tmpsize; 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; } if ((tmpsize = snprintf(modebuf, DEFMAX, "%s%s", comptr, cp)) >= DEFMAX) { fprintf(stderr, "Definition too large for ('%s', '%s', '%s'): size %d, maximum is %d\n", tmpcomp, tmpprod, tmprev, tmpsize, DEFMAX); *nextdef = c; errors++; continue; } *nextdef = c; if (verbose > 1) fprintf(stderr, "Mode %d definition: %s\n", mode + 1, modebuf); if ((t = find_string(modebuf, "disab", line, LINEMAX)) != NULL && strtol(t, NULL, 0) != 0) { defs->modedefs[mode].defined = FALSE; continue; } if ((t = find_string(modebuf, "drive-", line, LINEMAX)) != NULL) defs->drive_buffering = num_arg(t); if ((t = find_string(modebuf, "timeout", line, LINEMAX)) != NULL) defs->timeout = num_arg(t); if ((t = find_string(modebuf, "long-time", line, LINEMAX)) != NULL) defs->long_timeout = num_arg(t); if ((t = find_string(modebuf, "clean", line, LINEMAX)) != NULL) defs->cleaning = num_arg(t); if ((t = find_string(modebuf, "no-w", line, LINEMAX)) != NULL) defs->nowait = num_arg(t); if ((t = find_string(modebuf, "weof-n", line, LINEMAX)) != NULL) defs->weof_nowait = num_arg(t); if ((t = find_string(modebuf, "sili", line, LINEMAX)) != NULL) defs->sili = num_arg(t); defs->modedefs[mode].defined = TRUE; if ((t = find_string(modebuf, "block", line, LINEMAX)) != NULL) defs->modedefs[mode].blocksize = num_arg(t); if ((t = find_string(modebuf, "dens", line, LINEMAX)) != NULL) defs->modedefs[mode].density = num_arg(t); if ((t = find_string(modebuf, "buff", line, LINEMAX)) != NULL) defs->modedefs[mode].buffer_writes = num_arg(t); if ((t = find_string(modebuf, "async", line, LINEMAX)) != NULL) defs->modedefs[mode].async_writes = num_arg(t); if ((t = find_string(modebuf, "read", line, LINEMAX)) != NULL) defs->modedefs[mode].read_ahead = num_arg(t); if ((t = find_string(modebuf, "two", line, LINEMAX)) != NULL) defs->modedefs[mode].two_fm = num_arg(t); if ((t = find_string(modebuf, "comp", line, LINEMAX)) != NULL) defs->modedefs[mode].compression = num_arg(t); if ((t = find_string(modebuf, "auto", line, LINEMAX)) != NULL) defs->modedefs[mode].auto_lock = num_arg(t); if ((t = find_string(modebuf, "fast", line, LINEMAX)) != NULL) defs->modedefs[mode].fast_eod = num_arg(t); if ((t = find_string(modebuf, "can-b", line, LINEMAX)) != NULL) defs->modedefs[mode].can_bsr = num_arg(t); if ((t = find_string(modebuf, "noblk", line, LINEMAX)) != NULL) defs->modedefs[mode].no_blklimits = num_arg(t); if ((t = find_string(modebuf, "can-p", line, LINEMAX)) != NULL) defs->modedefs[mode].can_partitions = num_arg(t); if ((t = find_string(modebuf, "scsi2", line, LINEMAX)) != NULL) defs->modedefs[mode].scsi2logical = num_arg(t); if ((t = find_string(modebuf, "sysv", line, LINEMAX)) != NULL) defs->modedefs[mode].sysv = num_arg(t); if ((t = find_string(modebuf, "defs-for-w", line, LINEMAX)) != NULL) defs->modedefs[mode].defs_for_writes = num_arg(t); for (t = modebuf; *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, modebuf); 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) { 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, fails; int clear_set[2]; struct mtop op; for (i = fails = 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; if (ioctl(tape, MTIOCTOP, &op) != 0) { fails++; fprintf(stderr, "Rewind of %s fails.\n", fnames[i]); } } 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) { fails++; 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) { fails++; 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) { fails++; 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) { fails++; 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->weof_nowait >= 0) clear_set[defs->weof_nowait != 0] |= MT_ST_NOWAIT_EOF; 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) { fails++; 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) { fails++; 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) { fails++; 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) { fails++; 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) { fails++; fprintf(stderr, "Can't set compression %d for mode %d.\n", defs->modedefs[i].compression, i); } } close(tape); } return (fails == 0); } 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] [--version] [-f dbname] [-p] [-r] " "[drivename_or_number ...]\n"); exit(retval); } int main(int argc, char **argv) { FILE *dbf = NULL; int argn, retval = 0; int tapeno, parse_only = FALSE; char *dbname = NULL; char *convp; 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); } else if (isdigit(*argv[argn])) { tapeno = strtol(argv[argn], &convp, 0); if (*argv[argn] != '\0' && *convp != '\0') { fprintf(stderr, "Invalid tape device index '%s': don't " "know how to parse '%s'\n", argv[argn], convp); continue; } } 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]); retval = 1; } } } 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 retval; } mt-st-1.8/stinit.80000644000000000000000000002344114751736512012546 0ustar rootroot.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 .IR /etc/stinit.def . .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 weof-n[o-wait] The immediate mode is used when writing filemarks 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, option -p is given and parsing the definition file fails, or defining one or more of the options fails when the tape number(s) are given on command line. .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 , and is currently maintained by Iustin Pop . .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 BUGS Please report bugs to . .SH SEE ALSO st(4) mt(1) mt-st-1.8/mt-st.bash_completion0000644000000000000000000000306114751736512015273 0ustar rootroot#mt bash completion by Paweł Marciniak gmail.com> # _mt () { local cur prev words cword _init_completion || return #possible commands commands="weof wset eof fsf fsfm bsf bsfm fsr bsr fss bss rewind offline rewoffl eject retension eod seod seek tell status erase setblk lock unlock load compression setdensity drvbuffer stwrthreshold stoptions stsetoptions stclearoptions defblksize defdensity defdrvbuffer defcompression stsetcln sttimeout stlongtimeout densities setpartition mkpartition partseek asf stshowoptions" stoptions="buffer-writes async-writes read-ahead debug two-fms fast-eod no-wait weof-no-wait auto-lock def-writes can-bsr no-blklimits can-partitions scsi2logical sili sysv" COMPREPLY=() case $prev in -v | --version) return ;; -f ) #list tape devices for tape in /sys/class/scsi_tape/*; do devs+="/dev/${tape##*/} "; done; COMPREPLY=($(compgen -W "$devs" -- "$cur")) return ;; stsetoptions) # show list of stoptions COMPREPLY=($(compgen -W "$stoptions" -- "$cur")) return ;; esac # if "$prev" is a substring of "$stoptions" show more "$stoptions" if [[ "$stoptions" == *"$prev"* ]]; then COMPREPLY=($(compgen -W "$stoptions" -- "$cur")) return fi if [[ $cur == -* ]]; then COMPREPLY=($(compgen -W '-f -v' -- "$cur")) return fi COMPREPLY=($(compgen -W "$commands" -- "$cur")) } complete -F _mt mt mt-st-1.8/README.md0000644000000000000000000000674014417562152012421 0ustar rootroot# mt-st tools This directory contains two programs; `mt` and `stinit`, used for dealing with Linux-specific tape-drive handling. The project was authored and is copyright by Kai Mäkisara (), and since version 1.2 is maintained by Iustin Pop (). For copyright information, see the `COPYING` file. For more information, bug reports and the source code repository, please see the project homepage at . [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/iustin/mt-st/ci.yml?branch=main)](https://github.com/iustin/mt-st/actions/workflows/ci.yml) [![Codecov](https://img.shields.io/codecov/c/github/iustin/mt-st)](https://codecov.io/gh/iustin/mt-st) ![Debian package](https://img.shields.io/debian/v/mt-st/sid) ![Ubuntu package](https://img.shields.io/ubuntu/v/mt-st) ![GitHub Release Date](https://img.shields.io/github/release-date/iustin/mt-st) ![GitHub commits since latest release](https://img.shields.io/github/commits-since/iustin/mt-st/latest) ## mt `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 The 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`.) ## Contents The files: - `README.md`: This file. - `CHANGELOG.md`: Changes between versions. - `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 - `stinit.c`: The stinit source - `stinit.8`: The man page for stinit - `stinit.def.examples`: example configurations for different devices - `mt-st.bash_completion`: bash auto completion file ## Installation Really simple: - review the makefile - `make` - `make install` mt-st-1.8/mtio.h0000644000000000000000000002117114727340005012251 0ustar rootroot/* * 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 */ #define MTWEOFI 35 /* write an end-of-file record (mark) in immediate mode */ /* 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 #define MT_ST_NOWAIT_EOF 0x8000 /* 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.8/mt.c0000644000000000000000000007633314751736512011737 0ustar rootroot/* 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 Iustin Pop (iustin@k1024.org). Copyright by Kai Mäkisara, 1998 - 2008. The program may be distributed according to the GNU Public License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mtio.h" #include "version.h" #ifndef DEFTAPE #define DEFTAPE "/dev/tape" /* default tape device */ #endif /* DEFTAPE */ typedef struct cmdef_tr cmdef_tr; typedef int (*cmdfunc)(int, struct cmdef_tr *, int, char **); 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; }; #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 MANY_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 explain, int exitcode) __attribute__((noreturn)); static void version() __attribute__((noreturn)); 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 *); /* Formatting note: the tables below were formatted using Emacs's * extended align regex, using <,\(\s-+\)[A-Za-z0-9"]> as complex align * regex (without <>), and repeated=y. The final curly brace is aligned * separately. */ static cmdef_tr cmds[] = { /* clang-format off */ { "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, MANY_ARGS, 0 }, { "stsetoptions", SET_BOOLEANS, do_options, 0, FD_RDONLY, MANY_ARGS, 0 }, { "stclearoptions", CLEAR_BOOLEANS, do_options, 0, FD_RDONLY, MANY_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 }, { "stshowoptions", 0, do_show_options, 0, FD_RDONLY, ONE_ARG, 0 }, { NULL, 0, 0, 0, NO_FD, NO_ARGS, 0 }, /* clang-format on */ }; static struct densities { int code; char *name; } density_tbl[] = { /* clang-format off */ /* Information taken from https://www.t10.org/ftp/x3t9.2/document.93/93-013r0.pdf: * NZRI: Non-Return to Zero, change on ones * GCR: Group Code Recording * PE: Phase Encoding * IMFM: Inverted Modified Frequency Modulation * MFM: Modified Frequency Modulation * DDS: DAT Data Storage * RLL: Run Length Limited */ { 0x00, "default" }, { 0x01, "NRZI (800 bpi) 9 Track Reel" }, { 0x02, "PE (1600 bpi) 9 Track Reel" }, { 0x03, "GCR (6250 bpi) 9 Track Reel" }, { 0x04, "QIC-11" }, { 0x05, "QIC-45/60 (GCR, 8000 bpi)" }, { 0x06, "PE (3200 bpi) 9 Track Reel" }, { 0x07, "IMFM (6400 bpi)" }, { 0x08, "GCR (8000 bpi)" }, { 0x09, "3480/3490E, 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, IBM 3590 B" }, { 0x2a, "IBM 3590 E" }, { 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" }, { 0x48, "SDLT220" }, { 0x49, "SDLT320" }, { 0x4a, "SDLT600, T10000A" }, { 0x4b, "T10000B" }, { 0x4c, "T10000C" }, { 0x4d, "T10000D" }, { 0x51, "IBM 3592 J1A" }, { 0x52, "IBM 3592 E05 (TS1120)" }, { 0x53, "IBM 3592 E06 (TS1130)" }, { 0x54, "IBM 3592 E07 (TS1140)" }, { 0x55, "IBM 3592 E08 (TS1150)" }, { 0x56, "IBM 3592 55F (TS1155)" }, { 0x57, "IBM 3592 60F (TS1160)" }, { 0x58, "LTO-5" }, { 0x59, "IBM 3592 70F (TS1170)" }, { 0x5a, "LTO-6" }, { 0x5c, "LTO-7" }, { 0x5d, "LTO-7-M8" }, { 0x5e, "LTO-8" }, { 0x60, "LTO-9" }, { 0x71, "IBM 3592 J1A, encrypted" }, { 0x72, "IBM 3592 E05, encrypted" }, { 0x73, "IBM 3592 E06, encrypted" }, { 0x74, "IBM 3592 E07, encrypted" }, { 0x75, "IBM 3592 E08, encrypted" }, { 0x76, "IBM 3592 55F, encrypted" }, { 0x77, "IBM 3592 60F, encrypted" }, { 0x79, "IBM 3592 70F, encrypted" }, { 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 compressed" } /* clang-format on */ }; #define NBR_DENSITIES (sizeof(density_tbl) / sizeof(struct densities)) static struct booleans { char *name; unsigned long bitmask; char *expl; } boolean_tbl[] = { /* clang-format off */ { "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." }, { "weof-no-wait", MT_ST_NOWAIT_EOF, "immediate mode for writing filemarks" }, #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, NULL } /* clang-format on */ }; static char *tape_name; /* The tape name for messages */ int main(int argc, char **argv) { int mtfd, i, argn, oflags; unsigned int len; 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, 1); } tape_name = argv[argn]; break; case 'h': usage(1, 0); break; case 'v': version(); break; case '-': if (*(argv[argn] + 1) == '-' && *(argv[argn] + 2) == 'v') { version(); } /* Fall through */ default: usage(0, 1); } else break; if (tape_name == NULL && (tape_name = getenv("TAPE")) == NULL) { struct stat stbuf; tape_name = DEFTAPE; if (!stat(tape_name, &stbuf) && !S_ISCHR(stbuf.st_mode)) { fprintf(stderr, "The default '%s' is not a character device.\n\n", tape_name); usage(1, 1); } } if (argn >= argc) { usage(0, 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, 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, 1); } } if (comp->arg_cnt != MANY_ARGS && comp->arg_cnt < argc - argn) { fprintf(stderr, "mt: too many arguments for the command '%s'.\n", comp->cmd_name); exit(1); } 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 version() { printf("mt-st v. %s\n", VERSION); printf("default tape device: %s\n", DEFTAPE); exit(0); } static void usage(int explain, int exit_code) { int ind; int counter = 0; fprintf(stderr, "usage: mt [-v] [--version] [-h] [ -f device ] command [ " "count ]\n"); fprintf(stderr, "default tape device: %s\n", DEFTAPE); if (explain) { for (ind = 0; cmds[ind].cmd_name != NULL;) { if (ind == 0) counter = fprintf(stderr, "commands: "); else counter = fprintf(stderr, " "); for (; cmds[ind].cmd_name != NULL; ind++) { counter += fprintf(stderr, "%s", cmds[ind].cmd_name); if (cmds[ind + 1].cmd_name != NULL) counter += fprintf(stderr, ", "); else counter += fprintf(stderr, "."); if (counter >= 70 || cmds[ind + 1].cmd_name == NULL) { fprintf(stderr, "\n"); ind++; break; } } } } exit(exit_code); } /* 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) { int multiplier, max_count; 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) { multiplier = 1; if (*endp == 'k') multiplier = 1024; else if (*endp == 'M') multiplier = 1024 * 1024; else if (*endp == 'G') multiplier = 1024 * 1024 * 1024; else if (*endp != 0) { fprintf(stderr, "mt: illegal count unit.\n"); return 3; } max_count = INT_MAX / multiplier; if (abs(mt_com.mt_count) > max_count) { fprintf(stderr, "mt: repeat count too large.\n"); return 3; } mt_com.mt_count *= multiplier; } mt_com.mt_count |= cmd->cmd_count_bits; if (mt_com.mt_op != MTMKPART && 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; unsigned int 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 __attribute__((unused)), int argc __attribute__((unused)), char **argv __attribute__((unused))) { 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 __attribute__((unused)), 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 __attribute__((unused)), 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 __attribute__((unused)), int argc __attribute__((unused)), char **argv __attribute__((unused))) { struct mtget status; int dens; unsigned int 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) - 1))) #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 __attribute__((unused)), int argc __attribute__((unused)), char **argv __attribute__((unused))) { int i, fd, options, tapeminor, tapeno, tapemode; struct stat stat; char fname[100], buf[20]; 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); if (fd >= 0) close(fd); 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 __attribute__((unused)), cmdef_tr *cmd __attribute__((unused)), int argc __attribute__((unused)), char **argv __attribute__((unused))) { unsigned 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.8/mt.10000644000000000000000000002324514751736512011647 0ustar rootroot.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. .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, then backward space one record. This leaves the tape positioned at the last block of the file that is count - 1 files past the current file. .IP bsf Backward space .I count files. The tape is positioned on the last block of the previous file. .IP bsfm Backward space .I count files, then forward space one record. This leaves the tape positioned at the first block of the file that is count - 1 files before the current file. .IP asf The tape is positioned at the beginning of the .I count 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. Note that this is a long erase, which on modern (high-capacity) tapes can take many hours, and which usually can't be aborted. .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). If the count is positive, it specifies the size of partition 1. From kernel version 4.6, if the count is negative, it specifies the size of partition 0. With older kernels, a negative argument formats the tape with one partition. 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 function 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 weof-no-wait don't wait until writing filemarks completes .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 OPTIONS .TP .B \-h, \-\-help Print a usage message on standard output and exit successfully. .TP .B \-v, \-\-version Print version of mt. .TP .B \-f, \-t The path of the tape device on which to operate. 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 (note that the actual path to .I mtio.h can vary per architecture and/or distribution). .SH NOTES The argument of mkpartition specifies the size of the partition in megabytes. If you add a postfix, it applies to this definition. For example, argument 1G means 1 giga megabytes, which probably is not what the user is anticipating. .SH AUTHOR The program is written by Kai Makisara , and is currently maintained by Iustin Pop . .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 BUGS Please report bugs to . .SH SEE ALSO st(4) mt-st-1.8/Makefile0000644000000000000000000000560314751737342012605 0ustar rootrootCFLAGS?= -Wall -O2 PREFIX?= /usr EXEC_PREFIX?= / SBINDIR= $(DESTDIR)/$(EXEC_PREFIX)/sbin BINDIR= $(DESTDIR)$(EXEC_PREFIX)/bin DATAROOTDIR= $(DESTDIR)/$(PREFIX)/share MANDIR= $(DATAROOTDIR)/man COMPLETIONINSTALLDIR=$(DESTDIR)/etc/bash_completion.d DEFTAPE?= /dev/tape INSTALL= install PROGS=mt stinit # Release-related variables DISTFILES = \ CHANGELOG.md \ COPYING \ Makefile \ mt.1 \ mt.c \ mtio.h \ README.md \ mt-st.bash_completion \ stinit.8 \ stinit.c \ stinit.def.examples \ .dir-locals.el \ .clang-format TESTFILES = $(wildcard tests/*.test) TESTDATAFILES = $(wildcard tests/data/*) VERSION=1.8 RELEASEDIR=mt-st-$(VERSION) TARFILE=mt-st-$(VERSION).tar.gz all: $(PROGS) version.h: Makefile echo '#define VERSION "$(VERSION)"' > $@ %: %.c version.h $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -DDEFTAPE='"$(DEFTAPE)"' -o $@ $< install: $(PROGS) $(INSTALL) -d $(BINDIR) $(SBINDIR) $(MANDIR) $(MANDIR)/man1 $(MANDIR)/man8 $(COMPLETIONINSTALLDIR) $(INSTALL) mt $(BINDIR) $(INSTALL) -m 444 mt.1 $(MANDIR)/man1 $(INSTALL) -m 644 mt-st.bash_completion $(COMPLETIONINSTALLDIR)/mt-st (if [ -f $(MANDIR)/man1/mt.1.gz ] ; then \ rm -f $(MANDIR)/man1/mt.1.gz; gzip $(MANDIR)/man1/mt.1; fi) $(INSTALL) stinit $(SBINDIR) $(INSTALL) -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: rm -f "$(TARFILE)" && \ BASE=`mktemp -d` && \ trap "rm -rf $$BASE" EXIT && \ DIST="$$BASE/$(RELEASEDIR)" && \ mkdir "$$DIST" && \ mkdir "$$DIST/tests" && mkdir "$$DIST/tests/data" && \ $(INSTALL) -m 0644 -p -t "$$DIST/" $(DISTFILES) && \ $(INSTALL) -m 0644 -p -t "$$DIST/tests" $(TESTFILES) && \ $(INSTALL) -m 0644 -p -t "$$DIST/tests/data" $(TESTDATAFILES) && \ tar czvf $(TARFILE) -C "$$BASE" \ --owner root --group root \ $(RELEASEDIR) distcheck: dist BASE=`mktemp -d` && \ trap "rm -rf $$BASE" EXIT && \ SRC="$$BASE/src" && mkdir "$$SRC" && \ DST="$$BASE/dst" && mkdir "$$DST" && \ tar xvf $(TARFILE) -C "$$SRC" && \ cd "$$SRC/$(RELEASEDIR)" && \ make CFLAGS="-Wall -Wextra -Werror" && \ make check && \ make dist && \ make install DESTDIR="$$DST" && \ numfiles=$$( \ find "$$DST" -type f \ \( -name mt -o -name stinit -o -name mt.1 -o -name stinit.8 -o -name mt-st \) | \ wc -l) && \ echo "$$numfiles files installed (5 expected)" && \ test "$$numfiles" -eq 5 check: $(PROGS) shelltest -DVERSION=$(VERSION) tests # This needs lcov installed, and it's useful for local testing. coverage: clean $(MAKE) CFLAGS=-coverage $(MAKE) check lcov --capture --directory . --no-external --output-file lcov.info genhtml lcov.info --output-directory out release-tag: git tag -s -m 'Release version $(VERSION)' v$(VERSION) clean: rm -f *~ \#*\# *.o *.gcno *.gcda coverage.info $(PROGS) version.h rm -rf out reindent: clang-format -i mt.c stinit.c .PHONY: dist distcheck clean reindent mt-st-1.8/COPYING0000644000000000000000000004440512711462732012173 0ustar rootroot 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. Also note that the only valid version of the GPL as far as the kernel is concerned is _this_ particular version of the license (ie v2, not v2.2 or v3.x or whatever), unless explicitly otherwise stated. Linus Torvalds ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 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) 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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) year 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.8/CHANGELOG.md0000644000000000000000000001726214751736512012760 0ustar rootroot# Changelog ## Changes in version 1.8 (Sat, 08 Feb 2025) After almost two years, a number of minor fixes and improvements have accumulated: - A number of density code updates: LTO-9, 9track Reels, IBM 3480 and 3490E (from Chris Dinneen) and IBM 3592 (Kai Mäkisara) - Fix write protect reporting (Kai Mäkisara) - Implement support for the `MT_ST_NOWAIT_EOF` flag, via the new `weof-no-wait` for stinit (Kai Mäkisara) - While at it, correctly report errors if some options cannot be set, rather than pretending all is good (also Kai Mäkisara) - Improve the bash completion code to handle option completion better (Chris Dinneen) - Very minor build system improvements and coverage extension (Iustin Pop) Thanks Kai and Chris for the many improvements! ## Changes in version 1.7 (Thu, 20 Apr 2023) Fixes a single bug in stinit parsing of invalid definitions. This is a trivial bug, and only affects config files manually installed by root, so the impact should be minimal. The bug also does not appear on amd64/x86, but (in Debian) was only triggered (as undefined behaviour) on mips64el, arm64 and s390x, likely due to different platform behaviour. ## Changes in version 1.6 (Wed, 19 Apr 2023) This is bugfix release agains 1.5. In between 1.4 and 1.5, the "make check" target was migrated to using [shelltest](https://github.com/simonmichael/shelltestrunner), but the `make dist` and `distcheck` targets were no, so the built archive was actually not. This only fixes that and has no functional code changes from 1.5. ## Changes in version 1.5 (Wed, 19 Apr 2023) Trivial release: - add IBM 3590 B/E format to tape densities table (Chris Dinneen). Thanks! ## Changes in version 1.4 (Sun, 30 Aug 2020) Small bugfixes and improvements release: - show default tape device in usage output (Iustin Pop). - improve parsing of the stinit.def configuration file to detect and flag some of the possible errors, and add tests to prevent regressions (Iustin Pop). - add LTO-8 (hrchu) and LTO-7 formatted as M8 (Iustin Pop) density codes. - internal code improvements for issues flagged by Coverity scan warnings (Gris Ge). - add bash auto-completion file (Paweł Marciniak). - don't strip anymore binaries on installation, as nowadays this is the job of package managers (Dan Horák). Thanks to all the contributors! ## Changes in version 1.3 (Sun, 01 May 2016) Small bugfixes and improvements: - add more density codes (Kai Mäkisara) - check for overflow when using k, M or G suffixes (Kai Mäkisara) - allow negative argument for mkpartition, supported by Linux 4.6 and later (Kai Mäkisara) - fix compilation with musl libc (Felix Janda) - allow configuring the tape device and installation paths (e.g. /bin vs. /usr/bin) at build/install time (Iustin Pop) - code cleanups from the SUSE package (Alexey Svistunov) - update the supplied example file (Alexey Svistunov) - fix config file parsing bug in stinit (Iustin Pop) ## Changes in version 1.2 (Sun, 07 Feb 2016) This a mostly a cleanup release after many years of no updates, integrating pending fixes and distribution patches from Debian and RedHat, and a change of maintainership: - many updates to density codes (SDLT, LTO 5,6 and 7, etc.) (various people) - multiple man page updates (various people) - improve default tape device handling in `mt`: check that it actually is a character device, in order to show better error messages when `/dev/tape` is a different type (e.g. directory when using `udev`) - small bug fix in stinit in parsing the input file (David Binderman) - improve build system by allowing easier customisation of build flags and installation directory (via `DESTDIR`, not prefix) and by sanitising the creation of the dist archive (Iustin Pop) - sanitise the source code to get rid of GCC warnings (Jan Christoph Nordholz, Iustin Pop) - add `stshowoptions` alias to `stshowopt` (Ivo De Decker) - expand the provided `stinit.def.examples` file (Suggested by Ralf-Peter Rohbeck) - improve the `--help` output of stinit (Dan Horák) - change of maintainership to Iustin Pop ## Changes in version 1.1 (Sun, 27 Apr 2008) - unused defines removed from `mtio.h` (compiles also with distributions not having `linux/qic117.h`) - add support for `MT_ST_SILI` to mt and stinit - add mt command `showoptions` for kernels >= 2.6.26 - fix mode number printing in stinit's verbose mode (from Martin Jacobs) ## Changes in version 0.9b (Sun, 21 Aug 2005) - stinit: 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 (Sun, 29 May 2005) - mt: more density codes - stinit: try first `SG_IO` for inquiry, if the ioctl fails, try `SCSI_IOCTL_SEND_COMMAND`; note that error checking for `SG_IO` is very simplistic for now ## Changes in version 0.8 (Tue, 13 Apr 2004) - put man pages into `/usr/share/man/man1`, respectively `/usr/share/man/man8` - in devfs, `/dev/tapes/tape` does not match *n*th drive after rmmoding and insmodding the st driver; fix provided by Philippe Troin - documentation cleanup - add some density translations - counts can use the `k`, `M`, or `G` postfix ## Changes in version 0.7 (Wed, 21 Nov 2001) mt: - add command `eject` for compatibility with GNU mt (synonym for `offline` and `rewoffl`) - the `load` and `erase` 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 stinit: - 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 (Thu, 30 Nov 2000) mt: - 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` stinit: - fix the bug with whitespace at the beginning of lines in the configuration file - use `O_NONBLOCK` to open the tape (anticipate kernel change) ## Changes in version 0.5b (Sun, 16 Aug 1998) mt: - corrected the bug that caused the command argument to be ignored if option `-f` was used - density `0x45` (TR-4) added to known density list stinit: - added `#include ` to enable compilation with glibc ## Changes in version 0.5 - utility stinit added to package - GNU Public License used for both programs - binaries not distributed any more mt: - command `asf` added - command `datcompression` not compiled in default configuration - support added for setting timeouts - bugs in argument parsing corrected - help prints all commands - some code cleanup ## 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 overridden by the command compression using a new ioctl) - bus fixes mt-st-1.8/tests/0000775000000000000000000000000014751737673012314 5ustar rootrootmt-st-1.8/tests/stinit-parsing.test0000644000000000000000000000161314417574105016153 0ustar rootroot# Check example file parsing ./stinit -p -f stinit.def.examples >>>= 0 # Extra arguments ignored while file parsing ./stinit -p -f stinit.def.examples abc >>>2 /Extra arguments .* ignored/ >>>= 0 # Checking complete stinit parsing ./stinit -v -v -p -f stinit.def.examples >>> /No errors found./ >>>2 /Mode 1 definition: scsi2logical=1 can-bsr=1 auto-lock=0 two-fms=0 drive-buffering=1 buffer-writes read-ahead=1 async-writes=1 can-partitions=0 fast-eom=1 blocksize=0 sili=1 timeout=900 long-timeout=14400 density=0x44 compression=0/ >>>= 0 # Check units ./stinit -v -v -p -f tests/data/large-units.data >>> /No errors found./ >>>2 /blocksize=1(k|M)/ >>>= 0 # Incomplete block ./stinit -p -v -f tests/data/incomplete-block.data >>>2 /End of definition block not found for/ >>>= 1 # Wrong definition ./stinit -p -v -f tests/data/bad-definition.data >>>2 /Warning: errors in definition for/ >>>= 1 mt-st-1.8/tests/stinit-errors.test0000644000000000000000000000205614751736512016031 0ustar rootroot# Check handling of missing file ./stinit -p -f no-such-database >>>2 /Can't find SCSI tape database/ >>>= 1 # No file passed ./stinit -p -f >>>2 /Usage:/ >>>= 1 # Wrong arguments ./stinit -x >>>2 /Usage:/ >>>= 1 # Illegal ordering of arguments ./stinit -f stinit.def.examples 1000 - >>>2 /Usage:/ >>>= 1 # Check bad mode ./stinit -v -v -p -f tests/data/illegal-mode.data >>> /Errors found!/ >>>2 /Illegal mode for/ >>>= 1 # No modes defined #./stinit -v -v -f tests/data/no-modes.data #>>> /Errors found!/ #>>>2 /Illegal mode for/ #>>>= 1 # Wrong tape device ./stinit -f stinit.def.examples /dev/no-such-tape >>>2 /Can't find tape number for name/ >>>= 0 # Wrong tape number. Well, this is flaky, but let's hope nobody has # 1000 tapes. ./stinit -f stinit.def.examples 1000 >>>2 /Can't find any device files for tape 1000/ >>>= 1 ./stinit -f stinit.def.examples 1000 >>>2 /Definition for '1000' failed/ >>>= 1 # Wrong tape number (non-numeric). ./stinit -f stinit.def.examples 1000a >>>2 /Invalid tape device index '1000a': don't know how to parse 'a'/ >>>= 0 mt-st-1.8/tests/mt-errors.test0000644000000000000000000000100414751736512015127 0ustar rootroot# Wrong tape argument ./mt -f /dev/no-such-tape rewind >>>2 /no-such-tape: No such file or directory/ >>>= 1 # Wrong file in show options ./mt -f tests/data/not-a-char-device stshowoptions >>>2 /mt: not a character device/ >>>= 1 # /dev/null is not a tape device. Error message is ugly, though. ./mt -f /dev/null stshowoptions >>>2 /Can't read the sysfs file '\/sys\/class\/scsi_tape/ >>>= 2 # /dev/null is not a tape device, so status fails. ./mt -f /dev/null status >>>2 /Inappropriate ioctl for device/ >>>= 2 mt-st-1.8/tests/mt-cli.test0000644000000000000000000000106114057014614014354 0ustar rootroot# Wrong argument ./mt -x >>>2 /usage: / >>>= 1 # Missing tape argument ./mt -f >>>2 /usage: / >>>= 1 # Unknown command ./mt to-the-moon >>>2 /mt: unknown command "to-the-moon"/ >>>= 1 # Too many arguments ./mt rewind 1 >>>2 /mt: too many arguments for the command 'rewind'\./ >>>= 1 # Ambigous command ./mt l >>>2 /mt: ambiguous command "l"/ >>>= 1 # Shortened but not ambiguous command. ./mt rewi 1 >>>2 /mt: too many arguments for the command 'rewind'\./ >>>= 1 # Densities command - the only one not requiring a tape. ./mt densities >>> /LTO-6/ >>>= 0 mt-st-1.8/tests/basic.test0000644000000000000000000000035614057014614014256 0ustar rootroot# Check --version works ./mt --version >>> /VERSION/ >>>= 0 ./mt -v >>> /VERSION/ >>>= 0 ./stinit --version >>> /VERSION/ >>>= 0 # Check -h works ./mt -h >>>2 /commands: weof, wset, eof/ >>>= 0 ./stinit -h >>>2 /Usage: stinit/ >>>= 0 mt-st-1.8/tests/data/0000775000000000000000000000000014751737673013225 5ustar rootrootmt-st-1.8/tests/data/not-a-char-device0000644000000000000000000000000014751736512016311 0ustar rootrootmt-st-1.8/tests/data/large-units.data0000644000000000000000000000042214417570762016277 0ustar rootroot# 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=1k mode2 blocksize=1M } mt-st-1.8/tests/data/incomplete-block.data0000644000000000000000000000027214417572132017270 0ustar rootroot# 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" { mt-st-1.8/tests/data/illegal-mode.data0000644000000000000000000000042514057014614016370 0ustar rootroot# 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 modeABC blocksize=1024 } mt-st-1.8/tests/data/bad-definition.data0000644000000000000000000000031514417573071016716 0ustar rootroot# 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" { mode1 blocksize= }