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