File indexing completed on 2025-02-02 04:26:01

0001 /* Copyright 2015 the unarr project authors (see AUTHORS file).
0002    License: LGPLv3 */
0003 
0004 #include "rar.h"
0005 
0006 static void rar_close(ar_archive *ar)
0007 {
0008     ar_archive_rar *rar = (ar_archive_rar *)ar;
0009     free(rar->entry.name);
0010     rar_clear_uncompress(&rar->uncomp);
0011 }
0012 
0013 static bool rar_parse_entry(ar_archive *ar, off64_t offset)
0014 {
0015     ar_archive_rar *rar = (ar_archive_rar *)ar;
0016     struct rar_header header;
0017     struct rar_entry entry;
0018     bool out_of_order = offset != ar->entry_offset_next;
0019 
0020     if (!ar_seek(ar->stream, offset, SEEK_SET)) {
0021         warn("Couldn't seek to offset %" PRIi64, offset);
0022         return false;
0023     }
0024 
0025     for (;;) {
0026         ar->entry_offset = ar_tell(ar->stream);
0027         ar->entry_size_uncompressed = 0;
0028 
0029         if (!rar_parse_header(ar, &header))
0030             return false;
0031 
0032         ar->entry_offset_next = ar->entry_offset + header.size + header.datasize;
0033         if (ar->entry_offset_next < ar->entry_offset + header.size) {
0034             warn("Integer overflow due to overly large data size");
0035             return false;
0036         }
0037 
0038         switch (header.type) {
0039         case TYPE_MAIN_HEADER:
0040             if ((header.flags & MHD_PASSWORD)) {
0041                 warn("Encrypted archives aren't supported");
0042                 return false;
0043             }
0044             ar_skip(ar->stream, 6 /* reserved data */);
0045             if ((header.flags & MHD_ENCRYPTVER)) {
0046                 log("MHD_ENCRYPTVER is set");
0047                 ar_skip(ar->stream, 1);
0048             }
0049             if ((header.flags & MHD_COMMENT))
0050                 log("MHD_COMMENT is set");
0051             if (ar_tell(ar->stream) - ar->entry_offset > header.size) {
0052                 warn("Invalid RAR header size: %d", header.size);
0053                 return false;
0054             }
0055             rar->archive_flags = header.flags;
0056             break;
0057 
0058         case TYPE_FILE_ENTRY:
0059             if (!rar_parse_header_entry(rar, &header, &entry))
0060                 return false;
0061             if ((header.flags & LHD_PASSWORD))
0062                 warn("Encrypted entries will fail to uncompress");
0063             if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) {
0064                 if (header.datasize == 0) {
0065                     log("Skipping directory entry \"%s\"", rar_get_name(ar));
0066                     break;
0067                 }
0068                 warn("Can't skip directory entries containing data");
0069             }
0070             if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER)))
0071                 warn("Splitting files isn't really supported");
0072             ar->entry_size_uncompressed = (size_t)entry.size;
0073             ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
0074             if (!rar->entry.solid || rar->entry.method == METHOD_STORE || out_of_order) {
0075                 rar_clear_uncompress(&rar->uncomp);
0076                 memset(&rar->solid, 0, sizeof(rar->solid));
0077             }
0078             else {
0079                 br_clear_leftover_bits(&rar->uncomp);
0080             }
0081 
0082             rar->solid.restart = rar->entry.solid && (out_of_order || !rar->solid.part_done);
0083             rar->solid.part_done = !ar->entry_size_uncompressed;
0084             rar->progress.data_left = (size_t)header.datasize;
0085             rar->progress.bytes_done = 0;
0086             rar->progress.crc = 0;
0087 
0088             /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
0089             if (!rar_check_header_crc(ar))
0090                 warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
0091             if (ar_tell(ar->stream) != ar->entry_offset + rar->entry.header_size) {
0092                 warn("Couldn't seek to offset %" PRIi64, ar->entry_offset + rar->entry.header_size);
0093                 return false;
0094             }
0095             return true;
0096 
0097         case TYPE_NEWSUB:
0098             log("Skipping newsub header @%" PRIi64, ar->entry_offset);
0099             break;
0100 
0101         case TYPE_END_OF_ARCHIVE:
0102             ar->at_eof = true;
0103             return false;
0104 
0105         default:
0106             log("Unknown RAR header type %02x", header.type);
0107             break;
0108         }
0109 
0110         /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
0111         if (!rar_check_header_crc(ar))
0112             warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
0113         if (!ar_seek(ar->stream, ar->entry_offset_next, SEEK_SET)) {
0114             warn("Couldn't seek to offset %" PRIi64, ar->entry_offset_next);
0115             return false;
0116         }
0117     }
0118 }
0119 
0120 static bool rar_copy_stored(ar_archive_rar *rar, void *buffer, size_t count)
0121 {
0122     if (count > rar->progress.data_left) {
0123         warn("Unexpected EOS in stored data");
0124         return false;
0125     }
0126     if (ar_read(rar->super.stream, buffer, count) != count) {
0127         warn("Unexpected EOF in stored data");
0128         return false;
0129     }
0130     rar->progress.data_left -= count;
0131     rar->progress.bytes_done += count;
0132     return true;
0133 }
0134 
0135 static bool rar_restart_solid(ar_archive *ar)
0136 {
0137     ar_archive_rar *rar = (ar_archive_rar *)ar;
0138     off64_t current_offset = ar->entry_offset;
0139     log("Restarting decompression for solid entry");
0140     if (!ar_parse_entry_at(ar, ar->entry_offset_first)) {
0141         ar_parse_entry_at(ar, current_offset);
0142         return false;
0143     }
0144     while (ar->entry_offset < current_offset) {
0145         size_t size = ar->entry_size_uncompressed;
0146         rar->solid.restart = false;
0147         while (size > 0) {
0148             unsigned char buffer[1024];
0149             size_t count = smin(size, sizeof(buffer));
0150             if (!ar_entry_uncompress(ar, buffer, count)) {
0151                 ar_parse_entry_at(ar, current_offset);
0152                 return false;
0153             }
0154             size -= count;
0155         }
0156         if (!ar_parse_entry(ar)) {
0157             ar_parse_entry_at(ar, current_offset);
0158             return false;
0159         }
0160     }
0161     rar->solid.restart = false;
0162     return true;
0163 }
0164 
0165 static bool rar_uncompress(ar_archive *ar, void *buffer, size_t count)
0166 {
0167     ar_archive_rar *rar = (ar_archive_rar *)ar;
0168     if (count > ar->entry_size_uncompressed - rar->progress.bytes_done) {
0169         warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - rar->progress.bytes_done, count);
0170         return false;
0171     }
0172     if (rar->entry.method == METHOD_STORE) {
0173         if (!rar_copy_stored(rar, buffer, count))
0174             return false;
0175     }
0176     else if (rar->entry.method == METHOD_FASTEST || rar->entry.method == METHOD_FAST ||
0177              rar->entry.method == METHOD_NORMAL || rar->entry.method == METHOD_GOOD ||
0178              rar->entry.method == METHOD_BEST) {
0179         if (rar->solid.restart && !rar_restart_solid(ar)) {
0180             warn("Failed to produce the required solid decompression state");
0181             return false;
0182         }
0183         if (!rar_uncompress_part(rar, buffer, count))
0184             return false;
0185     }
0186     else {
0187         warn("Unknown compression method %#02x", rar->entry.method);
0188         return false;
0189     }
0190 
0191     rar->progress.crc = ar_crc32(rar->progress.crc, buffer, count);
0192     if (rar->progress.bytes_done < ar->entry_size_uncompressed)
0193         return true;
0194     if (rar->progress.data_left)
0195         log("Compressed block has more data than required");
0196     rar->solid.part_done = true;
0197     rar->solid.size_total += rar->progress.bytes_done;
0198     if (rar->progress.crc != rar->entry.crc) {
0199         warn("Checksum of extracted data doesn't match");
0200         return false;
0201     }
0202     return true;
0203 }
0204 
0205 ar_archive *ar_open_rar_archive(ar_stream *stream)
0206 {
0207     char signature[FILE_SIGNATURE_SIZE];
0208     if (!ar_seek(stream, 0, SEEK_SET))
0209         return NULL;
0210     if (ar_read(stream, signature, sizeof(signature)) != sizeof(signature))
0211         return NULL;
0212     if (memcmp(signature, "Rar!\x1A\x07\x00", sizeof(signature)) != 0) {
0213         if (memcmp(signature, "Rar!\x1A\x07\x01", sizeof(signature)) == 0)
0214             warn("RAR 5 format isn't supported");
0215         else if (memcmp(signature, "RE~^", 4) == 0)
0216             warn("Ancient RAR format isn't supported");
0217         else if (memcmp(signature, "MZ", 2) == 0 || memcmp(signature, "\x7F\x45LF", 4) == 0)
0218             warn("SFX archives aren't supported");
0219         return NULL;
0220     }
0221 
0222     return ar_open_archive(stream, sizeof(ar_archive_rar), rar_close, rar_parse_entry, rar_get_name, rar_uncompress, NULL, FILE_SIGNATURE_SIZE);
0223 }