*** empty log message ***
[debian/tar] / src / extract.c
1 /* Extract files from a tar archive.
2    Copyright (C) 1988, 1992 Free Software Foundation
3
4 This file is part of GNU Tar.
5
6 GNU Tar is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Tar is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Tar; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 /*
21  * Extract files from a tar archive.
22  *
23  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
24  */
25
26 #include <stdio.h>
27 #include <errno.h>
28 #ifndef STDC_HEADERS
29 extern int errno;
30 #endif
31 #include <sys/types.h>
32 #include <time.h>
33 time_t time();
34
35 #ifdef BSD42
36 #include <sys/file.h>
37 #else
38 #ifndef V7
39 #include <fcntl.h>
40 #endif
41 #endif
42
43 #ifdef NO_OPEN3
44 /* We need the #define's even though we don't use them. */
45 #include "open3.h"
46 #endif
47
48 #ifdef EMUL_OPEN3
49 /* Simulated 3-argument open for systems that don't have it */
50 #include "open3.h"
51 #endif
52
53 #include "tar.h"
54 #include "port.h"
55
56 #if defined(_POSIX_VERSION)
57 #include <utime.h>
58 #endif
59
60 extern FILE *msg_file;
61
62 extern union record *head;              /* Points to current tape header */
63 extern struct stat hstat;               /* Stat struct corresponding */
64 extern int head_standard;               /* Tape header is in ANSI format */
65
66 extern char *save_name;
67 extern long save_totsize;
68 extern long save_sizeleft;
69
70 int confirm();
71 void decode_header();
72 void extract_mangle();
73 void extract_sparse_file();
74 long from_oct();
75 void gnu_restore();
76 extern void print_header();
77 extern void skip_file();
78 extern void skip_extended_headers();
79 extern void pr_mkdir();
80 void saverec();
81
82 int make_dirs();                        /* Makes required directories */
83
84 static time_t now = 0;                  /* Current time */
85 static we_are_root = 0;                 /* True if our effective uid == 0 */
86 static int notumask = ~0;               /* Masks out bits user doesn't want */
87
88 /*
89  * "Scratch" space to store the information about a sparse file before
90  * writing the info into the header or extended header
91  */
92 /*struct sp_array       *sparsearray;*/
93
94 /* number of elts storable in the sparsearray */
95 /*int   sp_array_size = 10;*/
96
97 struct saved_dir_info
98 {
99   char *path;
100   int mode;
101   int atime;
102   int mtime;
103   struct saved_dir_info *next;
104 };
105   
106 struct saved_dir_info *saved_dir_info_head;
107
108 /*
109  * Set up to extract files.
110  */
111 void
112 extr_init()
113 {
114         int ourmask;
115
116         now = time((time_t *)0);
117         if (geteuid() == 0)
118                 we_are_root = 1;
119
120         /*
121          * We need to know our umask.  But if f_use_protection is set,
122          * leave our kernel umask at 0, and our "notumask" at ~0.
123          */
124         ourmask = umask(0);             /* Read it */
125         if (!f_use_protection) {
126                 (void) umask (ourmask); /* Set it back how it was */
127                 notumask = ~ourmask;    /* Make umask override permissions */
128         }
129 }
130
131
132 /*
133  * Extract a file from the archive.
134  */
135 void
136 extract_archive()
137 {
138         register char *data;
139         int fd, check, namelen, written, openflag;
140         long size;
141         time_t acc_upd_times[2];
142         register int skipcrud;
143         register int i;
144 /*      int sparse_ind = 0;*/
145         union record *exhdr;    
146         struct saved_dir_info *tmp;
147 /*      int end_nulls; */
148         char **longp;
149         char *bp;
150         
151         saverec(&head);                 /* Make sure it sticks around */
152         userec(head);                   /* And go past it in the archive */
153         decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */
154
155         if(f_confirm && !confirm("extract",current_file_name)) {
156                 if (head->header.isextended)
157                         skip_extended_headers();
158                 skip_file((long)hstat.st_size);
159                 saverec((union record **)0);
160                 return;
161         }
162
163         /* Print the record from 'head' and 'hstat' */
164         if (f_verbose)
165                 print_header();
166
167         /*
168          * Check for fully specified pathnames and other atrocities.
169          *
170          * Note, we can't just make a pointer to the new file name,
171          * since saverec() might move the header and adjust "head".
172          * We have to start from "head" every time we want to touch
173          * the header record.
174          */
175         skipcrud = 0;
176         while (!f_absolute_paths
177                && '/' == current_file_name[skipcrud]) {
178                 static int warned_once = 0;
179
180                 skipcrud++;     /* Force relative path */
181                 if (!warned_once++) {
182                         msg("Removing leading / from absolute path names in the archive.");
183                  }
184          }
185
186          switch (head->header.linkflag) {
187
188          default:
189                  msg("Unknown file type '%c' for %s, extracted as normal file",
190                          head->header.linkflag, skipcrud+current_file_name);
191                  /* FALL THRU */
192
193          /* 
194           * JK - What we want to do if the file is sparse is loop through
195           * the array of sparse structures in the header and read in
196           * and translate the character strings representing  1) the offset
197           * at which to write and 2) how many bytes to write into numbers,
198           * which we store into the scratch array, "sparsearray".  This
199           * array makes our life easier the same way it did in creating
200           * the tar file that had to deal with a sparse file.
201           *
202           * After we read in the first five (at most) sparse structures,
203           * we check to see if the file has an extended header, i.e., 
204           * if more sparse structures are needed to describe the contents
205           * of the new file.  If so, we read in the extended headers
206           * and continue to store their contents into the sparsearray.
207           */
208          case LF_SPARSE:
209                  sp_array_size = 10;
210                  sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array));
211                  for (i = 0; i < SPARSE_IN_HDR; i++) {
212                          sparsearray[i].offset = 
213                                  from_oct(1+12, head->header.sp[i].offset);
214                          sparsearray[i].numbytes = 
215                                  from_oct(1+12, head->header.sp[i].numbytes);
216                          if (!sparsearray[i].numbytes)
217                                  break;
218                  }
219
220  /*             end_nulls = from_oct(1+12, head->header.ending_blanks);*/
221
222                  if (head->header.isextended) {
223                          /* read in the list of extended headers
224                             and translate them into the sparsearray 
225                             as before */
226
227                          /* static */ int ind = SPARSE_IN_HDR;
228
229                          for (;;) {
230
231                                  exhdr = findrec();
232                                  for (i = 0; i < SPARSE_EXT_HDR; i++) {
233
234                                          if (i+ind > sp_array_size-1) {
235                                          /*
236                                           * realloc the scratch area
237                                           * since we've run out of room --
238                                           */
239                                                  sparsearray = (struct sp_array *) 
240                                                                  realloc(sparsearray,
241                                                                  2 * sp_array_size * (sizeof(struct sp_array)));
242                                                  sp_array_size *= 2;
243                                          }
244                                          if (!exhdr->ext_hdr.sp[i].numbytes)
245                                                  break;
246                                          sparsearray[i+ind].offset = 
247                                                  from_oct(1+12, exhdr->ext_hdr.sp[i].offset);
248                                          sparsearray[i+ind].numbytes = 
249                                                  from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes);
250                                  }
251                                  if (!exhdr->ext_hdr.isextended) 
252                                          break;
253                                  else {
254                                          ind += SPARSE_EXT_HDR;
255                                          userec(exhdr);
256                                  }
257                          }
258                          userec(exhdr);
259                  }
260
261                  /* FALL THRU */
262          case LF_OLDNORMAL:
263          case LF_NORMAL:
264          case LF_CONTIG:
265                  /*
266                   * Appears to be a file.
267                   * See if it's really a directory.
268                   */
269                  namelen = strlen(skipcrud+current_file_name)-1;
270                  if (current_file_name[skipcrud+namelen] == '/')
271                          goto really_dir;
272
273                  /* FIXME, deal with protection issues */
274          again_file:
275                  openflag = (f_keep?
276                          O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_EXCL:
277                          O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC)
278                          | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND);                       
279                          /*
280                           * JK - The last | is a kludge to solve the problem
281                           * the O_APPEND flag  causes with files we are
282                           * trying to make sparse:  when a file is opened
283                           * with O_APPEND, it writes  to the last place
284                           * that something was written, thereby ignoring
285                           * any lseeks that we have done.  We add this
286                           * extra condition to make it able to lseek when
287                           * a file is sparse, i.e., we don't open the new
288                           * file with this flag.  (Grump -- this bug caused
289                           * me to waste a good deal of time, I might add)
290                           */
291
292                  if(f_exstdout) {
293                          fd = 1;
294                          goto extract_file;
295                  }
296 #ifdef O_CTG
297                  /*
298                   * Contiguous files (on the Masscomp) have to specify
299                   * the size in the open call that creates them.
300                   */
301                  if (head->header.linkflag == LF_CONTIG)
302                          fd = open((longname ? longname : head->header.name)
303                                    + skipcrud,
304                                    openflag | O_CTG,
305                                    hstat.st_mode, hstat.st_size);
306                  else
307 #endif
308                  {
309 #ifdef NO_OPEN3
310                          /*
311                           * On raw V7 we won't let them specify -k (f_keep), but
312                           * we just bull ahead and create the files.
313                           */
314                          fd = creat((longname
315                                      ? longname
316                                      : head->header.name) + skipcrud, 
317                                     hstat.st_mode);
318 #else
319                          /*
320                           * With 3-arg open(), we can do this up right.
321                           */
322                          fd = open(skipcrud + current_file_name,
323                                    openflag, hstat.st_mode);
324 #endif
325                  }
326
327                  if (fd < 0) {
328                          if (make_dirs(skipcrud + current_file_name))
329                                  goto again_file;
330                          msg_perror("Could not create file %s",
331                                     skipcrud + current_file_name);
332                          if (head->header.isextended)
333                                  skip_extended_headers();
334                          skip_file((long)hstat.st_size);
335                          goto quit;
336                  }
337
338          extract_file:
339                  if (head->header.linkflag == LF_SPARSE) {
340                          char   *name;
341                          int    namelen;
342
343                          /*
344                           * Kludge alert.  NAME is assigned to header.name
345                           * because during the extraction, the space that
346                           * contains the header will get scribbled on, and
347                           * the name will get munged, so any error messages
348                           * that happen to contain the filename will look
349                           * REAL interesting unless we do this.
350                           */
351                          namelen = strlen(skipcrud + current_file_name);
352                          name = (char *) malloc((sizeof(char)) * namelen);
353                          bcopy(skipcrud+current_file_name, name, namelen);
354                          size = hstat.st_size;
355                          extract_sparse_file(fd, &size, hstat.st_size, name);
356                  }                      
357                  else           
358                    for (size = hstat.st_size;
359                         size > 0;
360                         size -= written) {
361
362  /*                     long    offset,
363                                  numbytes;*/
364
365                          if(f_multivol) {
366                                  save_name=current_file_name;
367                                  save_totsize=hstat.st_size;
368                                  save_sizeleft=size;
369                          }
370
371                          /*
372                           * Locate data, determine max length
373                           * writeable, write it, record that
374                           * we have used the data, then check
375                           * if the write worked.
376                           */
377                          data = findrec()->charptr;
378                          if (data == NULL) {    /* Check it... */
379                                  msg("Unexpected EOF on archive file");
380                                  break;
381                          }
382                          /*
383                           * JK - If the file is sparse, use the sparsearray
384                           * that we created before to lseek into the new
385                           * file the proper amount, and to see how many
386                           * bytes we want to write at that position.
387                           */
388  /*                     if (head->header.linkflag == LF_SPARSE) {
389                                  off_t pos;
390
391                                  pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0);
392                                  printf("%d at %d\n", (int) pos, sparse_ind);
393                                  written = sparsearray[sparse_ind++].numbytes;
394                          } else*/
395                          written = endofrecs()->charptr - data;
396                          if (written > size)
397                                  written = size;
398                          errno = 0;
399                          check = write(fd, data, written);
400                          /*
401                           * The following is in violation of strict
402                           * typing, since the arg to userec
403                           * should be a struct rec *.  FIXME.
404                           */
405                          userec((union record *)(data + written - 1));
406                          if (check == written) continue;
407                          /*
408                           * Error in writing to file.
409                           * Print it, skip to next file in archive.
410                           */
411                          if(check<0)
412                                  msg_perror("couldn't write to file %s",
413                                             skipcrud + current_file_name);
414                          else
415                            msg("could only write %d of %d bytes to file %s",
416                                written,check,skipcrud + current_file_name);
417                          skip_file((long)(size - written));
418                          break; /* Still do the close, mod time, chmod, etc */
419                  }
420
421                  if(f_multivol)
422                          save_name = 0;
423
424                          /* If writing to stdout, don't try to do anything
425                             to the filename; it doesn't exist, or we don't
426                             want to touch it anyway */
427                  if(f_exstdout)
428                          break;
429
430  /*             if (head->header.isextended) {
431                          register union record *exhdr;
432                          register int i;
433
434                          for (i = 0; i < 21; i++) {
435                                  long offset;
436
437                                  if (!exhdr->ext_hdr.sp[i].numbytes)
438                                          break;
439                                  offset = from_oct(1+12,
440                                                  exhdr->ext_hdr.sp[i].offset);
441                                  written = from_oct(1+12,
442                                                  exhdr->ext_hdr.sp[i].numbytes);
443                                  lseek(fd, offset, 0);
444                                  check = write(fd, data, written);
445                                  if (check == written) continue;
446
447                          }
448
449
450                  }*/
451                  check = close(fd);
452                  if (check < 0) {
453                          msg_perror("Error while closing %s",
454                                     skipcrud + current_file_name);
455                  }
456
457
458          set_filestat:
459
460                  /*
461                   * If we are root, set the owner and group of the extracted
462                   * file.  This does what is wanted both on real Unix and on
463                   * System V.  If we are running as a user, we extract as that
464                   * user; if running as root, we extract as the original owner.
465                   */
466                  if (we_are_root || f_do_chown) {
467                          if (chown(skipcrud + current_file_name,
468                                    hstat.st_uid, hstat.st_gid) < 0) {
469                            msg_perror("cannot chown file %s to uid %d gid %d",
470                                       skipcrud + current_file_name,
471                                       hstat.st_uid,hstat.st_gid);
472                          }
473                  }
474
475                  /*
476                   * Set the modified time of the file.
477                   * 
478                   * Note that we set the accessed time to "now", which
479                   * is really "the time we started extracting files".
480                   * unless f_gnudump is used, in which case .st_atime is used
481                   */
482                  if (!f_modified) {
483                          /* fixme if f_gnudump should set ctime too, but how? */
484                         if(f_gnudump)
485                                 acc_upd_times[0]=hstat.st_atime;
486                         else acc_upd_times[0] = now;             /* Accessed now */
487                         acc_upd_times[1] = hstat.st_mtime; /* Mod'd */
488                         if (utime(skipcrud + current_file_name,
489                                   acc_upd_times) < 0) {
490                           msg_perror("couldn't change access and modification times of %s",skipcrud + current_file_name);
491                         }
492                 }
493                 /* We do the utime before the chmod because some versions of
494                    utime are broken and trash the modes of the file.  Since
495                    we then change the mode anyway, we don't care. . . */
496
497                 /*
498                  * If '-k' is not set, open() or creat() could have saved
499                  * the permission bits from a previously created file,
500                  * ignoring the ones we specified.
501                  * Even if -k is set, if the file has abnormal
502                  * mode bits, we must chmod since writing or chown() has
503                  * probably reset them.
504                  *
505                  * If -k is set, we know *we* created this file, so the mode
506                  * bits were set by our open().   If the file is "normal", we
507                  * skip the chmod.  This works because we did umask(0) if -p
508                  * is set, so umask will have left the specified mode alone.
509                  */
510                 if ((!f_keep)
511                     || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
512                   if (chmod(skipcrud + current_file_name,
513                             notumask & (int)hstat.st_mode) < 0) {
514                     msg_perror("cannot change mode of file %s to %ld",
515                                skipcrud + current_file_name,
516                                notumask & (int)hstat.st_mode);
517                         }
518                 }
519
520         quit:
521                 break;
522
523         case LF_LINK:
524         again_link:
525         {
526                 struct stat st1,st2;
527
528                 check = link (current_link_name, skipcrud + current_file_name);
529
530                 if (check == 0)
531                         break;
532                 if (make_dirs(skipcrud + current_file_name))
533                         goto again_link;
534                 if(f_gnudump && errno==EEXIST)
535                         break;
536                 if(stat(current_link_name, &st1) == 0
537                    && stat(current_file_name + skipcrud, &st2)==0
538                    && st1.st_dev==st2.st_dev
539                    && st1.st_ino==st2.st_ino)
540                         break;
541                 msg_perror("Could not link %s to %s",
542                            skipcrud + current_file_name,
543                            current_link_name);
544         }
545                 break;
546
547 #ifdef S_ISLNK
548         case LF_SYMLINK:
549         again_symlink:
550                  check = symlink(current_link_name,
551                                  skipcrud + current_file_name);
552                 /* FIXME, don't worry uid, gid, etc... */
553                 if (check == 0)
554                         break;
555                 if (make_dirs(current_file_name + skipcrud))
556                         goto again_symlink;
557                 msg_perror("Could not create symlink to %s",
558                            current_link_name);
559                 break;
560 #endif
561
562 #ifdef S_IFCHR
563         case LF_CHR:
564                 hstat.st_mode |= S_IFCHR;
565                 goto make_node;
566 #endif
567
568 #ifdef S_IFBLK
569         case LF_BLK:
570                 hstat.st_mode |= S_IFBLK;
571 #endif
572 #if defined(S_IFCHR) || defined(S_IFBLK)
573         make_node:
574                 check = mknod(current_file_name + skipcrud,
575                               (int) hstat.st_mode, (int) hstat.st_rdev);
576                 if (check != 0) {
577                         if (make_dirs(skipcrud + current_file_name))
578                                 goto make_node;
579                         msg_perror("Could not make %s",
580                                    current_file_name + skipcrud);
581                         break;
582                 };
583                 goto set_filestat;
584 #endif
585
586 #ifdef S_ISFIFO
587         /* If local system doesn't support FIFOs, use default case */
588         case LF_FIFO:
589         make_fifo:
590                 check = mkfifo(current_file_name + skipcrud,
591                                (int) hstat.st_mode);
592                 if (check != 0) {
593                   if (make_dirs(current_file_name + skipcrud))
594                     goto make_fifo;
595                   msg_perror("Could not make %s",
596                              skipcrud + current_file_name);
597                   break;
598                 };
599                  goto set_filestat;
600 #endif
601
602         case LF_DIR:
603         case LF_DUMPDIR:
604                  namelen = strlen(current_file_name) + skipcrud - 1;
605         really_dir:
606                 /* Check for trailing /, and zap as many as we find. */
607                 while (namelen
608                        && current_file_name[skipcrud+namelen] == '/')
609                   current_file_name[skipcrud+namelen--] = '\0';
610                 if(f_gnudump) {         /* Read the entry and delete files
611                                            that aren't listed in the archive */
612                         gnu_restore(skipcrud);
613                 
614                 } else if(head->header.linkflag==LF_DUMPDIR)
615                         skip_file((long)(hstat.st_size));
616
617         
618         again_dir:
619                 check = mkdir(skipcrud+current_file_name,
620                               (we_are_root ? 0 : 0300) | (int)hstat.st_mode);
621                 if (check != 0) {
622                         struct stat st1;
623
624                         if (make_dirs(skipcrud+current_file_name))
625                                 goto again_dir;
626                         /* If we're trying to create '.', let it be. */
627                         if (current_file_name[skipcrud+namelen] == '.' && 
628                             (namelen==0 ||
629                              current_file_name[skipcrud+namelen-1]=='/'))
630                                 goto check_perms;
631                         if(   errno==EEXIST
632                            && stat(skipcrud+current_file_name,&st1)==0
633                            && (S_ISDIR(st1.st_mode)))
634                                 break;
635                         msg_perror("Could not create directory %s",skipcrud+current_file_name);
636                         break;
637                 }
638                 
639         check_perms:
640                 if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode)) {
641                         hstat.st_mode |= 0300;
642                         msg("Added write and execute permission to directory %s",
643                           skipcrud+current_file_name);
644                 }
645
646                 if (f_modified)
647                   goto set_filestat;
648                 tmp = (struct saved_dir_info *) malloc (sizeof (struct saved_dir_info));
649                 tmp->path = malloc (strlen (skipcrud + current_file_name) + 1);
650                 strcpy (tmp->path, skipcrud + current_file_name);
651                 tmp->mode = hstat.st_mode;
652                 tmp->atime = hstat.st_atime;
653                 tmp->mtime = hstat.st_mtime;
654                 tmp->next = saved_dir_info_head;
655                 saved_dir_info_head = tmp;
656         case LF_VOLHDR:
657                 if(f_verbose) {
658                         printf("Reading %s\n", current_file_name);
659                 }
660                 break;
661
662         case LF_NAMES:
663                 extract_mangle(head);
664                 break;
665
666         case LF_MULTIVOL:
667                 msg("Can't extract '%s'--file is continued from another volume\n",current_file_name);
668                 skip_file((long)hstat.st_size);
669                 break;
670
671         case LF_LONGNAME:
672         case LF_LONGLINK:
673                  msg ("Visible long name error\n");
674                  skip_file ((long)hstat.st_size);
675                  break;
676         }       
677         
678         /* We don't need to save it any longer. */
679         saverec((union record **) 0);   /* Unsave it */
680 }
681
682 /*
683  * After a file/link/symlink/dir creation has failed, see if
684  * it's because some required directory was not present, and if
685  * so, create all required dirs.
686  */
687 int
688 make_dirs(pathname)
689         char *pathname;
690 {
691         char *p;                        /* Points into path */
692         int madeone = 0;                /* Did we do anything yet? */
693         int save_errno = errno;         /* Remember caller's errno */
694         int check;
695
696         if (errno != ENOENT)
697                 return 0;               /* Not our problem */
698
699         for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
700                 /* Avoid mkdir of empty string, if leading or double '/' */
701                 if (p == pathname || p[-1] == '/')
702                         continue;
703                 /* Avoid mkdir where last part of path is '.' */
704                 if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
705                         continue;
706                 *p = 0;                         /* Truncate the path there */
707                 check = mkdir (pathname, 0777); /* Try to create it as a dir */
708                 if (check == 0) {
709                         /* Fix ownership */
710                         if (we_are_root) {
711                                 if (chown(pathname, hstat.st_uid,
712                                           hstat.st_gid) < 0) {
713                                         msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid);
714                                 }
715                         }
716                         pr_mkdir(pathname, p-pathname, notumask&0777);
717                         madeone++;              /* Remember if we made one */
718                         *p = '/';
719                         continue;
720                 }
721                 *p = '/';
722                 if (errno == EEXIST)            /* Directory already exists */
723                         continue;
724                 /*
725                  * Some other error in the mkdir.  We return to the caller.
726                  */
727                 break;
728         }
729
730         errno = save_errno;             /* Restore caller's errno */
731         return madeone;                 /* Tell them to retry if we made one */
732 }
733
734 void
735 extract_sparse_file(fd, sizeleft, totalsize, name)
736         int     fd;
737         long    *sizeleft,
738                 totalsize;
739         char    *name;
740 {               
741 /*      register char   *data;*/
742         union record    *datarec;
743         int     sparse_ind = 0;
744         int     written,
745                 count;
746         
747         /* assuming sizeleft is initially totalsize */
748
749
750         while (*sizeleft > 0) {
751                 datarec = findrec();
752                 if (datarec == NULL) {
753                         msg("Unexpected EOF on archive file");
754                         return;
755                 }
756                 lseek(fd, sparsearray[sparse_ind].offset, 0);
757                 written = sparsearray[sparse_ind++].numbytes;
758                 while (written > RECORDSIZE) {
759                         count = write(fd, datarec->charptr, RECORDSIZE);
760                         if (count < 0) 
761                                 msg_perror("couldn't write to file %s", name);
762                         written -= count;
763                         *sizeleft -= count;
764                         userec(datarec);
765                         datarec = findrec();
766                 }
767
768                 count = write(fd, datarec->charptr, written);
769                 
770                 if (count < 0) {
771                         msg_perror("couldn't write to file %s", name);
772                 } else if (count != written) {
773                         msg("could only write %d of %d bytes to file %s", totalsize - *sizeleft, totalsize, name);
774                         skip_file((long) (*sizeleft));
775                 }
776
777                 written -= count;
778                 *sizeleft -= count;             
779                 userec(datarec);
780         }
781         free(sparsearray);
782 /*      if (end_nulls) {
783                 register int i;
784
785                 printf("%d\n", (int) end_nulls);
786                 for (i = 0; i < end_nulls; i++)
787                         write(fd, "\000", 1);
788         }*/
789         userec(datarec);
790 }
791
792 /* Set back the utime and mode for all the extracted directories. */
793 void restore_saved_dir_info ()
794 {
795   time_t acc_upd_times[2];
796   struct saved_dir_info *tmp;
797
798   while (saved_dir_info_head != NULL)
799     {
800       /* fixme if f_gnudump should set ctime too, but how? */
801       if(f_gnudump)
802         acc_upd_times[0]=saved_dir_info_head -> atime;
803       else acc_upd_times[0] = now; /* Accessed now */
804       acc_upd_times[1] = saved_dir_info_head -> mtime; /* Mod'd */
805       if (utime(saved_dir_info_head -> path, acc_upd_times) < 0) {
806         msg_perror("couldn't change access and modification times of %s",
807                    saved_dir_info_head -> path);
808       }
809       if ((!f_keep) || (saved_dir_info_head -> mode & (S_ISUID|S_ISGID|S_ISVTX)))
810         {
811           if (chmod(saved_dir_info_head -> path,
812                     notumask & saved_dir_info_head -> mode) < 0) {
813             msg_perror("cannot change mode of file %s to %ld",
814                        saved_dir_info_head -> path,
815                        notumask & saved_dir_info_head -> mode);
816           }
817         }
818       saved_dir_info_head = saved_dir_info_head -> next;
819     }
820 }