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