Updates
[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
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             devFileFlag = TRUE;
563             outFd = mkfifo(name, mode);
564         } else {
565             outFd = open (name, O_WRONLY | O_CREAT | O_TRUNC, mode);
566         }
567     }
568
569     if (outFd < 0) {
570         perror (name);
571         skipFileFlag = TRUE;
572         return;
573     }
574
575     /* 
576      * If the file is empty, then that's all we need to do.
577      */
578     if (size == 0 && (tostdoutFlag == FALSE) && (devFileFlag == FALSE)) {
579         (void) close (outFd);
580         outFd = -1;
581     }
582 }
583
584
585 /*
586  * Handle a data block of some specified size that was read.
587  */
588 static void readData (const char *cp, int count)
589 {
590     /* 
591      * Reduce the amount of data left in this file.
592      * If there is no more data left, then we need to read
593      * the header again.
594      */
595     dataCc -= count;
596
597     if (dataCc <= 0)
598         inHeader = TRUE;
599
600     /* 
601      * If we aren't extracting files or this file is being
602      * skipped then do nothing more.
603      */
604     if (extractFlag==FALSE || skipFileFlag==TRUE)
605         return;
606
607     /* 
608      * Write the data to the output file.
609      */
610     if (fullWrite (outFd, cp, count) < 0) {
611         perror (outName);
612         if (tostdoutFlag == FALSE) {
613             (void) close (outFd);
614             outFd = -1;
615         }
616         skipFileFlag = TRUE;
617         return;
618     }
619
620     /* 
621      * If the write failed, close the file and disable further
622      * writes to this file.
623      */
624     if (dataCc <= 0 && tostdoutFlag == FALSE) {
625         if (close (outFd))
626             perror (outName);
627
628         outFd = -1;
629     }
630 }
631
632
633 /*
634  * Write a tar file containing the specified files.
635  */
636 static void writeTarFile (int fileCount, char **fileTable)
637 {
638     struct stat statbuf;
639
640     /* 
641      * Make sure there is at least one file specified.
642      */
643     if (fileCount <= 0) {
644         fprintf (stderr, "No files specified to be saved\n");
645         errorFlag = TRUE;
646     }
647
648     /* 
649      * Create the tar file for writing.
650      */
651     if ((tarName == NULL) || !strcmp (tarName, "-")) {
652         tostdoutFlag = TRUE;
653         tarFd = STDOUT;
654     } else
655         tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666);
656
657     if (tarFd < 0) {
658         perror (tarName);
659         errorFlag = TRUE;
660         return;
661     }
662
663     /* 
664      * Get the device and inode of the tar file for checking later.
665      */
666     if (fstat (tarFd, &statbuf) < 0) {
667         perror (tarName);
668         errorFlag = TRUE;
669         goto done;
670     }
671
672     tarDev = statbuf.st_dev;
673     tarInode = statbuf.st_ino;
674
675     /* 
676      * Append each file name into the archive file.
677      * Follow symbolic links for these top level file names.
678      */
679     while (errorFlag==FALSE && (fileCount-- > 0)) {
680         saveFile (*fileTable++, FALSE);
681     }
682
683     /* 
684      * Now write an empty block of zeroes to end the archive.
685      */
686     writeTarBlock ("", 1);
687
688
689   done:
690     /* 
691      * Close the tar file and check for errors if it was opened.
692      */
693     if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close (tarFd) < 0))
694         perror (tarName);
695 }
696
697
698 /*
699  * Save one file into the tar file.
700  * If the file is a directory, then this will recursively save all of
701  * the files and directories within the directory.  The seeLinks
702  * flag indicates whether or not we want to see symbolic links as
703  * they really are, instead of blindly following them.
704  */
705 static void saveFile (const char *fileName, int seeLinks)
706 {
707     int status;
708     int mode;
709     struct stat statbuf;
710
711     if (verboseFlag==TRUE)
712         printf ("a %s\n", fileName);
713
714     /* 
715      * Check that the file name will fit in the header.
716      */
717     if (strlen (fileName) >= TAR_NAME_SIZE) {
718         fprintf (stderr, "%s: File name is too long\n", fileName);
719
720         return;
721     }
722
723     /* 
724      * Find out about the file.
725      */
726 #ifdef  S_ISLNK
727     if (seeLinks==TRUE)
728         status = lstat (fileName, &statbuf);
729     else
730 #endif
731         status = stat (fileName, &statbuf);
732
733     if (status < 0) {
734         perror (fileName);
735
736         return;
737     }
738
739     /* 
740      * Make sure we aren't trying to save our file into itself.
741      */
742     if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) {
743         fprintf (stderr, "Skipping saving of archive file itself\n");
744
745         return;
746     }
747
748     /* 
749      * Check the type of file.
750      */
751     mode = statbuf.st_mode;
752
753     if (S_ISDIR (mode)) {
754         saveDirectory (fileName, &statbuf);
755
756         return;
757     }
758     if (S_ISREG (mode)) {
759         saveRegularFile (fileName, &statbuf);
760
761         return;
762     }
763     
764     /* Some day add support for tarring these up... but not today. :) */
765 //  if (S_ISLNK(mode) || S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR (mode) ) {
766 //      fprintf (stderr, "%s: This version of tar can't store this type of file\n", fileName);
767 //  }
768
769     /* 
770      * The file is a strange type of file, ignore it.
771      */
772     fprintf (stderr, "%s: not a directory or regular file\n", fileName);
773 }
774
775
776 /*
777  * Save a regular file to the tar file.
778  */
779 static void
780 saveRegularFile (const char *fileName, const struct stat *statbuf)
781 {
782     int sawEof;
783     int fileFd;
784     int cc;
785     int dataCount;
786     long fullDataCount;
787     char data[TAR_BLOCK_SIZE * 16];
788
789     /* 
790      * Open the file for reading.
791      */
792     fileFd = open (fileName, O_RDONLY);
793
794     if (fileFd < 0) {
795         perror (fileName);
796
797         return;
798     }
799
800     /* 
801      * Write out the header for the file.
802      */
803     writeHeader (fileName, statbuf);
804
805     /* 
806      * Write the data blocks of the file.
807      * We must be careful to write the amount of data that the stat
808      * buffer indicated, even if the file has changed size.  Otherwise
809      * the tar file will be incorrect.
810      */
811     fullDataCount = statbuf->st_size;
812     sawEof = FALSE;
813
814     while (fullDataCount > 0) {
815         /* 
816          * Get the amount to write this iteration which is
817          * the minumum of the amount left to write and the
818          * buffer size.
819          */
820         dataCount = sizeof (data);
821
822         if (dataCount > fullDataCount)
823             dataCount = (int) fullDataCount;
824
825         /* 
826          * Read the data from the file if we haven't seen the
827          * end of file yet.
828          */
829         cc = 0;
830
831         if (sawEof==FALSE) {
832             cc = fullRead (fileFd, data, dataCount);
833
834             if (cc < 0) {
835                 perror (fileName);
836
837                 (void) close (fileFd);
838                 errorFlag = TRUE;
839
840                 return;
841             }
842
843             /* 
844              * If the file ended too soon, complain and set
845              * a flag so we will zero fill the rest of it.
846              */
847             if (cc < dataCount) {
848                 fprintf (stderr,
849                          "%s: Short read - zero filling", fileName);
850
851                 sawEof = TRUE;
852             }
853         }
854
855         /* 
856          * Zero fill the rest of the data if necessary.
857          */
858         if (cc < dataCount)
859             memset (data + cc, 0, dataCount - cc);
860
861         /* 
862          * Write the buffer to the TAR file.
863          */
864         writeTarBlock (data, dataCount);
865
866         fullDataCount -= dataCount;
867     }
868
869     /* 
870      * Close the file.
871      */
872     if ((tostdoutFlag == FALSE) && close (fileFd) < 0)
873         fprintf (stderr, "%s: close: %s\n", fileName, strerror (errno));
874 }
875
876
877 /*
878  * Save a directory and all of its files to the tar file.
879  */
880 static void saveDirectory (const char *dirName, const struct stat *statbuf)
881 {
882     DIR *dir;
883     struct dirent *entry;
884     int needSlash;
885     char fullName[NAME_MAX];
886
887     /* 
888      * Construct the directory name as used in the tar file by appending
889      * a slash character to it.
890      */
891     strcpy (fullName, dirName);
892     strcat (fullName, "/");
893
894     /* 
895      * Write out the header for the directory entry.
896      */
897     writeHeader (fullName, statbuf);
898
899     /* 
900      * Open the directory.
901      */
902     dir = opendir (dirName);
903
904     if (dir == NULL) {
905         fprintf (stderr, "Cannot read directory \"%s\": %s\n",
906                  dirName, strerror (errno));
907
908         return;
909     }
910
911     /* 
912      * See if a slash is needed.
913      */
914     needSlash = (*dirName && (dirName[strlen (dirName) - 1] != '/'));
915
916     /* 
917      * Read all of the directory entries and check them,
918      * except for the current and parent directory entries.
919      */
920     while (errorFlag==FALSE && ((entry = readdir (dir)) != NULL)) {
921         if ((strcmp (entry->d_name, ".") == 0) ||
922             (strcmp (entry->d_name, "..") == 0)) {
923             continue;
924         }
925
926         /* 
927          * Build the full path name to the file.
928          */
929         strcpy (fullName, dirName);
930
931         if (needSlash)
932             strcat (fullName, "/");
933
934         strcat (fullName, entry->d_name);
935
936         /* 
937          * Write this file to the tar file, noticing whether or not
938          * the file is a symbolic link.
939          */
940         saveFile (fullName, TRUE);
941     }
942
943     /* 
944      * All done, close the directory.
945      */
946     closedir (dir);
947 }
948
949
950 /*
951  * Write a tar header for the specified file name and status.
952  * It is assumed that the file name fits.
953  */
954 static void writeHeader (const char *fileName, const struct stat *statbuf)
955 {
956     long checkSum;
957     const unsigned char *cp;
958     int len;
959     TarHeader header;
960
961     /* 
962      * Zero the header block in preparation for filling it in.
963      */
964     memset ((char *) &header, 0, sizeof (header));
965
966     /* 
967      * Fill in the header.
968      */
969     strcpy (header.name, fileName);
970
971     strncpy (header.magic, TAR_MAGIC, sizeof (header.magic));
972     strncpy (header.version, TAR_VERSION, sizeof (header.version));
973
974     putOctal (header.mode, sizeof (header.mode), statbuf->st_mode & 0777);
975     putOctal (header.uid, sizeof (header.uid), statbuf->st_uid);
976     putOctal (header.gid, sizeof (header.gid), statbuf->st_gid);
977     putOctal (header.size, sizeof (header.size), statbuf->st_size);
978     putOctal (header.mtime, sizeof (header.mtime), statbuf->st_mtime);
979
980     header.typeFlag = TAR_TYPE_REGULAR;
981
982     /* 
983      * Calculate and store the checksum.
984      * This is the sum of all of the bytes of the header,
985      * with the checksum field itself treated as blanks.
986      */
987     memset (header.checkSum, ' ', sizeof (header.checkSum));
988
989     cp = (const unsigned char *) &header;
990     len = sizeof (header);
991     checkSum = 0;
992
993     while (len-- > 0)
994         checkSum += *cp++;
995
996     putOctal (header.checkSum, sizeof (header.checkSum), checkSum);
997
998     /* 
999      * Write the tar header.
1000      */
1001     writeTarBlock ((const char *) &header, sizeof (header));
1002 }
1003
1004
1005 /*
1006  * Write data to one or more blocks of the tar file.
1007  * The data is always padded out to a multiple of TAR_BLOCK_SIZE.
1008  * The errorFlag static variable is set on an error.
1009  */
1010 static void writeTarBlock (const char *buf, int len)
1011 {
1012     int partialLength;
1013     int completeLength;
1014     char fullBlock[TAR_BLOCK_SIZE];
1015
1016     /* 
1017      * If we had a write error before, then do nothing more.
1018      */
1019     if (errorFlag==TRUE)
1020         return;
1021
1022     /* 
1023      * Get the amount of complete and partial blocks.
1024      */
1025     partialLength = len % TAR_BLOCK_SIZE;
1026     completeLength = len - partialLength;
1027
1028     /* 
1029      * Write all of the complete blocks.
1030      */
1031     if ((completeLength > 0) && !fullWrite (tarFd, buf, completeLength)) {
1032         perror (tarName);
1033
1034         errorFlag = TRUE;
1035
1036         return;
1037     }
1038
1039     /* 
1040      * If there are no partial blocks left, we are done.
1041      */
1042     if (partialLength == 0)
1043         return;
1044
1045     /* 
1046      * Copy the partial data into a complete block, and pad the rest
1047      * of it with zeroes.
1048      */
1049     memcpy (fullBlock, buf + completeLength, partialLength);
1050     memset (fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength);
1051
1052     /* 
1053      * Write the last complete block.
1054      */
1055     if (!fullWrite (tarFd, fullBlock, TAR_BLOCK_SIZE)) {
1056         perror (tarName);
1057
1058         errorFlag = TRUE;
1059     }
1060 }
1061
1062
1063 /*
1064  * Read an octal value in a field of the specified width, with optional
1065  * spaces on both sides of the number and with an optional null character
1066  * at the end.  Returns -1 on an illegal format.
1067  */
1068 static long getOctal (const char *cp, int len)
1069 {
1070     long val;
1071
1072     while ((len > 0) && (*cp == ' ')) {
1073         cp++;
1074         len--;
1075     }
1076
1077     if ((len == 0) || !isOctal (*cp))
1078         return -1;
1079
1080     val = 0;
1081
1082     while ((len > 0) && isOctal (*cp)) {
1083         val = val * 8 + *cp++ - '0';
1084         len--;
1085     }
1086
1087     while ((len > 0) && (*cp == ' ')) {
1088         cp++;
1089         len--;
1090     }
1091
1092     if ((len > 0) && *cp)
1093         return -1;
1094
1095     return val;
1096 }
1097
1098
1099 /*
1100  * Put an octal string into the specified buffer.
1101  * The number is zero and space padded and possibly null padded.
1102  * Returns TRUE if successful.
1103  */
1104 static int putOctal (char *cp, int len, long value)
1105 {
1106     int tempLength;
1107     char *tempString;
1108     char tempBuffer[32];
1109
1110     /* 
1111      * Create a string of the specified length with an initial space,
1112      * leading zeroes and the octal number, and a trailing null.
1113      */
1114     tempString = tempBuffer;
1115
1116     sprintf (tempString, " %0*lo", len - 2, value);
1117
1118     tempLength = strlen (tempString) + 1;
1119
1120     /* 
1121      * If the string is too large, suppress the leading space.
1122      */
1123     if (tempLength > len) {
1124         tempLength--;
1125         tempString++;
1126     }
1127
1128     /* 
1129      * If the string is still too large, suppress the trailing null.
1130      */
1131     if (tempLength > len)
1132         tempLength--;
1133
1134     /* 
1135      * If the string is still too large, fail.
1136      */
1137     if (tempLength > len)
1138         return FALSE;
1139
1140     /* 
1141      * Copy the string to the field.
1142      */
1143     memcpy (cp, tempString, len);
1144
1145     return TRUE;
1146 }
1147
1148
1149 /*
1150  * See if the specified file name belongs to one of the specified list
1151  * of path prefixes.  An empty list implies that all files are wanted.
1152  * Returns TRUE if the file is selected.
1153  */
1154 static int
1155 wantFileName (const char *fileName, int fileCount, char **fileTable)
1156 {
1157     const char *pathName;
1158     int fileLength;
1159     int pathLength;
1160
1161     /* 
1162      * If there are no files in the list, then the file is wanted.
1163      */
1164     if (fileCount == 0)
1165         return TRUE;
1166
1167     fileLength = strlen (fileName);
1168
1169     /* 
1170      * Check each of the test paths.
1171      */
1172     while (fileCount-- > 0) {
1173         pathName = *fileTable++;
1174
1175         pathLength = strlen (pathName);
1176
1177         if (fileLength < pathLength)
1178             continue;
1179
1180         if (memcmp (fileName, pathName, pathLength) != 0)
1181             continue;
1182
1183         if ((fileLength == pathLength) || (fileName[pathLength] == '/')) {
1184             return TRUE;
1185         }
1186     }
1187
1188     return FALSE;
1189 }
1190
1191
1192
1193 /* END CODE */