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