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