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