2 dds2tar Digital-Data-Storage Extract Tool
4 This tool makes use of the fast seek command of DAT devices.
5 Files from a selected file archive can be extracted within
8 J"org Weule weule@cs.uni-duesseldorf.de
11 ----------------------------------------------------------------------------
15 This program is free software; you can redistribute it and/or modify
16 it under the terms of the GNU General Public License as published
17 by the Free Software Foundation; either version 1, or (at your option)
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 ----------------------------------------------------------------------------
31 To anyone in Germany I declare the following:
33 ! Ich werde gegen jede Verbreitung dieser Software rechtlich !
34 ! vorgehen, welche die Lizenzbestimmungen nicht einh"alt. !
36 Wie f"ur jede andere Software gilt auch hier:
37 Schon mit dem Kopieren oder Benutzen von Teilen dieser Software
38 werden die Lizenzbedingungen stillschweigend akzeptiert. Lesen Sie
39 also die Lizenzbedingungen sorgf"altig! Unkenntnis sch"utzt nicht
44 /*-------------------------------------------------------------------------*/
46 #include <stdlib.h> /* getenv() */
48 #include <string.h> /* strcmp() strlen() strstr() */
49 #include <sys/types.h> /* open() stat() */
50 #include <sys/stat.h> /* stat() */
51 #include <fcntl.h> /* open() */
52 #include <unistd.h> /* open() */
53 #include <time.h> /* ctime() */
57 #include "zf-cre-open.h"
59 char vendor[9] = "\0\0\0\0\0\0\0\0\0" ;
60 int vid; /* vendor id */
62 /*-------------------------------------------------------------------------*/
64 static const char help_text_dds2index[] =
65 "This is dds2index, a tool for fast tape access.\n"
66 "Choose one of the following\n"
67 "-t,--table-of-contents F get file location from file F\n"
68 "-z,--compress filter location file through gzip\n"
69 "--z,--no-compress don't filter location file through gzip\n"
70 "-f,--device F read the archive from device F\n"
71 "-b,--block-size # set block size (non HP/SONY)\n"
72 "-B,--Block-size # set block size (HP or SONY)\n"
73 "-q,--quick don't extract parent directories from tape\n"
74 "-v,--verbose verbose mode\n"
75 "--hash-mode hash mode\n"
76 "--force force nochk\n"
78 "-V,--version print version number\n"
81 "TAPE specifies the tape device\n"
82 "DDS2TAR=[--compress] [-z] [-s #] [-b #]\n"
83 " set some defaults\n"
86 static const char help_text_dds2tar[] =
87 "This is dds2tar, a tool for fast tape access.\n"
88 "Usage: dds2tar [options... ] -t index [pathname ...] | tar -f - ...\n"
90 "dds2tar is a tool to read some tar-records from tape and write them\n"
91 "as a tar archive to stdout. You have to use tar to extract the\n"
92 "selected files. The index can either be created by dds2tar or tar.\n"
94 "Choose one of the following\n"
95 "-t,--table-of-contents F get file location from file F\n"
96 "-z,--compress filter location file through gzip\n"
97 "--z,--no-compress don't filter location file through gzip\n"
98 "-f,--device F read the archive from device F\n"
99 "-o,--output F write output to archive file\n"
100 "-b,--block-size # set block size (non HP/SONY)\n"
101 "-B,--Block-size # set block size (HP or SONY)\n"
102 "-s,--first_block # set number of the first block\n"
103 "--body print only the contents of the first file an quit\n"
104 "-v,--verbose verbose mode\n"
106 "-V,--version print version number\n"
109 "TAPE specifies the tape device\n"
110 "DDS2TAR=[--compress] [-z] [-s #] [-b #]\n"
111 " set some defaults\n"
114 static const char help_text_mt_dds[] =
115 "This is mt-dds, a tool to control the dds-device.\n"
116 "Usage: mt-dds [-f device] <action>\n"
118 "Choose one or none of the following actions:\n"
120 "comp-on set compression mode on\n"
121 "comp-off set compression mode off\n"
122 "comp-query query compression mode\n"
123 "comp-log print compression log page\n"
125 "-b set the buffer size\n"
126 "blksize print the block size of the archive with format\n"
127 "tell print current location and blocksize\n"
128 "where print current location\n"
129 "label print the label of the current archive\n"
130 " if it begins at the current position\n"
131 "filename print the filename of the current record\n"
132 " if a header record is found at the current position\n"
133 "ts print the timestamp of the current archive\n"
134 "date print the date of the current archive\n"
135 "scsi2logical select mode scsi2logical first\n"
136 " if it begins at the current position\n"
137 "date <timestamp> convert the stamp to a string\n"
140 static const char help_text_dds_dd[] =
141 "This is dds-dd, a tool to read the dds-device.\n"
142 "Usage: dds-dd` [-f device] | tar -f - -[x|t] ... \n";
144 /*-------------------------------------------------------------------------*/
146 static const char version[] =
147 "Version " VERSION " -- last change 1994 Dez 22." ;
149 /*-------------------------------------------------------------------------*/
151 /* This is the source for dds2tar, dds2index and mt-dds. The program will
152 use the variable pg to decide witch version is running. The default
153 can be set in the Makefile and is overwritten by the value of argv[0].
154 If you give --dds2tar, --dds2index or --mt-dds as an option, the
155 program will run instead of these.
158 #define DDS2TAR name_dds2tar
159 static char const name_dds2tar[] = "dds2tar";
161 #define DDS2INDEX name_dds2index
162 static char const name_dds2index[] = "dds2index";
164 #define MTDDS name_mtdds
165 static char const name_mtdds[] = "mt-dds";
167 #define DDS_DD name_dds_dd
168 static char const name_dds_dd[] = "dds-dd";
171 #if ( PROGRAM == DDS2TAR )
172 char const *pg = name_dds2tar;
173 char const *help_text = help_text_dds2tar;
175 #elif ( PROGRAM == DDS2INDEX )
176 char const *pg = name_dds2index;
177 char const *help_text = help_text_dds2index;
179 #elif ( PROGRAM == DDS2TAR )
180 char const *pg = name_mtdds;
181 char const *help_text = help_text_mt_dds;
183 #elif ( PROGRAM == DDS_DD )
184 char const *pg = name_dds_dd;
185 char const *help_text = help_text_dds_dd;
188 char const *pg = name_dds2tar;
189 char *help_text = help_text_dds2tar;
196 /*-------------------------------------------------------------------------*/
198 char const dds_old_headline[] =
199 "magic record blk: size name\n";
201 char const dds_old_index_format[] =
202 "%6s%7d%3d:%9d %s\n";
204 /* magic blkno recno: size name */
206 char const dds_old_index_scan_format[] =
209 char const dds_headline[] =
210 "magic record blk: size name\n";
212 char const dds_index_format[] =
213 "%6s%7d%4d:%9d %s\n";
215 /* magic blkno recno: size name */
217 char const dds_index_scan_format[] =
220 /* magic blkno recno: size name */
222 char const dds_loctext[] =
223 "first block number is %d\n"
224 "block size is %d\n" "block length is %d\n";
226 char const dds_locline1[] =
227 "loc number of the first block is %d\n";
229 char const dds_locline2[] =
230 "loc block length is %d bytes = %d * 512 bytes\n";
232 /* to handle long link and long file names */
233 int long_name_len = 0 ;
234 char long_name[MAXPATHLEN<<2] = { '\0' };
239 #define strneq(a,b,n) strncmp(a,b,n)==0
241 if ( strneq(cur_line, dds_locline1, 45) ) {
242 tar_fb = atoi(cur_line + 45);
244 if ( strneq(cur_line, "first block number is", 21) ) {
245 tar_fb = atoi(cur_line + 21);
247 if ( strneq(cur_line, dds_locline2, 31) ) {
248 tar_n = atoi(cur_line + 31);
251 if ( strneq(cur_line, "block size is", 13) ) {
252 tar_bs = atoi(cur_line + 13);
255 if ( strneq(cur_line, "block length is", 15) ) {
256 tar_n = atoi(cur_line + 15) ;
257 tar_bs = tar_n >> 9 ;
262 if ( verbose ) fprintf(stderr,
263 "block length is %d = %d * 512 \n", tar_n, tar_bs );
267 if ( buf_n < tar_n ) {
268 cur_block = realloc ( cur_block , buf_n = tar_n );
269 if ( cur_block == NULL ) {
270 fprintf(stderr, "%s: No memory available.\n", pg);
278 /*-------------------------------------------------------------------------*/
282 int list_only = 0; /* print only the matched names */
283 int quick_mode = 0; /* don't extract parent directories also */
284 int device; /* file number of the device */
285 int write_body = 0 ; /* write only the body of the first file */
286 int get_label = 0 ; /* get the label of the archive */
287 int get_filename = 0 ; /* get the filename of the current record */
288 int get_blocksize = 0 ; /* get the blocksize of the current record */
289 int get_timestamp = 0 ; /* get the timestamp of the archive */
290 int get_date = 0 ; /* get the date of the archive */
291 int get_fileno = 0 ; /* get the number of the current */
292 int force_nochk = 0 ; /* force tape read, no check of headers */
295 FILE *index_fp = NULL;
297 /* archive location as set by arguments or index file */
298 int tar_fb = 0; /* first block of the archive */
299 int tar_bs = 20; /* block size in records of 512 bytes */
300 int tar_n = 10240; /* block length in bytes */
302 /* archive buffer and location as set by tape access */
303 tar_record *cur_block;
305 int next_blkno = -1 ;
308 int buf_n = ST_BUFFER_BLOCKS << 10 ;
310 /* line buffer to scan the index file */
313 /*-------------------------------------------------------------------------*/
316 * input: name of the device ( e.g. /dev/nst0, /dev/nrmt0 or NULL )
317 * open mode of the device ( e.g. O_RDONLY or O_WRONLY )
318 * verbose flag to indicate the logging mode (integer)
319 * output: channel number ( integer ) for use with 'read()' or 'write()'
323 **********************************************************************/
326 open_device(char const *pathname, int const open_mode)
330 char const *p = "next try to open the tape %s\n";
337 i = stat(pathname,&s);
338 if ( i < 0 ) perror("dds2tar"),exit(1);
339 sprintf(cmd,"scsi_vendor tape %d",(int)(s.st_rdev&0x7f)+1);
343 for ( i = 0 ; i < 8 ; i++ )
344 ( vendor[i] < 0x20 ) && ( vendor[i] = '\0' ) ;
345 if ( !strcmp(vendor,"HP") ) vid = HP ;
346 if ( !strcmp(vendor,"SONY") ) vid = SONY ;
347 if ( !strcmp(vendor,"ARCHIVE") ) vid = ARCHIVE ;
348 if ( !strcmp(vendor,"SEGATE") ) vid = SEGATE ;
349 if ( !strcmp(vendor,"EXABYTE") ) vid = EXABYTE ;
353 if ((fd = open(pathname, open_mode)) >= 0)
357 fprintf(stderr, p, pathname);
358 } while ((++i) <= 15);
360 perror("tape busy?");
362 } /* else dds_has_partitions(fd); */
366 /*-------------------------------------------------------------------------*/
368 strprefix(char**s,char*p,char*q){
370 char *t = *s ; while (( *p != 0 ) && ( *p == *t )) p++ , t++ ;
371 if ( *p == 0 ) { *s = t ; return 1 ; }
375 /*-------------------------------------------------------------------------*/
381 main(int argc, char *const *argv)
385 #define DEVICE "/dev/rmt0"
390 int l = strlen(argv[0]);
391 int compressed_mode = T_MODE;
393 int child_proc = -1;/* child not running */
394 char const *index_file = NULL;
395 int print_location = 0;
396 int print_blksize = 0;
397 int mt_action = DDSCM_NULL; /* turn compression on/off */
398 int mode = 0; /* mode of dds2tar */
399 char const *const *pattern_list = NULL;
400 char const *device_name = DEVICE;
402 if ((p = getenv("TAPE")) != NULL)
405 if (!strcmp(argv[0] + l - 7, "dds2tar")) {
407 help_text = help_text_dds2tar;
409 if (!strcmp(argv[0] + l - 9, "dds2index")) {
411 help_text = help_text_dds2index;
413 if (!strcmp(argv[0] + l - 6, "mt-dds")) {
415 help_text = help_text_mt_dds;
417 if (!strcmp(argv[0] + l - 6, "dds-dd")) {
419 help_text = help_text_dds_dd;
423 * Scanning the environment.
425 p = getenv("DDS2TAR");
429 if ( *p == '\0' ) p = NULL ;
430 else if (strprefix(&p, "-z","--compress")) {
431 compressed_mode = C_MODE;
432 } else if (strprefix(&p, "--first-block ","-s")) {
433 sscanf(p, "%d%n", &tar_fb, &count);
435 } else if (strprefix(&p, "--block-size ","-b ")){
436 sscanf(p, "%d%n", &tar_bs, &count);
437 tar_n = tar_bs * 512 ;
440 } else if (strprefix(&p, "--Block-size ","-B ")){
441 sscanf(p, "%d%n", &tar_bs, &count);
442 tar_n = tar_bs * 512 ;
456 * Macros to test arguments ...
459 #define T1(s) if((!strcmp(argv[n],(s))))
460 #define T(s) else if((!strcmp(argv[n],(s))))
461 #define TT(s,t) else if((!strcmp(argv[n],(s)))||(!strcmp(argv[n],(t))))
463 #define ELSEIF(s) else if(s)
466 * Now scanning the arguments ...
470 fprintf(stdout, "dds2tar: %s\n", argv[0]);
478 fprintf(stdout, help_text);
481 TT("--version", "-V") {
482 fprintf(stdout, "%s: %s\n", pg, version);
487 * Some more options ...
489 TT("--verbose", "-v") verbose = 1;
490 T("--hash-mode") hash_mode = 1;
491 T("--force") force_nochk = 1;
496 T("-f") device_name = argv[++n];
501 TT("-z", "--compress") compressed_mode = C_MODE;
502 TT("--z", "--no-compress") compressed_mode = T_MODE;
503 TT("-t", "--table-of-contents") {
504 index_file = argv[++n];
506 * The format of the index is checked by the program.
511 * Location of the file
513 TT("-s", "--first-block") {
514 tar_fb = atoi(argv[++n]);
516 TT("-b", "--block-size") {
517 tar_bs = atoi(argv[++n]);
518 tar_n = tar_bs * 512 ;
521 TT("-B", "--Block-size") {
522 tar_bs = atoi(argv[++n]);
523 tar_n = tar_bs * 512 ;
527 TT("-m", "--buffer-size") {
528 fprintf(stderr,"buffer size is %d records, %d bytes\n",
533 ELSEIF(pg == DDS2TAR) {
535 * Mode of the program.
537 * If you install dds2tar as mt-dds, the program will
538 * act as mt-dds. Since for some operations you need
539 * root permissions on mt-dds, it would not be a good
540 * idea to let the user switch to dds2index or dds2tar
547 help_text = help_text_dds2index;
551 help_text = help_text_mt_dds;
555 help_text = help_text_dds_dd;
558 * Mode of extraction ...
560 * The location list an experimental mode.
563 T("--location-list") {
564 pattern_list = argv + n + 1;
570 * Select a file for the output.
572 * Since mt-dds may be run as root, we write on screen
573 * in this case. Writing to a file is supported for
576 TT("-o", "--output") {
577 reopen(1, argv[++n], O_WRONLY | O_CREAT, 0660);
581 * Do not read anything from tape.
586 fputs("--list\n", stderr);
589 * Write only the body of the file.
596 * Don't extract the parent directories from the tape.
602 * Pipe the output to tar -x.
604 * This is for testing only.
607 static char const *const a[5] =
609 "/bin/tar", "tfb", "-", "1", NULL
612 child_proc = creopen(1, 0, a[0], a);
614 T("--test-verbose") {
615 static char const *const a[5] =
617 "/bin/tar", "tfbv", "-", "1", NULL
620 child_proc = creopen(1, 0, a[0], a);
623 static char const *const a[5] =
625 "/bin/tar", "xfb", "-", "1", NULL
628 child_proc = creopen(1, 0, a[0], a);
632 char*const*p = argv+n ;
633 while (*p) dds_unquote(*p++) ;
635 pattern_list = (const char*const*)argv + n;
638 "first pattern is '%s'\n",
646 ELSEIF(pg == MTDDS) {
650 T1("tell") print_location = 1;
651 T("label") get_label = 1 ;
652 T("filename") get_filename = 1 ;
653 T("bs") get_blocksize = 1 ;
657 int j = atoi(argv[2]);
658 fputs(ctime((time_t*)&j),stdout);
662 T("ts") get_timestamp = 1 ;
663 T("blksize") print_blksize = 1 ;
665 #define R(c) if ( 0 != geteuid() ) { fprintf(stderr,\
666 "You have to be root to do this : %s\n",c);exit(1);}
669 mt_action = DDSCM_ON;
673 mt_action = DDSCM_OFF;
677 mt_action = DDSCM_QUERY;
681 mt_action = DDSCM_LOG;
685 mt_action = DDSCM_LOAD;
689 mt_action = DDSCM_UNLOAD;
692 mt_action = DDSCM_WHERE;
695 * Set mode scsi2logical first
698 mt_action = DDSCM_LOGICAL;
704 * tar-dds will handle the arguments in a very different way.
707 ELSEIF(pg == TAR_DDS) {
718 cur_block = malloc(buf_n);
719 cur_line = malloc(MAXPATHLEN<<2);
720 if (cur_block == NULL || cur_line == NULL) {
721 fprintf(stderr, "%s: No memory available.\n", pg);
725 * Switch the program mode ... dds2tar, dds2index or mt-dds ...
727 /*---------------------MT-DDS--------------------------------*/
731 * Is setting of compression mode or log page selected ?
733 * I think you have to run this as root.
735 if (mt_action != DDSCM_NULL) {
736 device = open_device(device_name, O_WRONLY);
737 if ( mt_action == DDSCM_LOGICAL )
740 if ( mt_action == DDSCM_WHERE ) {
741 tar_fb = dds_getpos(device);
742 printf(" -s %d \n", tar_fb);
746 set_compression_mode(device, mt_action);
750 * Print the current position (tree lines).
753 device = open_device(device_name, O_RDONLY);
755 tar_fb = dds_getpos(device);
756 dds_read_next_block();
757 /* dds_seek(device, tar_fb); */
760 tar_fb = dds_getpos(device);
764 if (( get_label || get_timestamp ||
765 get_date || get_filename ) &&
766 ( dds_is_tar_header_record(cur_block) != 0 )) {
767 if ( cur_block->hdr.linkflag == 'V' ) {
769 char*p=cur_block->hdr.mtime ;
770 for ( i = 0 ; i < 12 ; i++ ) {
771 if ((p[i]>='0')&&(p[i]<='7')) {
777 puts(cur_block->hdr.name);
778 else if ( get_timestamp ){
780 } else /* get_date */ {
781 fputs(ctime((time_t*)&j),stdout);
784 if ( get_filename ) puts(cur_block->hdr.name);
786 if (print_location != 0) {
787 printf(dds_loctext, tar_fb, tar_bs, cur_n);
789 if ( print_blksize != 0 ) {
790 printf("%d\n",tar_bs);
793 * Print the current position (one line).
795 printf(" -s %d -b %d \n", tar_fb, tar_bs);
799 /*---------------------DDS2TAR-------------------------------*/
800 } else if (pg == DDS2TAR) {
802 if (pattern_list == NULL)
804 if (list_only != 1) {
805 device = open_device(device_name, O_RDONLY);
807 if ( density ) dds_set_bs(density);
809 index_fp = zfopen(index_file, compressed_mode, "r");
813 extract_loc(pattern_list);
817 dds_cmp(pattern_list);
820 fprintf(stderr, "nothing to do ? Try --help\n");
824 if (child_proc != -1)
826 /*---------------------DDS2INDEX-----------------------------*/
827 } else if (pg == DDS2INDEX) {
828 device = open_device(device_name, O_RDONLY);
829 if ( density ) dds_set_bs(density);
830 index_fp = zfopen(index_file, compressed_mode, "w");
833 /*-----------------------------------------------------------*/
834 } else if ( pg == DDS_DD ) {
835 device = open_device(device_name, O_RDONLY);
836 if ( density ) dds_set_bs(density);
837 if ( verbose ) fprintf(stderr,"dds-dd ...\n");
838 while ( dds_read_next_block() , cur_n )
839 fwrite(cur_block,1,cur_n,stdout);
841 fprintf(stderr, "no program mode \n");
842 /*-----------------------------------------------------------*/