/src/mupdf/source/fitz/uncfb.c
Line | Count | Source |
1 | | // Copyright (C) 2023-2026 Artifex Software, Inc. |
2 | | // |
3 | | // This file is part of MuPDF. |
4 | | // |
5 | | // MuPDF is free software: you can redistribute it and/or modify it under the |
6 | | // terms of the GNU Affero General Public License as published by the Free |
7 | | // Software Foundation, either version 3 of the License, or (at your option) |
8 | | // any later version. |
9 | | // |
10 | | // MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY |
11 | | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | // FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more |
13 | | // details. |
14 | | // |
15 | | // You should have received a copy of the GNU Affero General Public License |
16 | | // along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html> |
17 | | // |
18 | | // Alternative licensing terms are available from the licensor. |
19 | | // For commercial licensing, see <https://www.artifex.com/> or contact |
20 | | // Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
21 | | // CA 94129, USA, for further information. |
22 | | |
23 | | #include "mupdf/fitz.h" |
24 | | |
25 | | #include <string.h> |
26 | | #include <limits.h> |
27 | | |
28 | | #define MAXREGSID 0xfffffffa |
29 | 0 | #define NOSTREAM 0xffffffff |
30 | 0 | #define MAXREGSECT 0xfffffffa |
31 | | #define DIRSECT 0xfffffffc |
32 | | #define FATSECT 0xfffffffd |
33 | 0 | #define ENDOFCHAIN 0xfffffffe |
34 | | #define FREESECT 0xffffffff |
35 | | |
36 | | //#define DEBUG_DIRENTRIES |
37 | | |
38 | | typedef struct |
39 | | { |
40 | | char *name; |
41 | | uint32_t sector; |
42 | | uint64_t size; |
43 | | uint32_t l, r, d; |
44 | | /* Flag word used for various different things. |
45 | | * initially the type, then marked as to whether the DFS reached it |
46 | | * then finally the original node number for debug. */ |
47 | | uint32_t t; |
48 | | } cfb_entry; |
49 | | |
50 | | typedef struct |
51 | | { |
52 | | fz_archive super; |
53 | | |
54 | | fz_list(cfb_entry, entries); |
55 | | |
56 | | /* Header information from the file */ |
57 | | uint16_t major; |
58 | | uint16_t sector_shift; |
59 | | uint32_t num_dir_sectors; |
60 | | uint32_t num_fat_sectors; |
61 | | uint32_t dir_sector0; |
62 | | uint32_t mini_fat_sector0; |
63 | | uint32_t num_mini_fat_sectors; |
64 | | uint32_t difat_sector0; |
65 | | uint32_t num_difat_sectors; |
66 | | uint32_t mini_stream_sector0; |
67 | | uint64_t mini_stream_len; |
68 | | uint32_t difat[109]; |
69 | | |
70 | | uint32_t fatcache_sector; |
71 | | uint8_t fatcache[4096]; |
72 | | |
73 | | uint32_t minifatcache_real_sector; |
74 | | uint32_t minifatcache_sector; |
75 | | uint8_t minifatcache[4096]; |
76 | | |
77 | | } fz_cfb_archive; |
78 | | |
79 | | static void |
80 | | read(fz_context *ctx, fz_stream *stm, uint8_t *buf, size_t size) |
81 | 0 | { |
82 | 0 | size_t n = fz_read(ctx, stm, buf, size); |
83 | |
|
84 | 0 | if (n != size) |
85 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Short read in CFB handling"); |
86 | 0 | } |
87 | | |
88 | 0 | #define get16 fz_unpack_uint16_le |
89 | 0 | #define get32 fz_unpack_uint32_le |
90 | 0 | #define get64 fz_unpack_uint64_le |
91 | | |
92 | | static uint64_t |
93 | | get_len(fz_context *ctx, fz_cfb_archive *cfb, const uint8_t *b) |
94 | 0 | { |
95 | 0 | uint64_t len = get64(b); |
96 | | |
97 | | /* In v3 files the top 32bits *should* be zero, but may not be. The |
98 | | * top bit of the lower 32bits should not be set though. */ |
99 | 0 | if (cfb->major == 3) |
100 | 0 | { |
101 | 0 | if (len & 0x80000000) |
102 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Illegal length in CFB"); |
103 | 0 | len &= 0xFFFFFFFFU; |
104 | 0 | } |
105 | 0 | return len; |
106 | 0 | } |
107 | | |
108 | | static void |
109 | | sector_seek(fz_context *ctx, fz_cfb_archive *cfb, uint32_t sector, uint32_t offset) |
110 | 0 | { |
111 | 0 | fz_seek(ctx, cfb->super.file, ((sector + (uint64_t)1)<<cfb->sector_shift)+offset, SEEK_SET); |
112 | 0 | } |
113 | | |
114 | | static uint32_t |
115 | | read_difat(fz_context *ctx, fz_cfb_archive *cfb, uint32_t sector) |
116 | 0 | { |
117 | 0 | uint32_t entries_per_sector; |
118 | 0 | uint32_t sect; |
119 | |
|
120 | 0 | if (sector < 109) |
121 | 0 | { |
122 | 0 | return cfb->difat[sector]; |
123 | 0 | } |
124 | 0 | sector -= 109; |
125 | | |
126 | | /* Run down the difat chain until we find the right sector. */ |
127 | 0 | entries_per_sector = (1<<(cfb->sector_shift-2)) - 1; |
128 | 0 | sect = cfb->difat_sector0; |
129 | 0 | while (sector > entries_per_sector) |
130 | 0 | { |
131 | 0 | sector_seek(ctx, cfb, sect, entries_per_sector * 4); |
132 | 0 | sect = fz_read_uint32_le(ctx, cfb->super.file); |
133 | 0 | sector -= entries_per_sector; |
134 | 0 | } |
135 | | |
136 | | /* Now get the actual entry. */ |
137 | 0 | sector_seek(ctx, cfb, sect, sector * 4); |
138 | |
|
139 | 0 | return fz_read_uint32_le(ctx, cfb->super.file); |
140 | 0 | } |
141 | | |
142 | | static uint32_t |
143 | | read_fat(fz_context *ctx, fz_cfb_archive *cfb, uint32_t sector) |
144 | 0 | { |
145 | 0 | uint32_t sector_size = 1<<cfb->sector_shift; |
146 | | /* We want to read the entry for sector 'sector' from the FAT. This |
147 | | * will be in FAT sector 'fatsect'. */ |
148 | 0 | uint32_t fatsect = sector>>(cfb->sector_shift-2); |
149 | | /* FAT sector fatsect will be physical sector real_sect. */ |
150 | 0 | uint32_t real_sect = read_difat(ctx, cfb, fatsect); |
151 | |
|
152 | 0 | if (real_sect > MAXREGSECT) |
153 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Corrupt FAT"); |
154 | | |
155 | 0 | if (real_sect != cfb->fatcache_sector) |
156 | 0 | { |
157 | 0 | sector_seek(ctx, cfb, real_sect, 0); |
158 | 0 | read(ctx, cfb->super.file, &cfb->fatcache[0], sector_size); |
159 | 0 | cfb->fatcache_sector = real_sect; |
160 | 0 | } |
161 | |
|
162 | 0 | sector &= (sector_size>>2)-1; |
163 | |
|
164 | 0 | return get32(&cfb->fatcache[sector*4]); |
165 | 0 | } |
166 | | |
167 | | static uint32_t |
168 | | read_mini_fat(fz_context *ctx, fz_cfb_archive *cfb, uint32_t sector) |
169 | 0 | { |
170 | 0 | uint32_t sector_size = 1<<cfb->sector_shift; |
171 | | /* A mini fat sector has lots of mini sector numbers in (each 4 bytes) */ |
172 | 0 | uint32_t mini_sectors_in_mini_fat_sector = (1<<(cfb->sector_shift-2)); |
173 | | /* We want to read the entry for sector 'sector' from the mini FAT. This |
174 | | * will be in mini FAT sector 'minifatsect'. */ |
175 | 0 | uint32_t minifatsect = sector / mini_sectors_in_mini_fat_sector; |
176 | 0 | uint32_t index_within_minifatsect = sector - minifatsect * mini_sectors_in_mini_fat_sector; |
177 | 0 | int cache_valid = 1; |
178 | | |
179 | | /* minifatsect is a count of how many sectors we are into the mini fat stream. |
180 | | * minifatsect_real_sector is the physical section that that corresponds to. */ |
181 | | |
182 | | /* If we're behind our cache position, start from scratch. */ |
183 | 0 | if (minifatsect < cfb->minifatcache_sector) |
184 | 0 | { |
185 | 0 | cfb->minifatcache_real_sector = cfb->mini_fat_sector0; |
186 | 0 | cfb->minifatcache_sector = 0; |
187 | 0 | cache_valid = 0; |
188 | 0 | } |
189 | | |
190 | | /* Skip forward until we are at the right position. */ |
191 | 0 | while (minifatsect != cfb->minifatcache_sector) |
192 | 0 | { |
193 | 0 | cfb->minifatcache_real_sector = read_fat(ctx, cfb, cfb->minifatcache_real_sector); |
194 | 0 | cfb->minifatcache_sector++; |
195 | 0 | cache_valid = 0; |
196 | 0 | } |
197 | | |
198 | | /* Prime the cache if we just moved */ |
199 | 0 | if (!cache_valid) |
200 | 0 | { |
201 | 0 | sector_seek(ctx, cfb, cfb->minifatcache_real_sector, 0); |
202 | 0 | read(ctx, cfb->super.file, cfb->minifatcache, sector_size); |
203 | 0 | } |
204 | |
|
205 | 0 | return get32(&cfb->minifatcache[index_within_minifatsect*4]); |
206 | 0 | } |
207 | | |
208 | | static void drop_cfb_archive(fz_context *ctx, fz_archive *arch) |
209 | 0 | { |
210 | 0 | fz_cfb_archive *cfb = (fz_cfb_archive *) arch; |
211 | 0 | int i; |
212 | 0 | for (i = 0; i < cfb->entries_len; ++i) |
213 | 0 | fz_free(ctx, cfb->entries[i].name); |
214 | 0 | fz_free(ctx, cfb->entries); |
215 | 0 | } |
216 | | |
217 | | static cfb_entry *lookup_cfb_entry(fz_context *ctx, fz_cfb_archive *cfb, const char *name) |
218 | 0 | { |
219 | 0 | int i; |
220 | 0 | for (i = 0; i < cfb->entries_len; i++) |
221 | 0 | if (!fz_strcasecmp(name, cfb->entries[i].name)) |
222 | 0 | return &cfb->entries[i]; |
223 | 0 | return NULL; |
224 | 0 | } |
225 | | |
226 | | typedef struct |
227 | | { |
228 | | fz_cfb_archive *archive; |
229 | | uint32_t first_sector; |
230 | | uint32_t next_sector; |
231 | | uint32_t next_sector_slow; |
232 | | uint32_t next_sector_slow_flag; |
233 | | uint64_t pos_at_next_sector; |
234 | | uint64_t size; |
235 | | fz_stream *mini_stream; |
236 | | uint8_t buffer[4096]; |
237 | | } cfb_state; |
238 | | |
239 | | static void |
240 | | cfb_close(fz_context *ctx, void *state_) |
241 | 0 | { |
242 | 0 | cfb_state *state = (cfb_state *)state_; |
243 | |
|
244 | 0 | fz_drop_archive(ctx, &state->archive->super); |
245 | 0 | fz_drop_stream(ctx, state->mini_stream); |
246 | 0 | fz_free(ctx, state); |
247 | 0 | } |
248 | | |
249 | | static int |
250 | | cfb_next(fz_context *ctx, fz_stream *stm, size_t required) |
251 | 0 | { |
252 | 0 | cfb_state *state = stm->state; |
253 | 0 | fz_cfb_archive *cfb = state->archive; |
254 | 0 | uint64_t sector_size = ((uint64_t)1)<<cfb->sector_shift; |
255 | 0 | uint64_t desired_sector_pos; |
256 | 0 | uint32_t pos_in_sector; |
257 | 0 | uint32_t this_sector; |
258 | |
|
259 | 0 | if ((uint64_t)stm->pos >= state->size) |
260 | 0 | stm->eof = 1; |
261 | |
|
262 | 0 | if (stm->eof) |
263 | 0 | { |
264 | 0 | stm->rp = stm->wp = state->buffer; |
265 | 0 | return EOF; |
266 | 0 | } |
267 | | |
268 | 0 | pos_in_sector = stm->pos & (sector_size-1); |
269 | 0 | desired_sector_pos = stm->pos & ~(sector_size-1); |
270 | 0 | if (desired_sector_pos != state->pos_at_next_sector) |
271 | 0 | { |
272 | 0 | state->pos_at_next_sector = 0; |
273 | 0 | state->next_sector = state->first_sector; |
274 | 0 | state->next_sector_slow = state->first_sector; |
275 | 0 | state->next_sector_slow_flag = 0; |
276 | 0 | } |
277 | |
|
278 | 0 | this_sector = state->next_sector; |
279 | 0 | while (desired_sector_pos >= state->pos_at_next_sector) |
280 | 0 | { |
281 | 0 | this_sector = state->next_sector; |
282 | 0 | state->next_sector = read_fat(ctx, cfb, state->next_sector); |
283 | 0 | state->pos_at_next_sector += sector_size; |
284 | 0 | if (state->next_sector > MAXREGSECT) |
285 | 0 | break; |
286 | | |
287 | 0 | state->next_sector_slow_flag = !state->next_sector_slow_flag; |
288 | 0 | if (state->next_sector_slow_flag == 0) |
289 | 0 | state->next_sector_slow = read_fat(ctx, cfb, state->next_sector_slow); |
290 | 0 | if (state->next_sector_slow == state->next_sector) |
291 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Loop in FAT chain"); |
292 | 0 | } |
293 | 0 | if (state->next_sector > MAXREGSECT && state->next_sector != ENDOFCHAIN) |
294 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected entry in FAT chain"); |
295 | | |
296 | 0 | if (this_sector > MAXREGSECT) |
297 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected end of FAT chain"); |
298 | 0 | sector_seek(ctx, cfb, this_sector, 0); |
299 | 0 | read(ctx, cfb->super.file, state->buffer, sector_size); |
300 | 0 | stm->rp = state->buffer; |
301 | 0 | stm->wp = stm->rp + sector_size; |
302 | 0 | stm->pos = state->pos_at_next_sector; |
303 | 0 | if ((uint64_t)stm->pos >= state->size) |
304 | 0 | { |
305 | 0 | stm->wp -= (stm->pos - state->size); |
306 | 0 | stm->pos = state->size; |
307 | 0 | } |
308 | 0 | stm->rp += pos_in_sector; |
309 | |
|
310 | 0 | return *stm->rp++; |
311 | 0 | } |
312 | | |
313 | 0 | #define MINI_SECTOR_SHIFT 6 |
314 | 0 | #define MINI_SECTOR_SIZE (1<<MINI_SECTOR_SHIFT) |
315 | | |
316 | | static int |
317 | | cfb_next_mini(fz_context *ctx, fz_stream *stm, size_t required) |
318 | 0 | { |
319 | 0 | cfb_state *state = stm->state; |
320 | 0 | fz_cfb_archive *cfb = state->archive; |
321 | 0 | uint64_t desired_sector_pos; |
322 | 0 | uint32_t pos_in_sector; |
323 | 0 | uint32_t this_sector; |
324 | |
|
325 | 0 | if ((uint64_t)stm->pos >= state->size) |
326 | 0 | stm->eof = 1; |
327 | |
|
328 | 0 | if (stm->eof) |
329 | 0 | { |
330 | 0 | stm->rp = stm->wp = state->buffer; |
331 | 0 | return EOF; |
332 | 0 | } |
333 | | |
334 | | /* Whenever we say 'sector' here, we mean 'mini sector'. */ |
335 | 0 | pos_in_sector = stm->pos & (MINI_SECTOR_SIZE-1); |
336 | 0 | desired_sector_pos = stm->pos & ~(MINI_SECTOR_SIZE-1); |
337 | 0 | if (desired_sector_pos != state->pos_at_next_sector) |
338 | 0 | { |
339 | 0 | state->pos_at_next_sector = 0; |
340 | 0 | state->next_sector = state->first_sector; |
341 | 0 | state->next_sector_slow = state->first_sector; |
342 | 0 | state->next_sector_slow_flag = 0; |
343 | 0 | } |
344 | |
|
345 | 0 | this_sector = state->next_sector; |
346 | 0 | while (desired_sector_pos >= state->pos_at_next_sector) |
347 | 0 | { |
348 | 0 | this_sector = state->next_sector; |
349 | 0 | state->next_sector = read_mini_fat(ctx, cfb, state->next_sector); |
350 | 0 | state->pos_at_next_sector += MINI_SECTOR_SIZE; |
351 | 0 | if (state->next_sector > MAXREGSECT) |
352 | 0 | break; |
353 | | |
354 | 0 | state->next_sector_slow_flag = !state->next_sector_slow_flag; |
355 | 0 | if (state->next_sector_slow_flag == 0) |
356 | 0 | state->next_sector_slow = read_mini_fat(ctx, cfb, state->next_sector_slow); |
357 | 0 | if (state->next_sector_slow == state->next_sector) |
358 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Loop in FAT chain"); |
359 | 0 | } |
360 | 0 | if (state->next_sector > MAXREGSECT && state->next_sector != ENDOFCHAIN) |
361 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected entry in FAT chain"); |
362 | | |
363 | 0 | if (this_sector > MAXREGSECT) |
364 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Unexpected end of FAT chain"); |
365 | | |
366 | 0 | fz_seek(ctx, state->mini_stream, ((uint64_t)this_sector) * MINI_SECTOR_SIZE, SEEK_SET); |
367 | 0 | read(ctx, state->mini_stream, state->buffer, MINI_SECTOR_SIZE); |
368 | 0 | stm->rp = state->buffer; |
369 | 0 | stm->wp = stm->rp + MINI_SECTOR_SIZE; |
370 | 0 | stm->pos += MINI_SECTOR_SIZE; |
371 | 0 | if ((uint64_t)stm->pos >= state->size) |
372 | 0 | { |
373 | 0 | stm->wp -= (stm->pos - state->size); |
374 | 0 | stm->pos = state->size; |
375 | 0 | } |
376 | 0 | stm->rp += pos_in_sector; |
377 | |
|
378 | 0 | return *stm->rp++; |
379 | 0 | } |
380 | | |
381 | | static void cfb_seek(fz_context *ctx, fz_stream *stm, int64_t offset, int whence) |
382 | 0 | { |
383 | 0 | cfb_state *state = stm->state; |
384 | 0 | int64_t pos = stm->pos - (stm->wp - stm->rp); |
385 | | /* Convert to absolute pos */ |
386 | 0 | if (whence == 1) |
387 | 0 | { |
388 | 0 | offset += pos; /* Was relative to current pos */ |
389 | 0 | } |
390 | 0 | else if (whence == 2) |
391 | 0 | { |
392 | 0 | offset += stm->pos; /* Was relative to end */ |
393 | 0 | } |
394 | |
|
395 | 0 | if (offset < 0) |
396 | 0 | offset = 0; |
397 | 0 | if ((uint64_t)offset > state->size) |
398 | 0 | offset = (int64_t)state->size; |
399 | 0 | stm->pos = offset; |
400 | 0 | stm->rp = stm->wp = state->buffer; |
401 | 0 | } |
402 | | |
403 | | static fz_stream *sector_stream(fz_context *ctx, fz_cfb_archive *cfb, uint32_t sector, uint64_t size) |
404 | 0 | { |
405 | 0 | fz_stream *stm; |
406 | 0 | cfb_state *state = fz_malloc_struct(ctx, cfb_state); |
407 | |
|
408 | 0 | state->archive = (fz_cfb_archive *)fz_keep_archive(ctx, &cfb->super); |
409 | 0 | state->pos_at_next_sector = 0; |
410 | 0 | state->size = size; |
411 | 0 | state->first_sector = sector; |
412 | 0 | state->next_sector = state->first_sector; |
413 | 0 | state->next_sector_slow = state->first_sector; |
414 | 0 | state->next_sector_slow_flag = 0; |
415 | |
|
416 | 0 | stm = fz_new_stream(ctx, state, cfb_next, cfb_close); |
417 | 0 | stm->seek = cfb_seek; |
418 | |
|
419 | 0 | return stm; |
420 | 0 | } |
421 | | |
422 | | static fz_stream *open_cfb_entry(fz_context *ctx, fz_archive *arch, const char *name) |
423 | 0 | { |
424 | 0 | fz_cfb_archive *cfb = (fz_cfb_archive *) arch; |
425 | 0 | cfb_entry *ent; |
426 | 0 | fz_stream *stm; |
427 | 0 | cfb_state *state; |
428 | |
|
429 | 0 | ent = lookup_cfb_entry(ctx, cfb, name); |
430 | 0 | if (!ent) |
431 | 0 | return NULL; |
432 | | |
433 | 0 | if (ent->size >= 0x1000) |
434 | 0 | { |
435 | | /* Working from entire sectors */ |
436 | 0 | return sector_stream(ctx, cfb, ent->sector, ent->size); |
437 | 0 | } |
438 | | |
439 | | /* We're working from the mini stream. */ |
440 | 0 | state = fz_malloc_struct(ctx, cfb_state); |
441 | |
|
442 | 0 | fz_try(ctx) |
443 | 0 | { |
444 | | /* Let's get a stream that gets us the mini stream, and then work from that. */ |
445 | 0 | state->mini_stream = sector_stream(ctx, cfb, cfb->mini_stream_sector0, cfb->mini_stream_len); |
446 | 0 | state->first_sector = ent->sector; |
447 | 0 | state->pos_at_next_sector = 0; |
448 | 0 | state->size = ent->size; |
449 | 0 | state->next_sector = state->first_sector; |
450 | 0 | state->next_sector_slow = state->first_sector; |
451 | 0 | state->next_sector_slow_flag = 0; |
452 | 0 | state->archive = (fz_cfb_archive *)fz_keep_archive(ctx, &cfb->super); |
453 | |
|
454 | 0 | } |
455 | 0 | fz_catch(ctx) |
456 | 0 | { |
457 | 0 | fz_free(ctx, state); |
458 | 0 | fz_rethrow(ctx); |
459 | 0 | } |
460 | | |
461 | 0 | stm = fz_new_stream(ctx, state, cfb_next_mini, cfb_close); |
462 | 0 | stm->seek = cfb_seek; |
463 | 0 | return stm; |
464 | 0 | } |
465 | | |
466 | | static fz_buffer *read_cfb_entry(fz_context *ctx, fz_archive *arch, const char *name) |
467 | 0 | { |
468 | 0 | fz_stream *stm; |
469 | 0 | fz_buffer *buf = NULL; |
470 | |
|
471 | 0 | stm = open_cfb_entry(ctx, arch, name); |
472 | 0 | if (!stm) |
473 | 0 | return NULL; |
474 | | |
475 | 0 | fz_try(ctx) |
476 | 0 | buf = fz_read_all(ctx, stm, 1024); |
477 | 0 | fz_always(ctx) |
478 | 0 | fz_drop_stream(ctx, stm); |
479 | 0 | fz_catch(ctx) |
480 | 0 | fz_rethrow(ctx); |
481 | | |
482 | 0 | return buf; |
483 | 0 | } |
484 | | |
485 | | static int has_cfb_entry(fz_context *ctx, fz_archive *arch, const char *name) |
486 | 0 | { |
487 | 0 | fz_cfb_archive *cfb = (fz_cfb_archive *) arch; |
488 | 0 | cfb_entry *ent = lookup_cfb_entry(ctx, cfb, name); |
489 | 0 | return ent != NULL; |
490 | 0 | } |
491 | | |
492 | | static const char *list_cfb_entry(fz_context *ctx, fz_archive *arch, int idx) |
493 | 0 | { |
494 | 0 | fz_cfb_archive *cfb = (fz_cfb_archive *) arch; |
495 | 0 | if (idx < 0 || idx >= cfb->entries_len) |
496 | 0 | return NULL; |
497 | 0 | return cfb->entries[idx].name; |
498 | 0 | } |
499 | | |
500 | | static int count_cfb_entries(fz_context *ctx, fz_archive *arch) |
501 | 0 | { |
502 | 0 | fz_cfb_archive *cfb = (fz_cfb_archive *) arch; |
503 | 0 | return cfb->entries_len; |
504 | 0 | } |
505 | | |
506 | | static const uint8_t sig[8] = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; |
507 | | static const uint8_t zeros[16] = { 0 }; |
508 | | |
509 | | int |
510 | | fz_is_cfb_archive(fz_context *ctx, fz_stream *file) |
511 | 284 | { |
512 | 284 | uint8_t data[nelem(sig)]; |
513 | 284 | size_t n; |
514 | | |
515 | 284 | fz_seek(ctx, file, 0, SEEK_SET); |
516 | 284 | n = fz_read(ctx, file, data, nelem(data)); |
517 | 284 | if (n != nelem(data)) |
518 | 0 | return 0; |
519 | 284 | if (!memcmp(data, sig, nelem(sig))) |
520 | 0 | return 1; |
521 | | |
522 | 284 | return 0; |
523 | 284 | } |
524 | | |
525 | | static void |
526 | | expect(fz_context *ctx, fz_stream *file, const uint8_t *pattern, size_t n, const char *msg) |
527 | 0 | { |
528 | 0 | uint8_t buffer[64]; |
529 | |
|
530 | 0 | assert(sizeof(buffer) >= n); |
531 | 0 | read(ctx, file, buffer, n); |
532 | |
|
533 | 0 | if (memcmp(buffer, pattern, n) != 0) |
534 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "%s in CFB", msg); |
535 | 0 | } |
536 | | |
537 | | static void |
538 | | expect16(fz_context *ctx, fz_stream *file, uint16_t v, const char *msg) |
539 | 0 | { |
540 | 0 | uint16_t u; |
541 | |
|
542 | 0 | u = fz_read_uint16_le(ctx, file); |
543 | 0 | if (u != v) |
544 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "%s in CFB: 0x%04x != 0x%04x", msg, u, v); |
545 | 0 | } |
546 | | |
547 | | static void |
548 | | expect32(fz_context *ctx, fz_stream *file, uint32_t v, const char *msg) |
549 | 0 | { |
550 | 0 | uint32_t u; |
551 | |
|
552 | 0 | u = fz_read_uint32_le(ctx, file); |
553 | 0 | if (u != v) |
554 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "%s in CFB: 0x%08x != 0x%08x", msg, u, v); |
555 | 0 | } |
556 | | |
557 | 0 | #define REACHED 0xFFFFFFFF |
558 | 0 | #define REACHED_KEEP 0xFFFFFFFE |
559 | | |
560 | | static void |
561 | | make_absolute(fz_context *ctx, fz_cfb_archive *cfb, char *prefix, int node, int depth) |
562 | 0 | { |
563 | 0 | uint32_t type; |
564 | | |
565 | | /* To avoid recursion where possible. */ |
566 | 0 | while (1) |
567 | 0 | { |
568 | 0 | if (node == (int)NOSTREAM) |
569 | 0 | return; |
570 | | |
571 | 0 | if (node < 0 || node >= cfb->entries_len) |
572 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid tree"); |
573 | | |
574 | 0 | if (depth >= 32) |
575 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "CBF Tree too deep"); |
576 | | |
577 | 0 | type = cfb->entries[node].t; |
578 | 0 | if (type == REACHED || type == REACHED_KEEP) |
579 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "CBF Tree has cycles"); |
580 | 0 | cfb->entries[node].t = (type == 2) ? REACHED_KEEP : REACHED; |
581 | |
|
582 | 0 | if (prefix) |
583 | 0 | { |
584 | 0 | size_t z0 = strlen(prefix); |
585 | 0 | size_t z1 = strlen(cfb->entries[node].name); |
586 | 0 | char *newname = fz_malloc(ctx, z0+z1+2); |
587 | 0 | memcpy(newname, prefix, z0); |
588 | 0 | newname[z0] = '/'; |
589 | 0 | memcpy(newname+z0+1, cfb->entries[node].name, z1+1); |
590 | 0 | fz_free(ctx, cfb->entries[node].name); |
591 | 0 | cfb->entries[node].name = newname; |
592 | 0 | } |
593 | |
|
594 | 0 | if (cfb->entries[node].d == NOSTREAM && cfb->entries[node].r == NOSTREAM) |
595 | 0 | { |
596 | | /* Handle 'l' without recursion, because there is no 'r' or 'd'. */ |
597 | 0 | node = cfb->entries[node].l; |
598 | 0 | continue; |
599 | 0 | } |
600 | 0 | make_absolute(ctx, cfb, prefix, cfb->entries[node].l, depth+1); |
601 | 0 | if (cfb->entries[node].d == NOSTREAM) |
602 | 0 | { |
603 | | /* Handle 'r' without recursion, because there is no 'd'. */ |
604 | 0 | node = cfb->entries[node].r; |
605 | 0 | continue; |
606 | 0 | } |
607 | 0 | make_absolute(ctx, cfb, prefix, cfb->entries[node].r, depth+1); |
608 | | |
609 | | /* Rather than recursing: |
610 | | * make_absolute(ctx, cfb, node == 0 ? NULL : cfb->entries[node].name, cfb->entries[node].d, depth+1); |
611 | | * instead just loop. */ |
612 | 0 | prefix = node == 0 ? NULL : cfb->entries[node].name; |
613 | 0 | node = cfb->entries[node].d; |
614 | 0 | } |
615 | |
|
616 | 0 | } |
617 | | |
618 | | static void |
619 | | absolutise_names(fz_context *ctx, fz_cfb_archive *cfb) |
620 | 0 | { |
621 | 0 | make_absolute(ctx, cfb, NULL, 0, 0); |
622 | 0 | } |
623 | | |
624 | | static void |
625 | | strip_unused_names(fz_context *ctx, fz_cfb_archive *cfb) |
626 | 0 | { |
627 | 0 | int i, j; |
628 | 0 | int n = cfb->entries_len; |
629 | | |
630 | | /* Init i and j so that we always delete the root node. */ |
631 | 0 | fz_free(ctx, cfb->entries[0].name); |
632 | 0 | for (i = 1, j = 0; i < n; i++) |
633 | 0 | { |
634 | 0 | if (cfb->entries[i].t == REACHED_KEEP) |
635 | 0 | { |
636 | 0 | if (i != j) |
637 | 0 | cfb->entries[j] = cfb->entries[i]; |
638 | 0 | cfb->entries[j].t = i; |
639 | 0 | j++; |
640 | 0 | } |
641 | 0 | else |
642 | 0 | fz_free(ctx, cfb->entries[i].name); |
643 | 0 | } |
644 | 0 | cfb->entries_len = j; |
645 | 0 | } |
646 | | |
647 | | fz_archive * |
648 | | fz_open_cfb_archive_with_stream(fz_context *ctx, fz_stream *file) |
649 | 0 | { |
650 | 0 | fz_cfb_archive *cfb; |
651 | 0 | uint8_t buffer[4096]; |
652 | 0 | uint32_t sector, slow_sector, slow_sector_flag; |
653 | 0 | int i; |
654 | |
|
655 | 0 | if (!fz_is_cfb_archive(ctx, file)) |
656 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "cannot recognize cfb archive"); |
657 | | |
658 | 0 | cfb = fz_new_derived_archive(ctx, file, fz_cfb_archive); |
659 | 0 | cfb->super.format = "cfb"; |
660 | 0 | cfb->super.count_entries = count_cfb_entries; |
661 | 0 | cfb->super.list_entry = list_cfb_entry; |
662 | 0 | cfb->super.has_entry = has_cfb_entry; |
663 | 0 | cfb->super.read_entry = read_cfb_entry; |
664 | 0 | cfb->super.open_entry = open_cfb_entry; |
665 | 0 | cfb->super.drop_archive = drop_cfb_archive; |
666 | |
|
667 | 0 | fz_try(ctx) |
668 | 0 | { |
669 | 0 | fz_seek(ctx, file, 0, SEEK_SET); |
670 | | /* Read the header */ |
671 | 0 | expect(ctx, file, sig, 8, "Bad signature"); |
672 | 0 | expect(ctx, file, zeros, 16, "Bad CLSID"); |
673 | | /* The minor version is SUPPOSED to be 0x3e, but we don't seem to be |
674 | | * able to rely on this. So just skip it. */ |
675 | 0 | (void)fz_read_uint16_le(ctx, file); |
676 | 0 | cfb->major = fz_read_uint16_le(ctx, file); |
677 | 0 | if (cfb->major != 3 && cfb->major != 4) |
678 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Bad major version of CFB: %d", cfb->major); |
679 | 0 | expect16(ctx, file, 0xfffe, "Bad byte order"); |
680 | 0 | cfb->sector_shift = fz_read_uint16_le(ctx, file); |
681 | 0 | if ((cfb->major == 3 && cfb->sector_shift != 9) || |
682 | 0 | (cfb->major == 4 && cfb->sector_shift != 12)) |
683 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Bad sector shift: %d", cfb->sector_shift); |
684 | 0 | expect16(ctx, file, 6, "Bad mini section shift"); |
685 | 0 | expect(ctx, file, zeros, 6, "Bad padding"); |
686 | 0 | cfb->num_dir_sectors = fz_read_uint32_le(ctx, file); |
687 | 0 | cfb->num_fat_sectors = fz_read_uint32_le(ctx, file); |
688 | 0 | cfb->dir_sector0 = fz_read_uint32_le(ctx, file); |
689 | 0 | (void)fz_read_uint32_le(ctx, file); /* Transaction signature number */ |
690 | 0 | expect32(ctx, file, 0x1000, "Bad mini stream cutoff size"); |
691 | 0 | cfb->mini_fat_sector0 = fz_read_uint32_le(ctx, file); |
692 | 0 | cfb->num_mini_fat_sectors = fz_read_uint32_le(ctx, file); |
693 | 0 | cfb->difat_sector0 = fz_read_uint32_le(ctx, file); |
694 | 0 | cfb->num_difat_sectors = fz_read_uint32_le(ctx, file); |
695 | 0 | for (i = 0; i < 109; i++) |
696 | 0 | cfb->difat[i] = fz_read_uint32_le(ctx, file); |
697 | 0 | cfb->fatcache_sector = (uint32_t)-1; |
698 | 0 | cfb->minifatcache_sector = (uint32_t)-1; |
699 | | |
700 | | /* Read the directory entries. */ |
701 | | /* On our first pass through, EVERYTHING goes into the entries. */ |
702 | 0 | sector = cfb->dir_sector0; |
703 | 0 | slow_sector = sector; |
704 | 0 | slow_sector_flag = 0; |
705 | 0 | do |
706 | 0 | { |
707 | 0 | size_t z = ((size_t)1)<<cfb->sector_shift; |
708 | 0 | size_t off; |
709 | | |
710 | | /* Fetch the sector. */ |
711 | 0 | fz_seek(ctx, file, ((int64_t)sector+1)<<cfb->sector_shift, SEEK_SET); |
712 | 0 | read(ctx, file, buffer, z); |
713 | |
|
714 | 0 | for (off = 0; off < z; off += 128) |
715 | 0 | { |
716 | 0 | int count = 0; |
717 | 0 | int type; |
718 | 0 | int namelen = get16(buffer+off+64); |
719 | 0 | cfb_entry *e; |
720 | |
|
721 | 0 | if (namelen == 0) |
722 | 0 | break; |
723 | | |
724 | | /* What flavour of object is this? */ |
725 | 0 | type = buffer[off+64+2]; |
726 | | |
727 | | /* Count the name length in utf8 encoded bytes, including terminator. */ |
728 | 0 | for (i = 0; i < 64; i += 2) |
729 | 0 | { |
730 | 0 | int ucs = get16(buffer+off+i); |
731 | 0 | if (ucs == 0) |
732 | 0 | break; |
733 | 0 | count += fz_runelen(ucs); |
734 | 0 | } |
735 | 0 | if (i+2 != namelen || i == 64) |
736 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Malformed name in CFB directory"); |
737 | | |
738 | | /* Ensure our entries list is long enough. */ |
739 | 0 | e = fz_push_list(ctx, cfb->entries); |
740 | | |
741 | | /* Copy the name. */ |
742 | 0 | e->name = fz_malloc(ctx, count + 1); |
743 | 0 | count = 0; |
744 | 0 | for (i = 0; i < 64; i += 2) |
745 | 0 | { |
746 | 0 | int ucs = buffer[off+i] + (buffer[off+i+1]<<8); |
747 | 0 | if (ucs == 0) |
748 | 0 | break; |
749 | 0 | count += fz_runetochar(&e->name[count], ucs); |
750 | 0 | } |
751 | 0 | e->name[count] = 0; |
752 | |
|
753 | 0 | e->sector = get32(buffer+off+128-12); |
754 | 0 | e->size = get_len(ctx, cfb, buffer+off+128-8); |
755 | 0 | e->l = get32(buffer+off+68); |
756 | 0 | e->r = get32(buffer+off+72); |
757 | 0 | e->d = get32(buffer+off+76); |
758 | 0 | e->t = type; |
759 | |
|
760 | | #ifdef DEBUG_DIRENTRIES |
761 | | fz_write_printf(ctx, fz_stddbg(ctx), "%d: ", cfb->entries_len-1); |
762 | | if (type == 1) |
763 | | fz_write_printf(ctx, fz_stddbg(ctx), "(storage) "); |
764 | | else if (type == 2) |
765 | | fz_write_printf(ctx, fz_stddbg(ctx), "(file) "); |
766 | | else if (type == 5) |
767 | | fz_write_printf(ctx, fz_stddbg(ctx), "(root) "); |
768 | | else |
769 | | fz_write_printf(ctx, fz_stddbg(ctx), "(%d?) ", type); |
770 | | |
771 | | fz_write_printf(ctx, fz_stddbg(ctx), "%q", e->name); |
772 | | fz_write_printf(ctx, fz_stddbg(ctx), " @%x+%x\n", e->sector, e->size ); |
773 | | if (cfb->entries[cfb->entries_len-1].l <= MAXREGSID) |
774 | | fz_write_printf(ctx, fz_stddbg(ctx), "\tleft=%d\n", e->l); |
775 | | if (cfb->entries[cfb->entries_len-1].r <= MAXREGSID) |
776 | | fz_write_printf(ctx, fz_stddbg(ctx), "\tright=%d\n", e->r); |
777 | | if (cfb->entries[cfb->entries_len-1].d <= MAXREGSID) |
778 | | fz_write_printf(ctx, fz_stddbg(ctx), "\tchild=%d\n", e->d); |
779 | | #endif |
780 | | |
781 | | /* Type 5 is just for the root. */ |
782 | 0 | if (type == 5) |
783 | 0 | { |
784 | 0 | cfb->mini_stream_sector0 = get32(buffer+off+128-12); |
785 | 0 | cfb->mini_stream_len = get_len(ctx, cfb, buffer+off+128-8); |
786 | 0 | } |
787 | 0 | } |
788 | | |
789 | | /* To get the next sector, we need to read it from the FAT. */ |
790 | 0 | sector = read_fat(ctx, cfb, sector); |
791 | 0 | slow_sector_flag = !slow_sector_flag; |
792 | 0 | if (slow_sector_flag == 0) |
793 | 0 | slow_sector = read_fat(ctx, cfb, slow_sector); |
794 | 0 | if (slow_sector == sector) |
795 | 0 | fz_throw(ctx, FZ_ERROR_FORMAT, "Loop in FAT"); |
796 | 0 | } |
797 | 0 | while (sector <= MAXREGSECT); |
798 | | |
799 | 0 | absolutise_names(ctx, cfb); |
800 | 0 | strip_unused_names(ctx, cfb); |
801 | |
|
802 | | #ifdef DEBUG_DIRENTRIES |
803 | | for (i = 0; i < cfb->entries_len; i++) |
804 | | fz_write_printf(ctx, fz_stddbg(ctx), "%d: %s (was %d)\n", i, cfb->entries[i].name, cfb->entries[i].t); |
805 | | #endif |
806 | 0 | } |
807 | 0 | fz_catch(ctx) |
808 | 0 | { |
809 | 0 | fz_drop_archive(ctx, &cfb->super); |
810 | 0 | fz_rethrow(ctx); |
811 | 0 | } |
812 | | |
813 | 0 | return &cfb->super; |
814 | 0 | } |
815 | | |
816 | | fz_archive * |
817 | | fz_open_cfb_archive(fz_context *ctx, const char *filename) |
818 | 0 | { |
819 | 0 | fz_archive *cfb = NULL; |
820 | 0 | fz_stream *file; |
821 | |
|
822 | 0 | file = fz_open_file(ctx, filename); |
823 | |
|
824 | 0 | fz_try(ctx) |
825 | 0 | cfb = fz_open_cfb_archive_with_stream(ctx, file); |
826 | 0 | fz_always(ctx) |
827 | 0 | fz_drop_stream(ctx, file); |
828 | 0 | fz_catch(ctx) |
829 | 0 | fz_rethrow(ctx); |
830 | | |
831 | 0 | return cfb; |
832 | 0 | } |