diff options
Diffstat (limited to 'lib/tarfn.c')
-rw-r--r-- | lib/tarfn.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/lib/tarfn.c b/lib/tarfn.c new file mode 100644 index 000000000..5a16a1642 --- /dev/null +++ b/lib/tarfn.c @@ -0,0 +1,165 @@ +/* + * Functions for extracting tar archives. + * Bruce Perens, April-May 1995 + * Copyright (C) 1995 Bruce Perens + * This is free software under the GNU General Public License. + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include "tarfn.h" + +struct TarHeader { + char Name[100]; + char Mode[8]; + char UserID[8]; + char GroupID[8]; + char Size[12]; + char ModificationTime[12]; + char Checksum[8]; + char LinkFlag; + char LinkName[100]; + char MagicNumber[8]; + char UserName[32]; + char GroupName[32]; + char MajorDevice[8]; + char MinorDevice[8]; +}; +typedef struct TarHeader TarHeader; + +static const unsigned int TarChecksumOffset + = (unsigned int)&(((TarHeader *)0)->Checksum); + +/* Octal-ASCII-to-long */ +static long +OtoL(const char * s, int size) +{ + int n = 0; + + while ( *s == ' ' ) { + s++; + size--; + } + + while ( --size >= 0 && *s >= '0' && *s <= '7' ) + n = (n * 010) + (*s++ - '0'); + + return n; +} + +static int +DecodeTarHeader(char * block, TarInfo * d) +{ + TarHeader * h = (TarHeader *)block; + unsigned char * s = (unsigned char *)block; + struct passwd * passwd = 0; + struct group * group = 0; + unsigned int i; + long sum; + long checksum; + + if ( *h->UserName ) + passwd = getpwnam(h->UserName); + if ( *h->GroupName ) + group = getgrnam(h->GroupName); + + d->Name = h->Name; + d->LinkName = h->LinkName; + d->Mode = (mode_t)OtoL(h->Mode, sizeof(h->Mode)); + d->Size = (size_t)OtoL(h->Size, sizeof(h->Size)); + d->ModTime = (time_t)OtoL(h->ModificationTime + ,sizeof(h->ModificationTime)); + d->Device = ((OtoL(h->MajorDevice, sizeof(h->MajorDevice)) & 0xff) << 8) + | (OtoL(h->MinorDevice, sizeof(h->MinorDevice)) & 0xff); + checksum = OtoL(h->Checksum, sizeof(h->Checksum)); + d->UserID = (uid_t)OtoL(h->UserID, sizeof(h->UserID)); + d->GroupID = (gid_t)OtoL(h->GroupID, sizeof(h->GroupID)); + d->Type = (TarFileType)h->LinkFlag; + + if ( passwd ) + d->UserID = passwd->pw_uid; + + if ( group ) + d->GroupID = group->gr_gid; + + + sum = ' ' * sizeof(h->Checksum);/* Treat checksum field as all blank */ + for ( i = TarChecksumOffset; i > 0; i-- ) + sum += *s++; + s += sizeof(h->Checksum); /* Skip the real checksum field */ + for ( i = (512 - TarChecksumOffset - sizeof(h->Checksum)); i > 0; i-- ) + sum += *s++; + + return ( sum == checksum ); +} + +extern int +TarExtractor( + void * userData +,const TarFunctions * functions) +{ + int status; + char buffer[512]; + TarInfo h; + + h.UserData = userData; + + while ( (status = functions->Read(userData, buffer, 512)) == 512 ) { + int nameLength; + + if ( !DecodeTarHeader(buffer, &h) ) { + if ( h.Name[0] == '\0' ) { + return 0; /* End of tape */ + } else { + errno = 0; /* Indicates broken tarfile */ + return -1; /* Header checksum error */ + } + } + if ( h.Name[0] == '\0' ) { + errno = 0; /* Indicates broken tarfile */ + return -1; /* Bad header data */ + } + + nameLength = strlen(h.Name); + + switch ( h.Type ) { + case NormalFile0: + case NormalFile1: + /* Compatibility with pre-ANSI ustar */ + if ( h.Name[nameLength - 1] != '/' ) { + status = (*functions->ExtractFile)(&h); + break; + } + /* Else, Fall Through */ + case Directory: + h.Name[nameLength - 1] = '\0'; + status = (*functions->MakeDirectory)(&h); + break; + case HardLink: + status = (*functions->MakeHardLink)(&h); + break; + case SymbolicLink: + status = (*functions->MakeSymbolicLink)(&h); + break; + case CharacterDevice: + case BlockDevice: + case FIFO: + status = (*functions->MakeSpecialFile)(&h); + break; + default: + errno = 0; /* Indicates broken tarfile */ + return -1; /* Bad header field */ + } + if ( status != 0 ) + return status; /* Pass on status from coroutine */ + } + if ( status > 0 ) { /* Read partial header record */ + errno = 0; /* Indicates broken tarfile */ + return -1; + } else { + return status; /* Whatever I/O function returned */ + } +} |