Evince
Evince is a document viewer capable of displaying multiple and single page document formats like PDF and Postscript.
Main Page
Related Pages
Data Structures
Files
File List
Globals
All
Data Structures
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Pages
rar.c
Go to the documentation of this file.
1
/* Copyright 2015 the unarr project authors (see AUTHORS file).
2
License: LGPLv3 */
3
4
#include "
rar.h
"
5
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
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
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
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
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
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
}
evince-master
cut-n-paste
unarr
rar
rar.c
Generated on Thu Jul 13 2017 13:41:45 for Evince by
1.8.4