diff options
Diffstat (limited to 'lib/star.c')
-rw-r--r-- | lib/star.c | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/lib/star.c b/lib/star.c new file mode 100644 index 000000000..e43ddf09b --- /dev/null +++ b/lib/star.c @@ -0,0 +1,158 @@ +#include "tarfn.h" +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <utime.h> +#include <errno.h> +#include <string.h> +#include <time.h> + +static int +Read(void * userData, char * buffer, int length) +{ + /* + * If the status of the read function is < 0, it will be returned to + * the caller of TarExtractor(). + */ + return read((int)userData, buffer, length); +} + +static int +IOError(TarInfo * i) +{ + int error = errno; /* fflush() could cause errno to change */ + fflush(stdout); + fprintf(stderr, "%s: %s\n", i->Name, strerror(error)); + + /* + * The status returned by a coroutine of TarExtractor(), if it + * is non-zero, will be returned to the caller of TarExtractor(). + */ + return -2; +} + +static int +ExtractFile(TarInfo * i) +{ + /* + * If you don't want to extract the file, you must advance the tape + * by the file size rounded up to the next 512-byte boundary and + * return 0. + */ + + int fd = open(i->Name, O_CREAT|O_TRUNC|O_WRONLY, i->Mode & ~S_IFMT); + char buffer[512]; + size_t size = i->Size; + struct utimbuf t; + + if ( fd < 0 ) + return IOError(i); + + printf("File: %s\n", i->Name); + + while ( size > 0 ) { + size_t writeSize = size >= 512 ? 512 : size; + + if ( Read(i->UserData, buffer, 512) != 512 ) + return -1; /* Something wrong with archive */ + if ( write(fd, buffer, writeSize) != writeSize ) + return IOError(i); /* Write failure. */ + + size -= writeSize; + } + /* fchown() and fchmod() are cheaper than chown() and chmod(). */ + fchown(fd, i->UserID, i->GroupID); + fchmod(fd, i->Mode & ~S_IFMT); + close(fd); + t.actime = time(0); + t.modtime = i->ModTime; + utime(i->Name, &t); + return 0; +} + +static int +SetModes(TarInfo * i) +{ + struct utimbuf t; + chown(i->Name, i->UserID, i->GroupID); + chmod(i->Name, i->Mode & ~S_IFMT); + t.actime = time(0); + t.modtime = i->ModTime; + utime(i->Name, &t); + return 0; +} + +static int +MakeDirectory(TarInfo * i) +{ + printf("Directory: %s\n", i->Name); + if ( mkdir(i->Name, i->Mode & ~S_IFMT) != 0 ) { + if ( errno == EEXIST ) { + struct stat s; + if ( stat(i->Name, &s) != 0 || !(s.st_mode & S_IFDIR) ) + return IOError(i); + } + else + return IOError(i); + } + SetModes(i); + return 0; +} + +static int +MakeHardLink(TarInfo * i) +{ + printf("Hard Link: %s\n", i->Name); + + if ( link(i->LinkName, i->Name) != 0 ) + return IOError(i); + SetModes(i); + return 0; +} + +static int +MakeSymbolicLink(TarInfo * i) +{ + printf("Symbolic Link: %s\n", i->Name); + + if ( symlink(i->LinkName, i->Name) != 0 ) + return -2; + SetModes(i); + return 0; +} + +static int +MakeSpecialFile(TarInfo * i) +{ + printf("Special File: %s\n", i->Name); + + if ( mknod(i->Name, i->Mode, i->Device) != 0 ) + return -2; + SetModes(i); + return 0; +} + +static const TarFunctions functions = { + Read, + ExtractFile, + MakeDirectory, + MakeHardLink, + MakeSymbolicLink, + MakeSpecialFile +}; + +int +main(int argc, char * * argv) +{ + int status = TarExtractor((void *)0, &functions); + + if ( status == -1 ) { + fflush(stdout); + fprintf(stderr, "Error in archive format.\n"); + return -1; + } + else + return status; +} |