~ubuntu-branches/ubuntu/wily/clamav/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/0005-libclamav-use-libmspack.patch/libclamav/cab.c

  • Committer: Package Import Robot
  • Author(s): Scott Kitterman, Sebastian Andrzej Siewior, Andreas Cadhalpun, Scott Kitterman, Javier Fernández-Sanguino
  • Date: 2015-01-28 00:25:13 UTC
  • mfrom: (0.48.14 sid)
  • Revision ID: package-import@ubuntu.com-20150128002513-lil2oi74cooy4lzr
Tags: 0.98.6+dfsg-1
[ Sebastian Andrzej Siewior ]
* update "fix-ssize_t-size_t-off_t-printf-modifier", include of misc.h was
  missing but was pulled in via the systemd patch.
* Don't leak return codes from libmspack to clamav API. (Closes: #774686).

[ Andreas Cadhalpun ]
* Add patch to avoid emitting incremental progress messages when not
  outputting to a terminal. (Closes: #767350)
* Update lintian-overrides for unused-file-paragraph-in-dep5-copyright.
* clamav-base.postinst: always chown /var/log/clamav and /var/lib/clamav
  to clamav:clamav, not only on fresh installations. (Closes: #775400)
* Adapt the clamav-daemon and clamav-freshclam logrotate scripts,
  so that they correctly work under systemd.
* Move the PidFile variable from the clamd/freshclam configuration files
  to the init scripts. This makes the init scripts more robust against
  misconfiguration and avoids error messages with systemd. (Closes: #767353)
* debian/copyright: drop files from Files-Excluded only present in github
  tarballs
* Drop Workaround-a-bug-in-libc-on-Hurd.patch, because hurd got fixed.
  (see #752237)
* debian/rules: Remove useless --with-system-tommath --without-included-ltdl
  configure options.

[ Scott Kitterman ]
* Stop stripping llvm when repacking the tarball as the system llvm on some
  releases is too old to use
* New upstream bugfix release
  - Library shared object revisions.
  - Includes a patch from Sebastian Andrzej Siewior making ClamAV pid files
    compatible with systemd.
  - Fix a heap out of bounds condition with crafted Yoda's crypter files.
    This issue was discovered by Felix Groebert of the Google Security Team.
  - Fix a heap out of bounds condition with crafted mew packer files. This
    issue was discovered by Felix Groebert of the Google Security Team.
  - Fix a heap out of bounds condition with crafted upx packer files. This
    issue was discovered by Kevin Szkudlapski of Quarkslab.
  - Fix a heap out of bounds condition with crafted upack packer files. This
    issue was discovered by Sebastian Andrzej Siewior. CVE-2014-9328.
  - Compensate a crash due to incorrect compiler optimization when handling
    crafted petite packer files. This issue was discovered by Sebastian
    Andrzej Siewior.
* Update lintian override for embedded zlib to match new so version

[ Javier Fernández-Sanguino ]
* Updated Spanish Debconf template translation (Closes: #773563)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2007-2008 Sourcefire, Inc.
 
3
 *
 
4
 *  Authors: Tomasz Kojm
 
5
 *
 
6
 *  This program is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License version 2 as
 
8
 *  published by the Free Software Foundation.
 
9
 *
 
10
 *  This program is distributed in the hope that it will be useful,
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 *  GNU General Public License for more details.
 
14
 *
 
15
 *  You should have received a copy of the GNU General Public License
 
16
 *  along with this program; if not, write to the Free Software
 
17
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
18
 *  MA 02110-1301, USA.
 
19
 */
 
20
 
 
21
#if HAVE_CONFIG_H
 
22
#include "clamav-config.h"
 
23
#endif
 
24
 
 
25
#include <stdio.h>
 
26
#include <string.h>
 
27
#include <ctype.h>
 
28
#include <sys/types.h>
 
29
#include <sys/stat.h>
 
30
#ifdef HAVE_UNISTD_H
 
31
#include <unistd.h>
 
32
#endif
 
33
#include <fcntl.h>
 
34
 
 
35
#include "clamav.h"
 
36
#include "cltypes.h"
 
37
#include "others.h"
 
38
#include "mspack.h"
 
39
#include "cab.h"
 
40
 
 
41
#define EC32(x) cli_readint32(&x) /* Convert little endian to host */
 
42
#define EC16(x) cli_readint16(&x)
 
43
 
 
44
/* hard limits */
 
45
#define CAB_FOLDER_LIMIT    5000
 
46
#define CAB_FILE_LIMIT      5000
 
47
 
 
48
/* Cabinet format data structures */
 
49
 
 
50
struct cab_hdr {
 
51
    uint32_t    signature;      /* file signature */
 
52
    uint32_t    res1;           /* reserved */
 
53
    uint32_t    cbCabinet;      /* size of cabinet file */
 
54
    uint32_t    res2;           /* reserved */
 
55
    uint32_t    coffFiles;      /* offset of the first file entry */
 
56
    uint32_t    res3;           /* reserved */
 
57
    uint8_t     versionMinor;   /* file format version, minor */
 
58
    uint8_t     versionMajor;   /* file format version, major */
 
59
    uint16_t    cFolders;       /* number of folder entries */
 
60
    uint16_t    cFiles;         /* number of file entries */
 
61
    uint16_t    flags;          /* option flags */
 
62
    uint16_t    setID;          /* multiple cabs related */
 
63
    uint16_t    iCabinet;       /* multiple cabs related */
 
64
};
 
65
 
 
66
struct cab_hdr_opt {
 
67
    uint16_t    cbCFHeader;     /* size of reserved header area */
 
68
    uint8_t     cbCFFolder;     /* size of reserved folder area */
 
69
    uint8_t     cbCFData;       /* size of reserved block area */
 
70
};
 
71
 
 
72
struct cab_folder_hdr
 
73
{
 
74
    uint32_t    coffCabStart;   /* offset of the first data block */
 
75
    uint16_t    cCFData;        /* number of data blocks */
 
76
    uint16_t    typeCompress;   /* compression type */
 
77
};
 
78
 
 
79
struct cab_file_hdr
 
80
{
 
81
    uint32_t    cbFile;             /* uncompressed size */
 
82
    uint32_t    uoffFolderStart;    /* uncompressed offset of file in folder */
 
83
    uint16_t    iFolder;            /* folder index */
 
84
    uint16_t    date;               /* date stamp */
 
85
    uint16_t    time;               /* time stamp */
 
86
    uint16_t    attribs;            /* attribute flags */
 
87
};
 
88
 
 
89
struct cab_block_hdr
 
90
{
 
91
    uint32_t    csum;       /* data block checksum */
 
92
    uint16_t    cbData;     /* number of compressed bytes */
 
93
    uint16_t    cbUncomp;   /* number of uncompressed bytes */
 
94
};
 
95
 
 
96
static char *cab_readstr(fmap_t *map, off_t *offset, int *ret)
 
97
{
 
98
        int i;
 
99
        const char *str;
 
100
        char *retstr;
 
101
 
 
102
    if(!(str = fmap_need_offstr(map, *offset, 256))) {
 
103
        *ret = CL_EFORMAT;
 
104
        return NULL;
 
105
    }
 
106
 
 
107
    i = strlen(str) + 1;
 
108
    if(i>=255) {
 
109
        fmap_unneed_ptr(map, str, i);
 
110
        *ret = CL_EFORMAT;
 
111
        return NULL;
 
112
    }
 
113
 
 
114
    *offset += i;
 
115
    if((retstr = cli_malloc(i)))
 
116
        memcpy(retstr, str, i);
 
117
    fmap_unneed_ptr(map, str, i);
 
118
 
 
119
    if(!retstr) {
 
120
        *ret = CL_EMEM;
 
121
        return NULL;
 
122
    }
 
123
 
 
124
    *ret = CL_SUCCESS;
 
125
    return retstr;
 
126
}
 
127
 
 
128
static int cab_chkname(char *name, int san)
 
129
{
 
130
        size_t i, len = strlen(name);
 
131
 
 
132
 
 
133
    for(i = 0; i < len; i++) {
 
134
        if(!san && (strchr("%/*?|\\\"+=<>;:\t ", name[i]) || !isascii(name[i]))) {
 
135
            cli_dbgmsg("cab_chkname: File name contains disallowed characters\n");
 
136
            return 1;
 
137
        } else if(san && !isalnum(name[i])) {
 
138
            name[i] = '*';
 
139
        }
 
140
    }
 
141
 
 
142
    return 0;
 
143
}
 
144
 
 
145
void cab_free(struct cab_archive *cab)
 
146
{
 
147
        struct cab_folder *folder;
 
148
        struct cab_file *file;
 
149
 
 
150
 
 
151
    if(cab->state) {
 
152
        if(cab->state->stream) {
 
153
            switch(cab->state->cmethod & 0x000f) {
 
154
                case 0x0001:
 
155
                    mszip_free(cab->state->stream);
 
156
                    break;
 
157
                case 0x0002:
 
158
                    qtm_free(cab->state->stream);
 
159
                    break;
 
160
                case 0x0003:
 
161
                    lzx_free(cab->state->stream);
 
162
            }
 
163
        }
 
164
        free(cab->state);
 
165
    }
 
166
 
 
167
    while(cab->folders) {
 
168
        folder = cab->folders;
 
169
        cab->folders = cab->folders->next;
 
170
        free(folder);
 
171
    }
 
172
 
 
173
    while(cab->files) {
 
174
        file = cab->files;
 
175
        cab->files = cab->files->next;
 
176
        free(file->name);
 
177
        free(file);
 
178
    }
 
179
}
 
180
 
 
181
int cab_open(fmap_t *map, off_t offset, struct cab_archive *cab)
 
182
{
 
183
        unsigned int i, folders = 0;
 
184
        struct cab_file *file, *lfile = NULL;
 
185
        struct cab_folder *folder, *lfolder = NULL;
 
186
        const struct cab_hdr *hdr;
 
187
        const struct cab_hdr_opt *hdr_opt;
 
188
        uint16_t fidx;
 
189
        uint32_t coffFiles;
 
190
        char *pt;
 
191
        int ret;
 
192
        off_t resfold = 0, rsize, cur_offset = offset;
 
193
 
 
194
    if(!(hdr=fmap_need_off_once(map, cur_offset, sizeof(*hdr)))) {
 
195
        cli_dbgmsg("cab_open: Can't read cabinet header\n");
 
196
        return CL_EFORMAT; /* most likely a corrupted file */
 
197
    }
 
198
    cur_offset += sizeof(*hdr);
 
199
 
 
200
    if(EC32(hdr->signature) != 0x4643534d) {
 
201
        cli_dbgmsg("cab_open: Incorrect CAB signature\n");
 
202
        return CL_EFORMAT;
 
203
    } else {
 
204
        cli_dbgmsg("CAB: -------------- Cabinet file ----------------\n");
 
205
    }
 
206
 
 
207
    rsize = map->len;
 
208
 
 
209
    memset(cab, 0, sizeof(struct cab_archive));
 
210
 
 
211
    cab->length = EC32(hdr->cbCabinet);
 
212
    cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length);
 
213
    if((off_t) cab->length > rsize) {
 
214
        cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize);
 
215
        cab->length = (uint32_t) rsize;
 
216
    }
 
217
 
 
218
    cab->nfolders = EC16(hdr->cFolders);
 
219
    if(!cab->nfolders) {
 
220
        cli_dbgmsg("cab_open: No folders in cabinet (fake cab?)\n");
 
221
        return CL_EFORMAT;
 
222
    } else {
 
223
        cli_dbgmsg("CAB: Folders: %u\n", cab->nfolders);
 
224
        if(cab->nfolders > CAB_FOLDER_LIMIT) {
 
225
            cab->nfolders = CAB_FOLDER_LIMIT;
 
226
            cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders);
 
227
        }
 
228
    }
 
229
 
 
230
    cab->nfiles = EC16(hdr->cFiles);
 
231
    if(!cab->nfiles) {
 
232
        cli_dbgmsg("cab_open: No files in cabinet (fake cab?)\n");
 
233
        return CL_EFORMAT;
 
234
    } else {
 
235
        cli_dbgmsg("CAB: Files: %u\n", cab->nfiles);
 
236
        if(cab->nfiles > CAB_FILE_LIMIT) {
 
237
            cab->nfiles = CAB_FILE_LIMIT;
 
238
            cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles);
 
239
        }
 
240
    }
 
241
 
 
242
    cli_dbgmsg("CAB: File format version: %u.%u\n", hdr->versionMajor, hdr->versionMinor);
 
243
 
 
244
    cab->flags = EC16(hdr->flags);
 
245
    coffFiles = EC16(hdr->coffFiles);
 
246
 
 
247
    if(cab->flags & 0x0004) {
 
248
        if(!(hdr_opt = fmap_need_off_once(map, cur_offset, sizeof(*hdr_opt)))) {
 
249
            cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n");
 
250
            return CL_EFORMAT; /* most likely a corrupted file */
 
251
        }
 
252
 
 
253
        cab->reshdr = EC16(hdr_opt->cbCFHeader);
 
254
        resfold = hdr_opt->cbCFFolder;
 
255
        cab->resdata = hdr_opt->cbCFData;
 
256
 
 
257
        cur_offset += sizeof(*hdr_opt) + cab->reshdr;
 
258
        if(cab->reshdr) {
 
259
            if(cab->reshdr >= rsize) {
 
260
                cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr);
 
261
                return CL_EFORMAT; /* most likely a corrupted file */
 
262
            }
 
263
        }
 
264
    }
 
265
 
 
266
    if(cab->flags & 0x0001) { /* preceding cabinet */
 
267
        /* name */
 
268
        pt = cab_readstr(map, &cur_offset, &ret);
 
269
        if(ret)
 
270
            return ret;
 
271
        if(cab_chkname(pt, 0))
 
272
            cli_dbgmsg("CAB: Invalid name of preceding cabinet\n");
 
273
        else
 
274
            cli_dbgmsg("CAB: Preceding cabinet name: %s\n", pt);
 
275
        free(pt);
 
276
        /* info */
 
277
        pt = cab_readstr(map, &cur_offset, &ret);
 
278
        if(ret)
 
279
            return ret;
 
280
        if(cab_chkname(pt, 0))
 
281
            cli_dbgmsg("CAB: Invalid info for preceding cabinet\n");
 
282
        else
 
283
            cli_dbgmsg("CAB: Preceding cabinet info: %s\n", pt);
 
284
        free(pt);
 
285
    }
 
286
 
 
287
    if(cab->flags & 0x0002) { /* next cabinet */
 
288
        /* name */
 
289
        pt = cab_readstr(map, &cur_offset, &ret);
 
290
        if(ret)
 
291
            return ret;
 
292
        if(cab_chkname(pt, 0))
 
293
            cli_dbgmsg("CAB: Invalid name of next cabinet\n");
 
294
        else
 
295
            cli_dbgmsg("CAB: Next cabinet name: %s\n", pt);
 
296
        free(pt);
 
297
        /* info */
 
298
        pt = cab_readstr(map, &cur_offset, &ret);
 
299
        if(ret)
 
300
            return ret;
 
301
        if(cab_chkname(pt, 0))
 
302
            cli_dbgmsg("CAB: Invalid info for next cabinet\n");
 
303
        else
 
304
            cli_dbgmsg("CAB: Next cabinet info: %s\n", pt);
 
305
        free(pt);
 
306
    }
 
307
 
 
308
    /* folders */
 
309
    for(i = 0; i < cab->nfolders; i++) {
 
310
        const struct cab_folder_hdr *folder_hdr;
 
311
 
 
312
        if(!(folder_hdr = fmap_need_off_once(map, cur_offset, sizeof(*folder_hdr)))) {
 
313
            cli_dbgmsg("cab_open: Can't read header for folder %u\n", i);
 
314
            break;
 
315
        }
 
316
 
 
317
        cur_offset += sizeof(*folder_hdr) + resfold;
 
318
 
 
319
        if(EC32(folder_hdr->coffCabStart) + offset > rsize) {
 
320
            cli_dbgmsg("CAB: Folder out of file\n");
 
321
            continue;
 
322
        }
 
323
 
 
324
        if((EC16(folder_hdr->typeCompress) & 0x000f) > 3) {
 
325
            cli_dbgmsg("CAB: Unknown compression method\n");
 
326
            continue;
 
327
        }
 
328
 
 
329
        folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder));
 
