libropkg: implement tar file reader

This commit is contained in:
2025-08-06 22:11:02 +01:00
parent f2caad8251
commit 22510ca52c
3 changed files with 289 additions and 0 deletions

View 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
View 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
View 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