work in progress...
[oweals/busybox.git] / tar.c
1 /*
2  * Mini tar implementation for busybox based on code taken from sash.
3  *
4  * Copyright (c) 1999 by David I. Bell
5  * Permission is granted to use, distribute, or modify this source,
6  * provided that this copyright notice remains intact.
7  *
8  * Permission to distribute this code under the GPL has been granted.
9  *
10  * Modified for busybox by Erik Andersen <andersee@debian.org>
11  * Adjusted to grok stdin/stdout options.
12  *
13  * Modified to handle device special files by Matt Porter
14  * <porter@debian.org>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24  * General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29  *
30  */
31
32
33 #include "internal.h"
34 #include <stdio.h>
35 #include <dirent.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <time.h>
40 #include <sys/types.h>
41 #include <sys/sysmacros.h>
42
43
44 static const char tar_usage[] =
45 "tar -[cxtvOf] [tarFileName] [FILE] ...\n\n"
46 "Create, extract, or list files from a tar file\n\n"
47 "Options:\n"
48 "\tc=create, x=extract, t=list contents, v=verbose,\n"
49 "\tO=extract to stdout, f=tarfile or \"-\" for stdin\n";
50
51
52
53 /*
54  * Tar file constants.
55  */
56 #define TAR_BLOCK_SIZE  512
57 #define TAR_NAME_SIZE   100
58
59
60 /*
61  * The POSIX (and basic GNU) tar header format.
62  * This structure is always embedded in a TAR_BLOCK_SIZE sized block
63  * with zero padding.  We only process this information minimally.
64  */
65 typedef struct {
66     char name[TAR_NAME_SIZE];
67     char mode[8];
68     char uid[8];
69     char gid[8];
70     char size[12];
71     char mtime[12];
72     char checkSum[8];
73     char typeFlag;
74     char linkName[TAR_NAME_SIZE];
75     char magic[6];
76     char version[2];
77     char uname[32];
78     char gname[32];
79     char devMajor[8];
80     char devMinor[8];
81     char prefix[155];
82 } TarHeader;
83
84 #define TAR_MAGIC       "ustar"
85 #define TAR_VERSION     "00"
86
87 #define TAR_TYPE_REGULAR        '0'
88 #define TAR_TYPE_HARD_LINK      '1'
89 #define TAR_TYPE_SOFT_LINK      '2'
90
91
92 /*
93  * Static data.
94  */
95 static int listFlag;
96 static int extractFlag;
97 static int createFlag;
98 static int verboseFlag;
99 static int tostdoutFlag;
100
101 static int inHeader; // <- check me
102 static int badHeader;
103 static int errorFlag;
104 static int skipFileFlag;
105 static int warnedRoot;
106 static int eofFlag;
107 static long dataCc;
108 static int outFd;
109 static char outName[TAR_NAME_SIZE];
110
111
112 /*
113  * Static data associated with the tar file.
114  */
115 static const char *tarName;
116 static int tarFd;
117 static dev_t tarDev;
118 static ino_t tarInode;
119
120
121 /*
122  * Local procedures to restore files from a tar file.
123  */
124 static void readTarFile (int fileCount, char **fileTable);
125 static void readData (const char *cp, int count);
126 static long getOctal (const char *cp, int len);
127
128 static void readHeader (const TarHeader * hp,
129                         int fileCount, char **fileTable);
130
131
132 /*
133  * Local procedures to save files into a tar file.
134  */
135 static void saveFile (const char *fileName, int seeLinks);
136
137 static void saveRegularFile (const char *fileName,
138                              const struct stat *statbuf);
139
140 static void saveDirectory (const char *fileName,
141                            const struct stat *statbuf);
142
143 static int wantFileName (const char *fileName,
144                          int fileCount, char **fileTable);
145
146 static void writeHeader (const char *fileName, const struct stat *statbuf);
147
148 static void writeTarFile (int fileCount, char **fileTable);
149 static void writeTarBlock (const char *buf, int len);
150 static int putOctal (char *cp, int len, long value);
151
152
153 extern int tar_main (int argc, char **argv)
154 {
155     const char *options;
156
157     argc--;
158     argv++;
159
160     if (argc < 1)
161         usage( tar_usage);
162
163
164     errorFlag = FALSE;
165     extractFlag = FALSE;
166     createFlag = FALSE;
167     listFlag = FALSE;
168     verboseFlag = FALSE;
169     tostdoutFlag = FALSE;
170     tarName = NULL;
171     tarDev = 0;
172     tarInode = 0;
173     tarFd = -1;
174
175     /* 
176      * Parse the options.
177      */
178     if (**argv == '-') {
179         options = (*argv++) + 1;
180         argc--;
181         for (; *options; options++) {
182             switch (*options) {
183             case 'f':
184                 if (tarName != NULL) {
185                     fprintf (stderr, "Only one 'f' option allowed\n");
186
187                     exit (FALSE);
188                 }
189
190                 tarName = *argv++;
191                 argc--;
192
193                 break;
194
195             case 't':
196                 listFlag = TRUE;
197                 break;
198
199             case 'x':
200                 extractFlag = TRUE;
201                 break;
202
203             case 'c':
204                 createFlag = TRUE;
205                 break;
206
207             case 'v':
208                 verboseFlag = TRUE;
209                 break;
210
211             case 'O':
212                 tostdoutFlag = TRUE;
213                 break;
214
215             case '-':
216                 usage( tar_usage);
217                 break;
218
219             default:
220                 fprintf (stderr, "Unknown tar flag '%c'\n"
221                         "Try `tar --help' for more information\n", 
222                         *options);
223
224                 exit (FALSE);
225             }
226         }
227     }
228
229     /* 
230      * Validate the options.
231      */
232     if (extractFlag + listFlag + createFlag != (TRUE+FALSE+FALSE)) {
233         fprintf (stderr,
234                  "Exactly one of 'c', 'x' or 't' must be specified\n");
235
236         exit (FALSE);
237     }
238
239     /* 
240      * Do the correct type of action supplying the rest of the
241      * command line arguments as the list of files to process.
242      */
243     if (createFlag==TRUE)
244         writeTarFile (argc, argv);
245     else
246         readTarFile (argc, argv);
247     if (errorFlag==TRUE)
248         fprintf (stderr, "\n");
249     exit (!errorFlag);
250 }
251
252
253 /*
254  * Read a tar file and extract or list the specified files within it.
255  * If the list is empty than all files are extracted or listed.
256  */
257 static void readTarFile (int fileCount, char **fileTable)
258 {
259     const char *cp;
260     int cc;
261     int inCc;
262     int blockSize;
263     char buf[BUF_SIZE];
264
265     skipFileFlag = FALSE;
266     badHeader = FALSE;
267     warnedRoot = FALSE;
268     eofFlag = FALSE;
269     inHeader = TRUE;
270     inCc = 0;
271     dataCc = 0;
272     outFd = -1;
273     blockSize = sizeof (buf);
274     cp = buf;
275
276     /* 
277      * Open the tar file for reading.
278      */
279     if ((tarName == NULL) || !strcmp (tarName, "-")) {
280         tarFd = fileno(stdin);
281     } else
282         tarFd = open (tarName, O_RDONLY);
283
284     if (tarFd < 0) {
285         perror (tarName);
286         errorFlag = TRUE;
287         return;
288     }
289
290     /* 
291      * Read blocks from the file until an end of file header block
292      * has been seen.  (A real end of file from a read is an error.)
293      */
294     while (eofFlag==FALSE) {
295         /* 
296          * Read the next block of data if necessary.
297          * This will be a large block if possible, which we will
298          * then process in the small tar blocks.
299          */
300         if (inCc <= 0) {
301             cp = buf;
302             inCc = fullRead (tarFd, buf, blockSize);
303
304             if (inCc < 0) {
305                 perror (tarName);
306                 errorFlag = TRUE;
307                 goto done;
308             }
309
310             if (inCc == 0) {
311                 fprintf (stderr,
312                          "Unexpected end of file from \"%s\"", tarName);
313                 errorFlag = TRUE;
314                 goto done;
315             }
316         }
317
318         /* 
319          * If we are expecting a header block then examine it.
320          */
321         if (inHeader==TRUE) {
322             readHeader ((const TarHeader *) cp, fileCount, fileTable);
323
324             cp += TAR_BLOCK_SIZE;
325             inCc -= TAR_BLOCK_SIZE;
326
327             continue;
328         }
329
330         /* 
331          * We are currently handling the data for a file.
332          * Process the minimum of the amount of data we have available
333          * and the amount left to be processed for the file.
334          */
335         cc = inCc;
336
337         if (cc > dataCc)
338             cc = dataCc;
339
340         readData (cp, cc);
341
342         /* 
343          * If the amount left isn't an exact multiple of the tar block
344          * size then round it up to the next block boundary since there
345          * is padding at the end of the file.
346          */
347         if (cc % TAR_BLOCK_SIZE)
348             cc += TAR_BLOCK_SIZE - (cc % TAR_BLOCK_SIZE);
349
350         cp += cc;
351         inCc -= cc;
352     }
353
354   done:
355     /* 
356      * Close the tar file if needed.
357      */
358     if ((tarFd >= 0) && (close (tarFd) < 0))
359         perror (tarName);
360
361     /* 
362      * Close the output file if needed.
363      * This is only done here on a previous error and so no
364      * message is required on errors.
365      */
366     if (tostdoutFlag == FALSE) {
367         if (outFd >= 0)
368             (void) close (outFd);
369     }
370 }
371
372
373 /*
374  * Examine the header block that was just read.
375  * This can specify the information for another file, or it can mark
376  * the end of the tar file.
377  */
378 static void
379 readHeader (const TarHeader * hp, int fileCount, char **fileTable)
380 {
381     int mode;
382     int uid;
383     int gid;
384     int checkSum;
385     unsigned int major;
386     unsigned int minor;
387     long size;
388     time_t mtime;
389     const char *name;
390     int cc;
391     int hardLink;
392     int softLink;
393     int devFileFlag;
394
395     /* 
396      * If the block is completely empty, then this is the end of the
397      * archive file.  If the name is null, then just skip this header.
398      */
399     name = hp->name;
400
401     if (*name == '\0') {
402         for (cc = TAR_BLOCK_SIZE; cc > 0; cc--) {
403             if (*name++)
404                 return;
405         }
406
407         eofFlag = TRUE;
408
409         return;
410     }
411
412     /* 
413      * There is another file in the archive to examine.
414      * Extract the encoded information and check it.
415      */
416     mode = getOctal (hp->mode, sizeof (hp->mode));
417     uid = getOctal (hp->uid, sizeof (hp->uid));
418     gid = getOctal (hp->gid, sizeof (hp->gid));
419     size = getOctal (hp->size, sizeof (hp->size));
420     mtime = getOctal (hp->mtime, sizeof (hp->mtime));
421     checkSum = getOctal (hp->checkSum, sizeof (hp->checkSum));
422     major = getOctal (hp->devMajor, sizeof (hp->devMajor));
423     minor = getOctal (hp->devMinor, sizeof (hp->devMinor));
424
425     if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) {
426         if (badHeader==FALSE)
427             fprintf (stderr, "Bad tar header, skipping\n");
428
429         badHeader = TRUE;
430
431         return;
432     }
433
434     badHeader = FALSE;
435     skipFileFlag = FALSE;
436     devFileFlag = FALSE;
437
438     /* 
439      * Check for the file modes.
440      */
441     hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) ||
442                 (hp->typeFlag == TAR_TYPE_HARD_LINK - '0'));
443
444     softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) ||
445                 (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0'));
446
447     /* 
448      * Check for a directory.
449      */
450     if (name[strlen (name) - 1] == '/')
451         mode |= S_IFDIR;
452
453     /* 
454      * Check for absolute paths in the file.
455      * If we find any, then warn the user and make them relative.
456      */
457     if (*name == '/') {
458         while (*name == '/')
459             name++;
460
461         if (warnedRoot==FALSE) {
462             fprintf (stderr,
463                      "Absolute path detected, removing leading slashes\n");
464         }
465
466         warnedRoot = TRUE;
467     }
468
469     /* 
470      * See if we want this file to be restored.
471      * If not, then set up to skip it.
472      */
473     if (wantFileName (name, fileCount, fileTable) == FALSE) {
474         if ( !hardLink && !softLink && (S_ISREG (mode) || S_ISCHR (mode)
475                     || S_ISBLK (mode) || S_ISSOCK(mode) || S_ISFIFO(mode) ) ) {
476             inHeader = (size == 0)? TRUE : FALSE;
477             dataCc = size;
478         }
479
480         skipFileFlag = TRUE;
481
482         return;
483     }
484
485     /* 
486      * This file is to be handled.
487      * If we aren't extracting then just list information about the file.
488      */
489     if (extractFlag==FALSE) {
490         if (verboseFlag==TRUE) {
491             printf ("%s %3d/%-d ", modeString (mode), uid, gid);
492             if( S_ISCHR (mode) || S_ISBLK (mode) )
493                 printf ("%4d,%4d %s ", major,minor, timeString (mtime));
494             else
495                 printf ("%9ld %s ", size, timeString (mtime));
496         }
497         printf ("%s", name);
498
499         if (hardLink)
500             printf (" (link to \"%s\")", hp->linkName);
501         else if (softLink)
502             printf (" (symlink to \"%s\")", hp->linkName);
503         else if (S_ISREG (mode) || S_ISCHR (mode) || S_ISBLK (mode) || 
504                 S_ISSOCK(mode) || S_ISFIFO(mode) ) {
505             inHeader = (size == 0)? TRUE : FALSE;
506             dataCc = size;
507         }
508
509         printf ("\n");
510
511         return;
512     }
513
514     /* 
515      * We really want to extract the file.
516      */
517     if (verboseFlag==TRUE)
518         printf ("x %s\n", name);
519
520     if (hardLink) {
521         if (link (hp->linkName, name) < 0)
522             perror (name);
523         chmod(name, mode);
524         chown(name, uid, gid);
525         return;
526     }
527
528     if (softLink) {
529 #ifdef  S_ISLNK
530         if (symlink (hp->linkName, name) < 0)
531             perror (name);
532         chmod(name, mode);
533         chown(name, uid, gid);
534 #else
535         fprintf (stderr, "Cannot create symbolic links\n");
536 #endif
537         return;
538     }
539
540     /* Set the umask for this process so it doesn't 
541      * screw things up. */
542     umask(0);
543
544     /* 
545      * If the file is a directory, then just create the path.
546      */
547     if (S_ISDIR (mode)) {
548         createPath (name, mode);
549         chmod(name, mode);
550         chown(name, uid, gid);
551
552         return;
553     }
554
555     /* 
556      * There is a file to write.
557      * First create the path to it if necessary with default permissions.
558      */
559     createPath (name, 0777);
560
561     inHeader = (size == 0)? TRUE : FALSE;
562     dataCc = size;
563
564     /* 
565      * Start the output file.
566      */
567     if (tostdoutFlag == TRUE)
568         outFd = fileno(stdout);
569     else {
570         if ( S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode) ) {
571             devFileFlag = TRUE;
572             outFd = mknod (name, mode, makedev(major, minor) );
573         }
574         else if (S_ISFIFO(mode) ) {
575             devFileFlag = TRUE;
576             outFd = mkfifo(name, mode);
577         } else {
578             outFd = open (name, O_WRONLY | O_CREAT | O_TRUNC, mode);
579         }
580     }
581
582     if (outFd < 0) {
583         perror (name);
584         skipFileFlag = TRUE;
585         return;
586     }
587     if (tostdoutFlag == FALSE) {
588         fchmod(outFd, mode);
589         fchown(outFd, uid, gid);
590     }
591
592     /* 
593      * If the file is empty, then that's all we need to do.
594      */
595     if (size == 0 && (tostdoutFlag == FALSE) && (devFileFlag == FALSE)) {
596         (void) close (outFd);
597         outFd = -1;
598     }
599 }
600
601
602 /*
603  * Handle a data block of some specified size that was read.
604  */
605 static void readData (const char *cp, int count)
606 {
607     /* 
608      * Reduce the amount of data left in this file.
609      * If there is no more data left, then we need to read
610      * the header again.
611      */
612     dataCc -= count;
613
614     if (dataCc <= 0)
615         inHeader = TRUE;
616
617     /* 
618      * If we aren't extracting files or this file is being
619      * skipped then do nothing more.
620      */
621     if (extractFlag==FALSE || skipFileFlag==TRUE)
622         return;
623
624     /* 
625      * Write the data to the output file.
626      */
627     if (fullWrite (outFd, cp, count) < 0) {
628         perror (outName);
629         if (tostdoutFlag == FALSE) {
630             (void) close (outFd);
631             outFd = -1;
632         }
633         skipFileFlag = TRUE;
634         return;
635     }
636
637     /* 
638      * If the write failed, close the file and disable further
639      * writes to this file.
640      */
641     if (dataCc <= 0 && tostdoutFlag == FALSE) {
642         if (close (outFd))
643             perror (outName);
644
645         outFd = -1;
646     }
647 }
648
649
650 /*
651  * Write a tar file containing the specified files.
652  */
653 static void writeTarFile (int fileCount, char **fileTable)
654 {
655     struct stat statbuf;
656
657     /* 
658      * Make sure there is at least one file specified.
659      */
660     if (fileCount <= 0) {
661         fprintf (stderr, "No files specified to be saved\n");
662         errorFlag = TRUE;
663     }
664
665     /* 
666      * Create the tar file for writing.
667      */
668     if ((tarName == NULL) || !strcmp (tarName, "-")) {
669         tostdoutFlag = TRUE;
670         tarFd = fileno(stdout);
671     } else
672         tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
673
674     if (tarFd < 0) {
675         perror (tarName);
676         errorFlag = TRUE;
677         return;
678     }
679
680     /* 
681      * Get the device and inode of the tar file for checking later.
682      */
683     if (fstat (tarFd, &statbuf) < 0) {
684         perror (tarName);
685         errorFlag = TRUE;
686         goto done;
687     }
688
689     tarDev = statbuf.st_dev;
690     tarInode = statbuf.st_ino;
691                 
692     /* 
693      * Append each file name into the archive file.
694      * Follow symbolic links for these top level file names.
695      */
696     while (errorFlag==FALSE && (fileCount-- > 0)) {
697         saveFile (*fileTable++, FALSE);
698     }
699
700     /* 
701      * Now write an empty block of zeroes to end the archive.
702      */
703     writeTarBlock ("", 1);
704
705
706   done:
707     /* 
708      * Close the tar file and check for errors if it was opened.
709      */
710     if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close (tarFd) < 0))
711         perror (tarName);
712 }
713
714
715 /*
716  * Save one file into the tar file.
717  * If the file is a directory, then this will recursively save all of
718  * the files and directories within the directory.  The seeLinks
719  * flag indicates whether or not we want to see symbolic links as
720  * they really are, instead of blindly following them.
721  */
722 static void saveFile (const char *fileName, int seeLinks)
723 {
724     int status;
725     int mode;
726     struct stat statbuf;
727
728     if (verboseFlag==TRUE)
729         printf ("a %s\n", fileName);
730
731     /* 
732      * Check that the file name will fit in the header.
733      */
734     if (strlen (fileName) >= TAR_NAME_SIZE) {
735         fprintf (stderr, "%s: File name is too long\n", fileName);
736
737         return;
738     }
739
740     /* 
741      * Find out about the file.
742      */
743 #ifdef  S_ISLNK
744     if (seeLinks==TRUE)
745         status = lstat (fileName, &statbuf);
746     else
747 #endif
748         status = stat (fileName, &statbuf);
749
750     if (status < 0) {
751         perror (fileName);
752
753         return;
754     }
755
756     /* 
757      * Make sure we aren't trying to save our file into itself.
758      */
759     if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) {
760         fprintf (stderr, "Skipping saving of archive file itself\n");
761
762         return;
763     }
764
765     /* 
766      * Check the type of file.
767      */
768     mode = statbuf.st_mode;
769
770     if (S_ISDIR (mode)) {
771         saveDirectory (fileName, &statbuf);
772
773         return;
774     }
775     if (S_ISREG (mode)) {
776         saveRegularFile (fileName, &statbuf);
777
778         return;
779     }
780     
781     /* Some day add support for tarring these up... but not today. :) */
782 //  if (S_ISLNK(mode) || S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR (mode) ) {
783 //      fprintf (stderr, "%s: This version of tar can't store this type of file\n", fileName);
784 //  }
785
786     /* 
787      * The file is a strange type of file, ignore it.
788      */
789     fprintf (stderr, "%s: not a directory or regular file\n", fileName);
790 }
791
792
793 /*
794  * Save a regular file to the tar file.
795  */
796 static void
797 saveRegularFile (const char *fileName, const struct stat *statbuf)
798 {
799     int sawEof;
800     int fileFd;
801     int cc;
802     int dataCount;
803     long fullDataCount;
804     char data[TAR_BLOCK_SIZE * 16];
805
806     /* 
807      * Open the file for reading.
808      */
809     fileFd = open (fileName, O_RDONLY);
810
811     if (fileFd < 0) {
812         perror (fileName);
813
814         return;
815     }
816
817     /* 
818      * Write out the header for the file.
819      */
820     writeHeader (fileName, statbuf);
821
822     /* 
823      * Write the data blocks of the file.
824      * We must be careful to write the amount of data that the stat
825      * buffer indicated, even if the file has changed size.  Otherwise
826      * the tar file will be incorrect.
827      */
828     fullDataCount = statbuf->st_size;
829     sawEof = FALSE;
830
831     while (fullDataCount > 0) {
832         /* 
833          * Get the amount to write this iteration which is
834          * the minumum of the amount left to write and the
835          * buffer size.
836          */
837         dataCount = sizeof (data);
838
839         if (dataCount > fullDataCount)
840             dataCount = (int) fullDataCount;
841
842         /* 
843          * Read the data from the file if we haven't seen the
844          * end of file yet.
845          */
846         cc = 0;
847
848         if (sawEof==FALSE) {
849             cc = fullRead (fileFd, data, dataCount);
850
851             if (cc < 0) {
852                 perror (fileName);
853
854                 (void) close (fileFd);
855                 errorFlag = TRUE;
856
857                 return;
858             }
859
860             /* 
861              * If the file ended too soon, complain and set
862              * a flag so we will zero fill the rest of it.
863              */
864             if (cc < dataCount) {
865                 fprintf (stderr,
866                          "%s: Short read - zero filling", fileName);
867
868                 sawEof = TRUE;
869             }
870         }
871
872         /* 
873          * Zero fill the rest of the data if necessary.
874          */
875         if (cc < dataCount)
876             memset (data + cc, 0, dataCount - cc);
877
878         /* 
879          * Write the buffer to the TAR file.
880          */
881         writeTarBlock (data, dataCount);
882
883         fullDataCount -= dataCount;
884     }
885
886     /* 
887      * Close the file.
888      */
889     if ((tostdoutFlag == FALSE) && close (fileFd) < 0)
890         fprintf (stderr, "%s: close: %s\n", fileName, strerror (errno));
891 }
892
893
894 /*
895  * Save a directory and all of its files to the tar file.
896  */
897 static void saveDirectory (const char *dirName, const struct stat *statbuf)
898 {
899     DIR *dir;
900     struct dirent *entry;
901     int needSlash;
902     char fullName[NAME_MAX];
903
904     /* 
905      * Construct the directory name as used in the tar file by appending
906      * a slash character to it.
907      */
908     strcpy (fullName, dirName);
909     strcat (fullName, "/");
910
911     /* 
912      * Write out the header for the directory entry.
913      */
914     writeHeader (fullName, statbuf);
915
916     /* 
917      * Open the directory.
918      */
919     dir = opendir (dirName);
920
921     if (dir == NULL) {
922         fprintf (stderr, "Cannot read directory \"%s\": %s\n",
923                  dirName, strerror (errno));
924
925         return;
926     }
927
928     /* 
929      * See if a slash is needed.
930      */
931     needSlash = (*dirName && (dirName[strlen (dirName) - 1] != '/'));
932
933     /* 
934      * Read all of the directory entries and check them,
935      * except for the current and parent directory entries.
936      */
937     while (errorFlag==FALSE && ((entry = readdir (dir)) != NULL)) {
938         if ((strcmp (entry->d_name, ".") == 0) ||
939             (strcmp (entry->d_name, "..") == 0)) {
940             continue;
941         }
942
943         /* 
944          * Build the full path name to the file.
945          */
946         strcpy (fullName, dirName);
947
948         if (needSlash)
949             strcat (fullName, "/");
950
951         strcat (fullName, entry->d_name);
952
953         /* 
954          * Write this file to the tar file, noticing whether or not
955          * the file is a symbolic link.
956          */
957         saveFile (fullName, TRUE);
958     }
959
960     /* 
961      * All done, close the directory.
962      */
963     closedir (dir);
964 }
965
966
967 /*
968  * Write a tar header for the specified file name and status.
969  * It is assumed that the file name fits.
970  */
971 static void writeHeader (const char *fileName, const struct stat *statbuf)
972 {
973     long checkSum;
974     const unsigned char *cp;
975     int len;
976     TarHeader header;
977
978     /* 
979      * Zero the header block in preparation for filling it in.
980      */
981     memset ((char *) &header, 0, sizeof (header));
982
983     /* 
984      * Fill in the header.
985      */
986     strcpy (header.name, fileName);
987
988     strncpy (header.magic, TAR_MAGIC, sizeof (header.magic));
989     strncpy (header.version, TAR_VERSION, sizeof (header.version));
990
991     putOctal (header.mode, sizeof (header.mode), statbuf->st_mode & 0777);
992     putOctal (header.uid, sizeof (header.uid), statbuf->st_uid);
993     putOctal (header.gid, sizeof (header.gid), statbuf->st_gid);
994     putOctal (header.size, sizeof (header.size), statbuf->st_size);
995     putOctal (header.mtime, sizeof (header.mtime), statbuf->st_mtime);
996
997     header.typeFlag = TAR_TYPE_REGULAR;
998
999     /* 
1000      * Calculate and store the checksum.
1001      * This is the sum of all of the bytes of the header,
1002      * with the checksum field itself treated as blanks.
1003      */
1004     memset (header.checkSum, ' ', sizeof (header.checkSum));
1005
1006     cp = (const unsigned char *) &header;
1007     len = sizeof (header);
1008     checkSum = 0;
1009
1010     while (len-- > 0)
1011         checkSum += *cp++;
1012
1013     putOctal (header.checkSum, sizeof (header.checkSum), checkSum);
1014
1015     /* 
1016      * Write the tar header.
1017      */
1018     writeTarBlock ((const char *) &header, sizeof (header));
1019 }
1020
1021
1022 /*
1023  * Write data to one or more blocks of the tar file.
1024  * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
1025  * The errorFlag static variable is set on an error.
1026  */
1027 static void writeTarBlock (const char *buf, int len)
1028 {
1029     int partialLength;
1030     int completeLength;
1031     char fullBlock[TAR_BLOCK_SIZE];
1032
1033     /* 
1034      * If we had a write error before, then do nothing more.
1035      */
1036     if (errorFlag==TRUE)
1037         return;
1038
1039     /* 
1040      * Get the amount of complete and partial blocks.
1041      */
1042     partialLength = len % TAR_BLOCK_SIZE;
1043     completeLength = len - partialLength;
1044
1045     /* 
1046      * Write all of the complete blocks.
1047      */
1048     if ((completeLength > 0) && !fullWrite (tarFd, buf, completeLength)) {
1049         perror (tarName);
1050
1051         errorFlag = TRUE;
1052
1053         return;
1054     }
1055
1056     /* 
1057      * If there are no partial blocks left, we are done.
1058      */
1059     if (partialLength == 0)
1060         return;
1061
1062     /* 
1063      * Copy the partial data into a complete block, and pad the rest
1064      * of it with zeroes.
1065      */
1066     memcpy (fullBlock, buf + completeLength, partialLength);
1067     memset (fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
1068
1069     /* 
1070      * Write the last complete block.
1071      */
1072     if (!fullWrite (tarFd, fullBlock, TAR_BLOCK_SIZE)) {
1073         perror (tarName);
1074
1075         errorFlag = TRUE;
1076     }
1077 }
1078
1079
1080 /*
1081  * Read an octal value in a field of the specified width, with optional
1082  * spaces on both sides of the number and with an optional null character
1083  * at the end.  Returns -1 on an illegal format.
1084  */
1085 static long getOctal (const char *cp, int len)
1086 {
1087     long val;
1088
1089     while ((len > 0) && (*cp == ' ')) {
1090         cp++;
1091         len--;
1092     }
1093
1094     if ((len == 0) || !isOctal (*cp))
1095         return -1;
1096
1097     val = 0;
1098
1099     while ((len > 0) && isOctal (*cp)) {
1100         val = val * 8 + *cp++ - '0';
1101         len--;
1102     }
1103
1104     while ((len > 0) && (*cp == ' ')) {
1105         cp++;
1106         len--;
1107     }
1108
1109     if ((len > 0) && *cp)
1110         return -1;
1111
1112     return val;
1113 }
1114
1115
1116 /*
1117  * Put an octal string into the specified buffer.
1118  * The number is zero and space padded and possibly null padded.
1119  * Returns TRUE if successful.
1120  */
1121 static int putOctal (char *cp, int len, long value)
1122 {
1123     int tempLength;
1124     char *tempString;
1125     char tempBuffer[32];
1126
1127     /* 
1128      * Create a string of the specified length with an initial space,
1129      * leading zeroes and the octal number, and a trailing null.
1130      */
1131     tempString = tempBuffer;
1132
1133     sprintf (tempString, " %0*lo", len - 2, value);
1134
1135     tempLength = strlen (tempString) + 1;
1136
1137     /* 
1138      * If the string is too large, suppress the leading space.
1139      */
1140     if (tempLength > len) {
1141         tempLength--;
1142         tempString++;
1143     }
1144
1145     /* 
1146      * If the string is still too large, suppress the trailing null.
1147      */
1148     if (tempLength > len)
1149         tempLength--;
1150
1151     /* 
1152      * If the string is still too large, fail.
1153      */
1154     if (tempLength > len)
1155         return FALSE;
1156
1157     /* 
1158      * Copy the string to the field.
1159      */
1160     memcpy (cp, tempString, len);
1161
1162     return TRUE;
1163 }
1164
1165
1166 /*
1167  * See if the specified file name belongs to one of the specified list
1168  * of path prefixes.  An empty list implies that all files are wanted.
1169  * Returns TRUE if the file is selected.
1170  */
1171 static int
1172 wantFileName (const char *fileName, int fileCount, char **fileTable)
1173 {
1174     const char *pathName;
1175     int fileLength;
1176     int pathLength;
1177
1178     /* 
1179      * If there are no files in the list, then the file is wanted.
1180      */
1181     if (fileCount == 0)
1182         return TRUE;
1183
1184     fileLength = strlen (fileName);
1185
1186     /* 
1187      * Check each of the test paths.
1188      */
1189     while (fileCount-- > 0) {
1190         pathName = *fileTable++;
1191
1192         pathLength = strlen (pathName);
1193
1194         if (fileLength < pathLength)
1195             continue;
1196
1197         if (memcmp (fileName, pathName, pathLength) != 0)
1198             continue;
1199
1200         if ((fileLength == pathLength) || (fileName[pathLength] == '/')) {
1201             return TRUE;
1202         }
1203     }
1204
1205     return FALSE;
1206 }
1207
1208
1209
1210 /* END CODE */