Coverage Report

Created: 2026-10-13 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}