Line | Count | Source |
1 | | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | | * |
3 | | * Copyright 2004 Komarov Valery |
4 | | * Copyright 2006 Christophe Leitienne |
5 | | * Copyright 2008-2017 David Hoerl |
6 | | * Copyright 2013 Bob Colbert |
7 | | * Copyright 2013-2018 Evan Miller |
8 | | * |
9 | | * This file is part of libxls -- A multiplatform, C/C++ library for parsing |
10 | | * Excel(TM) files. |
11 | | * |
12 | | * Redistribution and use in source and binary forms, with or without |
13 | | * modification, are permitted provided that the following conditions are met: |
14 | | * |
15 | | * 1. Redistributions of source code must retain the above copyright notice, |
16 | | * this list of conditions and the following disclaimer. |
17 | | * |
18 | | * 2. Redistributions in binary form must reproduce the above copyright |
19 | | * notice, this list of conditions and the following disclaimer in the |
20 | | * documentation and/or other materials provided with the distribution. |
21 | | * |
22 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS |
23 | | * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
24 | | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
25 | | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR |
26 | | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
27 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
28 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
29 | | * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
30 | | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
31 | | * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
32 | | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
33 | | * |
34 | | */ |
35 | | |
36 | | #include "config.h" |
37 | | |
38 | | #include <memory.h> |
39 | | #include <string.h> |
40 | | #include <stdio.h> |
41 | | #include <stdlib.h> |
42 | | |
43 | | #include "../include/libxls/ole.h" |
44 | | #include "../include/libxls/xlstool.h" |
45 | | #include "../include/libxls/endian.h" |
46 | | |
47 | | extern int xls_debug; |
48 | | |
49 | | //#define OLE_DEBUG |
50 | | |
51 | | //static const DWORD MSATSECT = 0xFFFFFFFC; // -4 |
52 | | //static const DWORD FATSECT = 0xFFFFFFFD; // -3 |
53 | | static const DWORD ENDOFCHAIN = 0xFFFFFFFE; // -2 |
54 | | static const DWORD FREESECT = 0xFFFFFFFF; // -1 |
55 | | |
56 | | static size_t sector_pos(OLE2* ole2, DWORD sid); |
57 | | static ssize_t sector_read(OLE2* ole2, void *buffer, size_t buffer_len, DWORD sid); |
58 | | static ssize_t read_MSAT(OLE2* ole2, OLE2Header *oleh); |
59 | | static void *ole_malloc(size_t len); |
60 | | |
61 | 12.1k | static void *ole_malloc(size_t len) { |
62 | 12.1k | if (len > (1<<24) || len == 0) { |
63 | 50 | return NULL; |
64 | 50 | } |
65 | 12.1k | return calloc(1, len); |
66 | 12.1k | } |
67 | | |
68 | | /* Reallocates memory and zero-fills only the newly grown region (from old_len to new_len). Frees ptr on failure. */ |
69 | 1.28k | static void *ole_realloc_zero(void *ptr, size_t old_len, size_t new_len) { |
70 | 1.28k | if (new_len > (1<<24) || new_len == 0) { |
71 | 11 | free(ptr); |
72 | 11 | return NULL; |
73 | 11 | } |
74 | 1.27k | void *new_ptr = realloc(ptr, new_len); |
75 | 1.27k | if (!new_ptr) { |
76 | 0 | free(ptr); |
77 | 0 | return NULL; |
78 | 0 | } |
79 | 1.27k | if (new_len > old_len) { |
80 | 1.22k | memset((char *)new_ptr + old_len, 0, new_len - old_len); |
81 | 1.22k | } |
82 | 1.27k | return new_ptr; |
83 | 1.27k | } |
84 | | |
85 | 4.32k | static int ole2_validate_sector_chain(DWORD *chain, DWORD chain_count, DWORD chain_start) { |
86 | 4.32k | if (chain == NULL || chain_count == 0) |
87 | 13 | return 0; |
88 | 4.30k | DWORD count = 0; |
89 | 4.30k | DWORD sector = chain_start; |
90 | 8.31M | while (sector != ENDOFCHAIN) { |
91 | 8.31M | if (sector >= chain_count) |
92 | 243 | return 0; |
93 | | |
94 | 8.31M | if (++count >= chain_count) |
95 | 30 | return 0; |
96 | | |
97 | 8.31M | sector = xlsIntVal(chain[sector]); |
98 | 8.31M | } |
99 | 4.03k | return 1; |
100 | 4.30k | } |
101 | | |
102 | 4.25M | static int ole2_validate_sector(DWORD sector, OLE2 *ole) { |
103 | 4.25M | if (sector >= ole->SecIDCount) { |
104 | 2.92k | if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SecID[%d]\n", |
105 | 0 | (int)sector, ole->SecIDCount); |
106 | 2.92k | return 0; |
107 | 2.92k | } |
108 | | |
109 | 4.24M | if (sector == xlsIntVal(ole->SecID[sector])) { |
110 | 4 | if (xls_debug) fprintf(stderr, "Error: Sector loop detected, SecID[%d] = %d\n", |
111 | 0 | (int)sector, (int)sector); |
112 | 4 | return 0; |
113 | 4 | } |
114 | | |
115 | 4.24M | return 1; |
116 | 4.24M | } |
117 | | |
118 | | // Read next sector of stream |
119 | | static int ole2_bufread(OLE2Stream* olest) |
120 | 887k | { |
121 | 887k | BYTE *ptr; |
122 | | |
123 | | #ifdef OLE_DEBUG |
124 | | fprintf(stderr, "----------------------------------------------\n"); |
125 | | fprintf(stderr, "ole2_bufread (start)\n"); |
126 | | #endif |
127 | | |
128 | 887k | if (olest == NULL || olest->ole == NULL) |
129 | 0 | return -1; |
130 | | |
131 | 887k | if ((DWORD)olest->fatpos!=ENDOFCHAIN) |
132 | 875k | { |
133 | 875k | if(olest->sfat) { |
134 | 250k | if (olest->ole->SSAT == NULL || olest->buf == NULL || olest->ole->SSecID == NULL) |
135 | 19 | return -1; |
136 | | |
137 | 250k | if (olest->fatpos*olest->ole->lssector + olest->bufsize > olest->ole->SSATCount) { |
138 | 138 | if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSAT\n", (int)olest->fatpos); |
139 | 138 | return -1; |
140 | 138 | } |
141 | | |
142 | 250k | ptr = olest->ole->SSAT + olest->fatpos*olest->ole->lssector; |
143 | 250k | memcpy(olest->buf, ptr, olest->bufsize); |
144 | | |
145 | 250k | if (olest->fatpos >= olest->ole->SSecIDCount) { |
146 | 0 | if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSecID[%d]\n", |
147 | 0 | (int)olest->fatpos, olest->ole->SSecIDCount); |
148 | 0 | return -1; |
149 | 0 | } |
150 | | |
151 | 250k | olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]); |
152 | 250k | olest->pos=0; |
153 | 250k | olest->cfat++; |
154 | 625k | } else { |
155 | 625k | if ((int)olest->fatpos < 0 || |
156 | 625k | sector_read(olest->ole, olest->buf, olest->bufsize, olest->fatpos) == -1) { |
157 | 9.03k | if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", (int)olest->fatpos); |
158 | 9.03k | return -1; |
159 | 9.03k | } |
160 | | |
161 | 616k | if (!ole2_validate_sector(olest->fatpos, olest->ole)) { |
162 | 0 | return -1; |
163 | 0 | } |
164 | | |
165 | 616k | olest->fatpos = xlsIntVal(olest->ole->SecID[olest->fatpos]); |
166 | 616k | olest->pos=0; |
167 | 616k | olest->cfat++; |
168 | 616k | } |
169 | 875k | } |
170 | | #ifdef OLE_DEBUG |
171 | | fprintf(stderr, "----------------------------------------------\n"); |
172 | | fprintf(stderr, "ole2_bufread (end)\n"); |
173 | | #endif |
174 | | // else printf("ENDOFCHAIN!!!\n"); |
175 | 878k | return 0; |
176 | 887k | } |
177 | | |
178 | | // Read part of stream |
179 | | ssize_t ole2_read(void* buf, size_t size, size_t count, OLE2Stream* olest) |
180 | 6.60M | { |
181 | 6.60M | size_t didReadCount=0; |
182 | 6.60M | size_t totalReadCount; |
183 | | |
184 | 6.60M | totalReadCount=size*count; |
185 | 6.60M | if (buf && totalReadCount > 0) { |
186 | 6.60M | memset(buf, 0, totalReadCount); |
187 | 6.60M | } |
188 | | |
189 | | // olest->size inited to -1 |
190 | | // printf("===== ole2_read(%ld bytes)\n", totalReadCount); |
191 | | |
192 | 6.60M | if ((long)olest->size>=0 && !olest->sfat) // directory is -1 |
193 | 4.35M | { |
194 | 4.35M | size_t rem; |
195 | 4.35M | rem = olest->size - (olest->cfat*olest->ole->lsector+olest->pos); |
196 | 4.35M | totalReadCount = rem<totalReadCount?rem:totalReadCount; |
197 | 4.35M | if (rem<=0) olest->eof=1; |
198 | | |
199 | | // printf(" rem=%ld olest->size=%d - subfunc=%d\n", rem, olest->size, (olest->cfat*olest->ole->lsector+olest->pos) ); |
200 | | //printf(" totalReadCount=%d (rem=%d size*count=%ld)\n", totalReadCount, rem, size*count); |
201 | 4.35M | } |
202 | | |
203 | 13.9M | while ((!olest->eof) && (didReadCount < totalReadCount)) |
204 | 7.37M | { |
205 | 7.37M | unsigned long remainingBytes; |
206 | 7.37M | size_t needToReadCount; |
207 | | |
208 | 7.37M | needToReadCount = totalReadCount - didReadCount; |
209 | 7.37M | remainingBytes = olest->bufsize - olest->pos; |
210 | | |
211 | 7.37M | if (needToReadCount < remainingBytes) { // does the current sector contain all the data I need? |
212 | 6.52M | memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, needToReadCount); |
213 | 6.52M | olest->pos += needToReadCount; |
214 | 6.52M | didReadCount += needToReadCount; |
215 | 6.52M | } else { |
216 | 848k | memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, remainingBytes); |
217 | 848k | olest->pos += remainingBytes; |
218 | 848k | didReadCount += remainingBytes; |
219 | 848k | if (ole2_bufread(olest) == -1) |
220 | 8.51k | return -1; |
221 | 848k | } |
222 | 7.36M | if (((DWORD)olest->fatpos == ENDOFCHAIN) && (olest->pos >= olest->bufsize)) { |
223 | 11.6k | olest->eof=1; |
224 | 11.6k | } |
225 | 7.36M | } |
226 | 6.59M | if (didReadCount > totalReadCount) |
227 | 0 | return -1; |
228 | | |
229 | | // printf(" didReadCount=%ld EOF=%d\n", didReadCount, olest->eof); |
230 | | // printf("=====\n"); |
231 | | |
232 | | #ifdef OLE_DEBUG |
233 | | fprintf(stderr, "----------------------------------------------\n"); |
234 | | fprintf(stderr, "ole2_read (end)\n"); |
235 | | fprintf(stderr, "start: %d \n",olest->start); |
236 | | fprintf(stderr, "pos: %d \n",(int)olest->pos); |
237 | | fprintf(stderr, "cfat: %d \n",(int)olest->cfat); |
238 | | fprintf(stderr, "size: %d \n",(int)olest->size); |
239 | | fprintf(stderr, "fatpos: %d \n",(int)olest->fatpos); |
240 | | fprintf(stderr, "bufsize: %d \n",(int)olest->bufsize); |
241 | | fprintf(stderr, "eof: %d \n",olest->eof); |
242 | | #endif |
243 | | |
244 | 6.59M | return didReadCount; |
245 | 6.59M | } |
246 | | |
247 | | // Open stream in logical ole file |
248 | | OLE2Stream* ole2_sopen(OLE2* ole,DWORD start, size_t size) |
249 | 4.32k | { |
250 | 4.32k | OLE2Stream* olest=NULL; |
251 | 4.32k | int success = 1; |
252 | | |
253 | | #ifdef OLE_DEBUG |
254 | | fprintf(stderr, "----------------------------------------------\n"); |
255 | | fprintf(stderr, "ole2_sopen start=%Xh\n", start); |
256 | | #endif |
257 | | |
258 | 4.32k | olest = calloc(1, sizeof(OLE2Stream)); |
259 | 4.32k | olest->ole=ole; |
260 | 4.32k | olest->size=size; |
261 | 4.32k | olest->fatpos=start; |
262 | 4.32k | olest->start=start; |
263 | 4.32k | olest->cfat=-1; |
264 | 4.32k | if((long)size > 0 && size < (size_t)ole->sectorcutoff) { |
265 | 925 | olest->bufsize=ole->lssector; |
266 | 925 | olest->sfat = 1; |
267 | 3.39k | } else { |
268 | 3.39k | olest->bufsize=ole->lsector; |
269 | 3.39k | } |
270 | 4.32k | if ((olest->buf = ole_malloc(olest->bufsize)) == NULL) { |
271 | 0 | success = 0; |
272 | 0 | goto cleanup; |
273 | 0 | } |
274 | | |
275 | 4.32k | if (olest->sfat) { |
276 | 925 | if (!ole2_validate_sector_chain(ole->SSecID, ole->SSecIDCount, start)) { |
277 | 121 | success = 0; |
278 | 121 | goto cleanup; |
279 | 121 | } |
280 | 3.39k | } else { |
281 | 3.39k | if (!ole2_validate_sector_chain(ole->SecID, ole->SecIDCount, start)) { |
282 | 165 | success = 0; |
283 | 165 | goto cleanup; |
284 | 165 | } |
285 | 3.39k | } |
286 | | |
287 | 4.03k | if (ole2_bufread(olest) == -1) { |
288 | 33 | success = 0; |
289 | 33 | goto cleanup; |
290 | 33 | } |
291 | | |
292 | 4.32k | cleanup: |
293 | 4.32k | if (!success) { |
294 | 319 | ole2_fclose(olest); |
295 | 319 | olest = NULL; |
296 | 319 | } |
297 | | |
298 | | // if(xls_debug) printf("sopen: sector=%d next=%d\n", start, olest->fatpos); |
299 | 4.32k | return olest; |
300 | 4.03k | } |
301 | | |
302 | | // Move in stream |
303 | | int ole2_seek(OLE2Stream* olest,DWORD ofs) |
304 | 38.7k | { |
305 | | #ifdef OLE_DEBUG |
306 | | fprintf(stderr, "SEEK %x\n", ofs); |
307 | | #endif |
308 | 38.7k | if(olest->sfat) { |
309 | 10.5k | ldiv_t div_rez=ldiv(ofs,olest->ole->lssector); |
310 | 10.5k | int i; |
311 | 10.5k | olest->fatpos=olest->start; |
312 | | |
313 | 10.5k | if (div_rez.quot!=0) |
314 | 6.49k | { |
315 | 79.6k | for (i=0;i<div_rez.quot;i++) { |
316 | 74.2k | if (olest->fatpos >= olest->ole->SSecIDCount) |
317 | 1.11k | return -1; |
318 | 73.1k | olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]); |
319 | 73.1k | } |
320 | 6.49k | } |
321 | | |
322 | 9.39k | if (ole2_bufread(olest) == -1) |
323 | 53 | return -1; |
324 | | |
325 | 9.34k | olest->pos=div_rez.rem; |
326 | 9.34k | olest->eof=0; |
327 | 9.34k | olest->cfat=div_rez.quot; |
328 | | //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem); |
329 | 28.2k | } else { |
330 | 28.2k | ldiv_t div_rez=ldiv(ofs,olest->ole->lsector); |
331 | 28.2k | int i; |
332 | | #ifdef OLE_DEBUG |
333 | | fprintf(stderr, "seeking fatpos%lu start %u\n", olest->fatpos, olest->start); |
334 | | #endif |
335 | 28.2k | olest->fatpos=olest->start; |
336 | | |
337 | 28.2k | if (div_rez.quot!=0) |
338 | 4.98k | { |
339 | 152k | for (i=0;i<div_rez.quot;i++) { |
340 | 150k | if (!ole2_validate_sector(olest->fatpos, olest->ole)) |
341 | 2.85k | return -1; |
342 | 147k | olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]); |
343 | 147k | } |
344 | 4.98k | } |
345 | | |
346 | 25.4k | if (ole2_bufread(olest) == -1) |
347 | 584 | return -1; |
348 | | |
349 | 24.8k | olest->pos=div_rez.rem; |
350 | 24.8k | olest->eof=0; |
351 | 24.8k | olest->cfat=div_rez.quot; |
352 | | //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem); |
353 | 24.8k | } |
354 | 34.1k | return 0; |
355 | 38.7k | } |
356 | | |
357 | | // Open logical file contained in physical OLE file |
358 | | OLE2Stream* ole2_fopen(OLE2* ole, const char *file) |
359 | 6.45k | { |
360 | 6.45k | int i; |
361 | | |
362 | | #ifdef OLE_DEBUG |
363 | | fprintf(stderr, "----------------------------------------------\n"); |
364 | | fprintf(stderr, "ole2_fopen %s\n", file); |
365 | | #endif |
366 | | |
367 | 17.6k | for (i=0;i<ole->files.count;i++) { |
368 | 13.1k | char *str = ole->files.file[i].name; |
369 | | #ifdef OLE_DEBUG |
370 | | fprintf(stderr, "----------------------------------------------\n"); |
371 | | fprintf(stderr, "ole2_fopen found %s\n", str); |
372 | | #endif |
373 | 13.1k | if (str && strcmp(str,file)==0) // newer versions of Excel don't write the "Root Entry" string for the first set of data |
374 | 1.97k | { |
375 | 1.97k | return ole2_sopen(ole,ole->files.file[i].start,ole->files.file[i].size); |
376 | 1.97k | } |
377 | 13.1k | } |
378 | 4.48k | return NULL; |
379 | 6.45k | } |
380 | | |
381 | 4.33M | static int ole2_fseek(OLE2 *ole2, size_t pos) { |
382 | 4.33M | if (ole2->file) |
383 | 0 | return fseek(ole2->file, pos, SEEK_SET); |
384 | | |
385 | 4.33M | if (pos > ole2->buffer_len) |
386 | 9.07k | return -1; |
387 | | |
388 | 4.32M | ole2->buffer_pos = pos; |
389 | 4.32M | return 0; |
390 | 4.33M | } |
391 | | |
392 | | // Will read up to `size' bytes from the input, and pad the rest of `size' with |
393 | | // zeros if the input file or buffer is short. |
394 | 4.32M | static size_t ole2_fread(OLE2 *ole2, void *buffer, size_t buffer_len, size_t size) { |
395 | 4.32M | if (size > buffer_len) |
396 | 6 | return 0; |
397 | | |
398 | 4.32M | memset(buffer, 0, size); |
399 | | |
400 | 4.32M | if (ole2->file) |
401 | 0 | return fread(buffer, 1, size, ole2->file) > 0; |
402 | | |
403 | 4.32M | if (ole2->buffer_pos >= ole2->buffer_len) |
404 | 293 | return 0; |
405 | | |
406 | 4.32M | if (ole2->buffer_pos + size > ole2->buffer_len) |
407 | 1.75M | size = ole2->buffer_len - ole2->buffer_pos; |
408 | | |
409 | 4.32M | memcpy(buffer, (const char *)ole2->buffer + ole2->buffer_pos, size); |
410 | 4.32M | ole2->buffer_pos += size; |
411 | | |
412 | 4.32M | return 1; |
413 | 4.32M | } |
414 | | |
415 | | // read header and check magic numbers |
416 | 2.95k | static ssize_t ole2_read_header(OLE2 *ole) { |
417 | 2.95k | ssize_t bytes_read = 0, total_bytes_read = 0; |
418 | 2.95k | OLE2Header *oleh = calloc(1, sizeof(OLE2Header)); |
419 | 2.95k | if (ole2_fread(ole, oleh, sizeof(OLE2Header), sizeof(OLE2Header)) != 1) { |
420 | 0 | total_bytes_read = -1; |
421 | 0 | goto cleanup; |
422 | 0 | } |
423 | 2.95k | total_bytes_read += sizeof(OLE2Header); |
424 | 2.95k | xlsConvertHeader(oleh); |
425 | | |
426 | | // make sure the file looks good. Note: this code only works on Little Endian machines |
427 | 2.95k | if(oleh->id[0] != 0xE011CFD0 || oleh->id[1] != 0xE11AB1A1 || oleh->byteorder != 0xFFFE) { |
428 | 127 | if (xls_debug) fprintf(stderr, "Not an excel file\n"); |
429 | 127 | total_bytes_read = -1; |
430 | 127 | goto cleanup; |
431 | 127 | } |
432 | | |
433 | | //ole->lsector=(WORD)pow(2,oleh->lsector); |
434 | | //ole->lssector=(WORD)pow(2,oleh->lssector); |
435 | 2.83k | ole->lsector=512; |
436 | 2.83k | ole->lssector=64; |
437 | | |
438 | 2.83k | if (oleh->lsectorB != 9 || oleh->lssectorB != 6) { // 2**9 == 512, 2**6 == 64 |
439 | 25 | if (xls_debug) fprintf(stderr, "Unexpected sector size\n"); |
440 | 25 | total_bytes_read = -1; |
441 | 25 | goto cleanup; |
442 | 25 | } |
443 | | |
444 | 2.80k | ole->cfat=oleh->cfat; |
445 | 2.80k | ole->dirstart=oleh->dirstart; |
446 | 2.80k | ole->sectorcutoff=oleh->sectorcutoff; |
447 | 2.80k | ole->sfatstart=oleh->sfatstart; |
448 | 2.80k | ole->csfat=oleh->csfat; |
449 | 2.80k | ole->difstart=oleh->difstart; |
450 | 2.80k | ole->cdif=oleh->cdif; |
451 | 2.80k | ole->files.count=0; |
452 | | |
453 | | #ifdef OLE_DEBUG |
454 | | fprintf(stderr, "==== OLE HEADER ====\n"); |
455 | | //printf ("Header Size: %i \n", sizeof(OLE2Header)); |
456 | | //printf ("id[0]-id[1]: %X-%X \n", oleh->id[0], oleh->id[1]); |
457 | | fprintf(stderr, "verminor: %X \n",oleh->verminor); |
458 | | fprintf(stderr, "verdll: %X \n",oleh->verdll); |
459 | | //printf ("Byte order: %X \n",oleh->byteorder); |
460 | | fprintf(stderr, "sect len: %X (%i)\n",ole->lsector,ole->lsector); // ole |
461 | | fprintf(stderr, "mini len: %X (%i)\n",ole->lssector,ole->lssector); // ole |
462 | | fprintf(stderr, "Fat sect.: %i \n",oleh->cfat); |
463 | | fprintf(stderr, "Dir Start: %i \n",oleh->dirstart); |
464 | | |
465 | | fprintf(stderr, "Mini Cutoff: %i \n",oleh->sectorcutoff); |
466 | | fprintf(stderr, "MiniFat Start: %X \n",oleh->sfatstart); |
467 | | fprintf(stderr, "Count MFat: %i \n",oleh->csfat); |
468 | | fprintf(stderr, "Dif start: %X \n",oleh->difstart); |
469 | | fprintf(stderr, "Count Dif: %i \n",oleh->cdif); |
470 | | fprintf(stderr, "Fat Size: %u (0x%X) \n",oleh->cfat*ole->lsector,oleh->cfat*ole->lsector); |
471 | | #endif |
472 | | // read directory entries |
473 | 2.80k | if ((bytes_read = read_MSAT(ole, oleh)) == -1) { |
474 | 459 | total_bytes_read = -1; |
475 | 459 | goto cleanup; |
476 | 459 | } |
477 | 2.34k | total_bytes_read += bytes_read; |
478 | | |
479 | 2.95k | cleanup: |
480 | 2.95k | free(oleh); |
481 | | |
482 | 2.95k | return total_bytes_read; |
483 | 2.34k | } |
484 | | |
485 | 2.34k | static ssize_t ole2_read_body(OLE2 *ole) { |
486 | | // reuse this buffer |
487 | 2.34k | PSS *pss = NULL; |
488 | 2.34k | OLE2Stream *olest = NULL; |
489 | 2.34k | char* name = NULL; |
490 | 2.34k | ssize_t bytes_read = 0, total_bytes_read = 0; |
491 | | |
492 | 2.34k | if ((olest = ole2_sopen(ole,ole->dirstart, -1)) == NULL) { |
493 | 114 | total_bytes_read = -1; |
494 | 114 | goto cleanup; |
495 | 114 | } |
496 | 2.23k | pss = calloc(1, sizeof(PSS)); |
497 | 10.8k | do { |
498 | 10.8k | if ((bytes_read = ole2_read(pss,1,sizeof(PSS),olest)) == -1) { |
499 | 18 | total_bytes_read = -1; |
500 | 18 | goto cleanup; |
501 | 18 | } |
502 | 10.8k | total_bytes_read += bytes_read; |
503 | 10.8k | xlsConvertPss(pss); |
504 | 10.8k | if (pss->bsize > sizeof(pss->name)) { |
505 | 24 | total_bytes_read = -1; |
506 | 24 | goto cleanup; |
507 | 24 | } |
508 | 10.8k | name=transcode_utf16_to_utf8(pss->name, pss->bsize); |
509 | | #ifdef OLE_DEBUG |
510 | | fprintf(stderr, "OLE NAME: %s count=%d\n", name, (int)ole->files.count); |
511 | | #endif |
512 | 10.8k | if (pss->type == PS_USER_ROOT || pss->type == PS_USER_STREAM) // (name!=NULL) // |
513 | 4.65k | { |
514 | | |
515 | | #ifdef OLE_DEBUG |
516 | | fprintf(stderr, "OLE TYPE: %s file=%d size=%d\n", |
517 | | pss->type == PS_USER_ROOT ? "root" : "user", |
518 | | (int)ole->files.count, (int)pss->size); |
519 | | #endif |
520 | 4.65k | ole->files.file = realloc(ole->files.file,(ole->files.count+1)*sizeof(struct st_olefiles_data)); |
521 | 4.65k | ole->files.file[ole->files.count].name=name; |
522 | 4.65k | ole->files.file[ole->files.count].start=pss->sstart; |
523 | 4.65k | ole->files.file[ole->files.count].size=pss->size; |
524 | 4.65k | ole->files.count++; |
525 | | |
526 | | #ifdef OLE_DEBUG |
527 | | fprintf(stderr, "----------------------------------------------\n"); |
528 | | fprintf(stderr, "name: %s (size=%d [c=%c])\n", name, pss->bsize, name ? name[0]:' '); |
529 | | fprintf(stderr, "bsize %i\n",pss->bsize); |
530 | | fprintf(stderr, "type %i\n",pss->type); |
531 | | fprintf(stderr, "flag %i\n",pss->flag); |
532 | | fprintf(stderr, "left %X\n",pss->left); |
533 | | fprintf(stderr, "right %X\n",pss->right); |
534 | | fprintf(stderr, "child %X\n",pss->child); |
535 | | fprintf(stderr, "guid %.4X-%.4X-%.4X-%.4X %.4X-%.4X-%.4X-%.4X\n", |
536 | | pss->guid[0],pss->guid[1],pss->guid[2],pss->guid[3], |
537 | | pss->guid[4],pss->guid[5],pss->guid[6],pss->guid[7]); |
538 | | fprintf(stderr, "user flag %.4X\n",pss->userflags); |
539 | | fprintf(stderr, "sstart %.4d\n",pss->sstart); |
540 | | fprintf(stderr, "size %.4d\n",pss->size); |
541 | | #endif |
542 | 4.65k | if(pss->sstart == ENDOFCHAIN) { |
543 | 56 | if (xls_debug) verbose("END OF CHAIN\n"); |
544 | 4.59k | } else if(pss->type == PS_USER_STREAM) { |
545 | 3.29k | } else if(pss->type == PS_USER_ROOT) { |
546 | 1.30k | DWORD sector, k, blocks; |
547 | 1.30k | BYTE *wptr; |
548 | 1.30k | size_t bytes_left; |
549 | | |
550 | 1.30k | blocks = (pss->size + (ole->lsector - 1)) / ole->lsector; // count partial |
551 | 1.30k | if (ole->lsector == 0 || blocks > (1u << 24) / ole->lsector) { |
552 | 21 | total_bytes_read = -1; |
553 | 21 | goto cleanup; |
554 | 21 | } |
555 | | #ifdef OLE_DEBUG |
556 | | fprintf(stderr, "OLE BLOCKS: %d = (%d + (%d - 1))/%d\n", |
557 | | (int)blocks, (int)pss->size, (int)ole->lsector, (int)ole->lsector); |
558 | | #endif |
559 | 1.28k | size_t old_ssat_bytes = ole->SSATCount; |
560 | 1.28k | if ((ole->SSAT = ole_realloc_zero(ole->SSAT, old_ssat_bytes, blocks*ole->lsector)) == NULL) { |
561 | 11 | total_bytes_read = -1; |
562 | 11 | goto cleanup; |
563 | 11 | } |
564 | 1.27k | ole->SSATCount = blocks*ole->lsector; |
565 | | // printf("blocks %d\n", blocks); |
566 | | |
567 | 1.27k | sector = pss->sstart; |
568 | 1.27k | wptr = (BYTE*)ole->SSAT; |
569 | 1.27k | bytes_left = blocks*ole->lsector; |
570 | 1.43M | for(k=0; k<blocks; ++k) { |
571 | | // printf("block %d sector %d\n", k, sector); |
572 | 1.43M | if (sector == ENDOFCHAIN || sector_read(ole, wptr, bytes_left, sector) == -1) { |
573 | 81 | if (xls_debug) fprintf(stderr, "Unable to read sector #%d\n", sector); |
574 | 81 | total_bytes_read = -1; |
575 | 81 | goto cleanup; |
576 | 81 | } |
577 | 1.43M | if (!ole2_validate_sector(sector, ole)) { |
578 | 33 | total_bytes_read = -1; |
579 | 33 | goto cleanup; |
580 | 33 | } |
581 | 1.43M | total_bytes_read += ole->lsector; |
582 | 1.43M | wptr += ole->lsector; |
583 | 1.43M | bytes_left -= ole->lsector; |
584 | 1.43M | sector = xlsIntVal(ole->SecID[sector]); |
585 | 1.43M | } |
586 | 1.27k | } |
587 | 6.17k | } else { |
588 | 6.17k | free(name); |
589 | 6.17k | } |
590 | 10.8k | } while (!olest->eof); |
591 | | |
592 | 2.34k | cleanup: |
593 | 2.34k | if (olest) |
594 | 2.23k | ole2_fclose(olest); |
595 | 2.34k | if (pss) |
596 | 2.23k | free(pss); |
597 | | |
598 | | #ifdef OLE_DEBUG |
599 | | fprintf(stderr, "----------------------------------------------\n"); |
600 | | fprintf(stderr, "ole2_read_body: %d bytes\n", (int)total_bytes_read); |
601 | | #endif |
602 | | |
603 | 2.34k | return total_bytes_read; |
604 | 2.23k | } |
605 | | |
606 | 2.95k | OLE2 *ole2_read_header_and_body(OLE2 *ole) { |
607 | 2.95k | if (ole2_read_header(ole) == -1) { |
608 | 611 | ole2_close(ole); |
609 | 611 | return NULL; |
610 | 611 | } |
611 | | |
612 | 2.34k | if (ole2_read_body(ole) == -1) { |
613 | 302 | ole2_close(ole); |
614 | 302 | return NULL; |
615 | 302 | } |
616 | | |
617 | 2.04k | return ole; |
618 | 2.34k | } |
619 | | |
620 | | // Open in-memory buffer |
621 | 2.95k | OLE2 *ole2_open_buffer(const void *buffer, size_t len) { |
622 | 2.95k | OLE2 *ole = calloc(1, sizeof(OLE2)); |
623 | | |
624 | 2.95k | ole->buffer = buffer; |
625 | 2.95k | ole->buffer_len = len; |
626 | | |
627 | 2.95k | return ole2_read_header_and_body(ole); |
628 | 2.95k | } |
629 | | |
630 | | // Open physical file |
631 | | OLE2* ole2_open_file(const char *file) |
632 | 0 | { |
633 | 0 | OLE2* ole = NULL; |
634 | |
|
635 | | #ifdef OLE_DEBUG |
636 | | fprintf(stderr, "----------------------------------------------\n"); |
637 | | fprintf(stderr, "ole2_open_file %s\n", file); |
638 | | #endif |
639 | |
|
640 | 0 | if(xls_debug) printf("ole2_open: %s\n", file); |
641 | 0 | ole = calloc(1, sizeof(OLE2)); |
642 | |
|
643 | 0 | if (!(ole->file=fopen(file, "rb"))) { |
644 | 0 | if(xls_debug) fprintf(stderr, "File not found\n"); |
645 | 0 | free(ole); |
646 | 0 | return NULL; |
647 | 0 | } |
648 | | |
649 | 0 | return ole2_read_header_and_body(ole); |
650 | 0 | } |
651 | | |
652 | | void ole2_close(OLE2* ole2) |
653 | 2.95k | { |
654 | 2.95k | int i; |
655 | 2.95k | if (ole2->file) |
656 | 0 | fclose(ole2->file); |
657 | | |
658 | 7.61k | for(i=0; i<ole2->files.count; ++i) { |
659 | 4.65k | free(ole2->files.file[i].name); |
660 | 4.65k | } |
661 | 2.95k | free(ole2->files.file); |
662 | 2.95k | free(ole2->SecID); |
663 | 2.95k | free(ole2->SSecID); |
664 | 2.95k | free(ole2->SSAT); |
665 | 2.95k | free(ole2); |
666 | 2.95k | } |
667 | | |
668 | | void ole2_fclose(OLE2Stream* ole2st) |
669 | 4.32k | { |
670 | 4.32k | free(ole2st->buf); |
671 | 4.32k | free(ole2st); |
672 | 4.32k | } |
673 | | |
674 | | // Return offset in bytes of a sector from its sid |
675 | | static size_t sector_pos(OLE2* ole2, DWORD sid) |
676 | 4.33M | { |
677 | 4.33M | return 512 + sid * ole2->lsector; |
678 | 4.33M | } |
679 | | // Read one sector from its sid |
680 | | static ssize_t sector_read(OLE2* ole2, void *buffer, size_t buffer_len, DWORD sid) |
681 | 4.33M | { |
682 | 4.33M | size_t num; |
683 | 4.33M | size_t seeked; |
684 | | |
685 | 4.33M | if ((seeked = ole2_fseek(ole2, sector_pos(ole2, sid))) != 0) { |
686 | 9.07k | if (xls_debug) fprintf(stderr, "Error: wanted to seek to sector %u (0x%x) loc=%u\n", sid, sid, |
687 | 0 | (unsigned int)sector_pos(ole2, sid)); |
688 | 9.07k | return -1; |
689 | 9.07k | } |
690 | | |
691 | 4.32M | if ((num = ole2_fread(ole2, buffer, buffer_len, ole2->lsector)) != 1) { |
692 | 299 | if (xls_debug) fprintf(stderr, "Error: fread wanted 1 got %lu loc=%u\n", (unsigned long)num, |
693 | 0 | (unsigned int)sector_pos(ole2, sid)); |
694 | 299 | return -1; |
695 | 299 | } |
696 | | |
697 | 4.32M | return ole2->lsector; |
698 | 4.32M | } |
699 | | |
700 | | // read first 109 sectors of MSAT from header |
701 | 2.74k | static ssize_t read_MSAT_header(OLE2* ole2, OLE2Header* oleh, DWORD sectorCount) { |
702 | 2.74k | BYTE *sector = (BYTE*)ole2->SecID; |
703 | 2.74k | ssize_t bytes_read = 0, total_bytes_read = 0; |
704 | 2.74k | size_t bytes_left = ole2->SecIDCount * sizeof(DWORD); |
705 | 2.74k | DWORD sectorNum; |
706 | | |
707 | 7.69k | for (sectorNum = 0; sectorNum < sectorCount && sectorNum < 109; sectorNum++) |
708 | 5.34k | { |
709 | 5.34k | DWORD s = oleh->MSAT[sectorNum]; |
710 | 5.34k | if (s == ENDOFCHAIN || s == FREESECT) |
711 | 319 | break; |
712 | 5.02k | if ((bytes_read = sector_read(ole2, sector, bytes_left, s)) == -1) { |
713 | 74 | if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s); |
714 | 74 | return -1; |
715 | 74 | } |
716 | 4.94k | sector += ole2->lsector; |
717 | 4.94k | bytes_left -= ole2->lsector; |
718 | 4.94k | total_bytes_read += bytes_read; |
719 | 4.94k | } |
720 | 2.67k | return total_bytes_read; |
721 | 2.74k | } |
722 | | |
723 | | // Add additional sectors of the MSAT |
724 | 2.67k | static ssize_t read_MSAT_body(OLE2 *ole2, DWORD sectorOffset, DWORD sectorCount) { |
725 | 2.67k | DWORD sid = ole2->difstart; |
726 | 2.67k | ssize_t bytes_read = 0, total_bytes_read = 0; |
727 | 2.67k | DWORD sectorNum = sectorOffset; |
728 | | |
729 | 2.67k | DWORD *sector = ole_malloc(ole2->lsector); |
730 | | //printf("sid=%u (0x%x) sector=%u\n", sid, sid, ole2->lsector); |
731 | 4.88k | while (sid != ENDOFCHAIN && sid != FREESECT) // FREESECT only here due to an actual file that requires it (old Apple Numbers bug) |
732 | 2.40k | { |
733 | 2.40k | int posInSector; |
734 | | // read MSAT sector |
735 | 2.40k | if ((bytes_read = sector_read(ole2, sector, ole2->lsector, sid)) == -1) { |
736 | 85 | total_bytes_read = -1; |
737 | 85 | if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", sid); |
738 | 85 | goto cleanup; |
739 | 85 | } |
740 | 2.31k | total_bytes_read += bytes_read; |
741 | | |
742 | | // read content |
743 | 287k | for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++) |
744 | 284k | { |
745 | 284k | DWORD s = sector[posInSector]; |
746 | | //printf(" s[%d]=%d (0x%x)\n", posInSector, s, s); |
747 | | |
748 | 284k | if (s != ENDOFCHAIN && s != FREESECT) // see patch in Bug 31. For very large files |
749 | 214k | { |
750 | 214k | if (sectorNum == sectorCount) { |
751 | 32 | if (xls_debug) fprintf(stderr, "Error: Unable to seek to sector #%d\n", s); |
752 | 32 | total_bytes_read = -1; |
753 | 32 | goto cleanup; |
754 | 32 | } |
755 | 214k | if ((bytes_read = sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, |
756 | 214k | (ole2->SecIDCount * sizeof(DWORD) - sectorNum*ole2->lsector), s)) == -1) { |
757 | 50 | if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s); |
758 | 50 | total_bytes_read = -1; |
759 | 50 | goto cleanup; |
760 | 50 | } |
761 | 214k | total_bytes_read += bytes_read; |
762 | 214k | sectorNum++; |
763 | 214k | } |
764 | 284k | } |
765 | 2.23k | if (sid == sector[posInSector]) { |
766 | 24 | if (xls_debug) fprintf(stderr, "Error: Loop detected in sector #%d\n", sid); |
767 | 24 | total_bytes_read = -1; |
768 | 24 | goto cleanup; |
769 | 24 | } |
770 | 2.21k | sid = sector[posInSector]; |
771 | | //printf(" s[%d]=%d (0x%x)\n", posInSector, sid, sid); |
772 | 2.21k | } |
773 | | #ifdef OLE_DEBUG |
774 | | if(xls_debug) { |
775 | | //printf("==== READ IN SECTORS FOR MSAT TABLE====\n"); |
776 | | int i; |
777 | | for(i=0; i<512/4; ++i) { // just the first block |
778 | | if(ole2->SecID[i] != FREESECT) printf("SecID[%d]=%d\n", i, ole2->SecID[i]); |
779 | | } |
780 | | } |
781 | | //exit(0); |
782 | | #endif |
783 | | |
784 | 2.67k | cleanup: |
785 | 2.67k | free(sector); |
786 | 2.67k | return total_bytes_read; |
787 | 2.67k | } |
788 | | |
789 | | // read in short table |
790 | 2.48k | static ssize_t read_MSAT_trailer(OLE2 *ole2) { |
791 | 2.48k | ssize_t total_bytes_read = 0; |
792 | 2.48k | DWORD sector, k; |
793 | 2.48k | BYTE *wptr; |
794 | 2.48k | size_t bytes_left; |
795 | | |
796 | 2.48k | if(ole2->sfatstart == ENDOFCHAIN) |
797 | 83 | return 0; |
798 | | |
799 | 2.39k | if ((ole2->SSecID = ole_malloc(ole2->csfat*(size_t)ole2->lsector)) == NULL) { |
800 | 25 | return -1; |
801 | 25 | } |
802 | 2.37k | ole2->SSecIDCount = ole2->csfat*(size_t)ole2->lsector/4; |
803 | 2.37k | memset(ole2->SSecID, 0xFF, ole2->SSecIDCount * sizeof(DWORD)); |
804 | 2.37k | sector = ole2->sfatstart; |
805 | 2.37k | wptr=(BYTE*)ole2->SSecID; |
806 | 2.37k | bytes_left = ole2->SSecIDCount * sizeof(DWORD); |
807 | 2.05M | for(k=0; k<ole2->csfat; ++k) { |
808 | 2.05M | if (sector == ENDOFCHAIN || sector_read(ole2, wptr, bytes_left, sector) == -1) { |
809 | 69 | total_bytes_read = -1; |
810 | 69 | goto cleanup; |
811 | 69 | } |
812 | 2.05M | if (!ole2_validate_sector(sector, ole2)) { |
813 | 42 | total_bytes_read = -1; |
814 | 42 | goto cleanup; |
815 | 42 | } |
816 | 2.05M | wptr += ole2->lsector; |
817 | 2.05M | bytes_left -= ole2->lsector; |
818 | 2.05M | total_bytes_read += ole2->lsector; |
819 | 2.05M | sector = xlsIntVal(ole2->SecID[sector]); |
820 | 2.05M | } |
821 | | #ifdef OLE_DEBUG |
822 | | if(xls_debug) { |
823 | | int i; |
824 | | for(i=0; i<ole2->csfat; ++i) { |
825 | | if(ole2->SSecID[i] != FREESECT) fprintf(stderr, "SSecID[%d]=%d\n", i, ole2->SSecID[i]); |
826 | | } |
827 | | } |
828 | | #endif |
829 | | |
830 | 2.37k | cleanup: |
831 | 2.37k | return total_bytes_read; |
832 | 2.37k | } |
833 | | |
834 | | |
835 | | // Read MSAT |
836 | | static ssize_t read_MSAT(OLE2* ole2, OLE2Header* oleh) |
837 | 2.80k | { |
838 | | // reconstitution of the MSAT |
839 | 2.80k | DWORD count = ole2->cfat; |
840 | 2.80k | if(count == 0 || count > (1 << 24)) { |
841 | 33 | if (xls_debug) fprintf(stderr, "Error: MSAT count %u out-of-bounds\n", count); |
842 | 33 | return -1; |
843 | 33 | } |
844 | | |
845 | 2.77k | ssize_t total_bytes_read = 0; |
846 | 2.77k | ssize_t bytes_read = 0; |
847 | | |
848 | 2.77k | ole2->SecIDCount = count*ole2->lsector/4; |
849 | 2.77k | if ((ole2->SecID = ole_malloc(ole2->SecIDCount * sizeof(DWORD))) == NULL) { |
850 | 25 | total_bytes_read = -1; |
851 | 25 | goto cleanup; |
852 | 25 | } |
853 | 2.74k | memset(ole2->SecID, 0xFF, ole2->SecIDCount * sizeof(DWORD)); |
854 | | |
855 | 2.74k | if ((bytes_read = read_MSAT_header(ole2, oleh, count)) == -1) { |
856 | 74 | total_bytes_read = -1; |
857 | 74 | goto cleanup; |
858 | 74 | } |
859 | 2.67k | total_bytes_read += bytes_read; |
860 | | |
861 | 2.67k | if ((bytes_read = read_MSAT_body(ole2, total_bytes_read / ole2->lsector, count)) == -1) { |
862 | 191 | total_bytes_read = -1; |
863 | 191 | goto cleanup; |
864 | 191 | } |
865 | 2.48k | total_bytes_read += bytes_read; |
866 | | |
867 | 2.48k | if ((bytes_read = read_MSAT_trailer(ole2)) == -1) { |
868 | 136 | total_bytes_read = -1; |
869 | 136 | goto cleanup; |
870 | 136 | } |
871 | 2.34k | total_bytes_read += bytes_read; |
872 | | |
873 | 2.77k | cleanup: |
874 | 2.77k | if (total_bytes_read == -1) { |
875 | 426 | if (ole2->SecID) { |
876 | 401 | free(ole2->SecID); |
877 | 401 | ole2->SecID = NULL; |
878 | 401 | } |
879 | 426 | if (ole2->SSecID) { |
880 | 111 | free(ole2->SSecID); |
881 | 111 | ole2->SSecID = NULL; |
882 | 111 | } |
883 | 426 | } |
884 | | |
885 | 2.77k | return total_bytes_read; |
886 | 2.34k | } |