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