330
        if(!folder) {
 
331
            cli_errmsg("cab_open: Can't allocate memory for folder\n");
 
332
            cab_free(cab);
 
333
            return CL_EMEM;
 
334
        }
 
335
 
 
336
        folder->cab = (struct cab_archive *) cab;
 
337
        folder->offset = (off_t) EC32(folder_hdr->coffCabStart) + offset;
 
338
        folder->nblocks = EC16(folder_hdr->cCFData);
 
339
        folder->cmethod = EC16(folder_hdr->typeCompress);
 
340
 
 
341
        cli_dbgmsg("CAB: Folder record %u\n", i);
 
342
        cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset);
 
343
        cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod);
 
344
 
 
345
        if(!lfolder)
 
346
            cab->folders = folder;
 
347
        else
 
348
            lfolder->next = folder;
 
349
 
 
350
        lfolder = folder;
 
351
        folders++;
 
352
    }
 
353
    cli_dbgmsg("CAB: Recorded folders: %u\n", folders);
 
354
 
 
355
    /* files */
 
356
    if(cab->nfolders != folders) {
 
357
        if(coffFiles >= rsize) {
 
358
            cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n");
 
359
            cab_free(cab);
 
360
            return CL_EFORMAT;
 
361
        }
 
362
        cur_offset = coffFiles;
 
363
    }
 
