libropkg: implement tar file reader
This commit is contained in:
33
libropkg/include/ropkg/reader.h
Normal file
33
libropkg/include/ropkg/reader.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#ifndef ROPKG_READER_H_
|
||||
#define ROPKG_READER_H_
|
||||
|
||||
#include <ropkg/misc.h>
|
||||
#include <ropkg/status.h>
|
||||
|
||||
struct ropkg_reader;
|
||||
struct b_cstream;
|
||||
|
||||
struct ropkg_file_info {
|
||||
char f_filename[255];
|
||||
int f_mode;
|
||||
int f_uid, f_gid;
|
||||
unsigned long f_filesize;
|
||||
unsigned long f_mtime;
|
||||
int f_type;
|
||||
char f_linkname[100];
|
||||
};
|
||||
|
||||
ROPKG_API enum ropkg_status ropkg_reader_open(
|
||||
struct b_cstream *fp,
|
||||
struct ropkg_reader **out);
|
||||
ROPKG_API enum ropkg_status ropkg_reader_close(struct ropkg_reader *pkg);
|
||||
|
||||
ROPKG_API const struct ropkg_file_info *ropkg_reader_current_file(
|
||||
struct ropkg_reader *reader);
|
||||
ROPKG_API enum ropkg_status ropkg_reader_move_next(struct ropkg_reader *pkg);
|
||||
ROPKG_API enum ropkg_status ropkg_reader_move_to_file(
|
||||
struct ropkg_reader *pkg,
|
||||
const char *name);
|
||||
ROPKG_API bool ropkg_reader_eof(const struct ropkg_reader *pkg);
|
||||
|
||||
#endif
|
||||
239
libropkg/reader.c
Normal file
239
libropkg/reader.c
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "reader.h"
|
||||
|
||||
#include <ropkg/reader.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct ustar_header null_header = {0};
|
||||
|
||||
static void copy_string(
|
||||
const char *src,
|
||||
size_t src_max,
|
||||
char *dest,
|
||||
size_t dest_max)
|
||||
{
|
||||
size_t start = strlen(dest);
|
||||
size_t max = b_min(size_t, src_max, dest_max - start);
|
||||
|
||||
for (size_t i = 0; i < max; i++) {
|
||||
dest[start + i] = src[i];
|
||||
|
||||
if (!src[i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static long long from_octal(const char *s, size_t len)
|
||||
{
|
||||
long at = (long)len - 2;
|
||||
long long value = 0;
|
||||
long mul = 1;
|
||||
while (at >= 0) {
|
||||
char c = s[at];
|
||||
if (c == 0 || c == ' ') {
|
||||
break;
|
||||
}
|
||||
|
||||
int v = c - '0';
|
||||
|
||||
value += (v * mul);
|
||||
mul *= 8;
|
||||
at--;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static enum ropkg_status read_file_header(struct ropkg_reader *reader)
|
||||
{
|
||||
if (b_cstream_in_compressed_section(reader->r_stream)) {
|
||||
b_cstream_tx_bytes_uncompressed(
|
||||
reader->r_stream,
|
||||
&reader->r_cur_header_offset);
|
||||
} else {
|
||||
b_cstream_tx_bytes(
|
||||
reader->r_stream,
|
||||
&reader->r_cur_header_offset);
|
||||
}
|
||||
|
||||
size_t nr_read = 0;
|
||||
b_status status = b_cstream_read(
|
||||
reader->r_stream,
|
||||
&reader->r_cur_header,
|
||||
sizeof reader->r_cur_header,
|
||||
&nr_read);
|
||||
if (!B_OK(status)) {
|
||||
return ROPKG_ERR_IO_FAILURE;
|
||||
}
|
||||
|
||||
if (nr_read != sizeof reader->r_cur_header) {
|
||||
return ROPKG_ERR_INVALID_PKG_FILE;
|
||||
}
|
||||
|
||||
memset(&reader->f_cur_file, 0x0, sizeof reader->f_cur_file);
|
||||
|
||||
copy_string(
|
||||
reader->r_cur_header.tar_filename_prefix,
|
||||
sizeof reader->r_cur_header.tar_filename_prefix,
|
||||
reader->f_cur_file.f_filename,
|
||||
sizeof reader->f_cur_file.f_filename);
|
||||
copy_string(
|
||||
reader->r_cur_header.tar_filename,
|
||||
sizeof reader->r_cur_header.tar_filename,
|
||||
reader->f_cur_file.f_filename,
|
||||
sizeof reader->f_cur_file.f_filename);
|
||||
copy_string(
|
||||
reader->r_cur_header.tar_linkname,
|
||||
sizeof reader->r_cur_header.tar_linkname,
|
||||
reader->f_cur_file.f_linkname,
|
||||
sizeof reader->f_cur_file.f_linkname);
|
||||
|
||||
reader->f_cur_file.f_mode = from_octal(
|
||||
reader->r_cur_header.tar_mode,
|
||||
sizeof reader->r_cur_header.tar_mode);
|
||||
reader->f_cur_file.f_uid = from_octal(
|
||||
reader->r_cur_header.tar_uid,
|
||||
sizeof reader->r_cur_header.tar_uid);
|
||||
reader->f_cur_file.f_gid = from_octal(
|
||||
reader->r_cur_header.tar_gid,
|
||||
sizeof reader->r_cur_header.tar_gid);
|
||||
reader->f_cur_file.f_filesize = from_octal(
|
||||
reader->r_cur_header.tar_filesize,
|
||||
sizeof reader->r_cur_header.tar_filesize);
|
||||
reader->f_cur_file.f_mtime = from_octal(
|
||||
reader->r_cur_header.tar_mtime,
|
||||
sizeof reader->r_cur_header.tar_mtime);
|
||||
reader->f_cur_file.f_type = reader->r_cur_header.tar_type[0];
|
||||
|
||||
return ROPKG_SUCCESS;
|
||||
}
|
||||
|
||||
enum ropkg_status ropkg_reader_open(b_cstream *fp, struct ropkg_reader **out)
|
||||
{
|
||||
struct ropkg_reader *reader = malloc(sizeof *reader);
|
||||
if (!reader) {
|
||||
return ROPKG_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(reader, 0x0, sizeof *reader);
|
||||
|
||||
reader->r_stream = fp;
|
||||
|
||||
enum ropkg_status status = read_file_header(reader);
|
||||
if (status != ROPKG_SUCCESS) {
|
||||
free(reader);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!memcmp(&reader->r_cur_header, &null_header, sizeof null_header)) {
|
||||
b_cstream_skip(reader->r_stream, sizeof null_header, NULL);
|
||||
reader->r_eof = true;
|
||||
}
|
||||
|
||||
*out = reader;
|
||||
return ROPKG_SUCCESS;
|
||||
}
|
||||
|
||||
enum ropkg_status ropkg_reader_close(struct ropkg_reader *pkg)
|
||||
{
|
||||
free(pkg);
|
||||
return ROPKG_SUCCESS;
|
||||
}
|
||||
|
||||
const struct ropkg_file_info *ropkg_reader_current_file(
|
||||
struct ropkg_reader *reader)
|
||||
{
|
||||
return &reader->f_cur_file;
|
||||
}
|
||||
|
||||
enum ropkg_status ropkg_reader_move_next(struct ropkg_reader *pkg)
|
||||
{
|
||||
if (pkg->r_eof) {
|
||||
return ROPKG_ERR_NO_DATA;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
if (b_cstream_in_compressed_section(pkg->r_stream)) {
|
||||
b_cstream_tx_bytes_uncompressed(pkg->r_stream, &pos);
|
||||
} else {
|
||||
b_cstream_tx_bytes(pkg->r_stream, &pos);
|
||||
}
|
||||
|
||||
size_t end = pkg->r_cur_header_offset + sizeof pkg->r_cur_header
|
||||
+ pkg->f_cur_file.f_filesize;
|
||||
if ((end % 512) != 0) {
|
||||
end += 512 - (end % 512);
|
||||
}
|
||||
|
||||
size_t skip = end - pos;
|
||||
|
||||
size_t nr_skipped = 0;
|
||||
b_cstream_skip(pkg->r_stream, skip, &nr_skipped);
|
||||
|
||||
if (nr_skipped != skip) {
|
||||
pkg->r_eof = true;
|
||||
return ROPKG_ERR_INVALID_PKG_FILE;
|
||||
}
|
||||
|
||||
enum ropkg_status status = read_file_header(pkg);
|
||||
if (status != ROPKG_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!memcmp(&pkg->r_cur_header, &null_header, sizeof null_header)) {
|
||||
b_cstream_skip(pkg->r_stream, sizeof null_header, NULL);
|
||||
pkg->r_eof = true;
|
||||
}
|
||||
|
||||
return ROPKG_SUCCESS;
|
||||
}
|
||||
|
||||
static bool compare_name(const char *target, const char *candidate)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
for (size_t i = 0;; i++) {
|
||||
if (target[i] == '*') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (target[i] != candidate[i]) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (candidate[i] == 0 || target[i] == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
enum ropkg_status ropkg_reader_move_to_file(
|
||||
struct ropkg_reader *pkg,
|
||||
const char *name)
|
||||
{
|
||||
enum ropkg_status status = ROPKG_ERR_NO_ENTRY;
|
||||
|
||||
while (!ropkg_reader_eof(pkg)) {
|
||||
struct ropkg_file_info *file = &pkg->f_cur_file;
|
||||
bool match = compare_name(name, file->f_filename);
|
||||
if (match) {
|
||||
return ROPKG_SUCCESS;
|
||||
}
|
||||
|
||||
status = ropkg_reader_move_next(pkg);
|
||||
if (status != ROPKG_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return ROPKG_ERR_NO_ENTRY;
|
||||
}
|
||||
|
||||
bool ropkg_reader_eof(const struct ropkg_reader *pkg)
|
||||
{
|
||||
return pkg->r_eof;
|
||||
}
|
||||
17
libropkg/reader.h
Normal file
17
libropkg/reader.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _READER_H_
|
||||
#define _READER_H_
|
||||
|
||||
#include "tar.h"
|
||||
|
||||
#include <blue/compress/cstream.h>
|
||||
#include <ropkg/reader.h>
|
||||
|
||||
struct ropkg_reader {
|
||||
b_cstream *r_stream;
|
||||
struct ustar_header r_cur_header;
|
||||
size_t r_cur_header_offset;
|
||||
struct ropkg_file_info f_cur_file;
|
||||
bool r_eof;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user