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