364
    for(i = 0; i < cab->nfiles; i++) {
 
365
        const struct cab_file_hdr *file_hdr;
 
366
 
 
367
        if(!(file_hdr = fmap_need_off_once(map, cur_offset, sizeof(*file_hdr)))) {
 
368
            cli_dbgmsg("cab_open: Can't read file %u header\n", i);
 
369
            break;
 
370
        }
 
371
        cur_offset += sizeof(*file_hdr);
 
372
 
 
373
        file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file));
 
374
        if(!file) {
 
375
            cli_errmsg("cab_open: Can't allocate memory for file\n");
 
376
            cab_free(cab);
 
377
            return CL_EMEM;
 
378
        }
 
379
 
 
380
        file->cab = cab;
 
381
        cab->map = map;
 
382
        file->offset = EC32(file_hdr->uoffFolderStart);
 
383
        file->length = EC32(file_hdr->cbFile);
 
384
        file->attribs = EC16(file_hdr->attribs);
 
385
        fidx = EC16(file_hdr->iFolder);
 
386
        file->error = CL_SUCCESS;
 
387
 
 
388
        file->name = cab_readstr(map, &cur_offset, &ret);
 
389
        if(ret) {
 
390
            free(file);
 
391
            continue;
 
392
        }
 
393
        cab_chkname(file->name, 1);
 
