Files
ropkg/libropkg/reader.c

240 lines
5.0 KiB
C
Raw Normal View History

2025-08-06 22:11:02 +01:00
#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;
}