Tar handler

This commit is contained in:
Thomas Klaehn 2021-06-09 07:45:02 +02:00
parent 828d55e708
commit 6bd3ff85a5
8 changed files with 255 additions and 80 deletions

16
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"files.associations": {
"array": "cpp",
"deque": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"iterator": "cpp",
"memory_resource": "cpp",
"optional": "cpp",
"string_view": "cpp",
"initializer_list": "cpp",
"type_traits": "cpp",
"utility": "cpp"
}
}

View File

@ -9,7 +9,7 @@ CXXFLAGS += -pedantic
CXXFLAGS += -pedantic-errors
CXXFLAGS += -ffunction-sections -fdata-sections
CXXFLAGS += -fno-implicit-inline-templates
CXXFLAGS += -Iinc
CXXFLAGS += -Isrc
LD_LIBS := -lc -lgcc -lpthread

43
inc/Buffer.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef __INC_BUFFER_H__
#define __INC_BUFFER_H__
template<typename T, std::size_t S>
class Buffer
: std::array<T, S>
{
typedef std::array<T,S> ArrayT;
typedef typename ArrayT::reference reference;
typedef typename ArrayT::const_reference const_reference;
typedef typename ArrayT::size_type size_type;
typedef typename ArrayT::value_type value_type;
typedef typename ArrayT::iterator iterator;
typedef typename ArrayT::const_iterator const_iterator;
public:
Buffer() : length(0) {}
constexpr reference front() noexcept { return ArrayT::front(); }
constexpr reference back() noexcept { return *(ArrayT::begin() + length); }
constexpr T * data() noexcept { return ArrayT::data(); }
constexpr iterator begin() noexcept { return ArrayT::begin();}
constexpr const_iterator cbegin() const noexcept { return ArrayT::cbegin(); }
constexpr iterator end() noexcept { return (ArrayT::begin() + length); }
constexpr const_iterator cend() const noexcept { return (ArrayT::cbegin() + length); }
constexpr size_type size() const noexcept {return length;}
constexpr size_type max_size() const noexcept {return ArrayT::max_size();}
constexpr void push_back(const_reference v) noexcept { *(this->begin() + length++) = v; };
constexpr void push_back(value_type&&v) noexcept { *(this->begin() + length++) = std::move(v); }
inline void fill(const_reference &v){ ArrayT::fill(v); length = ArrayT::max_size(); }
inline void fill(const_iterator b, const_iterator e) { std::copy(b, e, ArrayT::begin()); length = e - b; }
inline void fill_n(const_iterator b, size_type s) { std::copy(b, b + s, ArrayT::begin()); length = s; }
constexpr void clear() noexcept { this->length = 0; }
private:
size_type length;
};
#endif

View File

@ -1,24 +0,0 @@
#ifndef __INC_RINGBUFFER_H__
#define __INC_RINGBUFFER_H__
#include <array>
class ringbuffer
{
public:
ringbuffer();
int write(char);
int read(char&);
private:
enum {
MAX_BUFFER_SIZE = 2
};
std::array<char, MAX_BUFFER_SIZE> buffer;
std::array<char, MAX_BUFFER_SIZE>::iterator head;
std::array<char, MAX_BUFFER_SIZE>::iterator tail;
};
#endif

111
src/TarHandler.cc Normal file
View File

