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