--- doc/mt.1 +++ doc/mt.1 @@ -76,9 +76,6 @@ .IR count . Equivalent to rewind followed by fsf .IR count . -.IP seek -Seek to block number -.IR count . .IP eom Space to the end of the recorded media on the tape (for appending files onto tapes). @@ -93,6 +90,69 @@ then rewind it again. .IP erase Erase the tape. +.IP fss +(SCSI tapes) Forward space +.I count +setmarks. +.IP bss +(SCSI tapes) Backward space +.I count +setmarks. +.IP "wset" +(SCSI tapes) Write +.I count +setmarks at current position (only SCSI tape). +.IP "eod, seod" +Space to end of valid data. Used on streamer tape +drives to append data to the logical and of tape. +.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 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 case of a SCSI-2 drive, from the SCSI-2 standard. +.IP stoptions +(SCSI tapes) Set the driver options bits to +.I count +for the device. +The bits can be set by oring the following values: 1 to enable write +buffering, 2 to enable asynchronous writes, 4 to enable read ahead, +8 to enable debugging output (if it has been compiled to the driver). +.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. +.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. +.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 densities +(SCSI tapes) Write explanation of some common density codes to +standard output. +.IP datcompression +(some SCSI-2 DAT tapes) Inquire or set the compression status +(on/off). If the +.I count +is one the compression status is printed. If the +.I count +is zero, compression is disabled. Otherwise, compression is +enabled. The command uses the SCSI ioctl to read and write the Data +Compression Characteristics mode page (15). ONLY ROOT CAN USE THIS +COMMAND. .PP .B mt exits with a status of 0 if the operation succeeded, 1 if the --- lib/system.h +++ lib/system.h @@ -431,10 +431,11 @@ #if HAVE_LOCALE_H # include -#endif +#else #if !HAVE_SETLOCALE # define setlocale(category, locale) /* empty */ #endif +#endif #include #ifdef TIME_WITH_SYS_TIME --- src/Makefile.am +++ src/Makefile.am @@ -18,7 +18,7 @@ INCLUDES=-I. -I.. -I$(top_srcdir)/lib -bin_PROGRAMS=cpio @CPIO_MT_PROG@ +bin_PROGRAMS=cpio mt EXTRA_PROGRAMS=mt cpio_SOURCES = \ --- src/copyin.c +++ src/copyin.c @@ -768,7 +768,7 @@ static void copyin_file (struct cpio_file_stat* file_hdr, int in_file_des) { - int existing_dir; + int existing_dir=0; if (!to_stdout_option && try_existing_file (file_hdr, in_file_des, &existing_dir) < 0) @@ -1355,6 +1355,7 @@ int in_file_des; /* Input file descriptor. */ char skip_file; /* Flag for use with patterns. */ int i; /* Loop index variable. */ + int lastpattern = 0; umask (0); /* Reset umask to preserve modes of created files */ @@ -1463,8 +1464,10 @@ for (i = 0; i < num_patterns && skip_file == copy_matching_files; i++) { - if (fnmatch (save_patterns[i], file_hdr.c_name, 0) == 0) + if (fnmatch (save_patterns[lastpattern], file_hdr.c_name, 0) == 0) skip_file = !copy_matching_files; + else if (++lastpattern >= num_patterns) + lastpattern = 0; } } --- src/mt.c +++ src/mt.c @@ -17,6 +17,10 @@ 02110-1301 USA */ +/* Modified for the Linux SCSI tape driver by Brian Mays from code + written by Kai Makisara. + Last Modified: Tue Apr 23 15:37:54 EDT 1996 +*/ /* If -f is not given, the environment variable TAPE is used; if that is not set, a default device defined in sys/mtio.h is used. @@ -51,6 +55,46 @@ retension Rewind the tape, then wind it to the end of the reel, then rewind it again. erase Erase the tape. + fss (SCSI tapes) Forward space COUNT setmarks. + bss (SCSI tapes) Backward space COUNT setmarks. + wset (SCSI tapes) Write COUNT setmarks at current position + (only SCSI tape). + eod, seod Space to end of valid data. Used on streamer tape + drives to append data to the logical and of tape. + setblk (SCSI tapes) Set the block size of the drive to COUNT + bytes per record. + setdensity (SCSI tapes) Set the tape density code to COUNT. The + proper codes to use with each drive should be looked + up from the drive documentation. + drvbuffer (SCSI tapes) Set the tape drive buffer code to + 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 case of a SCSI-2 drive, from the + SCSI-2 standard. + stoptions (SCSI tapes) Set the driver options bits to COUNT for + the device. The bits can be set by oring the + following values: 1 to enable write buffering, 2 to + enable asynchronous writes, 4 to enable read ahead, 8 + to enable debugging output (if it has been compiled to + the driver). + stwrthreshold + (SCSI tapes) The write threshold for the tape device + is set to COUNT kilobytes. The value must be smaller + than or equal to the driver buffer size. + seek (SCSI tapes) Seek to the COUNT block on the tape. + This operation is available on some Tandberg and + Wangtek streamers and some SCSI-2 tape drives. + 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. + densities (SCSI tapes) Write explanation of some common density + codes to standard output. + datcompression + (some SCSI-2 DAT tapes) Inquire or set the compression + status (on/off). If the COUNT is one the compression + status is printed. If the COUNT is zero, compression + is disabled. Otherwise, compression is enabled. David MacKenzie */ @@ -105,6 +149,45 @@ void exit (); #endif +#include "../lib/argmatch.h" + +#if defined(linux) || defined(__linux) +#define MTDATCOMP 1000 /* Random unused number. */ +#define MTDENS 1001 /* Random unused number. */ + +struct densities { + int code; + char *name; +} density_tbl[] = { + {0x00, "default"}, + {0x01, "NRZI (800 bpi)"}, + {0x02, "PE (1600 bpi)"}, + {0x03, "GCR (6250 bpi)"}, + {0x05, "QIC-45/60 (GCR, 8000 bpi)"}, + {0x06, "PE (3200 bpi)"}, + {0x07, "IMFM (6400 bpi)"}, + {0x08, "GCR (8000 bpi)"}, + {0x09, "GCR /37871 bpi)"}, + {0x0a, "MFM (6667 bpi)"}, + {0x0b, "PE (1600 bpi)"}, + {0x0c, "GCR (12960 bpi)"}, + {0x0d, "GCR (25380 bpi)"}, + {0x0f, "QIC-120 (GCR 10000 bpi)"}, + {0x10, "QIC-150/250 (GCR 10000 bpi)"}, + {0x11, "QIC-320/525 (GCR 16000 bpi)"}, + {0x12, "QIC-1350 (RLL 51667 bpi)"}, + {0x13, "DDS (61000 bpi)"}, + {0x14, "EXB-8200 (RLL 43245 bpi)"}, + {0x15, "EXB-8500 (RLL 45434 bpi)"}, + {0x16, "MFM 10000 bpi"}, + {0x17, "MFM 42500 bpi"}, + {0x24, "DDS-2"}, + {140, "EXB-8505 compressed"}, + {144, "EXB-8205 compressed"}, + {-1, NULL}}; +#endif + + char *opnames[] = { "eof", "weof", "fsf", "bsf", "fsr", "bsr", @@ -114,6 +197,8 @@ #endif #ifdef MTEOM "eom", + "eod", + "seod", #endif #ifdef MTRETEN "retension", @@ -128,6 +213,39 @@ #ifdef MTSEEK "seek", #endif +#ifdef MTTELL + "tell", +#endif +#ifdef MTFSS + "fss", +#endif +#ifdef MTBSS + "bss", +#endif +#ifdef MTWSM + "wset", +#endif +#ifdef MTSETBLK + "setblk", +#endif +#ifdef MTSETDENSITY + "setdensity", +#endif +#ifdef MTSETDRVBUFFER + "drvbuffer", +#ifdef MT_ST_BOOLEANS + "stoptions", +#endif +#ifdef MT_ST_WRITE_THRESHOLD + "stwrthreshold", +#endif +#endif +#ifdef MTDATCOMP + "datcompression", +#endif +#ifdef MTDENS + "densities", +#endif NULL }; @@ -141,6 +259,8 @@ #endif #ifdef MTEOM MTEOM, + MTEOM, + MTEOM, #endif #ifdef MTRETEN MTRETEN, @@ -155,9 +275,69 @@ #ifdef MTSEEK MTSEEK, #endif +#ifdef MTTELL + MTTELL, +#endif +#ifdef MTFSS + MTFSS, +#endif +#ifdef MTBSS + MTBSS, +#endif +#ifdef MTWSM + MTWSM, +#endif +#ifdef MTSETBLK + MTSETBLK, +#endif +#ifdef MTSETDENSITY + MTSETDENSITY, +#endif +#ifdef MTSETDRVBUFFER + MTSETDRVBUFFER, +#ifdef MT_ST_BOOLEANS + MTSETDRVBUFFER, +#endif +#ifdef MT_ST_WRITE_THRESHOLD + MTSETDRVBUFFER, +#endif +#endif +#ifdef MTDATCOMP + MTDATCOMP, +#endif +#ifdef MTDENS + MTDENS, +#endif 0 }; +char *cbnames[] = +{ +#ifdef MT_ST_BOOLEANS + "stoptions", +#endif +#ifdef MT_ST_WRITE_THRESHOLD + "stwrthreshold", +#endif + NULL +}; + +int count_bits[] = +{ +#ifdef MT_ST_BOOLEANS + MT_ST_BOOLEANS, +#endif +#ifdef MT_ST_WRITE_THRESHOLD + MT_ST_WRITE_THRESHOLD, +#endif + 0 +}; + +#ifdef MT_TAPE_INFO + struct mt_tape_info tapes[] = MT_TAPE_INFO; +#endif + + struct option longopts[] = { {"file", 1, NULL, 'f'}, @@ -200,10 +380,22 @@ print_status (char *dev, int desc) { struct mtget status; +#ifdef MT_TAPE_INFO + struct mt_tape_info *mt; +#endif if (rmtioctl (desc, MTIOCGET, (char*)&status) == -1) error (2, errno, _("%s: rmtioctl failed"), dev); +#ifdef MT_TAPE_INFO + for (mt = tapes; mt->t_type; mt++) + if (mt->t_type == status.mt_type) break; + if (mt->t_type != 0) + { + printf ("drive type = %s\n", mt->t_name); + } + else +#endif printf ("drive type = %d\n", (int) status.mt_type); #if defined(hpux) || defined(__hpux) printf ("drive status (high) = %d\n", (int) status.mt_dsreg1); @@ -217,7 +409,177 @@ printf ("file number = %d\n", (int) status.mt_fileno); printf ("block number = %d\n", (int) status.mt_blkno); #endif +#if defined(linux) || defined(__linux) + if (status.mt_type == MT_ISSCSI1 || + status.mt_type == MT_ISSCSI2) + { + int dens, i; + char *density; + dens = (status.mt_dsreg & MT_ST_DENSITY_MASK) >> MT_ST_DENSITY_SHIFT; + density = "unknown"; + for (i=0; density_tbl[i].code >= 0; 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 (status.mt_gstat != 0) + putchar ('\n'); + } + else + { + printf("gstat = %0lx\n", status.mt_gstat); + } +#endif +} + + +#if defined(linux) || defined(__linux) +/*** Get and set the DAT compression (Mode Page 15) ***/ + +#define MODE_SENSE 0x1a +#define MODE_SELECT 0x15 + +int +read_mode_page(int fn, int page, int length, unsigned char *buffer, + int do_mask) +{ + int result, *ip; + unsigned char tmpbuffer[30], *cmd; + + memset(tmpbuffer, 0, 14); + ip = (int *)&(tmpbuffer[0]); + *ip = 0; + *(ip+1) = length + 4; + + cmd = &(tmpbuffer[8]); + cmd[0] = MODE_SENSE; + cmd[1] = 8; + cmd[2] = page; + if (do_mask) + cmd[2] |= 0x40; /* Get changeable parameter mask */ + cmd[4] = length + 4; + + result = ioctl(fn, 1, tmpbuffer); + if (result) { + fprintf(stderr, "Can't read mode page. Are you sure you are root?\n"); + return 0; + } + memcpy(buffer, tmpbuffer + 8, length + 4); + return 1; +} + + +int +write_mode_page(int fn, int page, int length, unsigned char *buffer) +{ + int result, *ip; + unsigned char tmpbuffer[40], *cmd; + + memset(tmpbuffer, 0, 14); + ip = (int *)&(tmpbuffer[0]); + *ip = length + 4; + *(ip+1) = 0; + + cmd = &(tmpbuffer[8]); + cmd[0] = MODE_SELECT; + cmd[1] = 0x10; + cmd[4] = length + 4; + + memcpy(tmpbuffer + 14, buffer, length + 4); + tmpbuffer[14] = 0; /* reserved data length */ + tmpbuffer[18] &= 0x3f; /* reserved bits in page code byte */ + + result = ioctl(fn, 1, tmpbuffer); + if (result) { + fprintf(stderr, "Can't write mode page.\n"); + return 0; + } + return 1; +} + + +int +do_dat_compression(char *dev, int fn, int count) +{ + int i; + unsigned char buffer[30], mask[30]; + + if (!read_mode_page(fn, 0x0f, 16, buffer, 0)) { + error (2, errno, "%s", dev); + } + + if (count != -1) { + if (count == 0) + buffer[4+2] &= 0x7f; + else + buffer[4+2] |= 0x80; +/* if (read_mode_page(fn, 0x0f, 16, mask, 1)) + for (i=3; i < 16; i++) + buffer[4+i] |= mask[4+i]; bug #223494 */ + if (!write_mode_page(fn, 0x0f, 16, buffer)) { + error (2, errno, "%s", dev); + } + if (!read_mode_page(fn, 0x0f, 16, buffer, 0)) { /* Re-read to check */ + error (2, errno, "%s", dev); + } + } + + if (buffer[4+2] & 0x80) + printf("Compression on.\n"); + else + printf("Compression off.\n"); + + return 1; } +#endif + +#ifdef MTTELL +void +print_position (dev, desc) + char *dev; + int desc; +{ + struct mtpos position; + + if (rmtioctl (desc, MTIOCPOS, &position) == -1) + error (2, errno, "%s", dev); + printf("At block %ld.\n", position.mt_blkno); + +} +#endif + void usage (FILE *fp,int status) @@ -276,7 +638,7 @@ if (optind == argc) usage (stderr, 1); - i = argmatch (argv[optind], opnames); + i = argmatch (argv[optind], opnames,NULL,0); if (i < 0) { argmatch_invalid ("tape operation", argv[optind], i); @@ -308,10 +670,29 @@ #endif } +#ifdef MTDENS + if (operation == MTDENS) + { + printf("Some SCSI tape density codes:\ncode explanation\n"); + for (i=0; density_tbl[i].code >= 0; i++) + printf("0x%02x %s\n", density_tbl[i].code, density_tbl[i].name); + exit (0); + } +#endif + if ( (operation == MTWEOF) #ifdef MTERASE || (operation == MTERASE) #endif +#ifdef MTWSM + || (operation == MTWSM) +#endif +#ifdef MTSETDRVBUFFER + || (operation == MTSETDRVBUFFER) +#endif +#ifdef MTDATCOMP + || (operation == MTDATCOMP) +#endif ) tapedesc = rmtopen (tapedev, O_WRONLY, 0, rsh_command_option); else @@ -320,6 +701,17 @@ error (1, errno, _("%s: rmtopen failed"), tapedev); check_type (tapedev, tapedesc); +#ifdef MTDATCOMP + if (operation == MTDATCOMP) + do_dat_compression(tapedev, tapedesc, count); + else +#endif +#ifdef MTTELL + if (operation == MTTELL) + print_position (tapedev, tapedesc); + else +#endif + { if (operation == MTASF) { perform_operation (tapedev, tapedesc, MTREW, 1); @@ -328,6 +720,7 @@ perform_operation (tapedev, tapedesc, operation, count); if (operation == MTNOP) print_status (tapedev, tapedesc); + } if (rmtclose (tapedesc) == -1) error (2, errno, _("%s: rmtclose failed"), tapedev);