394
 
 
395
        cli_dbgmsg("CAB: File record %u\n", i);
 
396
        cli_dbgmsg("CAB: File name: %s\n", file->name);
 
397
        cli_dbgmsg("CAB: File offset: %u\n", (unsigned int) file->offset);
 
398
        cli_dbgmsg("CAB: File folder index: %u\n", fidx);
 
399
        cli_dbgmsg("CAB: File attribs: 0x%x\n", file->attribs);
 
400
        if(file->attribs & 0x01)
 
401
            cli_dbgmsg("CAB:   * file is read-only\n");
 
402
        if(file->attribs & 0x02)
 
403
            cli_dbgmsg("CAB:   * file is hidden\n");
 
404
        if(file->attribs & 0x04)
 
405
            cli_dbgmsg("CAB:   * file is a system file\n");
 
406
        if(file->attribs & 0x20)
 
407
            cli_dbgmsg("CAB:   * file modified since last backup\n");
 
408
        if(file->attribs & 0x40)
 
409
            cli_dbgmsg("CAB:   * file to be run after extraction\n");
 
410
        if(file->attribs & 0x80)
 
411
            cli_dbgmsg("CAB:   * file name contains UTF\n");
 
412
 
 
413
        /* folder index */
 
414
        if(fidx < 0xfffd) {
 
415
            if(fidx > cab->nfolders) {
 
416
                cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name);
 
417
                free(file->name);
 
418
                free(file);
 
419
                continue;
 
420
            }
 
