1 /* Copyright 2015 the unarr project authors (see AUTHORS file).
2    License: LGPLv3 */
3 
4 #include "rar.h"
5 
rar_close(ar_archive * ar)6 static void rar_close(ar_archive *ar)
7 {
8     ar_archive_rar *rar = (ar_archive_rar *)ar;
9     free(rar->entry.name);
10     rar_clear_uncompress(&rar->uncomp);
11 }
12 
rar_parse_entry(ar_archive * ar,off64_t offset)13 static bool rar_parse_entry(ar_archive *ar, off64_t offset)
14 {
15     ar_archive_rar *rar = (ar_archive_rar *)ar;
16     struct rar_header header;
17     struct rar_entry entry;
18     bool out_of_order = offset != ar->entry_offset_next;
19 
20     if (!ar_seek(ar->stream, offset, SEEK_SET)) {
21         warn("Couldn't seek to offset %" PRIi64, offset);
22         return false;
23     }
24 
25     for (;;) {
26         ar->entry_offset = ar_tell(ar->stream);
27         ar->entry_size_uncompressed = 0;
28 
29         if (!rar_parse_header(ar, &header))
30             return false;
31 
32         ar->entry_offset_next = ar->entry_offset + header.size + header.datasize;
33         if (ar->entry_offset_next < ar->entry_offset + header.size) {
34             warn("Integer overflow due to overly large data size");
35             return false;
36         }
37 
38         switch (header.type) {
39         case TYPE_MAIN_HEADER:
40             if ((header.flags & MHD_PASSWORD)) {
41                 warn("Encrypted archives aren't supported");
42                 return false;
43             }
44             ar_skip(ar->stream, 6 /* reserved data */);
45             if ((header.flags & MHD_ENCRYPTVER)) {
46                 log("MHD_ENCRYPTVER is set");
47                 ar_skip(ar->stream, 1);
48             }
49             if ((header.flags & MHD_COMMENT))
50                 log("MHD_COMMENT is set");
51             if (ar_tell(ar->stream) - ar->entry_offset > header.size) {
52                 warn("Invalid RAR header size: %d", header.size);
53                 return false;
54             }
55             rar->archive_flags = header.flags;
56             break;
57 
58         case TYPE_FILE_ENTRY:
59             if (!rar_parse_header_entry(rar, &header, &entry))
60                 return false;
61             if ((header.flags & LHD_PASSWORD))
62                 warn("Encrypted entries will fail to uncompress");
63             if ((header.flags & LHD_DIRECTORY) == LHD_DIRECTORY) {
64                 if (header.datasize == 0) {
65                     log("Skipping directory entry \"%s\"", rar_get_name(ar));
66                     break;
67                 }
68                 warn("Can't skip directory entries containing data");
69             }
70             if ((header.flags & (LHD_SPLIT_BEFORE | LHD_SPLIT_AFTER)))
71                 warn("Splitting files isn't really supported");
72             ar->entry_size_uncompressed = (size_t)entry.size;
73             ar->entry_filetime = ar_conv_dosdate_to_filetime(entry.dosdate);
74             if (!rar->entry.solid || rar->entry.method == METHOD_STORE || out_of_order) {
75                 rar_clear_uncompress(&rar->uncomp);
76                 memset(&rar->solid, 0, sizeof(rar->solid));
77             }
78             else {
79                 br_clear_leftover_bits(&rar->uncomp);
80             }
81 
82             rar->solid.restart = rar->entry.solid && (out_of_order || !rar->solid.part_done);
83             rar->solid.part_done = !ar->entry_size_uncompressed;
84             rar->progress.data_left = (size_t)header.datasize;
85             rar->progress.bytes_done = 0;
86             rar->progress.crc = 0;
87 
88             /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
89             if (!rar_check_header_crc(ar))
90                 warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
91             if (ar_tell(ar->stream) != ar->entry_offset + rar->entry.header_size) {
92                 warn("Couldn't seek to offset %" PRIi64, ar->entry_offset + rar->entry.header_size);
93                 return false;
94             }
95             return true;
96 
97         case TYPE_NEWSUB:
98             log("Skipping newsub header @%" PRIi64, ar->entry_offset);
99             break;
100 
101         case TYPE_END_OF_ARCHIVE:
102             ar->at_eof = true;
103             return false;
104 
105         default:
106             log("Unknown RAR header type %02x", header.type);
107             break;
108         }
109 
110         /* TODO: CRC checks don't always hold (claim in XADRARParser.m @readBlockHeader) */
111         if (!rar_check_header_crc(ar))
112             warn("Invalid header checksum @%" PRIi64, ar->entry_offset);
113         if (!ar_seek(ar->stream, ar->entry_offset_next, SEEK_SET)) {
114             warn("Couldn't seek to offset %" PRIi64, ar->entry_offset_next);
115             return false;
116         }
117     }
118 }
119 
rar_copy_stored(ar_archive_rar * rar,void * buffer,size_t count)120 static bool rar_copy_stored(ar_archive_rar *rar, void *buffer, size_t count)
121 {
122     if (count > rar->progress.data_left) {
123         warn("Unexpected EOS in stored data");
124         return false;
125     }
126     if (ar_read(rar->super.stream, buffer, count) != count) {
127         warn("Unexpected EOF in stored data");
128         return false;
129     }
130     rar->progress.data_left -= count;
131     rar->progress.bytes_done += count;
132     return true;
133 }
134 
rar_restart_solid(ar_archive * ar)135 static bool rar_restart_solid(ar_archive *ar)
136 {
137     ar_archive_rar *rar = (ar_archive_rar *)ar;
138     off64_t current_offset = ar->entry_offset;
139     log("Restarting decompression for solid entry");
140     if (!ar_parse_entry_at(ar, ar->entry_offset_first)) {
141         ar_parse_entry_at(ar, current_offset);
142         return false;
143     }
144     while (ar->entry_offset < current_offset) {
145         size_t size = ar->entry_size_uncompressed;
146         rar->solid.restart = false;
147         while (size > 0) {
148             unsigned char buffer[1024];
149             size_t count = smin(size, sizeof(buffer));
150             if (!ar_entry_uncompress(ar, buffer, count)) {
151                 ar_parse_entry_at(ar, current_offset);
152                 return false;
153             }
154             size -= count;
155         }
156         if (!ar_parse_entry(ar)) {
157             ar_parse_entry_at(ar, current_offset);
158             return false;
159         }
160     }
161     rar->solid.restart = false;
162     return true;
163 }
164 
rar_uncompress(ar_archive * ar,void * buffer,size_t count)165 static bool rar_uncompress(ar_archive *ar, void *buffer, size_t count)
166 {
167     ar_archive_rar *rar = (ar_archive_rar *)ar;
168     if (count > ar->entry_size_uncompressed - rar->progress.bytes_done) {
169         warn("Requesting too much data (%" PRIuPTR " < %" PRIuPTR ")", ar->entry_size_uncompressed - rar->progress.bytes_done, count);
170         return false;
171     }
172     if (rar->entry.method == METHOD_STORE) {
173         if (!rar_copy_stored(rar, buffer, count))
174             return false;
175     }
176     else if (rar->entry.method == METHOD_FASTEST || rar->entry.method == METHOD_FAST ||
177              rar->entry.method == METHOD_NORMAL || rar->entry.method == METHOD_GOOD ||
178              rar->entry.method == METHOD_BEST) {
179         if (rar->solid.restart && !rar_restart_solid(ar)) {
180             warn("Failed to produce the required solid decompression state");
181             return false;
182         }
183         if (!rar_uncompress_part(rar, buffer, count))
184             return false;
185     }
186     else {
187         warn("Unknown compression method %#02x", rar->entry.method);
188         return false;
189     }
190 
191     rar->progress.crc = ar_crc32(rar->progress.crc, buffer, count);
192     if (rar->progress.bytes_done < ar->entry_size_uncompressed)
193         return true;
194     if (rar->progress.data_left)
195         log("Compressed block has more data than required");
196     rar->solid.part_done = true;
197     rar->solid.size_total += rar->progress.bytes_done;
198     if (rar->progress.crc != rar->entry.crc) {
199         warn("Checksum of extracted data doesn't match");
200         return false;
201     }
202     return true;
203 }
204 
ar_open_rar_archive(ar_stream * stream)205 ar_archive *ar_open_rar_archive(ar_stream *stream)
206 {
207     char signature[FILE_SIGNATURE_SIZE];
208     if (!ar_seek(stream, 0, SEEK_SET))
209         return NULL;
210     if (ar_read(stream, signature, sizeof(signature)) != sizeof(signature))
211         return NULL;
212     if (memcmp(signature, "Rar!\x1A\x07\x00", sizeof(signature)) != 0) {
213         if (memcmp(signature, "Rar!\x1A\x07\x01", sizeof(signature)) == 0)
214             warn("RAR 5 format isn't supported");
215         else if (memcmp(signature, "RE~^", 4) == 0)
216             warn("Ancient RAR format isn't supported");
217         else if (memcmp(signature, "MZ", 2) == 0 || memcmp(signature, "\x7F\x45LF", 4) == 0)
218             warn("SFX archives aren't supported");
219         return NULL;
220     }
221 
222     return ar_open_archive(stream, sizeof(ar_archive_rar), rar_close, rar_parse_entry, rar_get_name, rar_uncompress, NULL, FILE_SIGNATURE_SIZE);
223 }
224