/src/clamav/libclammspack/mspack/cabd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* This file is part of libmspack. |
2 | | * (C) 2003-2023 Stuart Caie. |
3 | | * |
4 | | * libmspack is free software; you can redistribute it and/or modify it under |
5 | | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
6 | | * |
7 | | * For further details, see the file COPYING.LIB distributed with libmspack |
8 | | */ |
9 | | |
10 | | /* Cabinet (.CAB) files are a form of file archive. Each cabinet contains |
11 | | * "folders", which are compressed spans of data. Each cabinet has |
12 | | * "files", whose metadata is in the cabinet header, but whose actual data |
13 | | * is stored compressed in one of the "folders". Cabinets can span more |
14 | | * than one physical file on disk, in which case they are a "cabinet set", |
15 | | * and usually the last folder of each cabinet extends into the next |
16 | | * cabinet. |
17 | | * |
18 | | * For a complete description of the format, see the MSDN site: |
19 | | * http://msdn.microsoft.com/en-us/library/bb267310.aspx |
20 | | */ |
21 | | |
22 | | /* CAB decompression implementation */ |
23 | | |
24 | | #include <system.h> |
25 | | #include <cab.h> |
26 | | #include <mszip.h> |
27 | | #include <lzx.h> |
28 | | #include <qtm.h> |
29 | | |
30 | | /* Notes on compliance with cabinet specification: |
31 | | * |
32 | | * One of the main changes between cabextract 0.6 and libmspack's cab |
33 | | * decompressor is the move from block-oriented decompression to |
34 | | * stream-oriented decompression. |
35 | | * |
36 | | * cabextract would read one data block from disk, decompress it with the |
37 | | * appropriate method, then write the decompressed data. The CAB |
38 | | * specification is specifically designed to work like this, as it ensures |
39 | | * compression matches do not span the maximum decompressed block size |
40 | | * limit of 32kb. |
41 | | * |
42 | | * However, the compression algorithms used are stream oriented, with |
43 | | * specific hacks added to them to enforce the "individual 32kb blocks" |
44 | | * rule in CABs. In other file formats, they do not have this limitation. |
45 | | * |
46 | | * In order to make more generalised decompressors, libmspack's CAB |
47 | | * decompressor has moved from being block-oriented to more stream |
48 | | * oriented. This also makes decompression slightly faster. |
49 | | * |
50 | | * However, this leads to incompliance with the CAB specification. The |
51 | | * CAB controller can no longer ensure each block of input given to the |
52 | | * decompressors is matched with their output. The "decompressed size" of |
53 | | * each individual block is thrown away. |
54 | | * |
55 | | * Each CAB block is supposed to be seen as individually compressed. This |
56 | | * means each consecutive data block can have completely different |
57 | | * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in |
58 | | * reality, all data blocks in a folder decompress to exactly 32768 bytes, |
59 | | * excepting the final block. |
60 | | * |
61 | | * Given this situation, the decompression algorithms are designed to |
62 | | * realign their input bitstreams on 32768 output-byte boundaries, and |
63 | | * various other special cases have been made. libmspack will not |
64 | | * correctly decompress LZX or Quantum compressed folders where the blocks |
65 | | * do not follow this "32768 bytes until last block" pattern. It could be |
66 | | * implemented if needed, but hopefully this is not necessary -- it has |
67 | | * not been seen in over 3Gb of CAB archives. |
68 | | */ |
69 | | |
70 | | /* prototypes */ |
71 | | static struct mscabd_cabinet * cabd_open( |
72 | | struct mscab_decompressor *base, const char *filename); |
73 | | static void cabd_close( |
74 | | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); |
75 | | static int cabd_read_headers( |
76 | | struct mspack_system *sys, struct mspack_file *fh, |
77 | | struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); |
78 | | static char *cabd_read_string( |
79 | | struct mspack_system *sys, struct mspack_file *fh, int permit_empty, |
80 | | int *error); |
81 | | |
82 | | static struct mscabd_cabinet *cabd_search( |
83 | | struct mscab_decompressor *base, const char *filename); |
84 | | static int cabd_find( |
85 | | struct mscab_decompressor_p *self, unsigned char *buf, |
86 | | struct mspack_file *fh, const char *filename, off_t flen, |
87 | | off_t *firstlen, struct mscabd_cabinet_p **firstcab); |
88 | | |
89 | | static int cabd_prepend( |
90 | | struct mscab_decompressor *base, struct mscabd_cabinet *cab, |
91 | | struct mscabd_cabinet *prevcab); |
92 | | static int cabd_append( |
93 | | struct mscab_decompressor *base, struct mscabd_cabinet *cab, |
94 | | struct mscabd_cabinet *nextcab); |
95 | | static int cabd_merge( |
96 | | struct mscab_decompressor *base, struct mscabd_cabinet *lcab, |
97 | | struct mscabd_cabinet *rcab); |
98 | | static int cabd_can_merge_folders( |
99 | | struct mspack_system *sys, struct mscabd_folder_p *lfol, |
100 | | struct mscabd_folder_p *rfol); |
101 | | |
102 | | static int cabd_extract( |
103 | | struct mscab_decompressor *base, struct mscabd_file *file, |
104 | | const char *filename); |
105 | | static int cabd_init_decomp( |
106 | | struct mscab_decompressor_p *self, unsigned int ct); |
107 | | static void cabd_free_decomp( |
108 | | struct mscab_decompressor_p *self); |
109 | | static int cabd_sys_read( |
110 | | struct mspack_file *file, void *buffer, int bytes); |
111 | | static int cabd_sys_write( |
112 | | struct mspack_file *file, void *buffer, int bytes); |
113 | | static int cabd_sys_read_block( |
114 | | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, |
115 | | int ignore_cksum, int ignore_blocksize); |
116 | | static unsigned int cabd_checksum( |
117 | | unsigned char *data, unsigned int bytes, unsigned int cksum); |
118 | | static struct noned_state *noned_init( |
119 | | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, |
120 | | int bufsize); |
121 | | |
122 | | static int noned_decompress( |
123 | | struct noned_state *s, off_t bytes); |
124 | | static void noned_free( |
125 | | struct noned_state *state); |
126 | | |
127 | | static int cabd_param( |
128 | | struct mscab_decompressor *base, int param, int value); |
129 | | |
130 | | static int cabd_error( |
131 | | struct mscab_decompressor *base); |
132 | | |
133 | | |
134 | | /*************************************** |
135 | | * MSPACK_CREATE_CAB_DECOMPRESSOR |
136 | | *************************************** |
137 | | * constructor |
138 | | */ |
139 | | struct mscab_decompressor * |
140 | | mspack_create_cab_decompressor(struct mspack_system *sys) |
141 | 436k | { |
142 | 436k | struct mscab_decompressor_p *self = NULL; |
143 | | |
144 | 436k | if (!sys) sys = mspack_default_system; |
145 | 436k | if (!mspack_valid_system(sys)) return NULL; |
146 | | |
147 | 436k | if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { |
148 | 436k | self->base.open = &cabd_open; |
149 | 436k | self->base.close = &cabd_close; |
150 | 436k | self->base.search = &cabd_search; |
151 | 436k | self->base.extract = &cabd_extract; |
152 | 436k | self->base.prepend = &cabd_prepend; |
153 | 436k | self->base.append = &cabd_append; |
154 | 436k | self->base.set_param = &cabd_param; |
155 | 436k | self->base.last_error = &cabd_error; |
156 | 436k | self->system = sys; |
157 | 436k | self->d = NULL; |
158 | 436k | self->error = MSPACK_ERR_OK; |
159 | | |
160 | 436k | self->searchbuf_size = 32768; |
161 | 436k | self->fix_mszip = 0; |
162 | 436k | self->buf_size = 4096; |
163 | 436k | self->salvage = 0; |
164 | 436k | } |
165 | 436k | return (struct mscab_decompressor *) self; |
166 | 436k | } |
167 | | |
168 | | /*************************************** |
169 | | * MSPACK_DESTROY_CAB_DECOMPRESSOR |
170 | | *************************************** |
171 | | * destructor |
172 | | */ |
173 | 436k | void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { |
174 | 436k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
175 | 436k | if (self) { |
176 | 436k | struct mspack_system *sys = self->system; |
177 | 436k | if (self->d) { |
178 | 11.8k | if (self->d->infh) sys->close(self->d->infh); |
179 | 11.8k | cabd_free_decomp(self); |
180 | 11.8k | sys->free(self->d); |
181 | 11.8k | } |
182 | 436k | sys->free(self); |
183 | 436k | } |
184 | 436k | } |
185 | | |
186 | | |
187 | | /*************************************** |
188 | | * CABD_OPEN |
189 | | *************************************** |
190 | | * opens a file and tries to read it as a cabinet file |
191 | | */ |
192 | | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, |
193 | | const char *filename) |
194 | 436k | { |
195 | 436k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
196 | 436k | struct mscabd_cabinet_p *cab = NULL; |
197 | 436k | struct mspack_system *sys; |
198 | 436k | struct mspack_file *fh; |
199 | 436k | int error; |
200 | | |
201 | 436k | if (!base) return NULL; |
202 | 436k | sys = self->system; |
203 | | |
204 | 436k | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
205 | 436k | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
206 | 436k | cab->base.filename = filename; |
207 | 436k | error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0); |
208 | 436k | if (error) { |
209 | 293k | cabd_close(base, (struct mscabd_cabinet *) cab); |
210 | 293k | cab = NULL; |
211 | 293k | } |
212 | 436k | self->error = error; |
213 | 436k | } |
214 | 0 | else { |
215 | 0 | self->error = MSPACK_ERR_NOMEMORY; |
216 | 0 | } |
217 | 436k | sys->close(fh); |
218 | 436k | } |
219 | 0 | else { |
220 | 0 | self->error = MSPACK_ERR_OPEN; |
221 | 0 | } |
222 | 436k | return (struct mscabd_cabinet *) cab; |
223 | 436k | } |
224 | | |
225 | | /*************************************** |
226 | | * CABD_CLOSE |
227 | | *************************************** |
228 | | * frees all memory associated with a given mscabd_cabinet. |
229 | | */ |
230 | | static void cabd_close(struct mscab_decompressor *base, |
231 | | struct mscabd_cabinet *origcab) |
232 | 436k | { |
233 | 436k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
234 | 436k | struct mscabd_folder_data *dat, *ndat; |
235 | 436k | struct mscabd_cabinet *cab, *ncab; |
236 | 436k | struct mscabd_folder *fol, *nfol; |
237 | 436k | struct mscabd_file *fi, *nfi; |
238 | 436k | struct mspack_system *sys; |
239 | | |
240 | 436k | if (!base) return; |
241 | 436k | sys = self->system; |
242 | | |
243 | 436k | self->error = MSPACK_ERR_OK; |
244 | | |
245 | 873k | while (origcab) { |
246 | | /* free files */ |
247 | 871k | for (fi = origcab->files; fi; fi = nfi) { |
248 | 434k | nfi = fi->next; |
249 | 434k | sys->free(fi->filename); |
250 | 434k | sys->free(fi); |
251 | 434k | } |
252 | | |
253 | | /* free folders */ |
254 | 32.6M | for (fol = origcab->folders; fol; fol = nfol) { |
255 | 32.1M | nfol = fol->next; |
256 | | |
257 | | /* free folder decompression state if it has been decompressed */ |
258 | 32.1M | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { |
259 | 120k | if (self->d->infh) sys->close(self->d->infh); |
260 | 120k | cabd_free_decomp(self); |
261 | 120k | sys->free(self->d); |
262 | 120k | self->d = NULL; |
263 | 120k | } |
264 | | |
265 | | /* free folder data segments */ |
266 | 32.1M | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { |
267 | 0 | ndat = dat->next; |
268 | 0 | sys->free(dat); |
269 | 0 | } |
270 | 32.1M | sys->free(fol); |
271 | 32.1M | } |
272 | | |
273 | | /* free predecessor cabinets (and the original cabinet's strings) */ |
274 | 873k | for (cab = origcab; cab; cab = ncab) { |
275 | 436k | ncab = cab->prevcab; |
276 | 436k | sys->free(cab->prevname); |
277 | 436k | sys->free(cab->nextname); |
278 | 436k | sys->free(cab->previnfo); |
279 | 436k | sys->free(cab->nextinfo); |
280 | 436k | if (cab != origcab) sys->free(cab); |
281 | 436k | } |
282 | | |
283 | | /* free successor cabinets */ |
284 | 436k | for (cab = origcab->nextcab; cab; cab = ncab) { |
285 | 0 | ncab = cab->nextcab; |
286 | 0 | sys->free(cab->prevname); |
287 | 0 | sys->free(cab->nextname); |
288 | 0 | sys->free(cab->previnfo); |
289 | 0 | sys->free(cab->nextinfo); |
290 | 0 | sys->free(cab); |
291 | 0 | } |
292 | | |
293 | | /* free actual cabinet structure */ |
294 | 436k | cab = origcab->next; |
295 | 436k | sys->free(origcab); |
296 | | |
297 | | /* repeat full procedure again with the cab->next pointer (if set) */ |
298 | 436k | origcab = cab; |
299 | 436k | } |
300 | 436k | } |
301 | | |
302 | | /*************************************** |
303 | | * CABD_READ_HEADERS |
304 | | *************************************** |
305 | | * reads the cabinet file header, folder list and file list. |
306 | | * fills out a pre-existing mscabd_cabinet structure, allocates memory |
307 | | * for folders and files as necessary |
308 | | */ |
309 | | static int cabd_read_headers(struct mspack_system *sys, |
310 | | struct mspack_file *fh, |
311 | | struct mscabd_cabinet_p *cab, |
312 | | off_t offset, int salvage, int quiet) |
313 | 436k | { |
314 | 436k | int num_folders, num_files, folder_resv, i, x, err, fidx; |
315 | 436k | struct mscabd_folder_p *fol, *linkfol = NULL; |
316 | 436k | struct mscabd_file *file, *linkfile = NULL; |
317 | 436k | unsigned char buf[64]; |
318 | | |
319 | | /* initialise pointers */ |
320 | 436k | cab->base.next = NULL; |
321 | 436k | cab->base.files = NULL; |
322 | 436k | cab->base.folders = NULL; |
323 | 436k | cab->base.prevcab = cab->base.nextcab = NULL; |
324 | 436k | cab->base.prevname = cab->base.nextname = NULL; |
325 | 436k | cab->base.previnfo = cab->base.nextinfo = NULL; |
326 | | |
327 | 436k | cab->base.base_offset = offset; |
328 | | |
329 | | /* seek to CFHEADER */ |
330 | 436k | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { |
331 | 0 | return MSPACK_ERR_SEEK; |
332 | 0 | } |
333 | | |
334 | | /* read in the CFHEADER */ |
335 | 436k | if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { |
336 | 12.5k | return MSPACK_ERR_READ; |
337 | 12.5k | } |
338 | | |
339 | | /* check for "MSCF" signature */ |
340 | 424k | if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { |
341 | 0 | return MSPACK_ERR_SIGNATURE; |
342 | 0 | } |
343 | | |
344 | | /* some basic header fields */ |
345 | 424k | cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); |
346 | 424k | cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); |
347 | 424k | cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); |
348 | | |
349 | | /* get the number of folders */ |
350 | 424k | num_folders = EndGetI16(&buf[cfhead_NumFolders]); |
351 | 424k | if (num_folders == 0) { |
352 | 36.8k | if (!quiet) sys->message(fh, "no folders in cabinet."); |
353 | 36.8k | return MSPACK_ERR_DATAFORMAT; |
354 | 36.8k | } |
355 | | |
356 | | /* get the number of files */ |
357 | 387k | num_files = EndGetI16(&buf[cfhead_NumFiles]); |
358 | 387k | if (num_files == 0) { |
359 | 12.3k | if (!quiet) sys->message(fh, "no files in cabinet."); |
360 | 12.3k | return MSPACK_ERR_DATAFORMAT; |
361 | 12.3k | } |
362 | | |
363 | | /* check cabinet version */ |
364 | 375k | if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { |
365 | 309k | if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); |
366 | 309k | } |
367 | | |
368 | | /* read the reserved-sizes part of header, if present */ |
369 | 375k | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); |
370 | | |
371 | 375k | if (cab->base.flags & cfheadRESERVE_PRESENT) { |
372 | 82.0k | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { |
373 | 708 | return MSPACK_ERR_READ; |
374 | 708 | } |
375 | 81.3k | cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); |
376 | 81.3k | folder_resv = buf[cfheadext_FolderReserved]; |
377 | 81.3k | cab->block_resv = buf[cfheadext_DataReserved]; |
378 | | |
379 | 81.3k | if (cab->base.header_resv > 60000) { |
380 | 7.15k | if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); |
381 | 7.15k | } |
382 | | |
383 | | /* skip the reserved header */ |
384 | 81.3k | if (cab->base.header_resv) { |
385 | 68.2k | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { |
386 | 32.7k | return MSPACK_ERR_SEEK; |
387 | 32.7k | } |
388 | 68.2k | } |
389 | 81.3k | } |
390 | 293k | else { |
391 | 293k | cab->base.header_resv = 0; |
392 | 293k | folder_resv = 0; |
393 | 293k | cab->block_resv = 0; |
394 | 293k | } |
395 | | |
396 | | /* read name and info of preceding cabinet in set, if present */ |
397 | 341k | if (cab->base.flags & cfheadPREV_CABINET) { |
398 | 61.7k | cab->base.prevname = cabd_read_string(sys, fh, 0, &err); |
399 | 61.7k | if (err) return err; |
400 | 46.3k | cab->base.previnfo = cabd_read_string(sys, fh, 1, &err); |
401 | 46.3k | if (err) return err; |
402 | 46.3k | } |
403 | | |
404 | | /* read name and info of next cabinet in set, if present */ |
405 | 324k | if (cab->base.flags & cfheadNEXT_CABINET) { |
406 | 56.3k | cab->base.nextname = cabd_read_string(sys, fh, 0, &err); |
407 | 56.3k | if (err) return err; |
408 | 34.0k | cab->base.nextinfo = cabd_read_string(sys, fh, 1, &err); |
409 | 34.0k | if (err) return err; |
410 | 34.0k | } |
411 | | |
412 | | /* read folders */ |
413 | 32.4M | for (i = 0; i < num_folders; i++) { |
414 | 32.2M | if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { |
415 | 75.0k | return MSPACK_ERR_READ; |
416 | 75.0k | } |
417 | 32.1M | if (folder_resv) { |
418 | 1.74M | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { |
419 | 4.91k | return MSPACK_ERR_SEEK; |
420 | 4.91k | } |
421 | 1.74M | } |
422 | | |
423 | 32.1M | if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { |
424 | 0 | return MSPACK_ERR_NOMEMORY; |
425 | 0 | } |
426 | 32.1M | fol->base.next = NULL; |
427 | 32.1M | fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); |
428 | 32.1M | fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); |
429 | 32.1M | fol->data.next = NULL; |
430 | 32.1M | fol->data.cab = (struct mscabd_cabinet_p *) cab; |
431 | 32.1M | fol->data.offset = offset + (off_t) |
432 | 32.1M | ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); |
433 | 32.1M | fol->merge_prev = NULL; |
434 | 32.1M | fol->merge_next = NULL; |
435 | | |
436 | | /* link folder into list of folders */ |
437 | 32.1M | if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; |
438 | 31.8M | else linkfol->base.next = (struct mscabd_folder *) fol; |
439 | 32.1M | linkfol = fol; |
440 | 32.1M | } |
441 | | |
442 | | /* read files */ |
443 | 2.12M | for (i = 0; i < num_files; i++) { |
444 | 1.95M | if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { |
445 | 53.5k | return MSPACK_ERR_READ; |
446 | 53.5k | } |
447 | | |
448 | 1.90M | if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { |
449 | 0 | return MSPACK_ERR_NOMEMORY; |
450 | 0 | } |
451 | | |
452 | 1.90M | file->next = NULL; |
453 | 1.90M | file->length = EndGetI32(&buf[cffile_UncompressedSize]); |
454 | 1.90M | file->attribs = EndGetI16(&buf[cffile_Attribs]); |
455 | 1.90M | file->offset = EndGetI32(&buf[cffile_FolderOffset]); |
456 | | |
457 | | /* set folder pointer */ |
458 | 1.90M | fidx = EndGetI16(&buf[cffile_FolderIndex]); |
459 | 1.90M | if (fidx < cffileCONTINUED_FROM_PREV) { |
460 | | /* normal folder index; count up to the correct folder */ |
461 | 1.82M | if (fidx < num_folders) { |
462 | 666k | struct mscabd_folder *ifol = cab->base.folders; |
463 | 249M | while (fidx--) if (ifol) ifol = ifol->next; |
464 | 666k | file->folder = ifol; |
465 | 666k | } |
466 | 1.15M | else { |
467 | 1.15M | D(("invalid folder index")) |
468 | 1.15M | file->folder = NULL; |
469 | 1.15M | } |
470 | 1.82M | } |
471 | 81.0k | else { |
472 | | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or |
473 | | * CONTINUED_PREV_AND_NEXT */ |
474 | 81.0k | if ((fidx == cffileCONTINUED_TO_NEXT) || |
475 | 81.0k | (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
476 | 80.2k | { |
477 | | /* get last folder */ |
478 | 80.2k | struct mscabd_folder *ifol = cab->base.folders; |
479 | 13.5M | while (ifol->next) ifol = ifol->next; |
480 | 80.2k | file->folder = ifol; |
481 | | |
482 | | /* set "merge next" pointer */ |
483 | 80.2k | fol = (struct mscabd_folder_p *) ifol; |
484 | 80.2k | if (!fol->merge_next) fol->merge_next = file; |
485 | 80.2k | } |
486 | | |
487 | 81.0k | if ((fidx == cffileCONTINUED_FROM_PREV) || |
488 | 81.0k | (fidx == cffileCONTINUED_PREV_AND_NEXT)) |
489 | 70.7k | { |
490 | | /* get first folder */ |
491 | 70.7k | file->folder = cab->base.folders; |
492 | | |
493 | | /* set "merge prev" pointer */ |
494 | 70.7k | fol = (struct mscabd_folder_p *) file->folder; |
495 | 70.7k | if (!fol->merge_prev) fol->merge_prev = file; |
496 | 70.7k | } |
497 | 81.0k | } |
498 | | |
499 | | /* get time */ |
500 | 1.90M | x = EndGetI16(&buf[cffile_Time]); |
501 | 1.90M | file->time_h = x >> 11; |
502 | 1.90M | file->time_m = (x >> 5) & 0x3F; |
503 | 1.90M | file->time_s = (x << 1) & 0x3E; |
504 | | |
505 | | /* get date */ |
506 | 1.90M | x = EndGetI16(&buf[cffile_Date]); |
507 | 1.90M | file->date_d = x & 0x1F; |
508 | 1.90M | file->date_m = (x >> 5) & 0xF; |
509 | 1.90M | file->date_y = (x >> 9) + 1980; |
510 | | |
511 | | /* get filename */ |
512 | 1.90M | file->filename = cabd_read_string(sys, fh, 0, &err); |
513 | | |
514 | | /* if folder index or filename are bad, either skip it or fail */ |
515 | 1.90M | if (err || !file->folder) { |
516 | 1.47M | sys->free(file->filename); |
517 | 1.47M | sys->free(file); |
518 | 1.47M | if (salvage) continue; |
519 | 0 | return err ? err : MSPACK_ERR_DATAFORMAT; |
520 | 1.47M | } |
521 | | |
522 | | /* link file entry into file list */ |
523 | 434k | if (!linkfile) cab->base.files = file; |
524 | 253k | else linkfile->next = file; |
525 | 434k | linkfile = file; |
526 | 434k | } |
527 | | |
528 | 167k | if (cab->base.files == NULL) { |
529 | | /* We never actually added any files to the file list. Something went wrong. |
530 | | * The file header may have been invalid */ |
531 | 24.0k | D(("No files found, even though header claimed to have %d files", num_files)) |
532 | 24.0k | return MSPACK_ERR_DATAFORMAT; |
533 | 24.0k | } |
534 | | |
535 | 143k | return MSPACK_ERR_OK; |
536 | 167k | } |
537 | | |
538 | | static char *cabd_read_string(struct mspack_system *sys, |
539 | | struct mspack_file *fh, int permit_empty, |
540 | | int *error) |
541 | 2.10M | { |
542 | 2.10M | off_t base = sys->tell(fh); |
543 | 2.10M | char buf[256], *str; |
544 | 2.10M | int len, i, ok; |
545 | | |
546 | | /* read up to 256 bytes */ |
547 | 2.10M | if ((len = sys->read(fh, &buf[0], 256)) <= 0) { |
548 | 1.25k | *error = MSPACK_ERR_READ; |
549 | 1.25k | return NULL; |
550 | 1.25k | } |
551 | | |
552 | | /* search for a null terminator in the buffer */ |
553 | 42.8M | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } |
554 | | /* optionally reject empty strings */ |
555 | 2.10M | if (i == 0 && !permit_empty) ok = 0; |
556 | | |
557 | 2.10M | if (!ok) { |
558 | 675k | *error = MSPACK_ERR_DATAFORMAT; |
559 | 675k | return NULL; |
560 | 675k | } |
561 | | |
562 | 1.42M | len = i + 1; |
563 | | |
564 | | /* set the data stream to just after the string and return */ |
565 | 1.42M | if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { |
566 | 0 | *error = MSPACK_ERR_SEEK; |
567 | 0 | return NULL; |
568 | 0 | } |
569 | | |
570 | 1.42M | if (!(str = (char *) sys->alloc(sys, len))) { |
571 | 0 | *error = MSPACK_ERR_NOMEMORY; |
572 | 0 | return NULL; |
573 | 0 | } |
574 | | |
575 | 1.42M | sys->copy(&buf[0], str, len); |
576 | 1.42M | *error = MSPACK_ERR_OK; |
577 | 1.42M | return str; |
578 | 1.42M | } |
579 | | |
580 | | /*************************************** |
581 | | * CABD_SEARCH, CABD_FIND |
582 | | *************************************** |
583 | | * cabd_search opens a file, finds its extent, allocates a search buffer, |
584 | | * then reads through the whole file looking for possible cabinet headers. |
585 | | * if it finds any, it tries to read them as real cabinets. returns a linked |
586 | | * list of results |
587 | | * |
588 | | * cabd_find is the inner loop of cabd_search, to make it easier to |
589 | | * break out of the loop and be sure that all resources are freed |
590 | | */ |
591 | | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, |
592 | | const char *filename) |
593 | 0 | { |
594 | 0 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
595 | 0 | struct mscabd_cabinet_p *cab = NULL; |
596 | 0 | struct mspack_system *sys; |
597 | 0 | unsigned char *search_buf; |
598 | 0 | struct mspack_file *fh; |
599 | 0 | off_t filelen, firstlen = 0; |
600 | |
|
601 | 0 | if (!base) return NULL; |
602 | 0 | sys = self->system; |
603 | | |
604 | | /* allocate a search buffer */ |
605 | 0 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); |
606 | 0 | if (!search_buf) { |
607 | 0 | self->error = MSPACK_ERR_NOMEMORY; |
608 | 0 | return NULL; |
609 | 0 | } |
610 | | |
611 | | /* open file and get its full file length */ |
612 | 0 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
613 | 0 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { |
614 | 0 | self->error = cabd_find(self, search_buf, fh, filename, |
615 | 0 | filelen, &firstlen, &cab); |
616 | 0 | } |
617 | | |
618 | | /* truncated / extraneous data warning: */ |
619 | 0 | if (firstlen && (firstlen != filelen) && |
620 | 0 | (!cab || (cab->base.base_offset == 0))) |
621 | 0 | { |
622 | 0 | if (firstlen < filelen) { |
623 | 0 | sys->message(fh, "WARNING; possible %" LD |
624 | 0 | " extra bytes at end of file.", |
625 | 0 | filelen - firstlen); |
626 | 0 | } |
627 | 0 | else { |
628 | 0 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", |
629 | 0 | firstlen - filelen); |
630 | 0 | } |
631 | 0 | } |
632 | | |
633 | 0 | sys->close(fh); |
634 | 0 | } |
635 | 0 | else { |
636 | 0 | self->error = MSPACK_ERR_OPEN; |
637 | 0 | } |
638 | | |
639 | | /* free the search buffer */ |
640 | 0 | sys->free(search_buf); |
641 | |
|
642 | 0 | return (struct mscabd_cabinet *) cab; |
643 | 0 | } |
644 | | |
645 | | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, |
646 | | struct mspack_file *fh, const char *filename, off_t flen, |
647 | | off_t *firstlen, struct mscabd_cabinet_p **firstcab) |
648 | 0 | { |
649 | 0 | struct mscabd_cabinet_p *cab, *link = NULL; |
650 | 0 | off_t caboff, offset, length; |
651 | 0 | struct mspack_system *sys = self->system; |
652 | 0 | unsigned char *p, *pend, state = 0; |
653 | 0 | unsigned int cablen_u32 = 0, foffset_u32 = 0; |
654 | 0 | int false_cabs = 0; |
655 | |
|
656 | | #if SIZEOF_OFF_T < 8 |
657 | | /* detect 32-bit off_t overflow */ |
658 | | if (flen < 0) { |
659 | | sys->message(fh, "library not compiled to support large files."); |
660 | | return MSPACK_ERR_OK; |
661 | | } |
662 | | #endif |
663 | | |
664 | | /* search through the full file length */ |
665 | 0 | for (offset = 0; offset < flen; offset += length) { |
666 | | /* search length is either the full length of the search buffer, or the |
667 | | * amount of data remaining to the end of the file, whichever is less. */ |
668 | 0 | length = flen - offset; |
669 | 0 | if (length > self->searchbuf_size) { |
670 | 0 | length = self->searchbuf_size; |
671 | 0 | } |
672 | | |
673 | | /* fill the search buffer with data from disk */ |
674 | 0 | if (sys->read(fh, &buf[0], (int) length) != (int) length) { |
675 | 0 | return MSPACK_ERR_READ; |
676 | 0 | } |
677 | | |
678 | | /* FAQ avoidance strategy */ |
679 | 0 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { |
680 | 0 | sys->message(fh, "WARNING; found InstallShield header. Use unshield " |
681 | 0 | "(https://github.com/twogood/unshield) to unpack this file"); |
682 | 0 | } |
683 | | |
684 | | /* read through the entire buffer. */ |
685 | 0 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { |
686 | 0 | switch (state) { |
687 | | /* starting state */ |
688 | 0 | case 0: |
689 | | /* we spend most of our time in this while loop, looking for |
690 | | * a leading 'M' of the 'MSCF' signature */ |
691 | 0 | while (p < pend && *p != 0x4D) p++; |
692 | | /* if we found tht 'M', advance state */ |
693 | 0 | if (p++ < pend) state = 1; |
694 | 0 | break; |
695 | | |
696 | | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ |
697 | 0 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; |
698 | 0 | case 2: state = (*p++ == 0x43) ? 3 : 0; break; |
699 | 0 | case 3: state = (*p++ == 0x46) ? 4 : 0; break; |
700 | | |
701 | | /* we don't care about bytes 4-7 (see default: for action) */ |
702 | | |
703 | | /* bytes 8-11 are the overall length of the cabinet */ |
704 | 0 | case 8: cablen_u32 = *p++; state++; break; |
705 | 0 | case 9: cablen_u32 |= *p++ << 8; state++; break; |
706 | 0 | case 10: cablen_u32 |= *p++ << 16; state++; break; |
707 | 0 | case 11: cablen_u32 |= *p++ << 24; state++; break; |
708 | | |
709 | | /* we don't care about bytes 12-15 (see default: for action) */ |
710 | | |
711 | | /* bytes 16-19 are the offset within the cabinet of the filedata */ |
712 | 0 | case 16: foffset_u32 = *p++; state++; break; |
713 | 0 | case 17: foffset_u32 |= *p++ << 8; state++; break; |
714 | 0 | case 18: foffset_u32 |= *p++ << 16; state++; break; |
715 | 0 | case 19: foffset_u32 |= *p++ << 24; |
716 | | /* now we have received 20 bytes of potential cab header. work out |
717 | | * the offset in the file of this potential cabinet */ |
718 | 0 | caboff = offset + (p - &buf[0]) - 20; |
719 | | |
720 | | /* should reading cabinet fail, restart search just after 'MSCF' */ |
721 | 0 | offset = caboff + 4; |
722 | | |
723 | | /* capture the "length of cabinet" field if there is a cabinet at |
724 | | * offset 0 in the file, regardless of whether the cabinet can be |
725 | | * read correctly or not */ |
726 | 0 | if (caboff == 0) *firstlen = (off_t) cablen_u32; |
727 | | |
728 | | /* check that the files offset is less than the alleged length of |
729 | | * the cabinet, and that the offset + the alleged length are |
730 | | * 'roughly' within the end of overall file length. In salvage |
731 | | * mode, don't check the alleged length, allow it to be garbage */ |
732 | 0 | if ((foffset_u32 < cablen_u32) && |
733 | 0 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && |
734 | 0 | (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage)) |
735 | 0 | { |
736 | | /* likely cabinet found -- try reading it */ |
737 | 0 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
738 | 0 | return MSPACK_ERR_NOMEMORY; |
739 | 0 | } |
740 | 0 | cab->base.filename = filename; |
741 | 0 | if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) { |
742 | | /* destroy the failed cabinet */ |
743 | 0 | cabd_close((struct mscab_decompressor *) self, |
744 | 0 | (struct mscabd_cabinet *) cab); |
745 | 0 | false_cabs++; |
746 | 0 | } |
747 | 0 | else { |
748 | | /* cabinet read correctly! */ |
749 | | |
750 | | /* link the cab into the list */ |
751 | 0 | if (!link) *firstcab = cab; |
752 | 0 | else link->base.next = (struct mscabd_cabinet *) cab; |
753 | 0 | link = cab; |
754 | | |
755 | | /* cause the search to restart after this cab's data. */ |
756 | 0 | offset = caboff + (off_t) cablen_u32; |
757 | |
|
758 | | #if SIZEOF_OFF_T < 8 |
759 | | /* detect 32-bit off_t overflow */ |
760 | | if (offset < caboff) { |
761 | | sys->message(fh, "library not compiled to support large files."); |
762 | | return MSPACK_ERR_OK; |
763 | | } |
764 | | #endif |
765 | 0 | } |
766 | 0 | } |
767 | | |
768 | | /* restart search */ |
769 | 0 | if (offset >= flen) return MSPACK_ERR_OK; |
770 | 0 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { |
771 | 0 | return MSPACK_ERR_SEEK; |
772 | 0 | } |
773 | 0 | length = 0; |
774 | 0 | p = pend; |
775 | 0 | state = 0; |
776 | 0 | break; |
777 | | |
778 | | /* for bytes 4-7 and 12-15, just advance state/pointer */ |
779 | 0 | default: |
780 | 0 | p++, state++; |
781 | 0 | } /* switch(state) */ |
782 | 0 | } /* for (... p < pend ...) */ |
783 | 0 | } /* for (... offset < length ...) */ |
784 | | |
785 | 0 | if (false_cabs) { |
786 | 0 | D(("%d false cabinets found", false_cabs)) |
787 | 0 | } |
788 | |
|
789 | 0 | return MSPACK_ERR_OK; |
790 | 0 | } |
791 | | |
792 | | /*************************************** |
793 | | * CABD_MERGE, CABD_PREPEND, CABD_APPEND |
794 | | *************************************** |
795 | | * joins cabinets together, also merges split folders between these two |
796 | | * cabinets only. This includes freeing the duplicate folder and file(s) |
797 | | * and allocating a further mscabd_folder_data structure to append to the |
798 | | * merged folder's data parts list. |
799 | | */ |
800 | | static int cabd_prepend(struct mscab_decompressor *base, |
801 | | struct mscabd_cabinet *cab, |
802 | | struct mscabd_cabinet *prevcab) |
803 | 0 | { |
804 | 0 | return cabd_merge(base, prevcab, cab); |
805 | 0 | } |
806 | | |
807 | | static int cabd_append(struct mscab_decompressor *base, |
808 | | struct mscabd_cabinet *cab, |
809 | | struct mscabd_cabinet *nextcab) |
810 | 0 | { |
811 | 0 | return cabd_merge(base, cab, nextcab); |
812 | 0 | } |
813 | | |
814 | | static int cabd_merge(struct mscab_decompressor *base, |
815 | | struct mscabd_cabinet *lcab, |
816 | | struct mscabd_cabinet *rcab) |
817 | 0 | { |
818 | 0 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
819 | 0 | struct mscabd_folder_data *data, *ndata; |
820 | 0 | struct mscabd_folder_p *lfol, *rfol; |
821 | 0 | struct mscabd_file *fi, *rfi, *lfi; |
822 | 0 | struct mscabd_cabinet *cab; |
823 | 0 | struct mspack_system *sys; |
824 | |
|
825 | 0 | if (!self) return MSPACK_ERR_ARGS; |
826 | 0 | sys = self->system; |
827 | | |
828 | | /* basic args check */ |
829 | 0 | if (!lcab || !rcab || (lcab == rcab)) { |
830 | 0 | D(("lcab NULL, rcab NULL or lcab = rcab")) |
831 | 0 | return self->error = MSPACK_ERR_ARGS; |
832 | 0 | } |
833 | | |
834 | | /* check there's not already a cabinet attached */ |
835 | 0 | if (lcab->nextcab || rcab->prevcab) { |
836 | 0 | D(("cabs already joined")) |
837 | 0 | return self->error = MSPACK_ERR_ARGS; |
838 | 0 | } |
839 | | |
840 | | /* do not create circular cabinet chains */ |
841 | 0 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { |
842 | 0 | if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} |
843 | 0 | } |
844 | 0 | for (cab = rcab->nextcab; cab; cab = cab->nextcab) { |
845 | 0 | if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} |
846 | 0 | } |
847 | | |
848 | | /* warn about odd set IDs or indices */ |
849 | 0 | if (lcab->set_id != rcab->set_id) { |
850 | 0 | sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); |
851 | 0 | } |
852 | |
|
853 | 0 | if (lcab->set_index > rcab->set_index) { |
854 | 0 | sys->message(NULL, "WARNING; merged cabinets with odd order."); |
855 | 0 | } |
856 | | |
857 | | /* merging the last folder in lcab with the first folder in rcab */ |
858 | 0 | lfol = (struct mscabd_folder_p *) lcab->folders; |
859 | 0 | rfol = (struct mscabd_folder_p *) rcab->folders; |
860 | 0 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; |
861 | | |
862 | | /* do we need to merge folders? */ |
863 | 0 | if (!lfol->merge_next && !rfol->merge_prev) { |
864 | | /* no, at least one of the folders is not for merging */ |
865 | | |
866 | | /* attach cabs */ |
867 | 0 | lcab->nextcab = rcab; |
868 | 0 | rcab->prevcab = lcab; |
869 | | |
870 | | /* attach folders */ |
871 | 0 | lfol->base.next = (struct mscabd_folder *) rfol; |
872 | | |
873 | | /* attach files */ |
874 | 0 | fi = lcab->files; |
875 | 0 | while (fi->next) fi = fi->next; |
876 | 0 | fi->next = rcab->files; |
877 | 0 | } |
878 | 0 | else { |
879 | | /* folder merge required - do the files match? */ |
880 | 0 | if (! cabd_can_merge_folders(sys, lfol, rfol)) { |
881 | 0 | return self->error = MSPACK_ERR_DATAFORMAT; |
882 | 0 | } |
883 | | |
884 | | /* allocate a new folder data structure */ |
885 | 0 | if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { |
886 | 0 | return self->error = MSPACK_ERR_NOMEMORY; |
887 | 0 | } |
888 | | |
889 | | /* attach cabs */ |
890 | 0 | lcab->nextcab = rcab; |
891 | 0 | rcab->prevcab = lcab; |
892 | | |
893 | | /* append rfol's data to lfol */ |
894 | 0 | ndata = &lfol->data; |
895 | 0 | while (ndata->next) ndata = ndata->next; |
896 | 0 | ndata->next = data; |
897 | 0 | *data = rfol->data; |
898 | 0 | rfol->data.next = NULL; |
899 | | |
900 | | /* lfol becomes rfol. |
901 | | * NOTE: special case, don't merge if rfol is merge prev and next, |
902 | | * rfol->merge_next is going to be deleted, so keep lfol's version |
903 | | * instead */ |
904 | 0 | lfol->base.num_blocks += rfol->base.num_blocks - 1; |
905 | 0 | if ((rfol->merge_next == NULL) || |
906 | 0 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) |
907 | 0 | { |
908 | 0 | lfol->merge_next = rfol->merge_next; |
909 | 0 | } |
910 | | |
911 | | /* attach the rfol's folder (except the merge folder) */ |
912 | 0 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; |
913 | 0 | lfol->base.next = rfol->base.next; |
914 | | |
915 | | /* free disused merge folder */ |
916 | 0 | sys->free(rfol); |
917 | | |
918 | | /* attach rfol's files */ |
919 | 0 | fi = lcab->files; |
920 | 0 | while (fi->next) fi = fi->next; |
921 | 0 | fi->next = rcab->files; |
922 | | |
923 | | /* delete all files from rfol's merge folder */ |
924 | 0 | lfi = NULL; |
925 | 0 | for (fi = lcab->files; fi ; fi = rfi) { |
926 | 0 | rfi = fi->next; |
927 | | /* if file's folder matches the merge folder, unlink and free it */ |
928 | 0 | if (fi->folder == (struct mscabd_folder *) rfol) { |
929 | 0 | if (lfi) lfi->next = rfi; else lcab->files = rfi; |
930 | 0 | sys->free(fi->filename); |
931 | 0 | sys->free(fi); |
932 | 0 | } |
933 | 0 | else lfi = fi; |
934 | 0 | } |
935 | 0 | } |
936 | | |
937 | | /* all done! fix files and folders pointers in all cabs so they all |
938 | | * point to the same list */ |
939 | 0 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { |
940 | 0 | cab->files = lcab->files; |
941 | 0 | cab->folders = lcab->folders; |
942 | 0 | } |
943 | |
|
944 | 0 | for (cab = lcab->nextcab; cab; cab = cab->nextcab) { |
945 | 0 | cab->files = lcab->files; |
946 | 0 | cab->folders = lcab->folders; |
947 | 0 | } |
948 | |
|
949 | 0 | return self->error = MSPACK_ERR_OK; |
950 | 0 | } |
951 | | |
952 | | /* decides if two folders are OK to merge */ |
953 | | static int cabd_can_merge_folders(struct mspack_system *sys, |
954 | | struct mscabd_folder_p *lfol, |
955 | | struct mscabd_folder_p *rfol) |
956 | 0 | { |
957 | 0 | struct mscabd_file *lfi, *rfi, *l, *r; |
958 | 0 | int matching = 1; |
959 | | |
960 | | /* check that both folders use the same compression method/settings */ |
961 | 0 | if (lfol->base.comp_type != rfol->base.comp_type) { |
962 | 0 | D(("folder merge: compression type mismatch")) |
963 | 0 | return 0; |
964 | 0 | } |
965 | | |
966 | | /* check there are not too many data blocks after merging */ |
967 | 0 | if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) { |
968 | 0 | D(("folder merge: too many data blocks in merged folders")) |
969 | 0 | return 0; |
970 | 0 | } |
971 | | |
972 | 0 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { |
973 | 0 | D(("folder merge: one cabinet has no files to merge")) |
974 | 0 | return 0; |
975 | 0 | } |
976 | | |
977 | | /* for all files in lfol (which is the last folder in whichever cab and |
978 | | * only has files to merge), compare them to the files from rfol. They |
979 | | * should be identical in number and order. to verify this, check the |
980 | | * offset and length of each file. */ |
981 | 0 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { |
982 | 0 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { |
983 | 0 | matching = 0; |
984 | 0 | break; |
985 | 0 | } |
986 | 0 | } |
987 | |
|
988 | 0 | if (matching) return 1; |
989 | | |
990 | | /* if rfol does not begin with an identical copy of the files in lfol, make |
991 | | * make a judgement call; if at least ONE file from lfol is in rfol, allow |
992 | | * the merge with a warning about missing files. */ |
993 | 0 | matching = 0; |
994 | 0 | for (l = lfi; l; l = l->next) { |
995 | 0 | for (r = rfi; r; r = r->next) { |
996 | 0 | if (l->offset == r->offset && l->length == r->length) break; |
997 | 0 | } |
998 | 0 | if (r) matching = 1; else sys->message(NULL, |
999 | 0 | "WARNING; merged file %s not listed in both cabinets", l->filename); |
1000 | 0 | } |
1001 | 0 | return matching; |
1002 | 0 | } |
1003 | | |
1004 | | |
1005 | | /*************************************** |
1006 | | * CABD_EXTRACT |
1007 | | *************************************** |
1008 | | * extracts a file from a cabinet |
1009 | | */ |
1010 | | static int cabd_extract(struct mscab_decompressor *base, |
1011 | | struct mscabd_file *file, const char *filename) |
1012 | 241k | { |
1013 | 241k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
1014 | 241k | struct mscabd_folder_p *fol; |
1015 | 241k | struct mspack_system *sys; |
1016 | 241k | struct mspack_file *fh; |
1017 | 241k | unsigned int filelen; |
1018 | | |
1019 | 241k | if (!self) return MSPACK_ERR_ARGS; |
1020 | 241k | if (!file) return self->error = MSPACK_ERR_ARGS; |
1021 | | |
1022 | 241k | sys = self->system; |
1023 | 241k | fol = (struct mscabd_folder_p *) file->folder; |
1024 | | |
1025 | | /* if offset is beyond 2GB, nothing can be extracted */ |
1026 | 241k | if (file->offset > CAB_LENGTHMAX) { |
1027 | 21.2k | return self->error = MSPACK_ERR_DATAFORMAT; |
1028 | 21.2k | } |
1029 | | |
1030 | | /* if file claims to go beyond 2GB either error out, |
1031 | | * or in salvage mode reduce file length so it fits 2GB limit |
1032 | | */ |
1033 | 220k | filelen = file->length; |
1034 | 220k | if (filelen > (CAB_LENGTHMAX - file->offset)) { |
1035 | 49.3k | if (self->salvage) { |
1036 | 49.3k | filelen = CAB_LENGTHMAX - file->offset; |
1037 | 49.3k | } |
1038 | 0 | else { |
1039 | 0 | return self->error = MSPACK_ERR_DATAFORMAT; |
1040 | 0 | } |
1041 | 49.3k | } |
1042 | | |
1043 | | /* extraction impossible if no folder, or folder needs predecessor */ |
1044 | 220k | if (!fol || fol->merge_prev) { |
1045 | 23.1k | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " |
1046 | 23.1k | "cabinet set is incomplete", file->filename); |
1047 | 23.1k | return self->error = MSPACK_ERR_DECRUNCH; |
1048 | 23.1k | } |
1049 | | |
1050 | | /* if file goes beyond what can be decoded, given an error. |
1051 | | * In salvage mode, don't assume block sizes, just try decoding |
1052 | | */ |
1053 | 197k | if (!self->salvage) { |
1054 | 0 | unsigned int maxlen = fol->base.num_blocks * CAB_BLOCKMAX; |
1055 | 0 | if (file->offset > maxlen || filelen > (maxlen - file->offset)) { |
1056 | 0 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " |
1057 | 0 | "cabinet set is incomplete", file->filename); |
1058 | 0 | return self->error = MSPACK_ERR_DECRUNCH; |
1059 | 0 | } |
1060 | 0 | } |
1061 | | |
1062 | | /* allocate generic decompression state */ |
1063 | 197k | if (!self->d) { |
1064 | 132k | self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); |
1065 | 132k | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; |
1066 | 132k | self->d->folder = NULL; |
1067 | 132k | self->d->data = NULL; |
1068 | 132k | self->d->sys = *sys; |
1069 | 132k | self->d->sys.read = &cabd_sys_read; |
1070 | 132k | self->d->sys.write = &cabd_sys_write; |
1071 | 132k | self->d->state = NULL; |
1072 | 132k | self->d->infh = NULL; |
1073 | 132k | self->d->incab = NULL; |
1074 | 132k | } |
1075 | | |
1076 | | /* do we need to change folder or reset the current folder? */ |
1077 | 197k | if ((self->d->folder != fol) || (self->d->offset > file->offset) || |
1078 | 197k | !self->d->state) |
1079 | 171k | { |
1080 | | /* free any existing decompressor */ |
1081 | 171k | cabd_free_decomp(self); |
1082 | | |
1083 | | /* do we need to open a new cab file? */ |
1084 | 171k | if (!self->d->infh || (fol->data.cab != self->d->incab)) { |
1085 | | /* close previous file handle if from a different cab */ |
1086 | 133k | if (self->d->infh) sys->close(self->d->infh); |
1087 | 133k | self->d->incab = fol->data.cab; |
1088 | 133k | self->d->infh = sys->open(sys, fol->data.cab->base.filename, |
1089 | 133k | MSPACK_SYS_OPEN_READ); |
1090 | 133k | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; |
1091 | 133k | } |
1092 | | /* seek to start of data blocks */ |
1093 | 171k | if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { |
1094 | 15.7k | return self->error = MSPACK_ERR_SEEK; |
1095 | 15.7k | } |
1096 | | |
1097 | | /* set up decompressor */ |
1098 | 155k | if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { |
1099 | 11.3k | return self->error; |
1100 | 11.3k | } |
1101 | | |
1102 | | /* initialise new folder state */ |
1103 | 144k | self->d->folder = fol; |
1104 | 144k | self->d->data = &fol->data; |
1105 | 144k | self->d->offset = 0; |
1106 | 144k | self->d->block = 0; |
1107 | 144k | self->d->outlen = 0; |
1108 | 144k | self->d->i_ptr = self->d->i_end = &self->d->input[0]; |
1109 | | |
1110 | | /* read_error lasts for the lifetime of a decompressor */ |
1111 | 144k | self->read_error = MSPACK_ERR_OK; |
1112 | 144k | } |
1113 | | |
1114 | | /* open file for output */ |
1115 | 170k | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { |
1116 | 0 | return self->error = MSPACK_ERR_OPEN; |
1117 | 0 | } |
1118 | | |
1119 | 170k | self->error = MSPACK_ERR_OK; |
1120 | | |
1121 | | /* if file has more than 0 bytes */ |
1122 | 170k | if (filelen) { |
1123 | 160k | off_t bytes; |
1124 | 160k | int error; |
1125 | | /* get to correct offset. |
1126 | | * - use NULL fh to say 'no writing' to cabd_sys_write() |
1127 | | * - if cabd_sys_read() has an error, it will set self->read_error |
1128 | | * and pass back MSPACK_ERR_READ |
1129 | | */ |
1130 | 160k | self->d->outfh = NULL; |
1131 | 160k | if ((bytes = file->offset - self->d->offset)) { |
1132 | 133k | error = self->d->decompress(self->d->state, bytes); |
1133 | 133k | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
1134 | 133k | } |
1135 | | |
1136 | | /* if getting to the correct offset was error free, unpack file */ |
1137 | 160k | if (!self->error) { |
1138 | 108k | self->d->outfh = fh; |
1139 | 108k | error = self->d->decompress(self->d->state, filelen); |
1140 | 108k | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; |
1141 | 108k | } |
1142 | 160k | } |
1143 | | |
1144 | | /* close output file */ |
1145 | 170k | sys->close(fh); |
1146 | 170k | self->d->outfh = NULL; |
1147 | | |
1148 | 170k | return self->error; |
1149 | 170k | } |
1150 | | |
1151 | | /*************************************** |
1152 | | * CABD_INIT_DECOMP, CABD_FREE_DECOMP |
1153 | | *************************************** |
1154 | | * cabd_init_decomp initialises decompression state, according to which |
1155 | | * decompression method was used. relies on self->d->folder being the same |
1156 | | * as when initialised. |
1157 | | * |
1158 | | * cabd_free_decomp frees decompression state, according to which method |
1159 | | * was used. |
1160 | | */ |
1161 | | static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) |
1162 | 155k | { |
1163 | 155k | struct mspack_file *fh = (struct mspack_file *) self; |
1164 | | |
1165 | 155k | self->d->comp_type = ct; |
1166 | | |
1167 | 155k | switch (ct & cffoldCOMPTYPE_MASK) { |
1168 | 8.71k | case cffoldCOMPTYPE_NONE: |
1169 | 8.71k | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; |
1170 | 8.71k | self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size); |
1171 | 8.71k | break; |
1172 | 40.1k | case cffoldCOMPTYPE_MSZIP: |
1173 | 40.1k | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; |
1174 | 40.1k | self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size, |
1175 | 40.1k | self->fix_mszip); |
1176 | 40.1k | break; |
1177 | 69.9k | case cffoldCOMPTYPE_QUANTUM: |
1178 | 69.9k | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; |
1179 | 69.9k | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, |
1180 | 69.9k | self->buf_size); |
1181 | 69.9k | break; |
1182 | 34.2k | case cffoldCOMPTYPE_LZX: |
1183 | 34.2k | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; |
1184 | 34.2k | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, |
1185 | 34.2k | self->buf_size, (off_t)0,0); |
1186 | 34.2k | break; |
1187 | 2.51k | default: |
1188 | 2.51k | return self->error = MSPACK_ERR_DATAFORMAT; |
1189 | 155k | } |
1190 | 153k | return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; |
1191 | 155k | } |
1192 | | |
1193 | 303k | static void cabd_free_decomp(struct mscab_decompressor_p *self) { |
1194 | 303k | if (!self || !self->d || !self->d->state) return; |
1195 | | |
1196 | 144k | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { |
1197 | 8.71k | case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; |
1198 | 40.1k | case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; |
1199 | 62.6k | case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; |
1200 | 32.7k | case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; |
1201 | 144k | } |
1202 | 144k | self->d->decompress = NULL; |
1203 | 144k | self->d->state = NULL; |
1204 | 144k | } |
1205 | | |
1206 | | /*************************************** |
1207 | | * CABD_SYS_READ, CABD_SYS_WRITE |
1208 | | *************************************** |
1209 | | * cabd_sys_read is the internal reader function which the decompressors |
1210 | | * use. will read data blocks (and merge split blocks) from the cabinet |
1211 | | * and serve the read bytes to the decompressors |
1212 | | * |
1213 | | * cabd_sys_write is the internal writer function which the decompressors |
1214 | | * use. it either writes data to disk (self->d->outfh) with the real |
1215 | | * sys->write() function, or does nothing with the data when |
1216 | | * self->d->outfh == NULL. advances self->d->offset |
1217 | | */ |
1218 | 330k | static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { |
1219 | 330k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; |
1220 | 330k | unsigned char *buf = (unsigned char *) buffer; |
1221 | 330k | struct mspack_system *sys = self->system; |
1222 | 330k | int avail, todo, outlen, ignore_cksum, ignore_blocksize; |
1223 | | |
1224 | 330k | ignore_cksum = self->salvage || |
1225 | 330k | (self->fix_mszip && |
1226 | 0 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); |
1227 | 330k | ignore_blocksize = self->salvage; |
1228 | | |
1229 | 330k | todo = bytes; |
1230 | 633k | while (todo > 0) { |
1231 | 593k | avail = self->d->i_end - self->d->i_ptr; |
1232 | | |
1233 | | /* if out of input data, read a new block */ |
1234 | 593k | if (avail) { |
1235 | | /* copy as many input bytes available as possible */ |
1236 | 168k | if (avail > todo) avail = todo; |
1237 | 168k | sys->copy(self->d->i_ptr, buf, (size_t) avail); |
1238 | 168k | self->d->i_ptr += avail; |
1239 | 168k | buf += avail; |
1240 | 168k | todo -= avail; |
1241 | 168k | } |
1242 | 424k | else { |
1243 | | /* out of data, read a new block */ |
1244 | | |
1245 | | /* check if we're out of input blocks, advance block counter */ |
1246 | 424k | if (self->d->block++ >= self->d->folder->base.num_blocks) { |
1247 | 257k | if (!self->salvage) { |
1248 | 0 | self->read_error = MSPACK_ERR_DATAFORMAT; |
1249 | 0 | } |
1250 | 257k | else { |
1251 | 257k | D(("Ran out of CAB input blocks prematurely")) |
1252 | 257k | } |
1253 | 257k | break; |
1254 | 257k | } |
1255 | | |
1256 | | /* read a block */ |
1257 | 167k | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, |
1258 | 167k | ignore_cksum, ignore_blocksize); |
1259 | 167k | if (self->read_error) return -1; |
1260 | 134k | self->d->outlen += outlen; |
1261 | | |
1262 | | /* special Quantum hack -- trailer byte to allow the decompressor |
1263 | | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have |
1264 | | * anything from 0 to 4 trailing null bytes. */ |
1265 | 134k | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { |
1266 | 57.7k | *self->d->i_end++ = 0xFF; |
1267 | 57.7k | } |
1268 | | |
1269 | | /* is this the last block? */ |
1270 | 134k | if (self->d->block >= self->d->folder->base.num_blocks) { |
1271 | 105k | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { |
1272 | | /* special LZX hack -- on the last block, inform LZX of the |
1273 | | * size of the output data stream. */ |
1274 | 28.7k | lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen); |
1275 | 28.7k | } |
1276 | 105k | } |
1277 | 134k | } /* if (avail) */ |
1278 | 593k | } /* while (todo > 0) */ |
1279 | 297k | return bytes - todo; |
1280 | 330k | } |
1281 | | |
1282 | 24.2M | static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { |
1283 | 24.2M | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; |
1284 | 24.2M | self->d->offset += bytes; |
1285 | 24.2M | if (self->d->outfh) { |
1286 | 12.5M | return self->system->write(self->d->outfh, buffer, bytes); |
1287 | 12.5M | } |
1288 | 11.6M | return bytes; |
1289 | 24.2M | } |
1290 | | |
1291 | | /*************************************** |
1292 | | * CABD_SYS_READ_BLOCK |
1293 | | *************************************** |
1294 | | * reads a whole data block from a cab file. the block may span more than |
1295 | | * one cab file, if it does then the fragments will be reassembled |
1296 | | */ |
1297 | | static int cabd_sys_read_block(struct mspack_system *sys, |
1298 | | struct mscabd_decompress_state *d, |
1299 | | int *out, int ignore_cksum, |
1300 | | int ignore_blocksize) |
1301 | 167k | { |
1302 | 167k | unsigned char hdr[cfdata_SIZEOF]; |
1303 | 167k | unsigned int cksum; |
1304 | 167k | int len, full_len; |
1305 | | |
1306 | | /* reset the input block pointer and end of block pointer */ |
1307 | 167k | d->i_ptr = d->i_end = &d->input[0]; |
1308 | | |
1309 | 167k | do { |
1310 | | /* read the block header */ |
1311 | 167k | if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { |
1312 | 1.56k | return MSPACK_ERR_READ; |
1313 | 1.56k | } |
1314 | | |
1315 | | /* skip any reserved block headers */ |
1316 | 165k | if (d->data->cab->block_resv && |
1317 | 165k | sys->seek(d->infh, (off_t) d->data->cab->block_resv, |
1318 | 796 | MSPACK_SYS_SEEK_CUR)) |
1319 | 15 | { |
1320 | 15 | return MSPACK_ERR_SEEK; |
1321 | 15 | } |
1322 | | |
1323 | | /* blocks must not be over CAB_INPUTMAX in size */ |
1324 | 165k | len = EndGetI16(&hdr[cfdata_CompressedSize]); |
1325 | 165k | full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */ |
1326 | 165k | if (full_len > CAB_INPUTMAX) { |
1327 | 6.49k | D(("block size %d > CAB_INPUTMAX", full_len)); |
1328 | | /* in salvage mode, blocks can be 65535 bytes but no more than that */ |
1329 | 6.49k | if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) { |
1330 | 0 | return MSPACK_ERR_DATAFORMAT; |
1331 | 0 | } |
1332 | 6.49k | } |
1333 | | |
1334 | | /* blocks must not expand to more than CAB_BLOCKMAX */ |
1335 | 165k | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { |
1336 | 33.7k | D(("block size > CAB_BLOCKMAX")) |
1337 | 33.7k | if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; |
1338 | 33.7k | } |
1339 | | |
1340 | | /* read the block data */ |
1341 | 165k | if (sys->read(d->infh, d->i_end, len) != len) { |
1342 | 25.5k | return MSPACK_ERR_READ; |
1343 | 25.5k | } |
1344 | | |
1345 | | /* perform checksum test on the block (if one is stored) */ |
1346 | 140k | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { |
1347 | 134k | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); |
1348 | 134k | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { |
1349 | 134k | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; |
1350 | 134k | sys->message(d->infh, "WARNING; bad block checksum found"); |
1351 | 134k | } |
1352 | 134k | } |
1353 | | |
1354 | | /* advance end of block pointer to include newly read data */ |
1355 | 140k | d->i_end += len; |
1356 | | |
1357 | | /* uncompressed size == 0 means this block was part of a split block |
1358 | | * and it continues as the first block of the next cabinet in the set. |
1359 | | * otherwise, this is the last part of the block, and no more block |
1360 | | * reading needs to be done. |
1361 | | */ |
1362 | | /* EXIT POINT OF LOOP -- uncompressed size != 0 */ |
1363 | 140k | if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { |
1364 | 134k | return MSPACK_ERR_OK; |
1365 | 134k | } |
1366 | | |
1367 | | /* otherwise, advance to next cabinet */ |
1368 | | |
1369 | | /* close current file handle */ |
1370 | 5.61k | sys->close(d->infh); |
1371 | 5.61k | d->infh = NULL; |
1372 | | |
1373 | | /* advance to next member in the cabinet set */ |
1374 | 5.61k | if (!(d->data = d->data->next)) { |
1375 | 5.61k | sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); |
1376 | 5.61k | return MSPACK_ERR_DATAFORMAT; |
1377 | 5.61k | } |
1378 | | |
1379 | | /* open next cab file */ |
1380 | 0 | d->incab = d->data->cab; |
1381 | 0 | if (!(d->infh = sys->open(sys, d->incab->base.filename, |
1382 | 0 | MSPACK_SYS_OPEN_READ))) |
1383 | 0 | { |
1384 | 0 | return MSPACK_ERR_OPEN; |
1385 | 0 | } |
1386 | | |
1387 | | /* seek to start of data blocks */ |
1388 | 0 | if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { |
1389 | 0 | return MSPACK_ERR_SEEK; |
1390 | 0 | } |
1391 | 0 | } while (1); |
1392 | | |
1393 | | /* not reached */ |
1394 | 0 | return MSPACK_ERR_OK; |
1395 | 167k | } |
1396 | | |
1397 | | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, |
1398 | | unsigned int cksum) |
1399 | 268k | { |
1400 | 268k | unsigned int len, ul = 0; |
1401 | | |
1402 | 54.5M | for (len = bytes >> 2; len--; data += 4) { |
1403 | 54.2M | cksum ^= EndGetI32(data); |
1404 | 54.2M | } |
1405 | | |
1406 | 268k | switch (bytes & 3) { |
1407 | 59.1k | case 3: ul |= *data++ << 16; /*@fallthrough@*/ |
1408 | 79.6k | case 2: ul |= *data++ << 8; /*@fallthrough@*/ |
1409 | 93.1k | case 1: ul |= *data; |
1410 | 268k | } |
1411 | 268k | cksum ^= ul; |
1412 | | |
1413 | 268k | return cksum; |
1414 | 268k | } |
1415 | | |
1416 | | /*************************************** |
1417 | | * NONED_INIT, NONED_DECOMPRESS, NONED_FREE |
1418 | | *************************************** |
1419 | | * the "not compressed" method decompressor |
1420 | | */ |
1421 | | struct noned_state { |
1422 | | struct mspack_system *sys; |
1423 | | struct mspack_file *i; |
1424 | | struct mspack_file *o; |
1425 | | unsigned char *buf; |
1426 | | int bufsize; |
1427 | | }; |
1428 | | |
1429 | | static struct noned_state *noned_init(struct mspack_system *sys, |
1430 | | struct mspack_file *in, |
1431 | | struct mspack_file *out, |
1432 | | int bufsize) |
1433 | 8.71k | { |
1434 | 8.71k | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); |
1435 | 8.71k | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); |
1436 | 8.71k | if (state && buf) { |
1437 | 8.71k | state->sys = sys; |
1438 | 8.71k | state->i = in; |
1439 | 8.71k | state->o = out; |
1440 | 8.71k | state->buf = buf; |
1441 | 8.71k | state->bufsize = bufsize; |
1442 | 8.71k | } |
1443 | 0 | else { |
1444 | 0 | sys->free(buf); |
1445 | 0 | sys->free(state); |
1446 | 0 | state = NULL; |
1447 | 0 | } |
1448 | 8.71k | return state; |
1449 | 8.71k | } |
1450 | | |
1451 | 10.7k | static int noned_decompress(struct noned_state *s, off_t bytes) { |
1452 | 10.7k | int run; |
1453 | 18.4k | while (bytes > 0) { |
1454 | 16.8k | run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; |
1455 | 16.8k | if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; |
1456 | 7.70k | if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; |
1457 | 7.70k | bytes -= run; |
1458 | 7.70k | } |
1459 | 1.57k | return MSPACK_ERR_OK; |
1460 | 10.7k | } |
1461 | | |
1462 | 8.71k | static void noned_free(struct noned_state *state) { |
1463 | 8.71k | struct mspack_system *sys; |
1464 | 8.71k | if (state) { |
1465 | 8.71k | sys = state->sys; |
1466 | 8.71k | sys->free(state->buf); |
1467 | 8.71k | sys->free(state); |
1468 | 8.71k | } |
1469 | 8.71k | } |
1470 | | |
1471 | | |
1472 | | /*************************************** |
1473 | | * CABD_PARAM |
1474 | | *************************************** |
1475 | | * allows a parameter to be set |
1476 | | */ |
1477 | 873k | static int cabd_param(struct mscab_decompressor *base, int param, int value) { |
1478 | 873k | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
1479 | 873k | if (!self) return MSPACK_ERR_ARGS; |
1480 | | |
1481 | 873k | switch (param) { |
1482 | 0 | case MSCABD_PARAM_SEARCHBUF: |
1483 | 0 | if (value < 4) return MSPACK_ERR_ARGS; |
1484 | 0 | self->searchbuf_size = value; |
1485 | 0 | break; |
1486 | 436k | case MSCABD_PARAM_FIXMSZIP: |
1487 | 436k | self->fix_mszip = value; |
1488 | 436k | break; |
1489 | 0 | case MSCABD_PARAM_DECOMPBUF: |
1490 | 0 | if (value < 4) return MSPACK_ERR_ARGS; |
1491 | 0 | self->buf_size = value; |
1492 | 0 | break; |
1493 | 436k | case MSCABD_PARAM_SALVAGE: |
1494 | 436k | self->salvage = value; |
1495 | 436k | break; |
1496 | 0 | default: |
1497 | 0 | return MSPACK_ERR_ARGS; |
1498 | 873k | } |
1499 | 873k | return MSPACK_ERR_OK; |
1500 | 873k | } |
1501 | | |
1502 | | /*************************************** |
1503 | | * CABD_ERROR |
1504 | | *************************************** |
1505 | | * returns the last error that occurred |
1506 | | */ |
1507 | 0 | static int cabd_error(struct mscab_decompressor *base) { |
1508 | 0 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; |
1509 | 0 | return (self) ? self->error : MSPACK_ERR_ARGS; |
1510 | 0 | } |