Imported Upstream version 2.5.2
[debian/dds2tar] / dds_extract.c
1
2 /*
3  * This file is part of dds2tar.
4  * Copyright by J"org Weule
5  */
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <sys/mtio.h>
10 #include <string.h>
11 #include <unistd.h>
12
13 /*
14  * fnmatch() and FNM_LEADING_DIR
15  */
16 #define _GNU_SOURCE 1
17 #include <fnmatch.h>
18
19 #include "dds2tar.h"
20 #include "dds_tape.h"
21
22 /*
23  * Compare two strings
24  *
25  */
26 int dds_strcmp(const char*n1,const char*n2){
27         int i = 0 ;
28         int j = 0 ;
29         while ( 1 ){
30                 while ( n1[i] == '\\' ) i++ ;
31                 while ( n2[j] == '\\' ) j++ ;
32                 if ( i > 90 || j > 90 || n1[i] == '\0' || n2[j] == '\0' )
33                         return 0 ;
34                 if  ( n1[i] != n2[j] ) return 1 ;
35                 i++ ;
36                 j++ ;
37         }
38         return 0 ;
39 }
40
41 /*
42  * To count the number of blocks written, we use the variable nblocks.
43  */
44 static int nblocks = 0;
45
46 /*
47  * Fill the buffer 'cur_block' the block of the given number.
48  * first we check the block number of the copy that is hold inside the
49  * buffer cur_block.
50  */
51 static int
52 set_cur_block(int const sb)
53 {
54         int     n;
55
56 #ifdef DDS_TRACE
57         fprintf(stderr,"%d\n",__LINE__);
58         fprintf(stderr, "set_cur_block(%d)\n", sb);
59 #endif
60
61         if (sb != cur_blkno) {
62                 /*
63                  * We have to read the block.
64                  */
65                 next_blkno = dds_getpos(device);
66                 /*
67                  * next_blkno stores now the number of the next block of the
68                  * tape.
69                  */
70                 n = sb - next_blkno;
71                 /*
72                  * In some cases reading is faster then seeking.
73                  */
74                 if ((n > 0) && (n < DONT_SKIP)) {
75                         do {
76                                 dds_read_block();
77                         } while ((--n) > 0);
78                 }
79                 /*
80                  * Now we should be at the position.
81                  */
82                 n = sb - next_blkno;
83                 if (n != 0) {
84                         dds_seek(device, sb);
85                         next_blkno = sb;
86                 }
87                 /*
88                  * Now we read the block. cur_n == 0 indicates a filemark.
89                  */
90                 dds_read_block();
91         }
92         return 0;
93 }
94
95 /*
96  * procedure to extract the files from the specified area (sb:sr-eb:er).
97  * If area is of the form (sb:sr-0:0), the file at position (sb,sr) is
98  * extracted. The length of the file is read from the tar header record.
99  *
100  * If list_only is set to 1, only the name of the extraction is printed.
101  */
102 static int
103 extract(char const *const name, int const sb, int const sr)
104 {
105
106         int     cur_rec, n, size;
107         char   *p;
108
109 #ifdef DDS_TRACE
110         fprintf(stderr,"%d\n",__LINE__);
111         fprintf(stderr, "extract(%s,%d,%d)\n", name, sb, sr);
112 #endif
113
114         /*
115          * Print only the name.
116          */
117         if (list_only == 1) {
118                 printf("%7d%3d: %s\n", sb, sr, name);
119                 return 0;
120         }
121         /*
122          * Print the filename and the location for verbose output.
123          */
124         if (verbose != 0) {
125                 fprintf(stderr,
126                         "dds2tar at rec %d: %7d%3d: %s\n",
127                         nblocks, sb, sr, name);
128         }
129         /*
130          * Check the buffer for the right tape block.
131          */
132         set_cur_block(sb);
133
134         cur_rec = sr;
135         /*
136          * Check the header block.
137          * The Name field should contain the given name.
138          * The Program will stop if its not.
139          *
140          * Note that for links we can only compare the first
141          * characters, when the index contains somethins like
142          * 'linkname -> filename' (soft link) or
143          * 'linkname link to filename' (hard link)
144          * doesn't match 'linkname'.
145          */
146         p = (char*) (cur_block + cur_rec) ;
147         if ( dds_is_tar_header_record((tar_record*)p) == 0 ) {
148                 fprintf(stderr,
149                         " dds2tar: FATAL ERROR\n"
150                         " dds2tar: header expected, not found\n"
151                 );
152                 if ( force_nochk == 0 ) exit(5);
153         }
154         if (name != NULL)
155         if ( ( (dds_strcmp(name, p)!=0 )
156                 && (((tar_record*)p)->hdr.linkflag!='2')
157                 && (((tar_record*)p)->hdr.linkflag!='1')
158                 ) || ( strstr(name, p)!=name ) ) {
159                 fprintf(stderr,
160                         "dds2tar: FATAL ERROR\n"
161                         "dds2tar: Looked for %s at %d %d\n"
162                         "dds2tar: Could not find %s at %d %d.\n"
163                         "dds2tar: Found %s\n"
164                         "dds2tar: Link flag: %c\n"
165                         "dds2tar: Is it the right tape?\n",
166                         name, sb , sr ,
167                         name, dds_getpos(device) - 1, cur_rec,p,
168                         ((tar_record*)p)->hdr.linkflag);
169                 if ( force_nochk == 0 ) exit(5);
170         }
171         /*
172          * First calculate the number of records of the object.
173          */
174         size = 0;
175         n = 1;
176
177         p = cur_block[cur_rec].hdr.size;
178         if (*p != '\0') {
179                 sscanf(p, "%o", &size);
180                 n = size;
181                 n += 1023;
182                 n >>= 9;
183         }
184         /*
185          * Now write the records to stdout.
186          */
187         if (verbose) {
188                 fprintf(stderr,
189                         "dd2tar: %s needs %d blocks\n", name, n);
190         }
191         if ( write_body == 1 ) {
192                 cur_rec++;
193                 while ((size > 0) && (cur_n > 0)) {
194                         if (cur_rec >= cur_bs) {
195                                 dds_read_block();
196                                 cur_rec = 0;
197                         }
198                         write(1, cur_block + cur_rec, (size>=512)? 512:size );
199                         size -= 512 ;
200                         cur_rec++;
201                 }
202                 exit(0);
203         }
204         while ((n > 0) && (cur_n > 0)) {
205                 if (cur_rec >= cur_bs) {
206                         dds_read_block();
207                         cur_rec = 0;
208                 }
209                 write(1, cur_block + cur_rec, 512);
210                 nblocks++;
211                 n--;
212                 cur_rec++;
213         }
214         return 0;
215 }
216
217 #ifdef EXP_STUFF
218 int
219 extract_loc(char const *const *loc_list)
220 {
221         int cur_rec ;
222
223         while (*loc_list != NULL) {
224                 int     eb, er, sb, sr;
225
226                 sscanf(*loc_list, "%u:%u-%u:%u", &sb, &sr, &eb, &er);
227                 set_cur_block(sb);
228                 cur_rec = sr ;
229                 while (cur_n > 0) {
230                         int     i;
231
232                         if ((cur_n & 0x1ff) != 0) {
233                                 fprintf(stderr,
234                                         "tape record size (%d) is not a"
235                                         " multiple of tar records\n"
236                                         , cur_n
237                                         );
238                                 close(device);
239                                 exit(6);
240                         }
241                         i = cur_n >> 9;
242                         if (cur_blkno == eb)
243                                 i = er;
244                         while (cur_rec < i) {
245                                 write(1, cur_block + cur_rec++, 512);
246                                 nblocks++;
247                         }
248                         /*
249                          * if eb==-1 extract until eof
250                          */
251                         if ((cur_rec == er && cur_blkno == eb))
252                                 break;
253                         i = cur_blkno + 1;
254                         cur_rec = 0;
255                         if ((cur_rec == er && cur_blkno == eb))
256                                 break;
257                         dds_read_block();
258                 }
259                 loc_list++;
260         }
261         return 0;
262 }
263
264 #endif
265
266 /*
267  * Now we are scanning the table of contents (index file) and match the
268  * pathnames there with the given pattern. If a pattern matches, we
269  * extract the parent directories (dir_extract()) and the file.
270  */
271 int
272 dds_cmp(char const *const *const pattern_list)
273 {
274         int i ;
275         char *fgets_return_value ;
276         char const *const *p;
277         int so = 0 ; /*scanoffset*/
278
279         /*
280          * To scan the line of the table of contents (index file)
281          * we need some variables.
282          */
283         char   *name = NULL;
284         int     blkno, recno, size;
285
286         /*
287          * List of directories entries.
288          */
289         struct dir_list {
290                 char    n[128-2*sizeof(int)];   /* name of the dir */
291                 int     b, r;   /* block address */
292         }
293                *dl;
294         int     de = 0;         /* first empty list entry */
295
296         /*
297          * Format of the table of contents.
298          *      dds2index --> tar_index_file == 0
299          *      tar -Rvt  --> tar_index_file == 1
300          */
301         int     tar_index_file = 1;
302
303         /*
304          * Bug fix for tar. First record number found inside the
305          * table of contents (index file).
306          */
307         int     tar_first_recno = -1;
308
309 #ifdef DDS_TRACE
310         fprintf(stderr,"%d\n",__LINE__);
311         fprintf(stderr,"dds_cmp(%s ...)\n",*pattern_list);
312 #endif
313
314
315         /*
316          * First we need some memory.
317          */
318         dl = malloc(sizeof (*dl) * 64);
319         if (dl == NULL) {
320                 close(device);
321                 fprintf(stderr, "dds2tar: no address space available\n");
322                 exit(7);
323         }
324         memset(cur_line, 0, 1024);
325
326         /*
327          * Scan the table of conten|s (index file) until eof.
328          */
329 #ifdef DDS_TRACE
330         fprintf(stderr,"%d\n",__LINE__);
331 #endif
332         while (!feof(index_fp)) {
333                 fgets_return_value = fgets(cur_line, MAXPATHLEN<<2, index_fp);
334                 if ( fgets_return_value == NULL ) {
335                         if ( feof(index_fp) ) {
336                                 break ;
337                         } else {
338                                 perror("dds2tar");
339                                 exit(1);
340                         }
341                 }
342 #ifdef DDS_TRACE
343                 fprintf(stderr,"%d\n",__LINE__);
344                 fputs(cur_line, stderr);
345 #endif
346
347                 /*
348                  * Check for comment and empty lines.
349                  */
350                 if ((*cur_line == '#') ||
351                     (*cur_line == ' ') ||
352                     (*cur_line == '\0'))
353                         continue;
354
355                 /*
356                  * Check the line for location information.
357                  */
358 #ifdef DDS_TRACE
359         fprintf(stderr,"%d\n",__LINE__);
360 #endif
361                 if (0 == rt_loc_line())
362                         continue;
363
364                 /*
365                  * Check for the first line of the dds2index.
366                  */
367                 if ((0 == strcmp(cur_line, dds_headline)) 
368                 || (0 == strcmp(cur_line, dds_old_headline))) {
369                         tar_index_file = 0;
370                         tar_n = buf_n ;
371                         tar_bs = buf_n >> 9 ;
372                         if ( vid != HP ) dds_set_bs(tar_n);
373                         continue;
374                 }
375 #ifdef DDS_TRACE
376                 fprintf(stderr,"%d\n",__LINE__);
377 #endif
378
379                 /*
380                  * dds2index indicates eof with the string '-end-'.
381                  * This line has to be processed in the normal way.
382                  * We can stop now processing.
383                  */
384                 if ((*cur_line == '-') &&
385                     (strncmp(cur_line, "-end-", 5) == 0)) {
386 #ifdef DDS_TRACE
387                         fprintf(stderr,"%d\n",__LINE__);
388 #endif
389                         break;
390                 }
391
392                 /*
393                  * Scan the line of the index.
394                  * Note: The index file of dds2index contains the magic string
395                  *   of the tar header, witch depends on the used tar version.
396                  */
397                 if (tar_index_file == 0) {
398                         rt_line(&blkno, &recno, &size, &name);
399                 } else {
400                         /*
401                          * check for record line
402                          */
403                         if ((0 != strncmp(cur_line, "rec", 3)) &&
404                            (0 != strncmp(cur_line, "block", 5)))
405                                 continue;
406
407                         /*
408                          * cook the input line and delete all the quoted
409                          * characters.
410                          */
411                         dds_unquote(cur_line);
412
413                         /*
414                          * Set scanoffset
415                          */
416                         so = (0 == strncmp(cur_line, "block", 5))? 2 : 0 ;
417                         
418                         recno = atoi(cur_line + 4 + so );
419                         /*
420                          * tar-1.13 writes now the block number:
421                          */
422                         if ( so == 0 ){
423                                 int x ;
424                                 int n ;
425                                 char c;
426                                 if ( 2 == sscanf(
427                                         cur_line+16,"block %d%c%n",&x,&c,&n
428                                         ) && c == ':' ) {
429                                         so += n + 1 ;
430                                         recno = x ;
431 #ifdef DDS_TRACE
432                                         fprintf(stderr,"Blocks found:%d\n",x);
433 #endif
434                                 }
435                         }
436                         /*
437                          * Now we are fixing a bug of gnu tar ...
438                          * The first number should be zero, othewise we
439                          * correct all the numbers.
440                          * If tar_first_recno is .lt. zero, no recno is read
441                          * up to now.
442                          */
443                         if (tar_first_recno < 0)
444                                 tar_first_recno = recno;
445                         recno -= tar_first_recno;
446                         /*
447                          * Calculate the block number of the record.
448                          */
449 #ifdef DDS_TRACE
450                         fprintf(stderr,"file-loc(%d,%d)\n",recno,tar_fb);
451 #endif
452                         blkno = recno / tar_bs;
453                         recno -= blkno * tar_bs;
454                         if ( vid != HP ) blkno *= tar_bs ;
455 #ifdef DDS_TRACE
456                         fprintf(stderr,"file-loc(%d:%d,%d)\n",tar_bs,blkno,recno);
457 #endif
458                         blkno += tar_fb;
459 #ifdef DDS_TRACE
460                         fprintf(stderr,"file-loc(%d:%d,%d)\n",tar_bs,blkno,recno);
461 #endif
462                         if (name == NULL) {     /* calculate only once */
463                                 if ( strlen(cur_line) >= (66) ) {
464                                         name = cur_line + (65);
465                                         while ( ( name[ 0] != '\0' )
466                                          && ((     ( name[-1] != ' '  )
467                                                 || ( name[-6] != ' '  )
468                                                 || ( name[-9] != ':'  )
469                                          )&&(      ( name[-1] != ' '  )
470                                                 || ( name[-4] != ':'  )
471                                                 || ( name[-7] != ' '  )
472                                                 || ( name[-10] != '-'  )
473                                                 || ( name[-13] != '-'  )
474                                          ))
475                                         ) name++ ;
476                                         if ( name[0] == '\0' ) {
477                                                 name = cur_line + (16+so);
478                                         }
479                                 } else {
480                                         if ( strlen(cur_line) <= (16+so) ) {
481                                                 strcat(cur_line,
482                                                 "                    ");
483                                         }
484                                         name = cur_line + (16+so);
485                                 }
486 #ifdef DDS_TRACE
487                                 fprintf(stderr,"%d\n",__LINE__);
488                                 fprintf(stderr,"filename=%s\n",name);
489 #endif
490                         }
491                         while ( ( name[ 0] != '\0' )
492                          &&     ( name != ( cur_line + (16+so) ) )
493                          && ((     ( name[-1] != ' '  )
494                                 || ( name[-6] != ' '  )
495                                 || ( name[-9] != ':'  )
496                          )&&(      ( name[-1] != ' '  )
497                                 || ( name[-4] != ':'  )
498                                 || ( name[-7] != ' '  )
499                                 || ( name[-10] != '-'  )
500                                 || ( name[-13] != '-'  )
501                          ))
502                         ) name++ ;
503                 }
504 #ifdef DDS_TRACE
505                 fprintf(stderr,"%d\n",__LINE__);
506 #endif
507                 i = strlen(name) -1 ;
508                 if (name[i] == '\n') name[i] = '\0', i-- ;
509                 /*
510                  * We leave the list of directories empty on quick mode.
511                  */
512                 if (( name[i] == '/' )&&( quick_mode == 0 )) {
513                         struct dir_list *dp;
514                         for (i = 0 , dp = dl ; i < de; i++ , dp ++ ) {
515                                 if (strstr(name, dp->n) != name)
516                                         break;
517                         }
518                         strcpy(dp->n, name);
519                         dp->b = blkno;
520                         dp->r = recno;
521                         de = i + 1 ;
522                 }
523                 /*
524                  * Now we try to match one pattern with the name.
525                  */
526 #ifdef DDS_TRACE
527                 fprintf(stderr,"%d\n",__LINE__);
528                 fprintf(stderr,"scanning pattern list for '%s'\n",name);
529 #endif
530                 p = pattern_list;
531                 while (*p != NULL) {
532                         static int ll_blkno = -1 ;
533                         static int ll_recno = -1 ;
534                         static int ln_blkno = -1 ;
535                         static int ln_recno = -1 ;
536                         static const char *ll = "././@LongLink" ;
537 #ifdef DDS_TRACE
538                         fprintf(stderr," p = '%p' , *p = %p \n",p,*p);
539 #endif
540                         /*
541                          * Thanks Andreas Bagge for this trick.
542                          * I use the fnmatch function from the
543                          * source of gnu tar.
544                          */
545 #ifdef DDS_TRACE
546                         fprintf(stderr,"fnmatch '%s' for '%s'\n",name,*p);
547 #endif
548                         if (0 == fnmatch(*p, name, FNM_LEADING_DIR)) {
549                                 struct dir_list *dp;
550                                 for (i = 0, dp = dl; i < de; i++, dp++) {
551                                         char   *p = strstr(name, dp->n);
552                                         if (p == name) {
553                                                 extract(dp->n, dp->b, dp->r);
554                                         } else break ;
555                                 }
556                                 de = 0;
557                                 if ( ln_blkno >= 0 ) extract(
558                                                 "././@LongLink",
559                                                 ln_blkno,
560                                                 ln_recno);
561                                 if ( ll_blkno >= 0 ) extract(
562                                                 "././@LongLink",
563                                                 ll_blkno,
564                                                 ll_recno);
565                                 extract(name, blkno, recno);
566                                 break;
567                         }
568                         if ( 0==strncmp(ll,name,strlen(name)) ){
569                                 if ( ln_blkno < 0 )
570                                 ln_blkno = blkno , ln_recno = recno ;
571                                 else
572                                 ll_blkno = blkno , ll_recno = recno ;
573                         } else {
574                                 ln_blkno = -1 ;
575                                 ln_recno = -1 ;
576                                 ll_blkno = -1 ;
577                                 ll_recno = -1 ;
578                         }
579                         p++;
580                 }
581 #ifdef DDS_TRACE
582                 fprintf(stderr,"end of scanning pattern list\n");
583 #endif
584         }
585         /*
586          * Write an empty record to the end of the archive.
587          */
588         memset(cur_block, 0, buf_n );
589         write(1, cur_block, 512);
590         nblocks++;
591         if (verbose)
592                 fprintf(stderr, "dds2tar: %d blocks written\n", nblocks);
593 #ifdef DDS_TRACE
594         fprintf(stderr,"%d\n",__LINE__);
595         fprintf(stderr,"return of dds_cmp(...)");
596 #endif
597         return 0;
598 }