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