421
 
 
422
            file->folder = cab->folders;
 
423
            while(file->folder && fidx--)
 
424
                file->folder = file->folder->next;
 
425
 
 
426
            if(!file->folder) {
 
427
                cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name);
 
428
                free(file->name);
 
429
                free(file);
 
430
                continue;
 
431
            }
 
432
 
 
433
        } else {
 
434
            cli_dbgmsg("CAB: File is split *skipping*\n");
 
435
            free(file->name);
 
436
            free(file);
 
437
            continue;
 
438
        }
 
439
 
 
440
        if(!lfile)
 
441
            cab->files = file;
 
442
        else
 
443
            lfile->next = file;
 
444
 
 
445
        lfile = file;
 
446
 
 
447
    }
 
448
 
 
449
    return CL_SUCCESS;
 
450
}
 
451
 
 
452
static int cab_read_block(struct cab_file *file)
 
453
{
 
454
        const struct cab_block_hdr *block_hdr;
 
455
        struct cab_state *state = file->cab->state;
 
456
 
 
457
    if(!(block_hdr = fmap_need_off_once(file->cab->map, file->cab->cur_offset, sizeof(*block_hdr)))) {
 
458
        cli_dbgmsg("cab_read_block: Can't read block header\n");
 
459
        return CL_EFORMAT; /* most likely a corrupted file */
 
460
    }
 
461
 
 
462
    file->cab->cur_offset += sizeof(*block_hdr) + file->cab->resdata;
 
463
    state->blklen = EC16(block_hdr->cbData);
 
464
    state->outlen = EC16(block_hdr->cbUncomp);
 
465
 
 
466
    if(fmap_readn(file->cab->map, state->block, file->cab->cur_offset, state->blklen) != state->blklen) {
 
467
        cli_dbgmsg("cab_read_block: Can't read block data\n");
 
468
        return CL_EFORMAT; /* most likely a corrupted file */
 
469
    }
 
470
 
 
471
    file->cab->cur_offset += state->blklen;
 
472
    state->pt = state->end = state->block;
 
473
    state->end += state->blklen;
 
474
 
 
475
    return CL_SUCCESS;
 
476
}
 
