Exemplo n.º 1
0
static void rar_close(ar_archive *ar)
{
    ar_archive_rar *rar = (ar_archive_rar *)ar;
    free(rar->entry.name);
    free(rar->entry.name_w);
    rar_clear_uncompress(&rar->uncomp);
}
Exemplo n.º 2
0
static bool rar_parse_entry(ar_archive *ar)
{
    ar_archive_rar *rar = (ar_archive_rar *)ar;
    struct rar_header header;
    struct rar_entry entry;
    /* without solid data, most/all previous files have to be decompressed again */
    bool has_solid_data = rar->super.entry_offset != 0 && rar->uncomp.initialized && rar->progr.data_left == 0;

    if (rar->super.entry_offset != 0) {
        if (!ar_seek(rar->super.stream, rar->super.entry_offset + rar->super.entry_size_block, SEEK_SET)) {
            warn("Couldn't seek to offset %" PRIuPTR, rar->super.entry_offset + rar->super.entry_size_block);
            return false;
        }
    }

    for (;;) {
        rar->super.entry_offset = ar_tell(rar->super.stream);
        rar->super.entry_size_block = 0;
        rar->super.entry_size_uncompressed = 0;

        if (!rar_parse_header(&rar->super, &header))
            return false;

        switch (header.type) {
        case TYPE_MAIN_HEADER:
            if ((header.flags & MHD_PASSWORD)) {
                warn("Encrypted archives aren't supported");
                return false;
            }
            ar_skip(rar->super.stream, 6 /* reserved data */);
            if ((header.flags & MHD_ENCRYPTVER)) {
                log("MHD_ENCRYPTVER is set");
                ar_skip(rar->super.stream, 1);
            }
            if ((header.flags & MHD_COMMENT))
                log("MHD_COMMENT is set");
            if (ar_tell(rar->super.stream) - rar->super.entry_offset > header.size) {
                warn("Invalid RAR header size: %" PRIuPTR, header.size);
                return false;
            }
            rar->archive_flags = header.flags;
            break;

        case TYPE_FILE_ENTRY:
            if (!rar_parse_header_entry(rar, &header, &entry))
                return false;
            if ((header.flags & LHD_PASSWORD))
                warn("Encrypted entries will fail to uncompress");
            if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) {
                log("Skipping directory entry \"%s\"", rar_get_name(&rar->super));
                break;
            }
            if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER)))
                warn("Splitting files isn't really supported");
            // TODO: handle multi-part files (only needed for split files)?
            rar->super.entry_size_block = header.size + (size_t)header.datasize;
            rar->super.entry_size_uncompressed = (size_t)entry.size;
            if (rar->super.entry_size_block < rar->entry.header_size) {
                warn("Integer overflow due to overly large data size");
                return false;
            }
            if (!has_solid_data || !rar->entry.restart_solid || rar->entry.method == METHOD_STORE)
                rar_clear_uncompress(&rar->uncomp);
            else
                rar->entry.restart_solid = false;
#ifdef DEBUG
            // TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader)
            if (!rar_check_header_crc(&rar->super))
                warn("Invalid header checksum @%" PRIuPTR, rar->super.entry_offset);
#endif
            if (!ar_seek(rar->super.stream, rar->super.entry_offset + rar->entry.header_size, SEEK_SET)) {
                warn("Couldn't seek to offset %" PRIuPTR, rar->super.entry_offset + rar->entry.header_size);
                return false;
            }
            return true;

        case TYPE_NEWSUB:
            log("Skipping newsub header @%" PRIuPTR, rar->super.entry_offset);
            break;

        case TYPE_END_OF_ARCHIVE:
            rar->super.at_eof = true;
            return false;

        default:
            log("Unknown RAR header type %02x", header.type);
            break;
        }

#ifdef DEBUG
        // TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader)
        if (!rar_check_header_crc(&rar->super))
            warn("Invalid header checksum @%" PRIuPTR, rar->super.entry_offset);
