2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1980 Regents of the University of California.
8 * All rights reserved. The Berkeley Software License Agreement
9 * specifies the terms and conditions for redistribution.
12 #pragma ident "%Z%%M% %I% %E% SMI"
15 * mt -- magnetic tape manipulation program
21 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <sys/param.h>
33 #include <sys/scsi/targets/stdef.h>
37 #define equal(s1, s2) (strcmp(s1, s2) == 0)
38 #define MTASF 100 /* absolute file positioning; first file is 0 */
41 * This can't be DEFTAPE in mtio.h because that is currently the rewinding
42 * unit which makes 'mt fsf' a questionable activity at best.
44 #define DEFAULT_NRW_TAPE "/dev/rmt/0n"
46 static int print_config(int mtfd
);
47 static char *print_key(short key_code
);
48 static void printreg(char *, ushort_t
, char *);
49 static int status(int mtfd
, struct mtget
*);
51 /* Pseudo flag for open even if drive is not ready (Unloaded) or reserved */
52 #define O_UNLOAD (O_RDWR | O_NDELAY)
54 static const struct commands
{
60 { "weof", MTWEOF
, O_RDWR
, 1 },
61 { "eof", MTWEOF
, O_RDWR
, 1 },
62 { "fsf", MTFSF
, O_RDONLY
, 1 },
63 { "bsf", MTBSF
, O_RDONLY
, 1 },
64 { "asf", MTASF
, O_RDONLY
, 1 },
65 { "fsr", MTFSR
, O_RDONLY
, 1 },
66 { "bsr", MTBSR
, O_RDONLY
, 1 },
67 { "rewind", MTREW
, O_RDONLY
, 0 },
68 { "offline", MTOFFL
, O_RDONLY
, 0 },
69 { "rewoffl", MTOFFL
, O_RDONLY
, 0 },
70 { "status", MTNOP
, O_RDONLY
, 0 },
71 { "retension", MTRETEN
, O_RDONLY
, 0 },
72 { "erase", MTERASE
, O_RDWR
, 0 },
73 { "eom", MTEOM
, O_RDONLY
, 0 },
74 { "nbsf", MTNBSF
, O_RDONLY
, 1 },
75 { "reserve", MTIOCRESERVE
, O_RDONLY
, 0 },
76 { "release", MTIOCRELEASE
, O_RDONLY
, 0 },
77 { "forcereserve", MTIOCFORCERESERVE
, O_UNLOAD
, 0 },
78 { "config", MTIOCGETDRIVETYPE
, O_UNLOAD
, 0 },
79 { "fssf", MTFSSF
, O_RDONLY
, 1 },
80 { "bssf", MTBSSF
, O_RDONLY
, 1 },
81 { "tell", MTTELL
, O_RDONLY
, 0 },
82 { "seek", MTSEEK
, O_RDONLY
, 1 },
83 { "load", MTLOAD
, O_UNLOAD
, 0 },
84 { "lock", MTLOCK
, O_RDONLY
, 0 },
85 { "unlock", MTUNLOCK
, O_RDONLY
, 0 },
91 main(int argc
, char **argv
)
96 struct commands
const *comp
;
97 struct mtget mt_status
;
100 if (argc
> 2 && (equal(argv
[1], "-t") || equal(argv
[1], "-f"))) {
105 tape
= getenv("TAPE");
107 tape
= DEFAULT_NRW_TAPE
;
112 (void) fprintf(stderr
,
113 "usage: mt [ -f device ] command [ count ]\n");
118 for (comp
= com
; comp
->c_name
!= NULL
; comp
++) {
119 if (strncmp(cp
, comp
->c_name
, strlen(cp
)) == 0) {
124 if (comp
->c_name
== NULL
) {
125 (void) fprintf(stderr
, "mt: unknown command: %s\n", cp
);
129 mtfd
= open(tape
, comp
->c_oflag
);
133 * Provide additional error message decoding since
134 * we need additional error codes to fix them problem.
137 (void) fprintf(stderr
,
138 "%s: no tape loaded or drive offline\n", tape
);
139 } else if (errno
== EACCES
) {
140 (void) fprintf(stderr
,
141 "%s: write protected or reserved.\n", tape
);
148 if (comp
->c_code
== MTIOCFORCERESERVE
||
149 comp
->c_code
== MTIOCRESERVE
||
150 comp
->c_code
== MTIOCRELEASE
) {
152 * Handle all MTIOC ioctls used in
153 * reservation/release/takeownership.
155 if (ioctl(mtfd
, comp
->c_code
) < 0) {
159 } else if (comp
->c_code
== MTASF
) {
161 * Handle absolute file positioning. Ask tape driver
162 * where tape is and then skip to desired file. If
163 * driver doesn't support get location ioctl, rewind
164 * the tape and then space to the desired file.
169 usecnt
= argc
> 2 && comp
->c_usecnt
;
170 mt_fileno
= usecnt
? atol(argv
[2]) : 1;
172 (void) fprintf(stderr
, "mt: negative file number\n");
175 (void) ioctl(mtfd
, MTIOCGET
, (char *)&mt_status
);
176 if (ioctl(mtfd
, MTIOCGET
, (char *)&mt_status
) < 0) {
181 * Check if device supports reporting current file
182 * tape file position. If not, rewind the tape, and
185 * If file number is -1 tape position is unknown!
187 if ((mt_status
.mt_flags
& MTF_ASF
) == 0 ||
188 (mt_status
.mt_fileno
== -1)) {
189 /* printf("mt: rewind\n"); */
191 mt_com
.mt_op
= MTREW
;
192 if (ioctl(mtfd
, MTIOCLTOP
, &mt_com
) < 0) {
193 (void) fprintf(stderr
, "%s %s %ld ",
194 tape
, comp
->c_name
, mt_fileno
);
198 /* Needed to rewind which worked now correct fileno */
199 mt_status
.mt_fileno
= 0;
200 mt_status
.mt_blkno
= 0;
202 if (mt_fileno
< mt_status
.mt_fileno
) {
203 mt_com
.mt_op
= MTNBSF
;
204 mt_com
.mt_count
= mt_status
.mt_fileno
- mt_fileno
;
205 /* printf("mt: bsf= %d\n", mt_com.mt_count); */
207 mt_com
.mt_op
= MTFSF
;
208 mt_com
.mt_count
= mt_fileno
- mt_status
.mt_fileno
;
209 /* printf("mt: fsf= %d\n", mt_com.mt_count); */
211 if (ioctl(mtfd
, MTIOCLTOP
, &mt_com
) < 0) {
212 (void) fprintf(stderr
, "%s %s %ld ", tape
, comp
->c_name
,
217 } else if (comp
->c_code
== MTIOCGETDRIVETYPE
) {
218 return (print_config(mtfd
));
220 /* Handle regular mag tape ioctls */
221 } else if (comp
->c_code
!= MTNOP
) {
224 mt_com
.mt_op
= comp
->c_code
;
225 usecnt
= argc
> 2 && comp
->c_usecnt
;
226 mt_com
.mt_count
= (usecnt
? atoll(argv
[2]) : 1);
227 if (mt_com
.mt_count
< 0) {
228 (void) fprintf(stderr
, "mt: negative %s count\n",
232 if (ioctl(mtfd
, MTIOCLTOP
, &mt_com
) < 0) {
234 * If we asked for a seek and it returns a tell
235 * we attempted to seek more then there was.
237 if (mt_com
.mt_op
== MTTELL
&&
238 comp
->c_code
== MTSEEK
) {
239 (void) printf("partial seek:at block = %llu.\n",
242 (void) fprintf(stderr
, "%s %s %lld ", tape
,
243 comp
->c_name
, mt_com
.mt_count
);
248 if (mt_com
.mt_op
== MTTELL
) {
249 (void) printf("At block = %llu.\n", mt_com
.mt_count
);
253 /* Handle status ioctl */
255 if (ioctl(mtfd
, MTIOCGET
, (char *)&mt_status
) < 0) {
259 return (status(mtfd
, &mt_status
));
265 print_config(int mtfd
)
267 struct mtdrivetype mdt
;
268 struct mtdrivetype_request mdt_req
;
274 mdt_req
.size
= sizeof (mdt
);
275 mdt_req
.mtdtp
= &mdt
;
277 if (ioctl(mtfd
, MTIOCGETDRIVETYPE
, &mdt_req
) != 0) {
283 * remove trailing spaces from product id.
285 for (i
= VIDPIDLEN
; i
; i
--) {
286 if (isspace(mdt
.vid
[i
]) || mdt
.vid
[i
] == '*') {
288 } else if (mdt
.vid
[i
] == 0) {
296 * If this is a generic name display the Vid and Pid instead.
298 if (strstr(mdt
.name
, "Vendor '") == NULL
) {
305 * Attempt to create a configuration name using vid and pid.
307 (void) strcpy(cfgname
, "CFG");
309 for (tmp
[1] = i
= 0; i
< VIDPIDLEN
; i
++) {
310 if (!isalnum(name
[i
]))
312 if (isspace(name
[i
]))
314 tmp
[0] = toupper(name
[i
]);
315 (void) strncat(cfgname
, tmp
, 1);
318 (void) printf("\"%s\", \"%s\", \"%s\";\n", mdt
.vid
, name
, cfgname
);
321 * Don't want show some bits, ST_DYNAMIC is set in the driver
322 * so one can tell that its not a compiled in config.
323 * The ST_LONG_ERASE and ST_LONG_TIMEOUTS are not displayed
324 * becouse the timeout values below already reflect them being
326 * Also ST_KNOWS_MEDIA is not displayed as it can not be configured
327 * from an st.conf entry.
329 (void) printf("%s = 2,0x%X,%d,0x%X,", cfgname
,
330 mdt
.type
, mdt
.bsize
, mdt
.options
&
331 ~(ST_DYNAMIC
| ST_LONG_ERASE
| ST_LONG_TIMEOUTS
| ST_KNOWS_MEDIA
));
333 (void) printf("4,0x%2.2X,0x%2.2X,0x%2.2X,0x%2.2X,%d,",
334 mdt
.densities
[0], mdt
.densities
[1], mdt
.densities
[2],
335 mdt
.densities
[3], mdt
.default_density
>> 3);
337 (void) printf("%d,%d,%d,%d,%d,%d,%d;\n", mdt
.non_motion_timeout
,
338 mdt
.io_timeout
, mdt
.rewind_timeout
, mdt
.space_timeout
,
339 mdt
.load_timeout
, mdt
.unload_timeout
, mdt
.erase_timeout
);
345 * Interpret the status buffer returned
348 status(int mtfd
, struct mtget
*bp
)
350 struct mtdrivetype mdt
;
351 struct mtdrivetype_request mdt_req
;
352 const char *name
= (char *)NULL
;
355 * Make a call to MTIOCGETDRIVETYPE ioctl, Also use old method
356 * of MT_TAPE_INFO for now, but MT_TAPE_INFO should dissapear in 2.7
358 mdt_req
.size
= sizeof (struct mtdrivetype
);
359 mdt_req
.mtdtp
= &mdt
;
361 if (ioctl(mtfd
, MTIOCGETDRIVETYPE
, &mdt_req
) == 0) {
363 if (strstr(mdt
.name
, "Vendor '") != NULL
) {
364 (void) printf("Unconfigured Drive: ");
367 perror("mt drivetype");
371 /* Handle SCSI tape drives specially. */
372 if ((bp
->mt_flags
& MTF_SCSI
)) {
373 if (name
== (char *)NULL
) {
378 (void) printf("%s tape drive:\n", name
);
380 (void) printf(" sense key(0x%x)= %s residual= %ld ",
381 bp
->mt_erreg
, print_key(bp
->mt_erreg
), bp
->mt_resid
);
382 (void) printf("retries= %d\n", bp
->mt_dsreg
);
384 * Can overflow the signed numbers.
385 * fileno will be -1 on error but all other positions are
386 * positive. blkno will never be negative.
388 if (bp
->mt_fileno
== -1) {
389 (void) printf(" file no= -1 block no= %lu\n",
390 (unsigned long)bp
->mt_blkno
);
392 (void) printf(" file no= %lu block no= %lu\n",
393 (unsigned long)bp
->mt_fileno
,
394 (unsigned long)bp
->mt_blkno
);
396 if ((bp
->mt_flags
& MTF_WORM_MEDIA
) != 0) {
397 (void) printf(" WORM media\n");
400 /* Handle non-SCSI drives here. */
402 (void) printf("unknown tape drive type (0x%x)\n",
406 (void) printf("%s tape drive:\n residual= %ld", name
,
408 printreg(" ds", (ushort_t
)bp
->mt_dsreg
, 0);
409 printreg(" er", (ushort_t
)bp
->mt_erreg
, 0);
410 (void) putchar('\n');
417 * Define SCSI sense key error messages.
419 * The first 16 sense keys are SCSI standard
420 * sense keys. The keys after this are
421 * Sun Specifice 'sense' keys- e.g., crap.
424 static char *sense_keys
[] = {
425 "No Additional Sense", /* 0x00 */
426 "Soft Error", /* 0x01 */
427 "Not Ready", /* 0x02 */
428 "Media Error", /* 0x03 */
429 "Hardware Error", /* 0x04 */
430 "Illegal Request", /* 0x05 */
431 "Unit Attention", /* 0x06 */
432 "Write Protected", /* 0x07 */
433 "Blank Check", /* 0x08 */
434 "Vendor Unique", /* 0x09 */
435 "Copy Aborted", /* 0x0a */
436 "Aborted Command", /* 0x0b */
437 "Equal Error", /* 0x0c */
438 "Volume Overflow", /* 0x0d */
439 "Miscompare Error", /* 0x0e */
440 "Reserved", /* 0x0f */
443 "timeout", /* 0x11 */
446 "length error", /* 0x14 */
448 "wrong tape media", /* 0x16 */
453 * Return the text string associated with the sense key value.
456 print_key(short key_code
)
458 static char unknown
[32];
460 if ((key_code
>= 0) &&
461 (key_code
< (sizeof (sense_keys
) / sizeof (sense_keys
[0])))) {
462 return (sense_keys
[key_code
]);
465 (void) sprintf(unknown
, "unknown sense key: 0x%x",
466 (unsigned int) key_code
);
472 * Print a register a la the %b format of the kernel's printf
475 printreg(char *s
, ushort_t v
, char *bits
)
480 if (bits
&& *bits
== 8) {
481 (void) printf("%s = %o", s
, v
);
483 (void) printf("%s = %x", s
, v
);
488 while ((i
= *bits
++) != 0) {
489 if (v
& (1 << (i
-1))) {
494 for (; (c
= *bits
) > 32; bits
++) {
498 for (; *bits
> 32; bits
++)