477
 
 
478
static int cab_read(struct cab_file *file, unsigned char *buffer, int bytes)
 
479
{
 
480
        uint16_t todo, left;
 
481
 
 
482
 
 
483
    if((file->cab->state->blknum > file->folder->nblocks) && !file->lread) {
 
484
        file->error = CL_BREAK;
 
485
        return -1;
 
486
    }
 
487
 
 
488
    todo = bytes;
 
489
    while(todo > 0) {
 
490
        left = file->cab->state->end - file->cab->state->pt;
 
491
 
 
492
        if(left) {
 
493
            if(left > todo)
 
494
                left = todo;
 
495
 
 
496
            memcpy(buffer, file->cab->state->pt, left);
 
497
            file->cab->state->pt += left;
 
498
            buffer += left;
 
499
            todo -= left;
 
500
 
 
501
        } else {
 
502
            if(file->cab->state->blknum++ >= file->folder->nblocks)
 
503
                break;
 
504
 
 
505
            file->error = cab_read_block(file);
 
506
            if(file->error)
 
507
                return -1;
 
508
 
 
509
            if((file->folder->cmethod & 0x000f) == 0x0002) /* Quantum hack */
 
510
                *file->cab->state->end++ = 0xff;
 
511
 
 
512
            if(file->cab->state->blknum >= file->folder->nblocks) {
 
513
                if((file->folder->cmethod & 0x000f) == 0x0003) { /* LZX hack */
 
514
                    lzx_set_output_length(file->cab->state->stream, (off_t) ((file->cab->state->blknum - 1) * 32768 + file->cab->state->outlen));
 
515
                }
 
516
            } else {
 
517
                if(file->cab->state->outlen != 32768) {
 
518
                    cli_dbgmsg("cab_read: WARNING: partial data block\n");
 
519
                }
 
520
            }
 
521
        }
 
522
    }
 
523
 
 
524
    return file->lread = bytes - todo;
 
525
}
 
