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