home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / filutl / pdtar.arc / CREATE.C < prev    next >
C/C++ Source or Header  |  1988-05-15  |  16KB  |  685 lines

  1. /*
  2.  * Create a tar archive.
  3.  *
  4.  * Written 25 Aug 1985 by John Gilmore, ihnp4!hoptoad!gnu.
  5.  * MS-DOS port 2/87 by Eric Roskos.
  6.  * Minix  port 3/88 by Eric Roskos.
  7.  *
  8.  * @(#)create.c 1.19 9/9/86 Public Domain - gnu
  9.  */
  10. #include <sys/types.h>
  11. #include <sys/stat.h>
  12. #include <fcntl.h>
  13. #include <stdio.h>
  14.  
  15. #ifndef MSDOS
  16. #include <pwd.h>
  17. #include <grp.h>
  18. #else                            /* ifdef MSDOS */
  19. #include <assert.h>
  20. #endif
  21.  
  22. #ifdef BSD42
  23. #include <sys/dir.h>
  24. #else
  25. /*
  26.  * FIXME: On other systems there is no standard place for the header file
  27.  * for the portable directory access routines.  Change the #include line
  28.  * below to bring it in from wherever it is.  Of course, where it is
  29.  * on BSD is the standard place ... :-)?
  30.  */
  31. #ifdef V7
  32. #include "dir.h"
  33. #else
  34. #include "ndir.h"
  35. #endif /* V7 */
  36. #endif /* !BSD42 */
  37.  
  38. #ifdef USG
  39. #ifdef MSDOS
  40. /* DOS doesn't have minor device numbers */
  41. #define major(n)    n
  42. #define minor(n)    0
  43. #else
  44. #include <sys/sysmacros.h>        /* major() and minor() defined here */
  45. #endif
  46. #endif
  47.  
  48. /*
  49.  * V7 doesn't have a #define for this.
  50.  */
  51. #ifndef O_RDONLY
  52. #define    O_RDONLY    0
  53. #endif
  54.  
  55. #include "tar.h"
  56.  
  57. /*
  58.  * If there are no symbolic links, there is no lstat().  Use stat().
  59.  */
  60. #ifndef S_IFLNK
  61. #define lstat stat
  62. #endif
  63.  
  64. extern char    *malloc();
  65. extern char    *strcpy();
  66. extern char    *strncpy();
  67. extern int      errno;
  68.  
  69. union record   *start_header();
  70. void            finish_header();
  71. void            finduname();
  72. void            findgname();
  73. char           *name_next();
  74. void            to_oct();
  75.  
  76.  
  77. void
  78. create_archive()
  79. {
  80.     register char  *p;
  81.  
  82.     open_archive(0);            /* Open for writing */
  83.  
  84.     while (p = name_next())
  85.     {
  86.         dump_file(p);
  87.     }
  88.  
  89.     write_eot();
  90.     close_archive();
  91.     name_close();
  92. }
  93.  
  94. #ifdef MSDOS
  95. /*
  96.  * count the number of bytes in file f.  This is done because, with DOS,
  97.  * the results of the stat call may not reflect the actual file size, due
  98.  * to conversion of CR/LF pairs to plain LF's.  The *only* way to find this
  99.  * out is to read the whole file in order to let the MSC library routines
  100.  * determine how many characters will actually be in the file...
  101.  */
  102. long
  103. countbytes(f)
  104. int             f;
  105. {
  106.     long            cb;
  107.     int             n;
  108.     char            buf[512];
  109.  
  110.     assert(lseek(f, 0L, 1) == 0L);
  111.  
  112.     for (cb = 0; (n = read(f, buf, sizeof(buf))) > 0;)
  113.         cb += n;
  114.     lseek(f, 0L, 0);
  115.  
  116.     assert(lseek(f, 0L, 1) == 0L);
  117.  
  118.     return (cb);
  119. }
  120.  
  121. #endif
  122.  
  123. /*
  124.  * Dump a single file.  If it's a directory, recurse.
  125.  * Result is 1 for success, 0 for failure.
  126.  */
  127. int
  128. dump_file(p)
  129. char           *p;                /* File name to dump */
  130. {
  131.     struct stat     statbuff[1];
  132.     struct stat    *statbuf = statbuff;    
  133.     union record   *header;
  134.     char            type;
  135.  
  136.     /*
  137.      * Use stat if following (rather than dumping) 4.2BSD's symbolic links. 
  138.      * Otherwise, use lstat (which, on non-4.2 systems, is #define'd to stat
  139.      * anyway. 
  140.      */
  141.     if (0 != f_follow_links ? stat(p, statbuf) : lstat(p, statbuf))
  142.     {
  143. badperror:
  144.         perror(p);
  145. badfile:
  146.         errors++;
  147.         return 0;
  148.     }
  149.  
  150.     switch (statbuf->st_mode & S_IFMT)
  151.     {
  152.  
  153.     case S_IFREG:                /* Regular file */
  154.         {
  155.             int             f;    /* File descriptor */
  156.             int             bufsize, count;
  157.             register long   sizeleft;
  158.             register union record *start;
  159.             long            exp, actl;    /* byte counts for file size errs */
  160.             int             nbytes, need;        /* for block read loop */
  161.             int             n;    /* # bytes read in this read() call */
  162.             char           *bufp;        /* where to start this read in buf */
  163.  
  164.             /*
  165.              * Handle a regular file with multiple links. 
  166.              *
  167.              * We maintain a list of all such files that we've written so far. 
  168.              * Any time we see another, we check the list and avoid dumping
  169.              * the data again if we've done it once already. 
  170.              */
  171.             if (statbuf->st_nlink > 1)
  172.             {
  173.                 register struct link *lp;
  174.  
  175.                 /*
  176.                  * First quick and dirty.  Hashing, etc later FIXME 
  177.                  */
  178.                 for (lp = linklist; lp; lp = lp->next)
  179.                 {
  180.                     if (lp->ino == statbuf->st_ino &&
  181.                         lp->dev == statbuf->st_dev)
  182.                     {
  183.                         /* We found a link. */
  184.                         statbuf->st_size = 0;
  185.                         header = start_header(p, statbuf);
  186.                         if (header == NULL)
  187.                             goto badfile;
  188.                         strcpy(header->header.linkname,
  189.                             lp->name);
  190.                         header->header.linkflag = LF_LINK;
  191.                         finish_header(header);
  192.                         if (f_verbose)
  193.                             annorec(stdout, (char *) NULL);
  194.                         printf("%s link to %s\n",
  195.                             p, lp->name);
  196.  
  197.                         /*
  198.                          * Maybe remove from list after all links found? 
  199.                          */
  200.  
  201.                         /*
  202.                          * If so, have to compare names in case he dumps
  203.                          * twice. 
  204.                          */
  205.  
  206.                         /*
  207.                          * Later: I don't understand the above.  If she dumps
  208.                          * the file twice, it would be BAD to dump it the
  209.                          * second time as a link... gnu 25Jul86 
  210.                          */
  211.                         /* FIXME */
  212.                         goto donefile;
  213.                     }
  214.                 }
  215.  
  216.                 /* Not found.  Add it to the list. */
  217.                 lp = (struct link *) malloc((unsigned)
  218.                     (strlen(p) + sizeof(struct link) - NAMSIZ));
  219.                 lp->ino = statbuf->st_ino;
  220.                 lp->dev = statbuf->st_dev;
  221.                 strcpy(lp->name, p);
  222.                 lp->next = linklist;
  223.                 linklist = lp;
  224.             }
  225.  
  226.             sizeleft = statbuf->st_size;
  227. #ifdef NOTDEF                    /* don't understand reason for following,
  228.                                  * causes abort */
  229.             /* Don't bother opening empty, world readable files. */
  230.             if (sizeleft > 0 || 0444 != (0444 & statbuf->st_mode))
  231.             {
  232. #endif
  233.                 f = open(p, O_RDONLY | convmode(p));
  234.                 if (f < 0)
  235.                     goto badperror;
  236. #ifdef NOTDEF
  237.             }
  238.             else
  239.             {
  240.                 f = -1;
  241.             }
  242. #endif
  243.  
  244. #ifdef MSDOS
  245.  
  246.             /*
  247.              * See comment before countbytes(), above. 
  248.              */
  249.             if (convmode(p) & O_TEXT)
  250.             {
  251.                 statbuf->st_size = countbytes(f);
  252.                 sizeleft = statbuf->st_size;
  253.             }
  254. #endif
  255.             exp = sizeleft;        /* number of bytes we expect to see */
  256.             actl = 0;            /* number of bytes we really saw */
  257.  
  258.             header = start_header(p, statbuf);
  259.             if (header == NULL)
  260.                 goto badfile;
  261.             finish_header(header);
  262.             while (sizeleft > 0)
  263.             {
  264.                 start = findrec();
  265.                 bufsize = endofrecs()->charptr - start->charptr;
  266.                 if (sizeleft < bufsize)
  267.                     bufsize = sizeleft;
  268.  
  269.                 /*
  270.                  * use a read loop since reads < number requested do NOT
  271.                  * imply EOF as John assumed -- jer 22Aug87 
  272.                  */
  273.                 need = bufsize;
  274.                 bufp = start->charptr;
  275.                 count = 0;
  276.                 do
  277.                 {
  278.                     n = read(f, bufp, need);
  279.                     if (n > 0)
  280.                     {
  281.                         count += n;
  282.                         bufp += n;
  283.                         need -= n;
  284.                     }
  285.                 } while (need > 0 && n > 0);
  286.  
  287.                 if (n < 0)
  288.                 {
  289.                     annorec(stderr, tar);
  290.                     fprintf(stderr,
  291.                         "read error at byte %ld, reading %d bytes, in file ",
  292.                         statbuf->st_size - sizeleft,
  293.                         bufsize);
  294.                     perror(p);    /* FIXME */
  295.                     goto padit;
  296.                 }
  297.  
  298.                 actl += count;
  299.                 sizeleft -= count;
  300.  
  301.                 userec(start + (count - 1) / RECORDSIZE);
  302.                 if (count == bufsize)
  303.                     continue;
  304.  
  305.                 annorec(stderr, tar);
  306.                 fprintf(stderr,
  307.                     "%s: file size error: expected %ld, read %ld.\n",
  308.                     p, exp, actl);
  309.                 fprintf(stderr,
  310.                     "%s: file shrunk by %d bytes, padding with zeros.\n",
  311.                     p, sizeleft);
  312.                 goto padit;        /* Short read */
  313.             }
  314.             if (f >= 0)
  315.                 (void) close(f);
  316.  
  317.             /* Clear last block garbage to zeros, FIXME */
  318.  
  319. #ifdef OLDVERBOSE
  320.             if (f_verbose)
  321.             {
  322.                 annorec(stdout, (char *) NULL);
  323.                 printf("%s\n", p);
  324.             }
  325. #endif
  326.     donefile:
  327.             break;
  328.  
  329.             /*
  330.              * File shrunk or gave error, pad out tape to match the size we
  331.              * specified in the header. ??? Doesn't do that here!  Must be a
  332.              * FIXME. -- jer 
  333.              */
  334.     padit:
  335.             abort();
  336.         }
  337.  
  338. #ifdef S_IFLNK
  339.     case S_IFLNK:                /* Symbolic link */
  340.         {
  341.             int             size;
  342.  
  343.             statbuf->st_size = 0;        /* Force 0 size on symlink */
  344.             header = start_header(p, statbuf);
  345.             if (header == NULL)
  346.                 goto badfile;
  347.             size = readlink(p, header->header.linkname, NAMSIZ);
  348.             if (size < 0)
  349.                 goto badperror;
  350.             if (size == NAMSIZ)
  351.             {
  352.                 annorec(stderr, tar);
  353.                 fprintf(stderr,
  354.                     "%s: symbolic link too long\n", p);
  355.                 break;
  356.             }
  357.             header->header.linkname[size] = '\0';
  358.             header->header.linkflag = LF_SYMLINK;
  359.             finish_header(header);        /* Nothing more to do to it */
  360. #ifdef OLDVERBOSE
  361.             if (f_verbose)
  362.             {
  363.                 annorec(stdout, (char *) NULL);
  364.                 printf("%s\n", p);
  365.             }
  366. #endif
  367.         }
  368.         break;
  369. #endif
  370.  
  371.     case S_IFDIR:                /* Directory */
  372.         {
  373.             register DIR   *dirp;
  374.             register struct direct *d;
  375.             char            namebuf[NAMSIZ + 2];
  376.             register int    len;
  377.  
  378.             /* Build new prototype name */
  379.             strncpy(namebuf, p, sizeof(namebuf));
  380.             len = strlen(namebuf);
  381.             while (len >= 1 && '/' == namebuf[len - 1])
  382.                 len--;            /* Delete trailing slashes */
  383.             namebuf[len++] = '/';        /* Now add exactly one back */
  384.  
  385.             /*
  386.              * Output directory header record with permissions FIXME, do this
  387.              * AFTER files, to avoid R/O dir problems? If Unix Std format,
  388.              * don't put / on end of dir name If old archive format, don't
  389.              * write record at all. 
  390.              */
  391.             if (!f_oldarch)
  392.             {
  393.                 statbuf->st_size = 0;    /* Force 0 size on dir */
  394.  
  395.                 /*
  396.                  * If people could really read standard archives, this should
  397.                  * be:        (FIXME) header = start_header(f_standard? p:
  398.                  * namebuf, statbuf); but since they'd interpret LF_DIR
  399.                  * records as regular files, we'd better put the / on the
  400.                  * name. 
  401.                  */
  402.                 header = start_header(namebuf, statbuf);
  403.                 if (header == NULL)
  404.                     goto badfile;        /* eg name too long */
  405.                 if (f_standard)
  406.                 {
  407.                     header->header.linkflag = LF_DIR;
  408.                 }
  409.                 finish_header(header);    /* Done with directory header */
  410.             }
  411. #ifdef OLDVERBOSE
  412.             if (f_verbose)
  413.             {
  414.                 annorec(stdout, (char *) NULL);
  415.                 printf("%s\n", p);
  416.             }
  417. #endif
  418.             /*
  419.              * Hack to remove "./" from the front of all the file names 
  420.              */
  421.             if (len == 2 && namebuf[0] == '.')
  422.             {
  423.                 len = 0;
  424.             }
  425.  
  426.             /* Now output all the files in the directory */
  427.             errno = 0;
  428.             dirp = opendir(p);
  429.             if (!dirp)
  430.             {
  431.                 if (errno)
  432.                 {
  433.                     perror(p);
  434.                 }
  435.                 else
  436.                 {
  437.                     annorec(stderr, tar);
  438.                     fprintf(stderr, "%s: error opening directory",
  439.                         p);
  440.                 }
  441.                 break;
  442.             }
  443.  
  444.             /* Should speed this up by cd-ing into the dir, FIXME */
  445.             while (NULL != (d = readdir(dirp)))
  446.             {
  447.                 /* Skip . and .. */
  448.                 if (d->d_name[0] == '.')
  449.                 {
  450.                     if (d->d_name[1] == '\0')
  451.                         continue;
  452.                     if (d->d_name[1] == '.')
  453.                     {
  454.                         if (d->d_name[2] == '\0')
  455.                             continue;
  456.                     }
  457.                 }
  458.                 if (strlen(d->d_name) + len >= NAMSIZ)
  459.                 {
  460.                     annorec(stderr, tar);
  461.                     fprintf(stderr, "%s%s: name too long\n",
  462.                         namebuf, d->d_name);
  463.                     continue;
  464.                 }
  465.                 strcpy(namebuf + len, d->d_name);
  466.                 dump_file(namebuf);
  467.             }
  468.  
  469.             closedir(dirp);
  470.         }
  471.         break;
  472.  
  473.     case S_IFCHR:                /* Character special file */
  474.         type = LF_CHR;
  475.         goto easy;
  476.  
  477. #ifdef S_IFBLK
  478.     case S_IFBLK:                /* Block     special file */
  479.         type = LF_BLK;
  480.         goto easy;
  481. #endif
  482.  
  483. #ifdef S_IFIFO
  484.     case S_IFIFO:                /* Fifo      special file */
  485.         type = LF_FIFO;
  486. #endif /* S_IFIFO */
  487.  
  488. easy:
  489.         if (!f_standard)
  490.             goto unknown;
  491.  
  492. #if 0
  493.         /* this was a bad kludge, it had the side-effect of causing
  494.          * skipping of the data part of files when a "list" was
  495.          * done to work right for special files.  But, Minix needs
  496.          * this size since it's used to size the device. - JER */
  497.         statbuf->st_size = 0;    /* Force 0 size */
  498. #endif
  499.         header = start_header(p, statbuf);
  500.         if (header == NULL)
  501.             goto badfile;        /* eg name too long */
  502.  
  503.         header->header.linkflag = type;
  504.         if (type != LF_FIFO)
  505.         {
  506.             to_oct((long) major(statbuf->st_rdev), 8,
  507.                 header->header.devmajor);
  508.             to_oct((long) minor(statbuf->st_rdev), 8,
  509.                 header->header.devminor);
  510.         }
  511.  
  512.         finish_header(header);
  513. #ifdef OLDVERBOSE
  514.         if (f_verbose)
  515.         {
  516.             annorec(stdout, (char *) NULL);
  517.             printf("%s\n", p);
  518.         }
  519. #endif
  520.         break;
  521.  
  522.     default:
  523. unknown:
  524.         annorec(stderr, tar);
  525.         fprintf(stderr,
  526.             "%s: Unknown file type; file ignored.\n", p);
  527.         break;
  528.     }
  529.  
  530.     return 1;                    /* Success */
  531. }
  532.  
  533.  
  534. /*
  535.  * Make a header block for the file  name  whose stat info is  st .
  536.  * Return header pointer for success, NULL if the name is too long.
  537.  */
  538. union record   *
  539. start_header(name, st)
  540. char           *name;
  541. register struct stat *st;
  542. {
  543.     register union record *header;
  544.     extern struct stat hstat[1];
  545.  
  546.     hstat[0] = *st;                /* save stat for verbose-mode listing */
  547.  
  548.     header = (union record *) findrec();
  549.     bzero(header->charptr, sizeof(*header));    /* XXX speed up */
  550.     strcpy(header->header.name, name);
  551.     if (header->header.name[NAMSIZ - 1])
  552.     {
  553.         annorec(stderr, tar);
  554.         fprintf(stderr, "%s: name too long\n", name);
  555.         return NULL;
  556.     }
  557. #ifdef MSDOS
  558.     to_oct((long) (st->st_mode & ~(S_IFMT | DOSUMASK)),
  559.         8, header->header.mode);
  560. #else
  561.     to_oct((long) (st->st_mode & ~S_IFMT),
  562.         8, header->header.mode);
  563. #endif
  564.     to_oct((long) st->st_uid, 8, header->header.uid);
  565.     to_oct((long) st->st_gid, 8, header->header.gid);
  566.     to_oct((long) st->st_size, 1 + 12, header->header.size);
  567.     to_oct((long) st->st_mtime, 1 + 12, header->header.mtime);
  568.     /* header->header.linkflag is left as null */
  569.  
  570.     /* Fill in new Unix Standard fields if desired. */
  571.     if (f_standard)
  572.     {
  573.         header->header.linkflag = LF_NORMAL;    /* New default */
  574.         strcpy(header->header.magic, TMAGIC);    /* Mark as Unix Std */
  575. #ifndef NONAMES
  576.         finduname(header->header.uname, st->st_uid);
  577.         findgname(header->header.gname, st->st_gid);
  578. #else
  579.         /* don't leave garbage in if no name - JER */
  580.         header->header.uname[0] = '\0';
  581.         header->header.gname[0] = '\0';
  582. #endif        
  583.     }
  584.     return header;
  585. }
  586.  
  587. /* 
  588.  * Finish off a filled-in header block and write it out.
  589.  */
  590. void
  591. finish_header(header)
  592. register union record *header;
  593. {
  594.     register int    i, sum;
  595.     register char  *p;
  596.     extern union record *head;
  597.  
  598.     bcopy(CHKBLANKS, header->header.chksum, sizeof(header->header.chksum));
  599.  
  600.     sum = 0;
  601.     p = header->charptr;
  602.     for (i = sizeof(*header); --i >= 0;)
  603.     {
  604.  
  605.         /*
  606.          * We can't use unsigned char here because of old compilers, e.g. V7. 
  607.          */
  608.         sum += 0xFF & *p++;
  609.     }
  610.  
  611.     /*
  612.      * Fill in the checksum field.  It's formatted differently from the other
  613.      * fields:  it has [6] digits, a null, then a space -- rather than
  614.      * digits, a space, then a null. We use to_oct then write the null in
  615.      * over to_oct's space. The final space is already there, from
  616.      * checksumming, and to_oct doesn't modify it. 
  617.      *
  618.      * This is a fast way to do: (void) sprintf(header->header.chksum, "%6o",
  619.      * sum); 
  620.      */
  621.     to_oct((long) sum, 8, header->header.chksum);
  622.     header->header.chksum[6] = '\0';    /* Zap the space */
  623.  
  624.     head = header;
  625.     if (f_verbose)
  626.         print_header(header->header.name);
  627.  
  628.     userec(header);
  629.     return;
  630. }
  631.  
  632.  
  633. /*
  634.  * Quick and dirty octal conversion.
  635.  * Converts long "value" into a "digs"-digit field at "where",
  636.  * including a trailing space and room for a null.  "digs"==3 means
  637.  * 1 digit, a space, and room for a null.
  638.  *
  639.  * We assume the trailing null is already there and don't fill it in.
  640.  * This fact is used by start_header and finish_header, so don't change it!
  641.  *
  642.  * This should be equivalent to:
  643.  *    (void) sprintf(where, "%*lo ", digs-2, value);
  644.  * except that sprintf fills in the trailing null and we don't.
  645.  */
  646. void
  647. to_oct(value, digs, where)
  648. register long   value;
  649. register int    digs;
  650. register char  *where;
  651. {
  652.  
  653.     --digs;                        /* Trailing null slot is left alone */
  654.     where[--digs] = ' ';        /* Put in the space, though */
  655.  
  656.     /* Produce the digits -- at least one */
  657.     do
  658.     {
  659.         where[--digs] = '0' + (value & 7);        /* one octal digit */
  660.         value >>= 3;
  661.     } while (digs > 0 && value != 0);
  662.  
  663.     /* Leading spaces, if necessary */
  664.     while (digs > 0)
  665.         where[--digs] = ' ';
  666.  
  667. }
  668.  
  669.  
  670. /*
  671.  * Write the EOT block(s).
  672.  */
  673. write_eot()
  674. {
  675.     union record   *p;
  676.  
  677.     p = findrec();
  678.     bzero(p->charptr, RECORDSIZE);
  679.     userec(p);
  680.     /* FIXME, only one EOT block should be needed. */
  681.     p = findrec();
  682.     bzero(p->charptr, RECORDSIZE);
  683.     userec(p);
  684. }
  685.