#endif

        if (!ar_seek(rar->super.stream, rar->super.entry_offset + header.size + (ptrdiff_t)header.datasize, SEEK_SET)) {
            warn("Couldn't seek to offset %" PRIu64, rar->super.entry_offset + header.size + header.datasize);
            return false;
        }
        if (ar_tell(rar->super.stream) <= rar->super.entry_offset) {
            warn("Integer overflow due to overly large data size");
            return false;
        }
    }
}
Exemplo n.º 3
0
static bool rar_parse_entry(ar_archive *ar, off64_t offset)
{
    ar_archive_rar *rar = (ar_archive_rar *)ar;
    struct rar_header header;
    struct rar_entry entry;
    bool out_of_order = offset != ar->entry_offset_next;

    if (!ar_seek(ar->stream, offset, SEEK_SET)) {
        warn("Couldn't seek to offset %" PRIi64, offset);
        return false;
    }

    for (;;) {
        ar->entry_offset = ar_tell(ar->stream);
        ar->entry_size_uncompressed = 0;

        if (!rar_parse_header(ar, &header))
            return false;

        ar->entry_offset_next = ar->entry_offset + header.size + header.datasize;
        if (ar->entry_offset_next < ar->entry_offset + header.size) {
            warn("Integer overflow due to overly large data size");
            return false;
        }

        switch (header.type) {
        case TYPE_MAIN_HEADER:
            if ((header.flags & MHD_PASSWORD)) {
                warn("Encrypted archives aren't supported");
                return false;
            }
            ar_skip(ar->stream, 6 /* reserved data */);
            if ((header.flags & MHD_ENCRYPTVER)) {
                log("MHD_ENCRYPTVER is set");
                ar_skip(ar->stream, 1);
            }
            if ((header.flags & MHD_COMMENT))
                log("MHD_COMMENT is set");
            if (ar_tell(ar->stream) - ar->entry_offset > header.size) {
                warn("Invalid RAR header size: %d", header.size);
                return false;
            }
            rar->archive_flags = header.flags;
            break;

        case TYPE_FILE_ENTRY:
            if (!rar_parse_header_entry(rar, &header, &entry))
                return false;
            if ((header.flags & LHD_PASSWORD))
                warn("Encrypted entries will fail to uncompress");
            if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) {
                if (header.datasize == 0) {
                    log("Skipping directory entry \"%s\"", rar_get_name(ar));
                    break;
                }
                warn("Can't skip directory entries containing data");
            }
            if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER)))
                warn("Splitting files isn't really supported");
            ar->entry_size_uncompressed = (size_t)entry.size;
            ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
            if (!rar->entry.solid || rar->entry.method == METHOD_STORE || out_of_order) {
                rar_clear_uncompress(&rar->uncomp);
                memset(&rar->solid, 0, sizeof(rar->solid));
            }
            else {
                br_clear_leftover_bits(&rar->uncomp);
            }

            rar->solid.restart = rar->entry.solid && (out_of_order || !rar->solid.part_done);
            rar->solid.part_done = !ar->entry_size_uncompressed;
            rar->progress.data_left = (size_t)header.datasize;
            rar->progress.bytes_done = 0;
            rar->progress.crc = 0;

            /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
            if (!rar_check_header_crc(ar))
                warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
            if (ar_tell(ar->stream) != ar->entry_offset + rar->entry.header_size) {
                warn("Couldn't seek to offset %" PRIi64, ar->entry_offset + rar->entry.header_size);
                return false;
            }
            return true;

        case TYPE_NEWSUB:
            log("Skipping newsub header @%" PRIi64, ar->entry_offset);
            break;

        case TYPE_END_OF_ARCHIVE:
            ar->at_eof = true;
            return false;

        default:
            log("Unknown RAR header type %02x", header.type);
            break;
        }

        /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
        if (!rar_check_header_crc(ar))
            warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
        if (!ar_seek(ar->stream, ar->entry_offset_next, SEEK_SET)) {
            warn("Couldn't seek to offset %" PRIi64, ar->entry_offset_next);
            return false;
        }
    }
}