526
 
 
527
static int cab_unstore(struct cab_file *file)
 
528
{
 
529
        int todo, bread, bytes = file->length;
 
530
        unsigned char buff[4096];
 
531
 
 
532
 
 
533
    if(bytes < 0) {
 
534
        cli_dbgmsg("cab_unstore: bytes < 0\n");
 
535
        return CL_EFORMAT;
 
536
    }
 
537
 
 
538
    todo = MIN((unsigned int) bytes, file->max_size);
 
539
 
 
540
    while(1) {
 
541
 
 
542
        if((unsigned int) todo <= sizeof(buff))
 
543
            bread = todo;
 
544
        else
 
545
            bread = sizeof(buff);
 
546
 
 
547
        if((bread = cab_read(file, buff, bread)) == -1) {
 
548
            cli_dbgmsg("cab_unstore: cab_read failed\n");
 
549
            return file->error;
 
550
        } else if(cli_writen(file->ofd, buff, bread) != bread) {
 
551
            cli_warnmsg("cab_unstore: Can't write %d bytes to descriptor %d\n", bread, file->ofd);
 
552
            return CL_EWRITE;
 
553
        }
 
554
 
 
555
        todo -= bread;
 
556
 
 
557
        if(!bread || todo <= 0)
 
558
            break;
 
559
    }
 
560
 
 
561
    return CL_SUCCESS;
 
562
}
 
563
 
 
564
#define CAB_CHGFOLDER                                                   \
 
565
    if(!file->cab->actfol || (file->folder != file->cab->actfol)        \
 
566
       || (file->cab->state && file->cab->state->cmethod != file->folder->cmethod)) { \
 
567
        if(file->cab->state) {                                          \
 
568
            if(file->cab->state->stream) {                              \
 
569
                switch(file->cab->state->cmethod & 0x000f) {            \
 
570
                    case 0x0001:                                        \
 
571
                        mszip_free(file->cab->state->stream);           \
 
572
                        break;                                          \
 
573
                    case 0x0002:                                        \
 
574
                        qtm_free(file->cab->state->stream);             \
 
575
                        break;                                          \
 
576
                    case 0x0003:                                        \
 
577
                        lzx_free(file->cab->state->stream);             \
 
578
                }                                                       \
 
579
            }                                                           \
 
580
            free(file->cab->state);                                     \
 
581
            file->cab->state = NULL;                                    \
 
582
        }                                                               \
 
583
        file->cab->cur_offset = file->folder->offset;                   \
 
584
        file->cab->state = (struct cab_state *) cli_calloc(1, sizeof(struct cab_state));        \
 
585
        if(!file->cab->state) {                                         \
 
586
            cli_errmsg("cab_extract: Can't allocate memory for internal state\n");              \
 
587
            close(file->ofd);                                           \
 
588
            return CL_EMEM;                                             \
 
589
        }                                                               \
 
590
        file->cab->state->cmethod = file->folder->cmethod;              \
 
591
        switch(file->folder->cmethod & 0x000f) {                        \
 
592
            case 0x0001:                                                \
 
593
                file->cab->state->stream = (struct mszip_stream *) mszip_init(file->ofd, 4096, 1, file, &cab_read);     \
 
594
                break;                                                  \
 
595
            case 0x0002:                                                \
 
596
                file->cab->state->stream = (struct qtm_stream *) qtm_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 4096, file, &cab_read);                                                                 \
 
597
                break;                                                  \
 
598
            case 0x0003:                                                \
 
599
                file->cab->state->stream = (struct lzx_stream *) lzx_init(file->ofd, (int) (file->folder->cmethod >> 8) & 0x1f, 0, 4096, 0, file, &cab_read);                                                                   \
 
600
        }                                                               \
 
