home *** CD-ROM | disk | FTP | other *** search
/ Fish 'n' More 2 / fishmore-publicdomainlibraryvol.ii1991xetec.iso / fish / misc_utils / tar / src / buffer.c < prev    next >
C/C++ Source or Header  |  1991-01-24  |  16KB  |  696 lines

  1. /*
  2.  * Buffer management for public domain tar.
  3.  *
  4.  * Written by John Gilmore, ihnp4!hoptoad!gnu, on 25 August 1985.
  5.  *
  6.  * @(#) buffer.c 1.28 11/6/87 Public Domain - gnu
  7.  */
  8.  
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <sys/types.h>        /* For non-Berkeley systems */
  12. #include <sys/stat.h>
  13. #include <signal.h>
  14.  
  15. #if defined(MSDOS) || defined (AMIGA)
  16. # include <fcntl.h>
  17. #else
  18. # ifdef XENIX
  19. #  include <sys/inode.h>
  20. # endif
  21. # include <sys/file.h>
  22. #endif
  23.  
  24. #include "tar.h"
  25. #include "port.h"
  26.  
  27. #define    STDIN    0        /* Standard input  file descriptor */
  28. #define    STDOUT    1        /* Standard output file descriptor */
  29.  
  30. #define    PREAD    0        /* Read  file descriptor from pipe() */
  31. #define    PWRITE    1        /* Write file descriptor from pipe() */
  32.  
  33. extern char    *valloc();
  34. extern char    *index(), *strcat();
  35.  
  36. /*
  37.  * V7 doesn't have a #define for this.
  38.  */
  39. #ifndef O_RDONLY
  40. #define    O_RDONLY    0
  41. #endif
  42.  
  43. #define    MAGIC_STAT    105    /* Magic status returned by child, if
  44.                    it can't exec.  We hope compress/sh
  45.                    never return this status! */
  46. /*
  47.  * The record pointed to by save_rec should not be overlaid
  48.  * when reading in a new tape block.  Copy it to record_save_area first, and
  49.  * change the pointer in *save_rec to point to record_save_area.
  50.  * Saved_recno records the record number at the time of the save.
  51.  * This is used by annofile() to print the record number of a file's
  52.  * header record.
  53.  */
  54. static union record **save_rec;
  55. static union record record_save_area;
  56. static long        saved_recno;
  57.  
  58. /*
  59.  * PID of child program, if f_compress or remote archive access.
  60.  */
  61. static int    childpid = 0;
  62.  
  63. /*
  64.  * Record number of the start of this block of records
  65.  */
  66. static long    baserec;
  67.  
  68. /*
  69.  * Error recovery stuff
  70.  */
  71. static int    r_error_count;
  72.  
  73. /*
  74.  * Have we hit EOF yet?
  75.  */
  76. static int    eof;
  77.  
  78.  
  79. /*
  80.  * Return the location of the next available input or output record.
  81.  * Return NULL for EOF.  Once we have returned NULL, we just keep returning
  82.  * it, to avoid accidentally going on to the next file on the "tape".
  83.  */
  84. union record *
  85. findrec()
  86. {
  87.     if (ar_record == ar_last) {
  88.         if (eof)
  89.             return (union record *)NULL;    /* EOF */
  90.         flush_archive();
  91.         if (ar_record == ar_last) {
  92.             eof++;
  93.             return (union record *)NULL;    /* EOF */
  94.         }
  95.     }
  96.     return ar_record;
  97. }
  98.  
  99.  
  100. /*
  101.  * Indicate that we have used all records up thru the argument.
  102.  * (should the arg have an off-by-1? XXX FIXME)
  103.  */
  104. void
  105. userec(rec)
  106.     union record *rec;
  107. {
  108.     while(rec >= ar_record)
  109.         ar_record++;
  110.     /*
  111.      * Do NOT flush the archive here.  If we do, the same
  112.      * argument to userec() could mean the next record (if the
  113.      * input block is exactly one record long), which is not what
  114.      * is intended.
  115.      */
  116.     if (ar_record > ar_last)
  117.         abort();
  118. }
  119.  
  120.  
  121. /*
  122.  * Return a pointer to the end of the current records buffer.
  123.  * All the space between findrec() and endofrecs() is available
  124.  * for filling with data, or taking data from.
  125.  */
  126. union record *
  127. endofrecs()
  128. {
  129.     return ar_last;
  130. }
  131.  
  132. #ifndef AMIGA
  133. /* 
  134.  * Duplicate a file descriptor into a certain slot.
  135.  * Equivalent to BSD "dup2" with error reporting.
  136.  */
  137. void
  138. dupto(from, to, msg)
  139.     int from, to;
  140.     char *msg;
  141. {
  142.     int err;
  143.  
  144.     if (from != to) {
  145.         (void) close(to);
  146.         err = dup(from);
  147.         if (err != to) {
  148.             fprintf(stderr, "tar: cannot dup ");
  149.             perror(msg);
  150.             exit(EX_SYSTEM);
  151.         }
  152.         (void) close(from);
  153.     }
  154. }
  155. #endif
  156.  
  157. /*
  158.  * Fork a child to deal with remote files or compression.
  159.  * If rem_host is zero, we are called only for compression.
  160.  */
  161. void
  162. child_open(rem_host, rem_file)
  163.     char *rem_host, *rem_file;
  164. {
  165.  
  166. #ifdef MSDOS
  167.     fprintf(stderr,
  168.       "MSDOS %s cannot deal with compressed or remote archives\n", tar);
  169.     exit(EX_ARGSBAD);
  170. #else
  171. #ifdef AMIGA
  172.     fprintf(stderr,
  173.       "Amiga %s cannot deal with compressed archives\n", tar);
  174.     exit(EX_ARGSBAD);
  175. #else
  176.     
  177.     int pipes[2];
  178.     int err;
  179.     struct stat arstat;
  180.     char cmdbuf[1000];        /* For big file and host names */
  181.  
  182.     /* Create a pipe to talk to the child over */
  183.     err = pipe(pipes);
  184.     if (err < 0) {
  185.         perror ("tar: cannot create pipe to child");
  186.         exit(EX_SYSTEM);
  187.     }
  188.     
  189.     /* Fork child process */
  190.     childpid = fork();
  191.     if (childpid < 0) {
  192.         perror("tar: cannot fork");
  193.         exit(EX_SYSTEM);
  194.     }
  195.  
  196.     /*
  197.      * Parent process.  Clean up.
  198.      *
  199.      * We always close the archive file (stdin, stdout, or opened file)
  200.      * since the child will end up reading or writing that for us.
  201.      * Note that this may leave standard input closed.
  202.      * We close the child's end of the pipe since they will handle
  203.      * that too; and we set <archive> to the other end of the pipe.
  204.      *
  205.      * If reading, we set f_reblock since reading pipes or network
  206.      * sockets produces odd length data.
  207.      */
  208.     if (childpid > 0) {
  209.         (void) close (archive);    
  210.         if (ar_reading) {
  211.             (void) close (pipes[PWRITE]);
  212.             archive = pipes[PREAD];
  213.             f_reblock++;
  214.         } else {
  215.             (void) close (pipes[PREAD]);
  216.             archive = pipes[PWRITE];
  217.         }
  218.         return;
  219.     }
  220.  
  221.     /*
  222.      * Child process.
  223.      */
  224.     if (ar_reading) {
  225.         /*
  226.          * Reading from the child...
  227.          *
  228.          * Close the read-side of the pipe, which our parent will use.
  229.          * Move the write-side of pipe to stdout,
  230.          * If local, move archive input to child's stdin,
  231.          * then run the child.
  232.          */
  233.         (void) close (pipes[PREAD]);
  234.         dupto(pipes[PWRITE], STDOUT, "to stdout");
  235.         if (rem_host) {
  236.             (void) close (STDIN);    /* rsh abuses stdin */
  237.             if (STDIN != open("/dev/null"))
  238.                 perror("Can't open /dev/null");
  239.             sprintf(cmdbuf,
  240.                 "rsh '%s' dd '<%s' bs=%db",
  241.                 rem_host, rem_file, blocking);
  242.             if (f_compress)
  243.                 strcat(cmdbuf, "| compress -d");
  244. #ifdef DEBUG
  245.             fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
  246. #endif
  247.             execlp("sh", "sh", "-c", cmdbuf, (char *)0);
  248.             perror("tar: cannot exec sh");
  249.         } else {
  250.             /*
  251.              * If we are reading a disk file, compress is OK;
  252.              * otherwise, we have to reblock the input in case it's
  253.              * coming from a tape drive.  This is an optimization.
  254.              */
  255.             dupto(archive, STDIN, "to stdin");
  256.             err = fstat(STDIN, &arstat);
  257.             if (err != 0) {
  258.                 perror("tar: can't fstat archive");
  259.                 exit(EX_SYSTEM);
  260.             }
  261.             if ((arstat.st_mode & S_IFMT) == S_IFREG) {
  262.                 execlp("compress", "compress", "-d", (char *)0);
  263.                 perror("tar: cannot exec compress");
  264.             } else {
  265.                 /* Non-regular file needs dd before compress */
  266.                 sprintf(cmdbuf,
  267.                     "dd bs=%db | compress -d",
  268.                     blocking);
  269. #ifdef DEBUG
  270.                 fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
  271. #endif
  272.                 execlp("sh", "sh", "-c", cmdbuf, (char *)0);
  273.                 perror("tar: cannot exec sh");
  274.             }
  275.         }
  276.         exit(MAGIC_STAT);
  277.     } else {
  278.         /*
  279.          * Writing archive to the child.
  280.          * It would like to run either:
  281.          *    compress
  282.          *    compress |            dd obs=20b
  283.          *           rsh 'host' dd obs=20b '>foo'
  284.          * or    compress | rsh 'host' dd obs=20b '>foo'
  285.          *
  286.          * We need the dd to reblock the output to the
  287.          * user's specs, if writing to a device or over
  288.          * the net.  However, it produces a stupid
  289.          * message about how many blocks it processed.
  290.          * Because the shell on the remote end could be just
  291.          * about any shell, we can't depend on it to do
  292.          * redirect stderr properly for us -- the csh
  293.          * doesn't use the same syntax as the Bourne shell.
  294.          * On the other hand, if we just ignore stderr on
  295.          * this end, we won't see errors from rsh, or from
  296.          * the inability of "dd" to write its output file.
  297.          * The combination of the local sh, the rsh, the
  298.          * remote csh, and maybe a remote sh conspires to mess
  299.          * up any possible quoting method, so grumble! we
  300.          * punt and just accept the fucking "xxx blocks"
  301.          * messages.  The real fix would be a "dd" that
  302.          * would shut up.
  303.          * 
  304.          * Close the write-side of the pipe, which our parent will use.
  305.          * Move the read-side of the pipe to stdin,
  306.          * If local, move archive output to the child's stdout.
  307.          * then run the child.
  308.          */
  309.         (void) close (pipes[PWRITE]);
  310.         dupto(pipes[PREAD], STDIN, "to stdin");
  311.         if (!rem_host)
  312.             dupto(archive, STDOUT, "to stdout");
  313.  
  314.         cmdbuf[0] = '\0';
  315.         if (f_compress) {
  316.             if (!rem_host) {
  317.                 err = fstat(STDOUT, &arstat);
  318.                 if (err != 0) {
  319.                     perror("tar: can't fstat archive");
  320.                     exit(EX_SYSTEM);
  321.                 }
  322.                 if ((arstat.st_mode & S_IFMT) == S_IFREG) {
  323.                     execlp("compress", "compress", (char *)0);
  324.                     perror("tar: cannot exec compress");
  325.                 }
  326.             }
  327.             strcat(cmdbuf, "compress | ");
  328.         }
  329.         if (rem_host) {
  330.             sprintf(cmdbuf+strlen(cmdbuf),
  331.               "rsh '%s' dd obs=%db '>%s'",
  332.                  rem_host, blocking, rem_file);
  333.         } else {
  334.             sprintf(cmdbuf+strlen(cmdbuf),
  335.                 "dd obs=%db",
  336.                 blocking);
  337.         }
  338. #ifdef DEBUG
  339.         fprintf(stderr, "Exec-ing: %s\n", cmdbuf);
  340. #endif
  341.         execlp("sh", "sh", "-c", cmdbuf, (char *)0);
  342.         perror("tar: cannot exec sh");
  343.         exit(MAGIC_STAT);
  344.     }
  345. #endif    /* AMIGA */
  346. #endif    /* MSDOS */
  347. }
  348.  
  349.  
  350. /*
  351.  * Open an archive file.  The argument specifies whether we are
  352.  * reading or writing.
  353.  */
  354. open_archive(read)
  355.     int read;
  356. {
  357.     char *colon, *slash;
  358.     char *rem_host = 0, *rem_file;
  359.  
  360. #ifndef AMIGA
  361.     colon = index(ar_file, ':');
  362.     if (colon) {
  363.         slash = index(ar_file, '/');
  364.         if (slash && slash > colon) {
  365.             /*
  366.              * Remote file specified.  Parse out separately,
  367.              * and don't try to open it on the local system.
  368.              */
  369.             rem_file = colon + 1;
  370.             rem_host = ar_file;
  371.             *colon = '\0';
  372.             goto gotit;
  373.         }
  374.     }
  375. #endif
  376.     if (ar_file[0] == '-' && ar_file[1] == '\0') {
  377.         f_reblock++;    /* Could be a pipe, be safe */
  378.         if (read)    archive = STDIN;
  379.         else        archive = STDOUT;
  380.     } else if (read) {
  381.         archive = open(ar_file, O_RDONLY);
  382.     } else {
  383.         archive = creat(ar_file, 0666);
  384.     }
  385.  
  386.     if (archive < 0) {
  387.         perror(ar_file);
  388.         exit(EX_BADARCH);
  389.     }
  390.  
  391. #ifdef    MSDOS
  392.     setmode(archive, O_BINARY);
  393. #endif
  394.  
  395. gotit:
  396.     if (blocksize == 0) {
  397.         fprintf(stderr, "tar: invalid value for blocksize\n");
  398.         exit(EX_ARGSBAD);
  399.     }
  400.  
  401.     /*NOSTRICT*/
  402.     ar_block = (union record *) valloc((unsigned)blocksize);
  403.     if (!ar_block) {
  404.         fprintf(stderr,
  405.         "tar: could not allocate memory for blocking factor %d\n",
  406.             blocking);
  407.         exit(EX_ARGSBAD);
  408.     }
  409.  
  410.     ar_record = ar_block;
  411.     ar_last   = ar_block + blocking;
  412.     ar_reading = read;
  413.  
  414.     if (f_compress || rem_host) 
  415.         child_open(rem_host, rem_file);
  416.  
  417.     if (read) {
  418.         ar_last = ar_block;        /* Set up for 1st block = # 0 */
  419.         (void) findrec();        /* Read it in, check for EOF */
  420.     }
  421. }
  422.  
  423.  
  424. /*
  425.  * Remember a union record * as pointing to something that we
  426.  * need to keep when reading onward in the file.  Only one such
  427.  * thing can be remembered at once, and it only works when reading
  428.  * an archive.
  429.  *
  430.  * We calculate "offset" then add it because some compilers end up
  431.  * adding (baserec+ar_record), doing a 9-bit shift of baserec, then
  432.  * subtracting ar_block from that, shifting it back, losing the top 9 bits.
  433.  */
  434. saverec(pointer)
  435.     union record **pointer;
  436. {
  437.     long offset;
  438.  
  439.     save_rec = pointer;
  440.     offset = ar_record - ar_block;
  441.     saved_recno = baserec + offset;
  442. }
  443.  
  444. /*
  445.  * Perform a write to flush the buffer.
  446.  */
  447. fl_write()
  448. {
  449.     int err;
  450.  
  451.     err = write(archive, ar_block->charptr, blocksize);
  452.     if (err == blocksize) return;
  453.     /* FIXME, multi volume support on write goes here */
  454.     if (err < 0)
  455.         perror(ar_file);
  456.     else
  457.         fprintf(stderr, "tar: %s: write failed, short %d bytes\n",
  458.             ar_file, blocksize - err);
  459.     exit(EX_BADARCH);
  460. }
  461.  
  462.  
  463. /*
  464.  * Handle read errors on the archive.
  465.  *
  466.  * If the read should be retried, readerror() returns to the caller.
  467.  */
  468. void
  469. readerror()
  470. {
  471. #    define    READ_ERROR_MAX    10
  472.  
  473.     read_error_flag++;        /* Tell callers */
  474.  
  475.     annorec(stderr, tar);
  476.     fprintf(stderr, "Read error on ");
  477.     perror(ar_file);
  478.  
  479.     if (baserec == 0) {
  480.         /* First block of tape.  Probably stupidity error */
  481.         exit(EX_BADARCH);
  482.     }    
  483.  
  484.     /*
  485.      * Read error in mid archive.  We retry up to READ_ERROR_MAX times
  486.      * and then give up on reading the archive.  We set read_error_flag
  487.      * for our callers, so they can cope if they want.
  488.      */
  489.     if (r_error_count++ > READ_ERROR_MAX) {
  490.         annorec(stderr, tar);
  491.         fprintf(stderr, "Too many errors, quitting.\n");
  492.         exit(EX_BADARCH);
  493.     }
  494.     return;
  495. }
  496.  
  497.  
  498. /*
  499.  * Perform a read to flush the buffer.
  500.  */
  501. fl_read()
  502. {
  503.     int err;        /* Result from system call */
  504.     int left;        /* Bytes left */
  505.     char *more;        /* Pointer to next byte to read */
  506.  
  507.     /*
  508.      * Clear the count of errors.  This only applies to a single
  509.      * call to fl_read.  We leave read_error_flag alone; it is
  510.      * only turned off by higher level software.
  511.      */
  512.     r_error_count = 0;    /* Clear error count */
  513.  
  514.     /*
  515.      * If we are about to wipe out a record that
  516.      * somebody needs to keep, copy it out to a holding
  517.      * area and adjust somebody's pointer to it.
  518.      */
  519.     if (save_rec &&
  520.         *save_rec >= ar_record &&
  521.         *save_rec < ar_last) {
  522.         record_save_area = **save_rec;
  523.         *save_rec = &record_save_area;
  524.     }
  525. error_loop:
  526.     err = read(archive, ar_block->charptr, blocksize);
  527.     if (err == blocksize) return;
  528.     if (err < 0) {
  529.         readerror();
  530.         goto error_loop;    /* Try again */
  531.     }
  532.  
  533.     more = ar_block->charptr + err;
  534.     left = blocksize - err;
  535.  
  536. again:
  537.     if (0 == (((unsigned)left) % RECORDSIZE)) {
  538.         /* FIXME, for size=0, multi vol support */
  539.         /* On the first block, warn about the problem */
  540.         if (!f_reblock && baserec == 0 && f_verbose && err > 0) {
  541.             annorec(stderr, tar);
  542.             fprintf(stderr, "Blocksize = %d record%s\n",
  543.                 err / RECORDSIZE, (err > RECORDSIZE)? "s": "");
  544.         }
  545.         ar_last = ar_block + ((unsigned)(blocksize - left))/RECORDSIZE;
  546.         return;
  547.     }
  548.     if (f_reblock) {
  549.         /*
  550.          * User warned us about this.  Fix up.
  551.          */
  552.         if (left > 0) {
  553. error2loop:
  554.             err = read(archive, more, left);
  555.             if (err < 0) {
  556.                 readerror();
  557.                 goto error2loop;    /* Try again */
  558.             }
  559.             if (err == 0) {
  560.                 annorec(stderr, tar);
  561.                 fprintf(stderr,
  562.         "%s: eof not on block boundary, strange...\n",
  563.                     ar_file);
  564.                 exit(EX_BADARCH);
  565.             }
  566.             left -= err;
  567.             more += err;
  568.             goto again;
  569.         }
  570.     } else {
  571.         annorec(stderr, tar);
  572.         fprintf(stderr, "%s: read %d bytes, strange...\n",
  573.             ar_file, err);
  574.         exit(EX_BADARCH);
  575.     }
  576. }
  577.  
  578.  
  579. /*
  580.  * Flush the current buffer to/from the archive.
  581.  */
  582. flush_archive()
  583. {
  584.  
  585.     baserec += ar_last - ar_block;    /* Keep track of block #s */
  586.     ar_record = ar_block;        /* Restore pointer to start */
  587.     ar_last = ar_block + blocking;    /* Restore pointer to end */
  588.  
  589.     if (!ar_reading) 
  590.         fl_write();
  591.     else
  592.         fl_read();
  593. }
  594.  
  595. /*
  596.  * Close the archive file.
  597.  */
  598. close_archive()
  599. {
  600.     int child;
  601.     int status;
  602.  
  603.     if (!ar_reading) flush_archive();
  604.     (void) close(archive);
  605.  
  606. #if !defined(MSDOS) && !defined(AMIGA)
  607.     if (childpid) {
  608.         /*
  609.          * Loop waiting for the right child to die, or for
  610.          * no more kids.
  611.          */
  612.         while (((child = wait(&status)) != childpid) && child != -1)
  613.             ;
  614.  
  615.         if (child != -1) {
  616.             switch (TERM_SIGNAL(status)) {
  617.             case 0:
  618.                 /* Child voluntarily terminated  -- but why? */
  619.                 if (TERM_VALUE(status) == MAGIC_STAT) {
  620.                     exit(EX_SYSTEM);/* Child had trouble */
  621.                 }
  622.                 if (TERM_VALUE(status) == (SIGPIPE + 128)) {
  623.                     /*
  624.                      * /bin/sh returns this if its child
  625.                      * dies with SIGPIPE.  'Sok.
  626.                      */
  627.                     break;
  628.                 } else if (TERM_VALUE(status))
  629.                     fprintf(stderr,
  630.                   "tar: child returned status %d\n",
  631.                         TERM_VALUE(status));
  632.             case SIGPIPE:
  633.                 break;        /* This is OK. */
  634.  
  635.             default:
  636.                 fprintf(stderr,
  637.                  "tar: child died with signal %d%s\n",
  638.                  TERM_SIGNAL(status),
  639.                  TERM_COREDUMP(status)? " (core dumped)": "");
  640.             }
  641.         }
  642.     }
  643. #endif    /* MSDOS */
  644. }
  645.  
  646.  
  647. /*
  648.  * Message management.
  649.  *
  650.  * anno writes a message prefix on stream (eg stdout, stderr).
  651.  *
  652.  * The specified prefix is normally output followed by a colon and a space.
  653.  * However, if other command line options are set, more output can come
  654.  * out, such as the record # within the archive.
  655.  *
  656.  * If the specified prefix is NULL, no output is produced unless the
  657.  * command line option(s) are set.
  658.  *
  659.  * If the third argument is 1, the "saved" record # is used; if 0, the
  660.  * "current" record # is used.
  661.  */
  662. void
  663. anno(stream, prefix, savedp)
  664.     FILE    *stream;
  665.     char    *prefix;
  666.     int    savedp;
  667. {
  668. #    define    MAXANNO    50
  669.     char    buffer[MAXANNO];    /* Holds annorecment */
  670. #    define    ANNOWIDTH 13
  671.     int    space;
  672.     long    offset;
  673.  
  674.     /* Make sure previous output gets out in sequence */
  675.     if (stream == stderr)
  676.         fflush(stdout);
  677.     if (f_sayblock) {
  678.         if (prefix) {
  679.             fputs(prefix, stream);
  680.             putc(' ', stream);
  681.         }
  682.         offset = ar_record - ar_block;
  683.         sprintf(buffer, "rec %d: ",
  684.             savedp?    saved_recno:
  685.                 baserec + offset);
  686.         fputs(buffer, stream);
  687.         space = ANNOWIDTH - strlen(buffer);
  688.         if (space > 0) {
  689.             fprintf(stream, "%*s", space, "");
  690.         }
  691.     } else if (prefix) {
  692.         fputs(prefix, stream);
  693.         fputs(": ", stream);
  694.     }
  695. }
  696.