From 22510ca52cbb0c753567445dd8e6eb9d1add1776 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Wed, 6 Aug 2025 22:11:02 +0100 Subject: [PATCH] libropkg: implement tar file reader --- libropkg/include/ropkg/reader.h | 33 +++++ libropkg/reader.c | 239 ++++++++++++++++++++++++++++++++ libropkg/reader.h | 17 +++ 3 files changed, 289 insertions(+) create mode 100644 libropkg/include/ropkg/reader.h create mode 100644 libropkg/reader.c create mode 100644 libropkg/reader.h diff --git a/libropkg/include/ropkg/reader.h b/libropkg/include/ropkg/reader.h new file mode 100644 index 0000000..52d2664 --- /dev/null +++ b/libropkg/include/ropkg/reader.h @@ -0,0 +1,33 @@ +#ifndef ROPKG_READER_H_ +#define ROPKG_READER_H_ + +#include +#include + +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 diff --git a/libropkg/reader.c b/libropkg/reader.c new file mode 100644 index 0000000..749a88d --- /dev/null +++ b/libropkg/reader.c @@ -0,0 +1,239 @@ +#include "reader.h" + +#include +#include +#include + +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; +} diff --git a/libropkg/reader.h b/libropkg/reader.h new file mode 100644 index 0000000..d0a162d --- /dev/null +++ b/libropkg/reader.h @@ -0,0 +1,17 @@ +#ifndef _READER_H_ +#define _READER_H_ + +#include "tar.h" + +#include +#include + +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