#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; }