]> git.sesse.net Git - vlc/blob - modules/access/rar/rar.c
rar: fix possible use of un undefined value (due to a goto on error).
[vlc] / modules / access / rar / rar.c
1 /*****************************************************************************
2  * rar.h: uncompressed RAR parser
3  *****************************************************************************
4  * Copyright (C) 2008-2010 Laurent Aimar
5  * $Id$
6  *
7  * Author: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_stream.h>
35
36 #include <assert.h>
37 #include <limits.h>
38
39 #include "rar.h"
40
41 static const uint8_t rar_marker[] = {
42     0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00
43 };
44 static const int rar_marker_size = sizeof(rar_marker);
45
46 void RarFileDelete(rar_file_t *file)
47 {
48     for (int i = 0; i < file->chunk_count; i++)
49         free(file->chunk[i]);
50     free(file->chunk);
51     free(file->name);
52     free(file);
53 }
54
55 typedef struct {
56     uint16_t crc;
57     uint8_t  type;
58     uint16_t flags;
59     uint16_t size;
60     uint32_t add_size;
61 } rar_block_t;
62
63 enum {
64     RAR_BLOCK_MARKER = 0x72,
65     RAR_BLOCK_ARCHIVE = 0x73,
66     RAR_BLOCK_FILE = 0x74,
67     RAR_BLOCK_END = 0x7b,
68 };
69 enum {
70     RAR_BLOCK_END_HAS_NEXT = 0x0001,
71 };
72 enum {
73     RAR_BLOCK_FILE_HAS_PREVIOUS = 0x0001,
74     RAR_BLOCK_FILE_HAS_NEXT     = 0x0002,
75     RAR_BLOCK_FILE_HAS_HIGH     = 0x0100,
76 };
77
78 static int PeekBlock(stream_t *s, rar_block_t *hdr)
79 {
80     const uint8_t *peek;
81     int peek_size = stream_Peek(s, &peek, 11);
82
83     if (peek_size < 7)
84         return VLC_EGENERIC;
85
86     hdr->crc   = GetWLE(&peek[0]);
87     hdr->type  = peek[2];
88     hdr->flags = GetWLE(&peek[3]);
89     hdr->size  = GetWLE(&peek[5]);
90     hdr->add_size = 0;
91     if (hdr->flags & 0x8000) {
92         if (peek_size < 11)
93             return VLC_EGENERIC;
94         hdr->add_size = GetDWLE(&peek[7]);
95     }
96
97     if (hdr->size < 7)
98         return VLC_EGENERIC;
99     return VLC_SUCCESS;
100 }
101 static int SkipBlock(stream_t *s, const rar_block_t *hdr)
102 {
103     uint64_t size = (uint64_t)hdr->size + hdr->add_size;
104
105     while (size > 0) {
106         int skip = __MIN(size, INT_MAX);
107         if (stream_Read(s, NULL, skip) < skip)
108             return VLC_EGENERIC;
109
110         size -= skip;
111     }
112     return VLC_SUCCESS;
113 }
114
115 static int IgnoreBlock(stream_t *s, int block)
116 {
117     /* */
118     rar_block_t bk;
119     if (PeekBlock(s, &bk) || bk.type != block)
120         return VLC_EGENERIC;
121     return SkipBlock(s, &bk);
122 }
123
124 static int SkipEnd(stream_t *s, const rar_block_t *hdr)
125 {
126     if (!(hdr->flags & RAR_BLOCK_END_HAS_NEXT))
127         return VLC_EGENERIC;
128
129     if (SkipBlock(s, hdr))
130         return VLC_EGENERIC;
131
132     /* Now, we need to look for a marker block,
133      * It seems that there is garbage at EOF */
134     for (;;) {
135         const uint8_t *peek;
136
137         if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
138             return VLC_EGENERIC;
139
140         if (!memcmp(peek, rar_marker, rar_marker_size))
141             break;
142
143         if (stream_Read(s, NULL, 1) != 1)
144             return VLC_EGENERIC;
145     }
146
147     /* Skip marker and archive blocks */
148     if (IgnoreBlock(s, RAR_BLOCK_MARKER))
149         return VLC_EGENERIC;
150     if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
151         return VLC_EGENERIC;
152
153     return VLC_SUCCESS;
154 }
155
156 static int SkipFile(stream_t *s, int *count, rar_file_t ***file, const rar_block_t *hdr)
157 {
158     const uint8_t *peek;
159
160     int min_size = 7+21;
161     if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
162         min_size += 8;
163     if (hdr->size < (unsigned)min_size)
164         return VLC_EGENERIC;
165
166     if (stream_Peek(s, &peek, min_size) < min_size)
167         return VLC_EGENERIC;
168
169     /* */
170     uint32_t file_size_low = GetDWLE(&peek[7+4]);
171     uint8_t  method = peek[7+18];
172     uint16_t name_size = GetWLE(&peek[7+19]);
173     uint32_t file_size_high = 0;
174     if (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH)
175         file_size_high = GetDWLE(&peek[7+25]);
176     const uint64_t file_size = ((uint64_t)file_size_high << 32) | file_size_low;
177
178     char *name = calloc(1, name_size + 1);
179     if (!name)
180         return VLC_EGENERIC;
181
182     const int name_offset = (hdr->flags & RAR_BLOCK_FILE_HAS_HIGH) ? (7+33) : (7+25);
183     if (name_offset + name_size <= hdr->size) {
184         const int max_size = name_offset + name_size;
185         if (stream_Peek(s, &peek, max_size) < max_size) {
186             free(name);
187             return VLC_EGENERIC;
188         }
189         memcpy(name, &peek[name_offset], name_size);
190     }
191
192     rar_file_t *current = NULL;
193     if (method != 0x30) {
194         msg_Warn(s, "Ignoring compressed file %s (method=0x%2.2x)", name, method);
195         goto exit;
196     }
197
198     /* */
199     if( *count > 0 )
200         current = (*file)[*count - 1];
201
202     if (current &&
203         (current->is_complete ||
204           current->size != file_size ||
205           strcmp(current->name, name) ||
206           (hdr->flags & RAR_BLOCK_FILE_HAS_PREVIOUS) == 0))
207         current = NULL;
208
209     if (!current) {
210         current = malloc(sizeof(*current));
211         if (!current)
212             goto exit;
213         TAB_APPEND(*count, *file, current);
214
215         current->name = name;
216         current->size = file_size;
217         current->is_complete = false;
218         current->real_size = 0;
219         TAB_INIT(current->chunk_count, current->chunk);
220
221         name = NULL;
222     }
223
224     /* Append chunks */
225     rar_file_chunk_t *chunk = malloc(sizeof(*chunk));
226     if (chunk) {
227         chunk->offset = stream_Tell(s) + hdr->size;
228         chunk->size = hdr->add_size;
229         chunk->cummulated_size = 0;
230         if (current->chunk_count > 0) {
231             rar_file_chunk_t *previous = current->chunk[current->chunk_count-1];
232
233             chunk->cummulated_size += previous->cummulated_size +
234                                       previous->size;
235         }
236
237         TAB_APPEND(current->chunk_count, current->chunk, chunk);
238
239         current->real_size += hdr->add_size;
240     }
241     if ((hdr->flags & RAR_BLOCK_FILE_HAS_NEXT) == 0)
242         current->is_complete = true;
243
244 exit:
245     /* */
246     free(name);
247
248     /* We stop on the first non empty file if we cannot seek */
249     if (current) {
250         bool can_seek = false;
251         stream_Control(s, STREAM_CAN_SEEK, &can_seek);
252         if (!can_seek && current->size > 0)
253             return VLC_EGENERIC;
254     }
255
256     if (SkipBlock(s, hdr))
257         return VLC_EGENERIC;
258     return VLC_SUCCESS;
259 }
260
261 int RarProbe(stream_t *s)
262 {
263     const uint8_t *peek;
264     if (stream_Peek(s, &peek, rar_marker_size) < rar_marker_size)
265         return VLC_EGENERIC;
266     if (memcmp(peek, rar_marker, rar_marker_size))
267         return VLC_EGENERIC;
268     return VLC_SUCCESS;
269 }
270
271 int RarParse(stream_t *s, int *count, rar_file_t ***file)
272 {
273     *count = 0;
274     *file = NULL;
275
276     /* Skip marker */
277     if (IgnoreBlock(s, RAR_BLOCK_MARKER))
278         return VLC_EGENERIC;
279
280     /* Skip archive  */
281     if (IgnoreBlock(s, RAR_BLOCK_ARCHIVE))
282         return VLC_EGENERIC;
283
284     /* */
285     for (;;) {
286         rar_block_t bk;
287         int ret;
288
289         if (PeekBlock(s, &bk))
290             break;
291
292         switch(bk.type) {
293         case RAR_BLOCK_END:
294             ret = SkipEnd(s, &bk);
295             break;
296         case RAR_BLOCK_FILE:
297             ret = SkipFile(s, count, file, &bk);
298             break;
299         default:
300             ret = SkipBlock(s, &bk);
301             break;
302         }
303         if (ret)
304             break;
305     }
306
307     return VLC_SUCCESS;
308 }
309