@ -0,0 +1,111 @@
#include <cstring>
#include <iostream>
#include "TarHandler.h"
#define ASCII_TO_NUMBER(num) ((num) - 48) //Converts an ascii digit to the corresponding number
TarHandler::TarHandler()
: idle(true)
, tar_index(0)
, next_tar_header(0)
, file_size(0)
, read_byte_count(0)
, processed_tar_blocks(0)
, file_processed_bytes(0)
{
}
void TarHandler::tcp_in_handler(char* data, std::size_t size)
{
if(this->idle) {
this->idle = false;
this->tar_index = 0;
this->file_size = 0;
this->next_tar_header;
this->read_byte_count = 0;
this->file_size = 0;
}
// process incoming buffer
for(std::size_t i = 0; i < size; i++) {
this->tar_block[this->tar_index] = data[i];
this->read_byte_count++;
if(this->tar_index++ == TAR_BLOCK_SIZE - 1) {
this->tar_block_handler();
this->tar_index = 0;
}
}
}
bool TarHandler::tar_block_handler()
{
this->processed_tar_blocks++;
if(this->next_tar_header == (this->read_byte_count) - TAR_BLOCK_SIZE) {
// Tar header detected
struct tar_file_header tar_header;
memcpy(&tar_header, this->tar_block, TAR_BLOCK_SIZE);
if(!check_checksum(&tar_header)) {
fprintf(stderr, "checksum missmatch\r\n");
return false;
}
this->file_size = decode_tar_octal(tar_header.file_size, sizeof(tar_header.file_size));
this->file_processed_bytes = 0;
printf("%s - size: %lu\r\n", tar_header.filename, file_size);
this->next_tar_header = (file_size / TAR_BLOCK_SIZE + 1) * TAR_BLOCK_SIZE + this->processed_tar_blocks * TAR_BLOCK_SIZE;
} else {
std::size_t i = 0;
if((this->file_size - this->file_processed_bytes) >= TAR_BLOCK_SIZE) {
for(i = 0; i < TAR_BLOCK_SIZE; i++) {
printf("%c", this->tar_block[i]);
}
}
else {
for(i = 0; i < (this->file_size - this->file_processed_bytes); i++) {
printf("%c", this->tar_block[i]);
}
}
this->file_processed_bytes += i;
printf("processed bytes: %lu\r\n", this->file_processed_bytes);
}
return true;
}
uint64_t TarHandler::decode_tar_octal(char* data, std::size_t size)
{
unsigned char *current_ptr = (unsigned char *) data + size;
uint64_t sum = 0;
uint64_t current_multiplier = 1;
unsigned char* check_ptr = current_ptr; //This is used to check where the last NUL/space char is
for(; check_ptr >= (unsigned char *) data; --check_ptr) {
if((*check_ptr) == 0 || (*check_ptr) == ' ') {
current_ptr = check_ptr - 1;
}
}
for(; current_ptr >= (unsigned char *) data; --current_ptr) {
sum += ASCII_TO_NUMBER(*current_ptr) * current_multiplier;
current_multiplier *= 8;
}
return sum;
}
bool TarHandler::check_checksum(struct tar_file_header *tar_header)
{
char original_checksum[8];
memcpy(original_checksum, tar_header->checksum, 8);
memset(tar_header->checksum, ' ', 8);
int64_t unsigned_sum = 0;
int64_t signed_sum = 0;
unsigned char *uc_tar = (unsigned char *)tar_header;
signed char *sc_tar = (signed char *)tar_header;
for(int i = 0; i < TAR_BLOCK_SIZE; i++) {
unsigned_sum += uc_tar[i];
signed_sum += sc_tar[i];
}
//Copy back the checksum
memcpy(tar_header->checksum, original_checksum, 8);
//Decode the original checksum
uint64_t reference_checksum = decode_tar_octal(original_checksum, sizeof(original_checksum));
return (reference_checksum == unsigned_sum || reference_checksum == signed_sum);
}

53
src/TarHandler.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <cstdint>
class TarHandler
{
public:
struct tar_file_header {
char filename[100];
char mode[8];
char uid[8];
char gid[8];
char file_size[12];
char last_modification[12];
char checksum[8];
char type_flag;
char linked_file_name[100];
char ustar_indicator[6];
char ustar_version[2];
char owner_user_name[32];
char owner_group_name[32];
char device_major_number[8];
char device_minor_number[8];
char filename_prefix[155];
char padding[12];
};
public:
TarHandler();
void tcp_in_handler(char* data, std::size_t size);
private:
uint64_t decode_tar_octal(char* data, std::size_t size);
bool check_checksum(struct tar_file_header *tar_header);
bool tar_block_handler();
enum {
TAR_BLOCK_SIZE = 512
};
char tar_block[TAR_BLOCK_SIZE];
std::size_t tar_index;
std::size_t next_tar_header;
std::size_t file_size;
std::size_t read_byte_count;
std::size_t processed_tar_blocks;
std::size_t file_processed_bytes;
bool idle;
};

View File

@ -1,23 +1,38 @@
#include <iostream>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "TarHandler.h"
#include "ringbuffer.h"
ringbuffer buf;
#define TAR_FILE "test.tar"
int main(void) {
buf.write('a');
buf.write('b');
buf.write('c');
#define BLOCK_SIZE 1522
char c;
if(buf.read(c)) {
std::cout << c;
}
if(buf.read(c)) {
std::cout << c;
}
if(buf.read(c)) {
std::cout << c;
int main(void)
{
int fd = open(TAR_FILE, O_RDONLY);
if(fd < 0) {
fprintf(stderr, "Unable to open %s\r\n", TAR_FILE);
return fd;
}
int res;
char block[BLOCK_SIZE];
size_t processed_blocks = 0;
TarHandler tar_handler;
do {
res = read(fd, block, sizeof(block));
tar_handler.tcp_in_handler(block, res);
processed_blocks++;
} while(res > 0);
printf("processed blocks: %lu\r\n", processed_blocks);
close(fd);
return 0;
}

View File

@ -1,39 +0,0 @@
#include "ringbuffer.h"
ringbuffer::ringbuffer()
{
head = buffer.begin();
tail = buffer.begin();
}
int ringbuffer::write(char c)
{
if(head - tail == MAX_BUFFER_SIZE) {
return 0;
}
if(head == buffer.end()) {
head = buffer.begin();
}
*head = c;
head++;
return 1;
}
int ringbuffer::read(char& c)
{
if(head - tail == 0) {
return 0;
}
if(tail == buffer.end()) {
tail = buffer.begin();
}
c = *tail;
tail++;
return 1;
}