601
        if((file->folder->cmethod & 0x000f) && !file->cab->state->stream) { \
 
602
            close(file->ofd);                                           \
 
603
            return CL_EUNPACK;                                          \
 
604
        }                                                               \
 
605
        file->cab->actfol = file->folder;                               \
 
606
    } else {                                                            \
 
607
        if(file->cab->state && file->cab->state->stream) {              \
 
608
            switch(file->cab->state->cmethod & 0x000f) {                \
 
609
                case 0x0001:                                            \
 
610
                    ((struct mszip_stream *) file->cab->state->stream)->ofd = file->ofd;        \
 
611
                    break;                                              \
 
612
                case 0x0002:                                            \
 
613
                    ((struct qtm_stream *) file->cab->state->stream)->ofd = file->ofd;          \
 
614
                    break;                                              \
 
615
                case 0x0003:                                            \
 
616
                    ((struct lzx_stream *) file->cab->state->stream)->ofd = file->ofd;          \
 
617
                    break;                                              \
 
618
            }                                                           \
 
619
        }                                                               \
 
620
    }
 
621
 
 
622
 
 
623
int cab_extract(struct cab_file *file, const char *name)
 
624
{
 
625
        int ret;
 
626
 
 
627
 
 
628
    if(!file || !name) {
 
629
        cli_errmsg("cab_extract: !file || !name\n");
 
630
        return CL_ENULLARG;
 
631
    }
 
632
 
 
633
    if(!file->folder) {
 
634
        cli_errmsg("cab_extract: file->folder == NULL\n");
 
635
        return CL_ENULLARG;
 
636
    }
 
637
 
 
638
    file->ofd = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, S_IRWXU);
 
639
    if(file->ofd == -1) {
 
640
        cli_errmsg("cab_extract: Can't open file %s in write mode\n", name);
 
641
        return CL_ECREAT;
 
642
    }
 
643
 
 
644
    switch(file->folder->cmethod & 0x000f) {
 
645
        case 0x0000: /* STORE */
 
646
            cli_dbgmsg("CAB: Compression method: STORED\n");
 
647
            CAB_CHGFOLDER;
 
648
            if(file->length > file->cab->length) {
 
649
                cli_dbgmsg("cab_extract: Stored file larger than archive itself, trimming down\n");
 
650
                file->length = file->cab->length;
 
651
            }
 
652
            ret = cab_unstore(file);
 
653
            break;
 
654
 
 
655
        case 0x0001: /* MSZIP */
 
656
            cli_dbgmsg("CAB: Compression method: MSZIP\n");
 
657
            CAB_CHGFOLDER;
 
658
            ret = mszip_decompress(file->cab->state->stream, file->length);
 
659
            break;
 
660
 
 
661
        case 0x0002: /* QUANTUM */
 
662
            cli_dbgmsg("CAB: Compression method: QUANTUM\n");
 
663
            CAB_CHGFOLDER;
 
664
            ret = qtm_decompress(file->cab->state->stream, file->length);
 
665
            break;
 
666
 
 
667
        case 0x0003: /* LZX */
 
668
            cli_dbgmsg("CAB: Compression method: LZX\n");
 
669
            CAB_CHGFOLDER;
 
670
            ret = lzx_decompress(file->cab->state->stream, file->length);
 
671
            break;
 
672
 
 
673
        default:
 
674
            cli_dbgmsg("CAB: Not supported compression method: 0x%x\n", file->folder->cmethod & 0x000f);
 
675
            ret = CL_EFORMAT;
 
676
    }
 
677
 
 
678
    close(file->ofd);
 
679
 
 
680
    if(ret == CL_BREAK)
 
681
        ret = CL_SUCCESS;
 
682
 
 
683
    return ret;
 
684
}