Kaizen/external/unarr/tar/tar.c

93 lines
2.7 KiB
C

/* Copyright 2015 the unarr project authors (see AUTHORS file).
License: LGPLv3 */
#include "tar.h"
static void tar_close(ar_archive *ar)
{
ar_archive_tar *tar = (ar_archive_tar *)ar;
free(tar->entry.name);
}
static bool tar_parse_entry(ar_archive *ar, off64_t offset)
{
ar_archive_tar *tar = (ar_archive_tar *)ar;
if (!ar_seek(ar->stream, offset, SEEK_SET)) {
warn("Couldn't seek to offset %" PRIi64, offset);
return false;
}
if (!tar_parse_header(tar)) {
warn("Invalid tar header data @%" PRIi64, offset);
return false;
}
if (!tar->entry.checksum) {
ar->at_eof = true;
return false;
}
ar->entry_offset = offset;
ar->entry_offset_next = offset + TAR_BLOCK_SIZE + (tar->entry.filesize + TAR_BLOCK_SIZE - 1) / TAR_BLOCK_SIZE * TAR_BLOCK_SIZE;
ar->entry_size_uncompressed = tar->entry.filesize;
ar->entry_filetime = tar->entry.mtime;
tar->bytes_done = 0;
if (tar->last_seen_dir > offset)
tar->last_seen_dir = 0;
switch (tar->entry.filetype) {
case TYPE_FILE:
case TYPE_FILE_OLD:
return true;
case TYPE_DIRECTORY:
log("Skipping directory entry \"%s\"", tar_get_name(ar, false));
tar->last_seen_dir = ar->entry_offset;
return tar_parse_entry(ar, ar->entry_offset_next);
case TYPE_PAX_GLOBAL:
log("Skipping PAX global extended header record");
return tar_parse_entry(ar, ar->entry_offset_next);
case TYPE_PAX_EXTENDED:
return tar_handle_pax_extended(ar);
case TYPE_GNU_LONGNAME:
return tar_handle_gnu_longname(ar);
default:
warn("Unknown entry type '%c'", tar->entry.filetype);
return true;
}
}
static bool tar_uncompress(ar_archive *ar, void *buffer, size_t count)
{
ar_archive_tar *tar = (ar_archive_tar *)ar;
if (count > ar->entry_size_uncompressed - tar->bytes_done) {
warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - tar->bytes_done, count);
return false;
}
if (ar_read(ar->stream, buffer, count) != count) {
warn("Unexpected EOF in stored data");
return false;
}
tar->bytes_done += count;
return true;
}
ar_archive *ar_open_tar_archive(ar_stream *stream)
{
ar_archive *ar;
ar_archive_tar *tar;
if (!ar_seek(stream, 0, SEEK_SET))
return NULL;
ar = ar_open_archive(stream, sizeof(ar_archive_tar), tar_close, tar_parse_entry, tar_get_name, tar_uncompress, NULL, 0);
if (!ar)
return NULL;
tar = (ar_archive_tar *)ar;
if (!tar_parse_header(tar) || !tar->entry.checksum) {
free(ar);
return NULL;
}
return ar;
}