home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 May / Chip_2000-05_cd2.bin / dosutils / gtar109 / extract.c < prev    next >
C/C++ Source or Header  |  1991-01-14  |  21KB  |  741 lines

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