home *** CD-ROM | disk | FTP | other *** search
/ Super Net 1 / SUPERNET_1.iso / PC / OTROS / MSDOS / WATTCP / PISA_TAR.TAR / tar / extract.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-10  |  8.2 KB  |  325 lines

  1. /*
  2.  * Extract files from a tar archive.
  3.  *
  4.  * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  5.  *
  6.  * @(#) extract.c 1.17 86/10/29 Public Domain - gnu
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <fcntl.h>
  14.  
  15. /* extern int errno;            /* From libc.a */
  16. extern char *index();            /* From libc.a or port.c */
  17.  
  18. #ifndef    O_NDELAY
  19. #define O_NDELAY 0
  20. #endif
  21.  
  22. #include <tar.h>
  23.  
  24. extern union record *head;        /* Points to current tape header */
  25. extern struct stat hstat[1];        /* Stat struct corresponding */
  26.  
  27. extern void print_header();
  28. extern void skip_file();
  29. extern void pr_mkdir();
  30.  
  31. int make_dirs();            /* Makes required directories */
  32.  
  33. time_t now = 0;                /* Current time */
  34.  
  35. /*
  36.  * Extract a file from the archive.
  37.  */
  38. void
  39. extract_archive()
  40. {
  41.     register char *data;
  42.     int fd, check, namelen, written;
  43.     long size;
  44.     time_t acc_upd_times[2];
  45.     int    standard;        /* Is header standard? */
  46.  
  47.     saverec(&head);            /* Make sure it sticks around */
  48.     userec(head);            /* And go past it in the archive */
  49.     decode_header(head, hstat, &standard, 1);    /* Snarf fields */
  50.  
  51.     /* Print the record from 'head' and 'hstat' */
  52.     if (f_verbose)
  53.         print_header();
  54.  
  55.     switch (head->header.linkflag) {
  56.  
  57.     default:
  58.         annofile(stderr, tar);
  59.         fprintf(stderr, "Unknown file type %d for %s\n",
  60.             head->header.linkflag, head->header.name);
  61.         /* FALL THRU */
  62.  
  63.     case LF_OLDNORMAL:
  64.     case LF_NORMAL:
  65.         /*
  66.          * Appears to be a file.
  67.          * See if it's really a directory.
  68.          */
  69.         namelen = strlen(head->header.name)-1;
  70.         if (head->header.name[namelen] == '/')
  71.             goto really_dir;
  72.  
  73.         /* FIXME, deal with protection issues */
  74.         /* FIXME, f_keep doesn't work on V7, st_mode loses too */
  75.     again_file:
  76.         fd = open(head->header.name,
  77.             f_keep?
  78. #ifdef    MSDOS
  79.             O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL|O_BINARY:
  80.             O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC|O_BINARY,
  81. #else
  82.             O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_EXCL:
  83.             O_NDELAY|O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,
  84. #endif
  85.             hstat->st_mode);
  86.         if (fd < 0) {
  87.             if (make_dirs(head->header.name))
  88.                 goto again_file;
  89.             annofile(stderr, tar);
  90.             fprintf(stderr, "Could not make file ");
  91.             perror(head->header.name);
  92.             skip_file((long)hstat->st_size);
  93.             goto quit;
  94.         }
  95.  
  96.         for (size = hstat->st_size;
  97.              size > 0;
  98.              size -= written) {
  99.             /*
  100.              * Locate data, determine max length
  101.              * writeable, write it, record that
  102.              * we have used the data, then check
  103.              * if the write worked.
  104.              */
  105.             data = findrec()->charptr;
  106.             written = endofrecs()->charptr - data;
  107.             if (written > size) written = size;
  108.             errno = 0;
  109.             check = write(fd, data, written);
  110.             /*
  111.              * The following is in violation of strict
  112.              * typing, since the arg to userec
  113.              * should be a struct rec *.  FIXME.
  114.              */
  115.             userec(data + written - 1);
  116.             if (check == written) continue;
  117.             /*
  118.              * Error in writing to file.
  119.              * Print it, skip to next file in archive.
  120.              */
  121.             annofile(stderr, tar);
  122.             fprintf(stderr,
  123.     "Tried to write %d bytes to file, could only write %d:\n",
  124.                 written, check);
  125.             perror(head->header.name);
  126.             (void) close(fd);
  127.             skip_file((long)(size - written));
  128.             goto quit;
  129.         }
  130.  
  131.         check = close(fd);
  132.         if (check < 0) {
  133.             annofile(stderr, tar);
  134.             fprintf(stderr, "Error while closing ");
  135.             perror(head->header.name);
  136.         }
  137.         
  138.         /* FIXME, deal with uid/gid/mtimes/suid */
  139.  
  140.         /*
  141.          * Set the modified time of the file.
  142.          * 
  143.          * Note that we set the accessed time to "now", which
  144.          * is really "the time we started extracting files".
  145.          */
  146.         if (!f_modified) {
  147.             if (!now)
  148.                 now = time((time_t *)0); /* Just do it once */
  149.             acc_upd_times[0] = now;             /* Accessed now */
  150.             acc_upd_times[1] = hstat->st_mtime; /* Mod'd */
  151.             if (utime(head->header.name, acc_upd_times) < 0) {
  152.                 annofile(stderr, tar);
  153.                 perror(head->header.name);
  154.             }
  155.         }
  156.  
  157.         /*
  158.          * If '-p' is not set, OR if the file has pretty normal
  159.          * mode bits, we can skip the chmod and save a sys call.
  160.          * This works because we did umask(0) if -p is set, so
  161.          * the open() that created the file will have set the modes
  162.          * properly.  
  163.          * FIXME: I don't know what open() does w/UID/GID/SVTX bits.
  164.          * However, if we've done a chown(), they got reset.
  165.          */
  166. #ifdef    S_ISUID
  167.         if (f_use_protection
  168.             && (hstat->st_mode & (S_ISUID|S_ISGID|S_ISVTX))) {
  169.             if (chmod(head->header.name, (int)hstat->st_mode) < 0) {
  170.                 annofile(stderr, tar);
  171.                 perror(head->header.name);
  172.             }
  173.         }
  174. #endif
  175.  
  176.     quit:
  177.         break;
  178.  
  179.     case LF_LINK:
  180. #ifdef    MSDOS
  181.         fprintf(stderr, "Link %s to %s ignored\n",
  182.             head->header.linkname, head->header.name);
  183. #else
  184.     again_link:
  185.         check = link (head->header.linkname,
  186.                   head->header.name);
  187.         /* FIXME, don't worry uid, gid, etc... */
  188.         if (check == 0)
  189.             break;
  190.         if (make_dirs(head->header.linkname))
  191.             goto again_link;
  192.         annofile(stderr, tar);
  193.         fprintf(stderr, "Could not link %s to ",
  194.             head->header.name);
  195.         perror(head->header.linkname);
  196. #endif
  197.         break;
  198.  
  199. #ifdef S_IFLNK
  200.     case LF_SYMLINK:
  201.     again_symlink:
  202.         check = symlink(head->header.linkname,
  203.                     head->header.name);
  204.         /* FIXME, don't worry uid, gid, etc... */
  205.         if (check == 0)
  206.             break;
  207.         if (make_dirs(head->header.linkname))
  208.             goto again_symlink;
  209.         annofile(stderr, tar);
  210.         fprintf(stderr, "Could not create symlink ");
  211.         perror(head->header.linkname);
  212.         break;
  213. #endif
  214.  
  215. #ifdef S_IFCHR
  216.     case LF_CHR:
  217.         hstat->st_mode |= S_IFCHR;
  218.         goto make_node;
  219. #endif
  220.  
  221. #ifdef S_IFBLK
  222.     case LF_BLK:
  223.         hstat->st_mode |= S_IFBLK;
  224. #endif
  225.     make_node:
  226. #ifdef    MSDOS
  227.         fprintf(stderr, "Node %s type %x ignored\n",
  228.             head->header.name, hstat->st_mode);
  229. #else
  230.         check = mknod(head->header.name, (int) hstat->st_mode,
  231. #ifdef    OS
  232.             major(hstat->st_dev), minor(hstat->st_dev));
  233. #else
  234.             (int) hstat->st_dev);
  235. #endif
  236.         if (check != 0) {
  237.             if (make_dirs(head->header.name))
  238.                 goto make_node;
  239.             annofile(stderr, tar);
  240.             fprintf(stderr, "Could not make special file ");
  241.             perror(head->header.name);
  242.             break;
  243.         };
  244. #endif
  245.         break;
  246.  
  247.     case LF_DIR:
  248.         /* Check for trailing / */
  249.         namelen = strlen(head->header.name)-1;
  250.     really_dir:
  251.         while (namelen && head->header.name[namelen] == '/')
  252.             head->header.name[namelen--] = '\0';    /* Zap / */
  253.         
  254.         /* FIXME, deal with umask */
  255.     again_dir:
  256.         check = mkdir(head->header.name, (int)hstat->st_mode);
  257.         if (check != 0) {
  258.             if (make_dirs(head->header.name))
  259.                 goto again_dir;
  260.             annofile(stderr, tar);
  261.             fprintf(stderr, "Could not make directory ");
  262.             perror(head->header.name);
  263.             break;
  264.         }
  265.         
  266.         /* FIXME, deal with uid/gid */
  267.         /* FIXME, Remember timestamps for after files created? */
  268.         break;
  269.  
  270.     case LF_FIFO:
  271.         abort();    /* FIXME */
  272.         break;
  273.  
  274.     }
  275.  
  276.     /* We don't need to save it any longer. */
  277.     saverec((union record **) 0);    /* Unsave it */
  278. }
  279.  
  280. /*
  281.  * After a file/link/symlink/dir creation has failed, see if
  282.  * it's because some required directory was not present, and if
  283.  * so, create all required dirs.
  284.  */
  285. int
  286. make_dirs(pathname)
  287.     char *pathname;
  288. {
  289.     char *p;            /* Points into path */
  290.     int madeone = 0;        /* Did we do anything yet? */
  291.     int save_errno = errno;        /* Remember caller's errno */
  292.     int check;
  293.  
  294.     if (errno != ENOENT)
  295.         return 0;        /* Not our problem */
  296.  
  297.     for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) {
  298.         /* Avoid mkdir of empty string, if leading or double '/' */
  299.         if (p == pathname || p[-1] == '/')
  300.             continue;
  301.         /* Avoid mkdir where last part of path is '.' */
  302.         if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
  303.             continue;
  304.         *p = 0;                /* Truncate the path there */
  305.         check = mkdir (pathname, 0777);    /* Try to create it as a dir */
  306.         *p = '/';
  307.         if (check == 0) {
  308.             /* FIXME chown, chgrp it same as file being created */
  309.             /* FIXME, show mode as modified by current umask */
  310.             pr_mkdir(pathname, p-pathname, 0777);
  311.             madeone++;        /* Remember if we made one */
  312.             continue;
  313.         }
  314.         if (errno == EEXIST)        /* Directory already exists */
  315.             continue;
  316.         /*
  317.          * Some other error in the mkdir.  We return to the caller.
  318.          */
  319.         break;
  320.     }
  321.  
  322.     errno = save_errno;        /* Restore caller's errno */
  323.     return madeone;            /* Tell them to retry if we made one */
  324. }
  325.