xref: /reactos/dll/win32/cabinet/fci.c (revision c7bba39a)
1 /*
2  * File Compression Interface
3  *
4  * Copyright 2002 Patrik Stridvall
5  * Copyright 2005 Gerold Jens Wucherpfennig
6  * Copyright 2011 Alexandre Julliard
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 /*
24 
25 There is still some work to be done:
26 
27 - unknown behaviour if files>=2GB or cabinet >=4GB
28 - check if the maximum size for a cabinet is too small to store any data
29 - call pfnfcignc on exactly the same position as MS FCIAddFile in every case
30 
31 */
32 
33 
34 
35 #include "config.h"
36 
37 #include <assert.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <string.h>
41 #ifdef HAVE_ZLIB
42 # include <zlib.h>
43 #endif
44 
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winerror.h"
48 #include "wine/winternl.h"
49 #include "fci.h"
50 #include "cabinet.h"
51 #include "wine/list.h"
52 #include "wine/debug.h"
53 
54 WINE_DEFAULT_DEBUG_CHANNEL(cabinet);
55 
56 #ifdef WORDS_BIGENDIAN
57 #define fci_endian_ulong(x) RtlUlongByteSwap(x)
58 #define fci_endian_uword(x) RtlUshortByteSwap(x)
59 #else
60 #define fci_endian_ulong(x) (x)
61 #define fci_endian_uword(x) (x)
62 #endif
63 
64 
65 typedef struct {
66   cab_UBYTE signature[4]; /* !CAB for unfinished cabinets else MSCF */
67   cab_ULONG reserved1;
68   cab_ULONG cbCabinet;    /*  size of the cabinet file in bytes*/
69   cab_ULONG reserved2;
70   cab_ULONG coffFiles;    /* offset to first CFFILE section */
71   cab_ULONG reserved3;
72   cab_UBYTE versionMinor; /* 3 */
73   cab_UBYTE versionMajor; /* 1 */
74   cab_UWORD cFolders;     /* number of CFFOLDER entries in the cabinet*/
75   cab_UWORD cFiles;       /* number of CFFILE entries in the cabinet*/
76   cab_UWORD flags;        /* 1=prev cab, 2=next cabinet, 4=reserved sections*/
77   cab_UWORD setID;        /* identification number of all cabinets in a set*/
78   cab_UWORD iCabinet;     /* number of the cabinet in a set */
79   /* additional area if "flags" were set*/
80 } CFHEADER; /* minimum 36 bytes */
81 
82 typedef struct {
83   cab_ULONG coffCabStart; /* offset to the folder's first CFDATA section */
84   cab_UWORD cCFData;      /* number of this folder's CFDATA sections */
85   cab_UWORD typeCompress; /* compression type of data in CFDATA section*/
86   /* additional area if reserve flag was set */
87 } CFFOLDER; /* minimum 8 bytes */
88 
89 typedef struct {
90   cab_ULONG cbFile;          /* size of the uncompressed file in bytes */
91   cab_ULONG uoffFolderStart; /* offset of the uncompressed file in the folder */
92   cab_UWORD iFolder;         /* number of folder in the cabinet 0=first  */
93                              /* for special values see below this structure*/
94   cab_UWORD date;            /* last modification date*/
95   cab_UWORD time;            /* last modification time*/
96   cab_UWORD attribs;         /* DOS fat attributes and UTF indicator */
97   /* ... and a C string with the name of the file */
98 } CFFILE; /* 16 bytes + name of file */
99 
100 
101 typedef struct {
102   cab_ULONG csum;          /* checksum of this entry*/
103   cab_UWORD cbData;        /* number of compressed bytes  */
104   cab_UWORD cbUncomp;      /* number of bytes when data is uncompressed */
105   /* optional reserved area */
106   /* compressed data */
107 } CFDATA;
108 
109 struct temp_file
110 {
111     INT_PTR   handle;
112     char      name[CB_MAX_FILENAME];
113 };
114 
115 struct folder
116 {
117     struct list      entry;
118     struct list      files_list;
119     struct list      blocks_list;
120     struct temp_file data;
121     cab_ULONG        data_start;
122     cab_UWORD        data_count;
123     TCOMP            compression;
124 };
125 
126 struct file
127 {
128     struct list entry;
129     cab_ULONG   size;    /* uncompressed size */
130     cab_ULONG   offset;  /* offset in folder */
131     cab_UWORD   folder;  /* index of folder */
132     cab_UWORD   date;
133     cab_UWORD   time;
134     cab_UWORD   attribs;
135     char        name[1];
136 };
137 
138 struct data_block
139 {
140     struct list entry;
141     cab_UWORD   compressed;
142     cab_UWORD   uncompressed;
143 };
144 
145 typedef struct FCI_Int
146 {
147   unsigned int       magic;
148   PERF               perf;
149   PFNFCIFILEPLACED   fileplaced;
150   PFNFCIALLOC        alloc;
151   PFNFCIFREE         free;
152   PFNFCIOPEN         open;
153   PFNFCIREAD         read;
154   PFNFCIWRITE        write;
155   PFNFCICLOSE        close;
156   PFNFCISEEK         seek;
157   PFNFCIDELETE       delete;
158   PFNFCIGETTEMPFILE  gettemp;
159   CCAB               ccab;
160   PCCAB              pccab;
161   BOOL               fPrevCab;
162   BOOL               fNextCab;
163   BOOL               fSplitFolder;
164   cab_ULONG          statusFolderCopied;
165   cab_ULONG          statusFolderTotal;
166   BOOL               fGetNextCabInVain;
167   void               *pv;
168   char               szPrevCab[CB_MAX_CABINET_NAME]; /* previous cabinet name */
169   char               szPrevDisk[CB_MAX_DISK_NAME];   /* disk name of previous cabinet */
170   unsigned char      data_in[CAB_BLOCKMAX];          /* uncompressed data blocks */
171   unsigned char      data_out[2 * CAB_BLOCKMAX];     /* compressed data blocks */
172   cab_UWORD          cdata_in;
173   ULONG              cCompressedBytesInFolder;
174   cab_UWORD          cFolders;
175   cab_UWORD          cFiles;
176   cab_ULONG          cDataBlocks;
177   cab_ULONG          cbFileRemainer; /* uncompressed, yet to be written data */
178                /* of spanned file of a spanning folder of a spanning cabinet */
179   struct temp_file   data;
180   BOOL               fNewPrevious;
181   cab_ULONG          estimatedCabinetSize;
182   struct list        folders_list;
183   struct list        files_list;
184   struct list        blocks_list;
185   cab_ULONG          folders_size;
186   cab_ULONG          files_size;          /* size of files not yet assigned to a folder */
187   cab_ULONG          placed_files_size;   /* size of files already placed into a folder */
188   cab_ULONG          pending_data_size;   /* size of data not yet assigned to a folder */
189   cab_ULONG          folders_data_size;   /* total size of data contained in the current folders */
190   TCOMP              compression;
191   cab_UWORD        (*compress)(struct FCI_Int *);
192 } FCI_Int;
193 
194 #define FCI_INT_MAGIC 0xfcfcfc05
195 
196 static void set_error( FCI_Int *fci, int oper, int err )
197 {
198     fci->perf->erfOper = oper;
199     fci->perf->erfType = err;
200     fci->perf->fError = TRUE;
201     if (err) SetLastError( err );
202 }
203 
204 static FCI_Int *get_fci_ptr( HFCI hfci )
205 {
206     FCI_Int *fci= (FCI_Int *)hfci;
207 
208     if (!fci || fci->magic != FCI_INT_MAGIC)
209     {
210         SetLastError( ERROR_INVALID_HANDLE );
211         return NULL;
212     }
213     return fci;
214 }
215 
216 /* compute the cabinet header size */
217 static cab_ULONG get_header_size( FCI_Int *fci )
218 {
219     cab_ULONG ret = sizeof(CFHEADER) + fci->ccab.cbReserveCFHeader;
220 
221     if (fci->ccab.cbReserveCFHeader || fci->ccab.cbReserveCFFolder || fci->ccab.cbReserveCFData)
222         ret += 4;
223 
224     if (fci->fPrevCab)
225         ret += strlen( fci->szPrevCab ) + 1 + strlen( fci->szPrevDisk ) + 1;
226 
227     if (fci->fNextCab)
228         ret += strlen( fci->pccab->szCab ) + 1 + strlen( fci->pccab->szDisk ) + 1;
229 
230     return ret;
231 }
232 
233 static BOOL create_temp_file( FCI_Int *fci, struct temp_file *file )
234 {
235     int err;
236 
237     if (!fci->gettemp( file->name, CB_MAX_FILENAME, fci->pv ))
238     {
239         set_error( fci, FCIERR_TEMP_FILE, ERROR_FUNCTION_FAILED );
240         return FALSE;
241     }
242     if ((file->handle = fci->open( file->name, _O_RDWR | _O_CREAT | _O_EXCL | _O_BINARY,
243                                    _S_IREAD | _S_IWRITE, &err, fci->pv )) == -1)
244     {
245         set_error( fci, FCIERR_TEMP_FILE, err );
246         return FALSE;
247     }
248     return TRUE;
249 }
250 
251 static BOOL close_temp_file( FCI_Int *fci, struct temp_file *file )
252 {
253     int err;
254 
255     if (file->handle == -1) return TRUE;
256     if (fci->close( file->handle, &err, fci->pv ) == -1)
257     {
258         set_error( fci, FCIERR_TEMP_FILE, err );
259         return FALSE;
260     }
261     file->handle = -1;
262     if (fci->delete( file->name, &err, fci->pv ) == -1)
263     {
264         set_error( fci, FCIERR_TEMP_FILE, err );
265         return FALSE;
266     }
267     return TRUE;
268 }
269 
270 static struct file *add_file( FCI_Int *fci, const char *filename )
271 {
272     unsigned int size = FIELD_OFFSET( struct file, name[strlen(filename) + 1] );
273     struct file *file = fci->alloc( size );
274 
275     if (!file)
276     {
277         set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
278         return NULL;
279     }
280     file->size    = 0;
281     file->offset  = fci->cDataBlocks * CAB_BLOCKMAX + fci->cdata_in;
282     file->folder  = fci->cFolders;
283     file->date    = 0;
284     file->time    = 0;
285     file->attribs = 0;
286     strcpy( file->name, filename );
287     list_add_tail( &fci->files_list, &file->entry );
288     fci->files_size += sizeof(CFFILE) + strlen(filename) + 1;
289     return file;
290 }
291 
292 static struct file *copy_file( FCI_Int *fci, const struct file *orig )
293 {
294     unsigned int size = FIELD_OFFSET( struct file, name[strlen(orig->name) + 1] );
295     struct file *file = fci->alloc( size );
296 
297     if (!file)
298     {
299         set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
300         return NULL;
301     }
302     memcpy( file, orig, size );
303     return file;
304 }
305 
306 static void free_file( FCI_Int *fci, struct file *file )
307 {
308     list_remove( &file->entry );
309     fci->free( file );
310 }
311 
312 /* create a new data block for the data in fci->data_in */
313 static BOOL add_data_block( FCI_Int *fci, PFNFCISTATUS status_callback )
314 {
315     int err;
316     struct data_block *block;
317 
318     if (!fci->cdata_in) return TRUE;
319 
320     if (fci->data.handle == -1 && !create_temp_file( fci, &fci->data )) return FALSE;
321 
322     if (!(block = fci->alloc( sizeof(*block) )))
323     {
324         set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
325         return FALSE;
326     }
327     block->uncompressed = fci->cdata_in;
328     block->compressed   = fci->compress( fci );
329 
330     if (fci->write( fci->data.handle, fci->data_out,
331                     block->compressed, &err, fci->pv ) != block->compressed)
332     {
333         set_error( fci, FCIERR_TEMP_FILE, err );
334         fci->free( block );
335         return FALSE;
336     }
337 
338     fci->cdata_in = 0;
339     fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed;
340     fci->cCompressedBytesInFolder += block->compressed;
341     fci->cDataBlocks++;
342     list_add_tail( &fci->blocks_list, &block->entry );
343 
344     if (status_callback( statusFile, block->compressed, block->uncompressed, fci->pv ) == -1)
345     {
346         set_error( fci, FCIERR_USER_ABORT, 0 );
347         return FALSE;
348     }
349     return TRUE;
350 }
351 
352 /* add compressed blocks for all the data that can be read from the file */
353 static BOOL add_file_data( FCI_Int *fci, char *sourcefile, char *filename, BOOL execute,
354                            PFNFCIGETOPENINFO get_open_info, PFNFCISTATUS status_callback )
355 {
356     int err, len;
357     INT_PTR handle;
358     struct file *file;
359 
360     if (!(file = add_file( fci, filename ))) return FALSE;
361 
362     handle = get_open_info( sourcefile, &file->date, &file->time, &file->attribs, &err, fci->pv );
363     if (handle == -1)
364     {
365         free_file( fci, file );
366         set_error( fci, FCIERR_OPEN_SRC, err );
367         return FALSE;
368     }
369     if (execute) file->attribs |= _A_EXEC;
370 
371     for (;;)
372     {
373         len = fci->read( handle, fci->data_in + fci->cdata_in,
374                          CAB_BLOCKMAX - fci->cdata_in, &err, fci->pv );
375         if (!len) break;
376 
377         if (len == -1)
378         {
379             set_error( fci, FCIERR_READ_SRC, err );
380             return FALSE;
381         }
382         file->size += len;
383         fci->cdata_in += len;
384         if (fci->cdata_in == CAB_BLOCKMAX && !add_data_block( fci, status_callback )) return FALSE;
385     }
386     fci->close( handle, &err, fci->pv );
387     return TRUE;
388 }
389 
390 static void free_data_block( FCI_Int *fci, struct data_block *block )
391 {
392     list_remove( &block->entry );
393     fci->free( block );
394 }
395 
396 static struct folder *add_folder( FCI_Int *fci )
397 {
398     struct folder *folder = fci->alloc( sizeof(*folder) );
399 
400     if (!folder)
401     {
402         set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
403         return NULL;
404     }
405     folder->data.handle = -1;
406     folder->data_start  = fci->folders_data_size;
407     folder->data_count  = 0;
408     folder->compression = fci->compression;
409     list_init( &folder->files_list );
410     list_init( &folder->blocks_list );
411     list_add_tail( &fci->folders_list, &folder->entry );
412     fci->folders_size += sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
413     fci->cFolders++;
414     return folder;
415 }
416 
417 static void free_folder( FCI_Int *fci, struct folder *folder )
418 {
419     struct file *file, *file_next;
420     struct data_block *block, *block_next;
421 
422     LIST_FOR_EACH_ENTRY_SAFE( file, file_next, &folder->files_list, struct file, entry )
423         free_file( fci, file );
424     LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &folder->blocks_list, struct data_block, entry )
425         free_data_block( fci, block );
426     close_temp_file( fci, &folder->data );
427     list_remove( &folder->entry );
428     fci->free( folder );
429 }
430 
431 /* reset state for the next cabinet file once the current one has been flushed */
432 static void reset_cabinet( FCI_Int *fci )
433 {
434     struct folder *folder, *folder_next;
435 
436     LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &fci->folders_list, struct folder, entry )
437         free_folder( fci, folder );
438 
439     fci->cFolders          = 0;
440     fci->cFiles            = 0;
441     fci->folders_size      = 0;
442     fci->placed_files_size = 0;
443     fci->folders_data_size = 0;
444 }
445 
446 static cab_ULONG fci_get_checksum( const void *pv, UINT cb, cab_ULONG seed )
447 {
448   cab_ULONG     csum;
449   cab_ULONG     ul;
450   int           cUlong;
451   const BYTE    *pb;
452 
453   csum = seed;
454   cUlong = cb / 4;
455   pb = pv;
456 
457   while (cUlong-- > 0) {
458     ul = *pb++;
459     ul |= (((cab_ULONG)(*pb++)) <<  8);
460     ul |= (((cab_ULONG)(*pb++)) << 16);
461     ul |= (((cab_ULONG)(*pb++)) << 24);
462     csum ^= ul;
463   }
464 
465   ul = 0;
466   switch (cb % 4) {
467     case 3:
468       ul |= (((ULONG)(*pb++)) << 16);
469       /* fall through */
470     case 2:
471       ul |= (((ULONG)(*pb++)) <<  8);
472       /* fall through */
473     case 1:
474       ul |= *pb;
475       /* fall through */
476     default:
477       break;
478   }
479   csum ^= ul;
480 
481   return csum;
482 }
483 
484 /* copy all remaining data block to a new temp file */
485 static BOOL copy_data_blocks( FCI_Int *fci, INT_PTR handle, cab_ULONG start_pos,
486                               struct temp_file *temp, PFNFCISTATUS status_callback )
487 {
488     struct data_block *block;
489     int err;
490 
491     if (fci->seek( handle, start_pos, SEEK_SET, &err, fci->pv ) != start_pos)
492     {
493         set_error( fci, FCIERR_TEMP_FILE, err );
494         return FALSE;
495     }
496     if (!create_temp_file( fci, temp )) return FALSE;
497 
498     LIST_FOR_EACH_ENTRY( block, &fci->blocks_list, struct data_block, entry )
499     {
500         if (fci->read( handle, fci->data_out, block->compressed,
501                        &err, fci->pv ) != block->compressed)
502         {
503             close_temp_file( fci, temp );
504             set_error( fci, FCIERR_TEMP_FILE, err );
505             return FALSE;
506         }
507         if (fci->write( temp->handle, fci->data_out, block->compressed,
508                         &err, fci->pv ) != block->compressed)
509         {
510             close_temp_file( fci, temp );
511             set_error( fci, FCIERR_TEMP_FILE, err );
512             return FALSE;
513         }
514         fci->pending_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + block->compressed;
515         fci->statusFolderCopied += block->compressed;
516 
517         if (status_callback( statusFolder, fci->statusFolderCopied,
518                              fci->statusFolderTotal, fci->pv) == -1)
519         {
520             close_temp_file( fci, temp );
521             set_error( fci, FCIERR_USER_ABORT, 0 );
522             return FALSE;
523         }
524     }
525     return TRUE;
526 }
527 
528 /* write all folders to disk and remove them from the list */
529 static BOOL write_folders( FCI_Int *fci, INT_PTR handle, cab_ULONG header_size, PFNFCISTATUS status_callback )
530 {
531     struct folder *folder;
532     int err;
533     CFFOLDER *cffolder = (CFFOLDER *)fci->data_out;
534     cab_ULONG folder_size = sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder;
535 
536     memset( cffolder, 0, folder_size );
537 
538     /* write the folders */
539     LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
540     {
541         cffolder->coffCabStart = fci_endian_ulong( folder->data_start + header_size );
542         cffolder->cCFData      = fci_endian_uword( folder->data_count );
543         cffolder->typeCompress = fci_endian_uword( folder->compression );
544         if (fci->write( handle, cffolder, folder_size, &err, fci->pv ) != folder_size)
545         {
546             set_error( fci, FCIERR_CAB_FILE, err );
547             return FALSE;
548         }
549     }
550     return TRUE;
551 }
552 
553 /* write all the files to the cabinet file */
554 static BOOL write_files( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback )
555 {
556     cab_ULONG file_size;
557     struct folder *folder;
558     struct file *file;
559     int err;
560     CFFILE *cffile = (CFFILE *)fci->data_out;
561 
562     LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
563     {
564         LIST_FOR_EACH_ENTRY( file, &folder->files_list, struct file, entry )
565         {
566             cffile->cbFile          = fci_endian_ulong( file->size );
567             cffile->uoffFolderStart = fci_endian_ulong( file->offset );
568             cffile->iFolder         = fci_endian_uword( file->folder );
569             cffile->date            = fci_endian_uword( file->date );
570             cffile->time            = fci_endian_uword( file->time );
571             cffile->attribs         = fci_endian_uword( file->attribs );
572             lstrcpynA( (char *)(cffile + 1), file->name, CB_MAX_FILENAME );
573             file_size = sizeof(CFFILE) + strlen( (char *)(cffile + 1) ) + 1;
574             if (fci->write( handle, cffile, file_size, &err, fci->pv ) != file_size)
575             {
576                 set_error( fci, FCIERR_CAB_FILE, err );
577                 return FALSE;
578             }
579             if (!fci->fSplitFolder)
580             {
581                 fci->statusFolderCopied = 0;
582                 /* TODO TEST THIS further */
583                 fci->statusFolderTotal = fci->folders_data_size + fci->placed_files_size;
584             }
585             fci->statusFolderCopied += file_size;
586             /* report status about copied size of folder */
587             if (status_callback( statusFolder, fci->statusFolderCopied,
588                                  fci->statusFolderTotal, fci->pv ) == -1)
589             {
590                 set_error( fci, FCIERR_USER_ABORT, 0 );
591                 return FALSE;
592             }
593         }
594     }
595     return TRUE;
596 }
597 
598 /* write all data blocks to the cabinet file */
599 static BOOL write_data_blocks( FCI_Int *fci, INT_PTR handle, PFNFCISTATUS status_callback )
600 {
601     struct folder *folder;
602     struct data_block *block;
603     int err, len;
604     CFDATA *cfdata;
605     void *data;
606     cab_UWORD header_size;
607 
608     header_size = sizeof(CFDATA) + fci->ccab.cbReserveCFData;
609     cfdata = (CFDATA *)fci->data_out;
610     memset( cfdata, 0, header_size );
611     data = (char *)cfdata + header_size;
612 
613     LIST_FOR_EACH_ENTRY( folder, &fci->folders_list, struct folder, entry )
614     {
615         if (fci->seek( folder->data.handle, 0, SEEK_SET, &err, fci->pv ) != 0)
616         {
617             set_error( fci, FCIERR_CAB_FILE, err );
618             return FALSE;
619         }
620         LIST_FOR_EACH_ENTRY( block, &folder->blocks_list, struct data_block, entry )
621         {
622             len = fci->read( folder->data.handle, data, block->compressed, &err, fci->pv );
623             if (len != block->compressed) return FALSE;
624 
625             cfdata->cbData = fci_endian_uword( block->compressed );
626             cfdata->cbUncomp = fci_endian_uword( block->uncompressed );
627             cfdata->csum = fci_endian_ulong( fci_get_checksum( &cfdata->cbData,
628                                                                header_size - FIELD_OFFSET(CFDATA, cbData),
629                                                                fci_get_checksum( data, len, 0 )));
630 
631             fci->statusFolderCopied += len;
632             len += header_size;
633             if (fci->write( handle, fci->data_out, len, &err, fci->pv ) != len)
634             {
635                 set_error( fci, FCIERR_CAB_FILE, err );
636                 return FALSE;
637             }
638             if (status_callback( statusFolder, fci->statusFolderCopied, fci->statusFolderTotal, fci->pv) == -1)
639             {
640                 set_error( fci, FCIERR_USER_ABORT, 0 );
641                 return FALSE;
642             }
643         }
644     }
645     return TRUE;
646 }
647 
648 /* write the cabinet file to disk */
649 static BOOL write_cabinet( FCI_Int *fci, PFNFCISTATUS status_callback )
650 {
651     char filename[CB_MAX_CAB_PATH + CB_MAX_CABINET_NAME];
652     int err;
653     char *ptr;
654     INT_PTR handle;
655     CFHEADER *cfheader = (CFHEADER *)fci->data_out;
656     cab_UWORD flags = 0;
657     cab_ULONG header_size = get_header_size( fci );
658     cab_ULONG total_size = header_size + fci->folders_size +
659                            fci->placed_files_size + fci->folders_data_size;
660 
661     assert( header_size <= sizeof(fci->data_out) );
662     memset( cfheader, 0, header_size );
663 
664     if (fci->fPrevCab) flags |= cfheadPREV_CABINET;
665     if (fci->fNextCab) flags |= cfheadNEXT_CABINET;
666     if (fci->ccab.cbReserveCFHeader || fci->ccab.cbReserveCFFolder || fci->ccab.cbReserveCFData)
667       flags |= cfheadRESERVE_PRESENT;
668 
669     memcpy( cfheader->signature, "!CAB", 4 );
670     cfheader->cbCabinet    = fci_endian_ulong( total_size );
671     cfheader->coffFiles    = fci_endian_ulong( header_size + fci->folders_size );
672     cfheader->versionMinor = 3;
673     cfheader->versionMajor = 1;
674     cfheader->cFolders     = fci_endian_uword( fci->cFolders );
675     cfheader->cFiles       = fci_endian_uword( fci->cFiles );
676     cfheader->flags        = fci_endian_uword( flags );
677     cfheader->setID        = fci_endian_uword( fci->ccab.setID );
678     cfheader->iCabinet     = fci_endian_uword( fci->ccab.iCab );
679     ptr = (char *)(cfheader + 1);
680 
681     if (flags & cfheadRESERVE_PRESENT)
682     {
683         struct
684         {
685             cab_UWORD cbCFHeader;
686             cab_UBYTE cbCFFolder;
687             cab_UBYTE cbCFData;
688         } *reserve = (void *)ptr;
689 
690         reserve->cbCFHeader = fci_endian_uword( fci->ccab.cbReserveCFHeader );
691         reserve->cbCFFolder = fci->ccab.cbReserveCFFolder;
692         reserve->cbCFData   = fci->ccab.cbReserveCFData;
693         ptr = (char *)(reserve + 1);
694     }
695     ptr += fci->ccab.cbReserveCFHeader;
696 
697     if (flags & cfheadPREV_CABINET)
698     {
699         strcpy( ptr, fci->szPrevCab );
700         ptr += strlen( ptr ) + 1;
701         strcpy( ptr, fci->szPrevDisk );
702         ptr += strlen( ptr ) + 1;
703     }
704 
705     if (flags & cfheadNEXT_CABINET)
706     {
707         strcpy( ptr, fci->pccab->szCab );
708         ptr += strlen( ptr ) + 1;
709         strcpy( ptr, fci->pccab->szDisk );
710         ptr += strlen( ptr ) + 1;
711     }
712 
713     assert( ptr - (char *)cfheader == header_size );
714 
715     strcpy( filename, fci->ccab.szCabPath );
716     strcat( filename, fci->ccab.szCab );
717 
718     if ((handle = fci->open( filename, _O_RDWR | _O_CREAT | _O_TRUNC | _O_BINARY,
719                              _S_IREAD | _S_IWRITE, &err, fci->pv )) == -1)
720     {
721         set_error( fci, FCIERR_CAB_FILE, err );
722         return FALSE;
723     }
724 
725     if (fci->write( handle, cfheader, header_size, &err, fci->pv ) != header_size)
726     {
727         set_error( fci, FCIERR_CAB_FILE, err );
728         goto failed;
729     }
730 
731     /* add size of header size of all CFFOLDERs and size of all CFFILEs */
732     header_size += fci->placed_files_size + fci->folders_size;
733     if (!write_folders( fci, handle, header_size, status_callback )) goto failed;
734     if (!write_files( fci, handle, status_callback )) goto failed;
735     if (!write_data_blocks( fci, handle, status_callback )) goto failed;
736 
737     /* update the signature */
738     if (fci->seek( handle, 0, SEEK_SET, &err, fci->pv ) != 0 )
739     {
740         set_error( fci, FCIERR_CAB_FILE, err );
741         goto failed;
742     }
743     memcpy( cfheader->signature, "MSCF", 4 );
744     if (fci->write( handle, cfheader->signature, 4, &err, fci->pv ) != 4)
745     {
746         set_error( fci, FCIERR_CAB_FILE, err );
747         goto failed;
748     }
749     fci->close( handle, &err, fci->pv );
750 
751     reset_cabinet( fci );
752     status_callback( statusCabinet, fci->estimatedCabinetSize, total_size, fci->pv );
753     return TRUE;
754 
755 failed:
756     fci->close( handle, &err, fci->pv );
757     fci->delete( filename, &err, fci->pv );
758     return FALSE;
759 }
760 
761 /* add all pending data blocks folder */
762 static BOOL add_data_to_folder( FCI_Int *fci, struct folder *folder, cab_ULONG *payload,
763                                 PFNFCISTATUS status_callback )
764 {
765     struct data_block *block, *new, *next;
766     BOOL split_block = FALSE;
767     cab_ULONG current_size, start_pos = 0;
768 
769     *payload = 0;
770     current_size = get_header_size( fci ) + fci->folders_size +
771                    fci->files_size + fci->placed_files_size + fci->folders_data_size;
772 
773     /* move the temp file into the folder structure */
774     folder->data = fci->data;
775     fci->data.handle = -1;
776     fci->pending_data_size = 0;
777 
778     LIST_FOR_EACH_ENTRY_SAFE( block, next, &fci->blocks_list, struct data_block, entry )
779     {
780         /* No more CFDATA fits into the cabinet under construction */
781         /* So don't try to store more data into it */
782         if (fci->fNextCab && (fci->ccab.cb <= sizeof(CFDATA) + fci->ccab.cbReserveCFData +
783                               current_size + sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder))
784             break;
785 
786         if (!(new = fci->alloc( sizeof(*new) )))
787         {
788             set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
789             return FALSE;
790         }
791         /* Is cabinet with new CFDATA too large? Then data block has to be split */
792         if( fci->fNextCab &&
793             (fci->ccab.cb < sizeof(CFDATA) + fci->ccab.cbReserveCFData +
794              block->compressed + current_size + sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder))
795         {
796             /* Modify the size of the compressed data to store only a part of the */
797             /* data block into the current cabinet. This is done to prevent */
798             /* that the maximum cabinet size will be exceeded. The remainder */
799             /* will be stored into the next following cabinet. */
800 
801             new->compressed = fci->ccab.cb - (sizeof(CFDATA) + fci->ccab.cbReserveCFData + current_size +
802                                               sizeof(CFFOLDER) + fci->ccab.cbReserveCFFolder );
803             new->uncompressed = 0; /* on split blocks of data this is zero */
804             block->compressed -= new->compressed;
805             split_block = TRUE;
806         }
807         else
808         {
809             new->compressed   = block->compressed;
810             new->uncompressed = block->uncompressed;
811         }
812 
813         start_pos += new->compressed;
814         current_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + new->compressed;
815         fci->folders_data_size += sizeof(CFDATA) + fci->ccab.cbReserveCFData + new->compressed;
816         fci->statusFolderCopied += new->compressed;
817         (*payload) += new->uncompressed;
818 
819         list_add_tail( &folder->blocks_list, &new->entry );
820         folder->data_count++;
821 
822         /* report status with pfnfcis about copied size of folder */
823         if (status_callback( statusFolder, fci->statusFolderCopied,
824                              fci->statusFolderTotal, fci->pv ) == -1)
825         {
826             set_error( fci, FCIERR_USER_ABORT, 0 );
827             return FALSE;
828         }
829         if (split_block) break;
830         free_data_block( fci, block );
831         fci->cDataBlocks--;
832     }
833 
834     if (list_empty( &fci->blocks_list )) return TRUE;
835     return copy_data_blocks( fci, folder->data.handle, start_pos, &fci->data, status_callback );
836 }
837 
838 /* add all pending files to folder */
839 static BOOL add_files_to_folder( FCI_Int *fci, struct folder *folder, cab_ULONG payload )
840 {
841     cab_ULONG sizeOfFiles = 0, sizeOfFilesPrev;
842     cab_ULONG cbFileRemainer = 0;
843     struct file *file, *next;
844 
845     LIST_FOR_EACH_ENTRY_SAFE( file, next, &fci->files_list, struct file, entry )
846     {
847         cab_ULONG size = sizeof(CFFILE) + strlen(file->name) + 1;
848 
849         /* fnfilfnfildest: placed file on cabinet */
850         fci->fileplaced( &fci->ccab, file->name, file->size,
851                          (file->folder == cffileCONTINUED_FROM_PREV), fci->pv );
852 
853         sizeOfFilesPrev = sizeOfFiles;
854         /* set complete size of all processed files */
855         if (file->folder == cffileCONTINUED_FROM_PREV && fci->cbFileRemainer != 0)
856         {
857             sizeOfFiles += fci->cbFileRemainer;
858             fci->cbFileRemainer = 0;
859         }
860         else sizeOfFiles += file->size;
861 
862         /* check if spanned file fits into this cabinet folder */
863         if (sizeOfFiles > payload)
864         {
865             if (file->folder == cffileCONTINUED_FROM_PREV)
866                 file->folder = cffileCONTINUED_PREV_AND_NEXT;
867             else
868                 file->folder = cffileCONTINUED_TO_NEXT;
869         }
870 
871         list_remove( &file->entry );
872         list_add_tail( &folder->files_list, &file->entry );
873         fci->placed_files_size += size;
874         fci->cFiles++;
875 
876         /* This is only true for files which will be written into the */
877         /* next cabinet of the spanning folder */
878         if (sizeOfFiles > payload)
879         {
880             /* add a copy back onto the list */
881             if (!(file = copy_file( fci, file ))) return FALSE;
882             list_add_before( &next->entry, &file->entry );
883 
884             /* Files which data will be partially written into the current cabinet */
885             if (file->folder == cffileCONTINUED_PREV_AND_NEXT || file->folder == cffileCONTINUED_TO_NEXT)
886             {
887                 if (sizeOfFilesPrev <= payload)
888                 {
889                     /* The size of the uncompressed, data of a spanning file in a */
890                     /* spanning data */
891                     cbFileRemainer = sizeOfFiles - payload;
892                 }
893                 file->folder = cffileCONTINUED_FROM_PREV;
894             }
895             else file->folder = 0;
896         }
897         else
898         {
899             fci->files_size -= size;
900         }
901     }
902     fci->cbFileRemainer = cbFileRemainer;
903     return TRUE;
904 }
905 
906 static cab_UWORD compress_NONE( FCI_Int *fci )
907 {
908     memcpy( fci->data_out, fci->data_in, fci->cdata_in );
909     return fci->cdata_in;
910 }
911 
912 #ifdef HAVE_ZLIB
913 
914 static void *zalloc( void *opaque, unsigned int items, unsigned int size )
915 {
916     FCI_Int *fci = opaque;
917     return fci->alloc( items * size );
918 }
919 
920 static void zfree( void *opaque, void *ptr )
921 {
922     FCI_Int *fci = opaque;
923     fci->free( ptr );
924 }
925 
926 static cab_UWORD compress_MSZIP( FCI_Int *fci )
927 {
928     z_stream stream;
929 
930     stream.zalloc = zalloc;
931     stream.zfree  = zfree;
932     stream.opaque = fci;
933     if (deflateInit2( &stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY ) != Z_OK)
934     {
935         set_error( fci, FCIERR_ALLOC_FAIL, ERROR_NOT_ENOUGH_MEMORY );
936         return 0;
937     }
938     stream.next_in   = fci->data_in;
939     stream.avail_in  = fci->cdata_in;
940     stream.next_out  = fci->data_out + 2;
941     stream.avail_out = sizeof(fci->data_out) - 2;
942     /* insert the signature */
943     fci->data_out[0] = 'C';
944     fci->data_out[1] = 'K';
945     deflate( &stream, Z_FINISH );
946     deflateEnd( &stream );
947     return stream.total_out + 2;
948 }
949 
950 #endif  /* HAVE_ZLIB */
951 
952 
953 /***********************************************************************
954  *		FCICreate (CABINET.10)
955  *
956  * FCICreate is provided with several callbacks and
957  * returns a handle which can be used to create cabinet files.
958  *
959  * PARAMS
960  *   perf       [IO]  A pointer to an ERF structure.  When FCICreate
961  *                    returns an error condition, error information may
962  *                    be found here as well as from GetLastError.
963  *   pfnfiledest [I]  A pointer to a function which is called when a file
964  *                    is placed. Only useful for subsequent cabinet files.
965  *   pfnalloc    [I]  A pointer to a function which allocates ram.  Uses
966  *                    the same interface as malloc.
967  *   pfnfree     [I]  A pointer to a function which frees ram.  Uses the
968  *                    same interface as free.
969  *   pfnopen     [I]  A pointer to a function which opens a file.  Uses
970  *                    the same interface as _open.
971  *   pfnread     [I]  A pointer to a function which reads from a file into
972  *                    a caller-provided buffer.  Uses the same interface
973  *                    as _read.
974  *   pfnwrite    [I]  A pointer to a function which writes to a file from
975  *                    a caller-provided buffer.  Uses the same interface
976  *                    as _write.
977  *   pfnclose    [I]  A pointer to a function which closes a file handle.
978  *                    Uses the same interface as _close.
979  *   pfnseek     [I]  A pointer to a function which seeks in a file.
980  *                    Uses the same interface as _lseek.
981  *   pfndelete   [I]  A pointer to a function which deletes a file.
982  *   pfnfcigtf   [I]  A pointer to a function which gets the name of a
983  *                    temporary file.
984  *   pccab       [I]  A pointer to an initialized CCAB structure.
985  *   pv          [I]  A pointer to an application-defined notification
986  *                    function which will be passed to other FCI functions
987  *                    as a parameter.
988  *
989  * RETURNS
990  *   On success, returns an FCI handle of type HFCI.
991  *   On failure, the NULL file handle is returned. Error
992  *   info can be retrieved from perf.
993  *
994  * INCLUDES
995  *   fci.h
996  *
997  */
998 HFCI __cdecl FCICreate(
999 	PERF perf,
1000 	PFNFCIFILEPLACED   pfnfiledest,
1001 	PFNFCIALLOC        pfnalloc,
1002 	PFNFCIFREE         pfnfree,
1003 	PFNFCIOPEN         pfnopen,
1004 	PFNFCIREAD         pfnread,
1005 	PFNFCIWRITE        pfnwrite,
1006 	PFNFCICLOSE        pfnclose,
1007 	PFNFCISEEK         pfnseek,
1008 	PFNFCIDELETE       pfndelete,
1009 	PFNFCIGETTEMPFILE  pfnfcigtf,
1010 	PCCAB              pccab,
1011 	void *pv)
1012 {
1013   FCI_Int *p_fci_internal;
1014 
1015   if (!perf) {
1016     SetLastError(ERROR_BAD_ARGUMENTS);
1017     return NULL;
1018   }
1019   if ((!pfnalloc) || (!pfnfree) || (!pfnopen) || (!pfnread) ||
1020       (!pfnwrite) || (!pfnclose) || (!pfnseek) || (!pfndelete) ||
1021       (!pfnfcigtf) || (!pccab)) {
1022     perf->erfOper = FCIERR_NONE;
1023     perf->erfType = ERROR_BAD_ARGUMENTS;
1024     perf->fError = TRUE;
1025 
1026     SetLastError(ERROR_BAD_ARGUMENTS);
1027     return NULL;
1028   }
1029 
1030   if (!((p_fci_internal = pfnalloc(sizeof(FCI_Int))))) {
1031     perf->erfOper = FCIERR_ALLOC_FAIL;
1032     perf->erfType = ERROR_NOT_ENOUGH_MEMORY;
1033     perf->fError = TRUE;
1034 
1035     SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1036     return NULL;
1037   }
1038 
1039   memset(p_fci_internal, 0, sizeof(FCI_Int));
1040   p_fci_internal->magic = FCI_INT_MAGIC;
1041   p_fci_internal->perf = perf;
1042   p_fci_internal->fileplaced = pfnfiledest;
1043   p_fci_internal->alloc = pfnalloc;
1044   p_fci_internal->free = pfnfree;
1045   p_fci_internal->open = pfnopen;
1046   p_fci_internal->read = pfnread;
1047   p_fci_internal->write = pfnwrite;
1048   p_fci_internal->close = pfnclose;
1049   p_fci_internal->seek = pfnseek;
1050   p_fci_internal->delete = pfndelete;
1051   p_fci_internal->gettemp = pfnfcigtf;
1052   p_fci_internal->ccab = *pccab;
1053   p_fci_internal->pccab = pccab;
1054   p_fci_internal->pv = pv;
1055   p_fci_internal->data.handle = -1;
1056   p_fci_internal->compress = compress_NONE;
1057 
1058   list_init( &p_fci_internal->folders_list );
1059   list_init( &p_fci_internal->files_list );
1060   list_init( &p_fci_internal->blocks_list );
1061 
1062   memcpy(p_fci_internal->szPrevCab, pccab->szCab, CB_MAX_CABINET_NAME);
1063   memcpy(p_fci_internal->szPrevDisk, pccab->szDisk, CB_MAX_DISK_NAME);
1064 
1065   return (HFCI)p_fci_internal;
1066 }
1067 
1068 
1069 
1070 
1071 static BOOL fci_flush_folder( FCI_Int *p_fci_internal,
1072 	BOOL                  fGetNextCab,
1073 	PFNFCIGETNEXTCABINET  pfnfcignc,
1074 	PFNFCISTATUS          pfnfcis)
1075 {
1076   cab_ULONG payload;
1077   cab_ULONG read_result;
1078   struct folder *folder;
1079 
1080   if ((!pfnfcignc) || (!pfnfcis)) {
1081     set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS );
1082     return FALSE;
1083   }
1084 
1085   if( p_fci_internal->fGetNextCabInVain &&
1086       p_fci_internal->fNextCab ){
1087     /* internal error */
1088     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1089     return FALSE;
1090   }
1091 
1092   /* If there was no FCIAddFile or FCIFlushFolder has already been called */
1093   /* this function will return TRUE */
1094   if( p_fci_internal->files_size == 0 ) {
1095     if ( p_fci_internal->pending_data_size != 0 ) {
1096       /* error handling */
1097       set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1098       return FALSE;
1099     }
1100     return TRUE;
1101   }
1102 
1103   /* FCIFlushFolder has already been called... */
1104   if (p_fci_internal->fSplitFolder && p_fci_internal->placed_files_size!=0) {
1105     return TRUE;
1106   }
1107 
1108   /* This can be set already, because it makes only a difference */
1109   /* when the current function exits with return FALSE */
1110   p_fci_internal->fSplitFolder=FALSE;
1111 
1112   /* START of COPY */
1113   if (!add_data_block( p_fci_internal, pfnfcis )) return FALSE;
1114 
1115   /* reset to get the number of data blocks of this folder which are */
1116   /* actually in this cabinet ( at least partially ) */
1117   p_fci_internal->cDataBlocks=0;
1118 
1119   p_fci_internal->statusFolderTotal = get_header_size( p_fci_internal ) +
1120       sizeof(CFFOLDER) + p_fci_internal->ccab.cbReserveCFFolder +
1121       p_fci_internal->placed_files_size+
1122       p_fci_internal->folders_data_size + p_fci_internal->files_size+
1123       p_fci_internal->pending_data_size + p_fci_internal->folders_size;
1124   p_fci_internal->statusFolderCopied = 0;
1125 
1126   /* report status with pfnfcis about copied size of folder */
1127   if( (*pfnfcis)(statusFolder, p_fci_internal->statusFolderCopied,
1128       p_fci_internal->statusFolderTotal, /* TODO total folder size */
1129       p_fci_internal->pv) == -1) {
1130     set_error( p_fci_internal, FCIERR_USER_ABORT, 0 );
1131     return FALSE;
1132   }
1133 
1134   /* USE the variable read_result */
1135   read_result = get_header_size( p_fci_internal ) + p_fci_internal->folders_data_size +
1136       p_fci_internal->placed_files_size + p_fci_internal->folders_size;
1137 
1138   if(p_fci_internal->files_size!=0) {
1139     read_result+= sizeof(CFFOLDER)+p_fci_internal->ccab.cbReserveCFFolder;
1140   }
1141 
1142   /* Check if multiple cabinets have to be created. */
1143 
1144   /* Might be too much data for the maximum allowed cabinet size.*/
1145   /* When any further data will be added later, it might not */
1146   /* be possible to flush the cabinet, because there might */
1147   /* not be enough space to store the name of the following */
1148   /* cabinet and name of the corresponding disk. */
1149   /* So take care of this and get the name of the next cabinet */
1150   if( p_fci_internal->fGetNextCabInVain==FALSE &&
1151       p_fci_internal->fNextCab==FALSE &&
1152       (
1153         (
1154           p_fci_internal->ccab.cb < read_result +
1155           p_fci_internal->pending_data_size +
1156           p_fci_internal->files_size +
1157           CB_MAX_CABINET_NAME +   /* next cabinet name */
1158           CB_MAX_DISK_NAME        /* next disk name */
1159         ) || fGetNextCab
1160       )
1161   ) {
1162     /* increment cabinet index */
1163     ++(p_fci_internal->pccab->iCab);
1164     /* get name of next cabinet */
1165     p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
1166     if (!(*pfnfcignc)(p_fci_internal->pccab,
1167         p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1168         p_fci_internal->pv)) {
1169       set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1170       return FALSE;
1171     }
1172 
1173     /* Skip a few lines of code. This is caught by the next if. */
1174     p_fci_internal->fGetNextCabInVain=TRUE;
1175   }
1176 
1177   /* too much data for cabinet */
1178   if( (p_fci_internal->fGetNextCabInVain ||
1179         p_fci_internal->fNextCab ) &&
1180       (
1181         (
1182           p_fci_internal->ccab.cb < read_result +
1183           p_fci_internal->pending_data_size +
1184           p_fci_internal->files_size +
1185           strlen(p_fci_internal->pccab->szCab)+1 +   /* next cabinet name */
1186           strlen(p_fci_internal->pccab->szDisk)+1    /* next disk name */
1187         ) || fGetNextCab
1188       )
1189   ) {
1190     p_fci_internal->fGetNextCabInVain=FALSE;
1191     p_fci_internal->fNextCab=TRUE;
1192 
1193     /* return FALSE if there is not enough space left*/
1194     /* this should never happen */
1195     if (p_fci_internal->ccab.cb <=
1196         p_fci_internal->files_size +
1197         read_result +
1198         strlen(p_fci_internal->pccab->szCab)+1 + /* next cabinet name */
1199         strlen(p_fci_internal->pccab->szDisk)+1  /* next disk name */
1200     ) {
1201 
1202       return FALSE;
1203     }
1204 
1205     /* the folder will be split across cabinets */
1206     p_fci_internal->fSplitFolder=TRUE;
1207 
1208   } else {
1209     /* this should never happen */
1210     if (p_fci_internal->fNextCab) {
1211       /* internal error */
1212       set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1213       return FALSE;
1214     }
1215   }
1216 
1217   if (!(folder = add_folder( p_fci_internal ))) return FALSE;
1218   if (!add_data_to_folder( p_fci_internal, folder, &payload, pfnfcis )) return FALSE;
1219   if (!add_files_to_folder( p_fci_internal, folder, payload )) return FALSE;
1220 
1221   /* reset CFFolder specific information */
1222   p_fci_internal->cDataBlocks=0;
1223   p_fci_internal->cCompressedBytesInFolder=0;
1224 
1225   return TRUE;
1226 }
1227 
1228 
1229 
1230 
1231 static BOOL fci_flush_cabinet( FCI_Int *p_fci_internal,
1232 	BOOL                  fGetNextCab,
1233 	PFNFCIGETNEXTCABINET  pfnfcignc,
1234 	PFNFCISTATUS          pfnfcis)
1235 {
1236   cab_ULONG read_result=0;
1237   BOOL returntrue=FALSE;
1238 
1239   /* TODO test if fci_flush_cabinet really aborts if there was no FCIAddFile */
1240 
1241   /* when FCIFlushCabinet was or FCIAddFile hasn't been called */
1242   if( p_fci_internal->files_size==0 && fGetNextCab ) {
1243     returntrue=TRUE;
1244   }
1245 
1246   if (!fci_flush_folder(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)){
1247     /* TODO set error */
1248     return FALSE;
1249   }
1250 
1251   if(returntrue) return TRUE;
1252 
1253   if ( (p_fci_internal->fSplitFolder && p_fci_internal->fNextCab==FALSE)||
1254        (p_fci_internal->folders_size==0 &&
1255          (p_fci_internal->files_size!=0 ||
1256           p_fci_internal->placed_files_size!=0 )
1257      ) )
1258   {
1259       /* error */
1260       set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1261       return FALSE;
1262   }
1263 
1264   /* create the cabinet */
1265   if (!write_cabinet( p_fci_internal, pfnfcis )) return FALSE;
1266 
1267   p_fci_internal->fPrevCab=TRUE;
1268   /* The sections szPrevCab and szPrevDisk are not being updated, because */
1269   /* MS CABINET.DLL always puts the first cabinet name and disk into them */
1270 
1271   if (p_fci_internal->fNextCab) {
1272     p_fci_internal->fNextCab=FALSE;
1273 
1274     if (p_fci_internal->files_size==0 && p_fci_internal->pending_data_size!=0) {
1275       /* THIS CAN NEVER HAPPEN */
1276       /* set error code */
1277       set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1278       return FALSE;
1279     }
1280 
1281     if( p_fci_internal->fNewPrevious ) {
1282       memcpy(p_fci_internal->szPrevCab, p_fci_internal->ccab.szCab,
1283         CB_MAX_CABINET_NAME);
1284       memcpy(p_fci_internal->szPrevDisk, p_fci_internal->ccab.szDisk,
1285         CB_MAX_DISK_NAME);
1286       p_fci_internal->fNewPrevious=FALSE;
1287     }
1288     p_fci_internal->ccab = *p_fci_internal->pccab;
1289 
1290     /* REUSE the variable read_result */
1291     read_result=get_header_size( p_fci_internal );
1292     if(p_fci_internal->files_size!=0) {
1293         read_result+=p_fci_internal->ccab.cbReserveCFFolder;
1294     }
1295     read_result+= p_fci_internal->pending_data_size +
1296       p_fci_internal->files_size + p_fci_internal->folders_data_size +
1297       p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1298       sizeof(CFFOLDER); /* set size of new CFFolder entry */
1299 
1300     /* too much data for the maximum size of a cabinet */
1301     if( p_fci_internal->fGetNextCabInVain==FALSE &&
1302         p_fci_internal->ccab.cb < read_result ) {
1303       return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1304     }
1305 
1306     /* Might be too much data for the maximum size of a cabinet.*/
1307     /* When any further data will be added later, it might not */
1308     /* be possible to flush the cabinet, because there might */
1309     /* not be enough space to store the name of the following */
1310     /* cabinet and name of the corresponding disk. */
1311     /* So take care of this and get the name of the next cabinet */
1312     if (p_fci_internal->fGetNextCabInVain==FALSE && (
1313       p_fci_internal->ccab.cb < read_result +
1314       CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
1315     )) {
1316       /* increment cabinet index */
1317       ++(p_fci_internal->pccab->iCab);
1318       /* get name of next cabinet */
1319       p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
1320       if (!(*pfnfcignc)(p_fci_internal->pccab,
1321           p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1322           p_fci_internal->pv)) {
1323         /* error handling */
1324         set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1325         return FALSE;
1326       }
1327       /* Skip a few lines of code. This is caught by the next if. */
1328       p_fci_internal->fGetNextCabInVain=TRUE;
1329     }
1330 
1331     /* too much data for cabinet */
1332     if (p_fci_internal->fGetNextCabInVain && (
1333         p_fci_internal->ccab.cb < read_result +
1334         strlen(p_fci_internal->ccab.szCab)+1+
1335         strlen(p_fci_internal->ccab.szDisk)+1
1336     )) {
1337       p_fci_internal->fGetNextCabInVain=FALSE;
1338       p_fci_internal->fNextCab=TRUE;
1339       return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1340     }
1341 
1342     /* if the FolderThreshold has been reached flush the folder automatically */
1343     if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh)
1344         return fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1345 
1346     if( p_fci_internal->files_size>0 ) {
1347       if( !fci_flush_folder(p_fci_internal, FALSE, pfnfcignc, pfnfcis) ) return FALSE;
1348       p_fci_internal->fNewPrevious=TRUE;
1349     }
1350   } else {
1351     p_fci_internal->fNewPrevious=FALSE;
1352     if( p_fci_internal->files_size>0 || p_fci_internal->pending_data_size) {
1353       /* THIS MAY NEVER HAPPEN */
1354       /* set error structures */
1355       set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1356       return FALSE;
1357     }
1358   }
1359 
1360   return TRUE;
1361 } /* end of fci_flush_cabinet */
1362 
1363 
1364 
1365 
1366 
1367 /***********************************************************************
1368  *		FCIAddFile (CABINET.11)
1369  *
1370  * FCIAddFile adds a file to the to be created cabinet file
1371  *
1372  * PARAMS
1373  *   hfci          [I]  An HFCI from FCICreate
1374  *   pszSourceFile [I]  A pointer to a C string which contains the name and
1375  *                      location of the file which will be added to the cabinet
1376  *   pszFileName   [I]  A pointer to a C string which contains the name under
1377  *                      which the file will be stored in the cabinet
1378  *   fExecute      [I]  A boolean value which indicates if the file should be
1379  *                      executed after extraction of self extracting
1380  *                      executables
1381  *   pfnfcignc     [I]  A pointer to a function which gets information about
1382  *                      the next cabinet
1383  *   pfnfcis      [IO]  A pointer to a function which will report status
1384  *                      information about the compression process
1385  *   pfnfcioi      [I]  A pointer to a function which reports file attributes
1386  *                      and time and date information
1387  *   typeCompress  [I]  Compression type
1388  *
1389  * RETURNS
1390  *   On success, returns TRUE
1391  *   On failure, returns FALSE
1392  *
1393  * INCLUDES
1394  *   fci.h
1395  *
1396  */
1397 BOOL __cdecl FCIAddFile(
1398 	HFCI                  hfci,
1399 	char                 *pszSourceFile,
1400 	char                 *pszFileName,
1401 	BOOL                  fExecute,
1402 	PFNFCIGETNEXTCABINET  pfnfcignc,
1403 	PFNFCISTATUS          pfnfcis,
1404 	PFNFCIGETOPENINFO     pfnfcigoi,
1405 	TCOMP                 typeCompress)
1406 {
1407   cab_ULONG read_result;
1408   FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1409 
1410   if (!p_fci_internal) return FALSE;
1411 
1412   if ((!pszSourceFile) || (!pszFileName) || (!pfnfcignc) || (!pfnfcis) ||
1413       (!pfnfcigoi) || strlen(pszFileName)>=CB_MAX_FILENAME) {
1414     set_error( p_fci_internal, FCIERR_NONE, ERROR_BAD_ARGUMENTS );
1415     return FALSE;
1416   }
1417 
1418   if (typeCompress != p_fci_internal->compression)
1419   {
1420       if (!FCIFlushFolder( hfci, pfnfcignc, pfnfcis )) return FALSE;
1421       switch (typeCompress)
1422       {
1423       case tcompTYPE_MSZIP:
1424 #ifdef HAVE_ZLIB
1425           p_fci_internal->compression = tcompTYPE_MSZIP;
1426           p_fci_internal->compress    = compress_MSZIP;
1427           break;
1428 #endif
1429       default:
1430           FIXME( "compression %x not supported, defaulting to none\n", typeCompress );
1431           /* fall through */
1432       case tcompTYPE_NONE:
1433           p_fci_internal->compression = tcompTYPE_NONE;
1434           p_fci_internal->compress    = compress_NONE;
1435           break;
1436       }
1437   }
1438 
1439   /* TODO check if pszSourceFile??? */
1440 
1441   if(p_fci_internal->fGetNextCabInVain && p_fci_internal->fNextCab) {
1442     /* internal error */
1443     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1444     return FALSE;
1445   }
1446 
1447   if(p_fci_internal->fNextCab) {
1448     /* internal error */
1449     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1450     return FALSE;
1451   }
1452 
1453   /* REUSE the variable read_result */
1454   read_result=get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder;
1455 
1456   read_result+= sizeof(CFFILE) + strlen(pszFileName)+1 +
1457     p_fci_internal->files_size + p_fci_internal->folders_data_size +
1458     p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1459     sizeof(CFFOLDER); /* size of new CFFolder entry */
1460 
1461   /* Might be too much data for the maximum size of a cabinet.*/
1462   /* When any further data will be added later, it might not */
1463   /* be possible to flush the cabinet, because there might */
1464   /* not be enough space to store the name of the following */
1465   /* cabinet and name of the corresponding disk. */
1466   /* So take care of this and get the name of the next cabinet */
1467   if( p_fci_internal->fGetNextCabInVain==FALSE &&
1468       p_fci_internal->fNextCab==FALSE &&
1469       ( p_fci_internal->ccab.cb < read_result +
1470         CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
1471       )
1472   ) {
1473     /* increment cabinet index */
1474     ++(p_fci_internal->pccab->iCab);
1475     /* get name of next cabinet */
1476     p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
1477     if (!(*pfnfcignc)(p_fci_internal->pccab,
1478         p_fci_internal->estimatedCabinetSize, /* estimated size of cab */
1479         p_fci_internal->pv)) {
1480       /* error handling */
1481       set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1482       return FALSE;
1483     }
1484     /* Skip a few lines of code. This is caught by the next if. */
1485     p_fci_internal->fGetNextCabInVain=TRUE;
1486   }
1487 
1488   if( p_fci_internal->fGetNextCabInVain &&
1489       p_fci_internal->fNextCab
1490   ) {
1491     /* THIS CAN NEVER HAPPEN */
1492     /* set error code */
1493     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1494     return FALSE;
1495   }
1496 
1497   /* too much data for cabinet */
1498   if( p_fci_internal->fGetNextCabInVain &&
1499      (
1500       p_fci_internal->ccab.cb < read_result +
1501       strlen(p_fci_internal->pccab->szCab)+1+
1502       strlen(p_fci_internal->pccab->szDisk)+1
1503   )) {
1504     p_fci_internal->fGetNextCabInVain=FALSE;
1505     p_fci_internal->fNextCab=TRUE;
1506     if(!fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis)) return FALSE;
1507   }
1508 
1509   if( p_fci_internal->fNextCab ) {
1510     /* THIS MAY NEVER HAPPEN */
1511     /* set error code */
1512     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1513     return FALSE;
1514   }
1515 
1516   if (!add_file_data( p_fci_internal, pszSourceFile, pszFileName, fExecute, pfnfcigoi, pfnfcis ))
1517       return FALSE;
1518 
1519   /* REUSE the variable read_result */
1520   read_result = get_header_size( p_fci_internal ) + p_fci_internal->ccab.cbReserveCFFolder;
1521   read_result+= p_fci_internal->pending_data_size +
1522     p_fci_internal->files_size + p_fci_internal->folders_data_size +
1523     p_fci_internal->placed_files_size + p_fci_internal->folders_size +
1524     sizeof(CFFOLDER); /* set size of new CFFolder entry */
1525 
1526   /* too much data for the maximum size of a cabinet */
1527   /* (ignoring the unflushed data block) */
1528   if( p_fci_internal->fGetNextCabInVain==FALSE &&
1529       p_fci_internal->fNextCab==FALSE && /* this is always the case */
1530       p_fci_internal->ccab.cb < read_result ) {
1531     return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1532   }
1533 
1534   /* Might be too much data for the maximum size of a cabinet.*/
1535   /* When any further data will be added later, it might not */
1536   /* be possible to flush the cabinet, because there might */
1537   /* not be enough space to store the name of the following */
1538   /* cabinet and name of the corresponding disk. */
1539   /* So take care of this and get the name of the next cabinet */
1540   /* (ignoring the unflushed data block) */
1541   if( p_fci_internal->fGetNextCabInVain==FALSE &&
1542       p_fci_internal->fNextCab==FALSE &&
1543       ( p_fci_internal->ccab.cb < read_result +
1544         CB_MAX_CABINET_NAME + CB_MAX_DISK_NAME
1545       )
1546   ) {
1547     /* increment cabinet index */
1548     ++(p_fci_internal->pccab->iCab);
1549     /* get name of next cabinet */
1550     p_fci_internal->estimatedCabinetSize=p_fci_internal->statusFolderTotal;
1551     if (!(*pfnfcignc)(p_fci_internal->pccab,
1552         p_fci_internal->estimatedCabinetSize,/* estimated size of cab */
1553         p_fci_internal->pv)) {
1554       /* error handling */
1555       set_error( p_fci_internal, FCIERR_NONE, ERROR_FUNCTION_FAILED );
1556       return FALSE;
1557     }
1558     /* Skip a few lines of code. This is caught by the next if. */
1559     p_fci_internal->fGetNextCabInVain=TRUE;
1560   }
1561 
1562   if( p_fci_internal->fGetNextCabInVain &&
1563       p_fci_internal->fNextCab
1564   ) {
1565     /* THIS CAN NEVER HAPPEN */
1566     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1567     return FALSE;
1568   }
1569 
1570   /* too much data for cabinet */
1571   if( (p_fci_internal->fGetNextCabInVain ||
1572       p_fci_internal->fNextCab) && (
1573       p_fci_internal->ccab.cb < read_result +
1574       strlen(p_fci_internal->pccab->szCab)+1+
1575       strlen(p_fci_internal->pccab->szDisk)+1
1576   )) {
1577 
1578     p_fci_internal->fGetNextCabInVain=FALSE;
1579     p_fci_internal->fNextCab=TRUE;
1580     return fci_flush_cabinet( p_fci_internal, FALSE, pfnfcignc, pfnfcis);
1581   }
1582 
1583   if( p_fci_internal->fNextCab ) {
1584     /* THIS MAY NEVER HAPPEN */
1585     /* set error code */
1586     set_error( p_fci_internal, FCIERR_NONE, ERROR_GEN_FAILURE );
1587     return FALSE;
1588   }
1589 
1590   /* if the FolderThreshold has been reached flush the folder automatically */
1591   if (p_fci_internal->cCompressedBytesInFolder >= p_fci_internal->ccab.cbFolderThresh)
1592       return FCIFlushFolder(hfci, pfnfcignc, pfnfcis);
1593 
1594   return TRUE;
1595 } /* end of FCIAddFile */
1596 
1597 
1598 
1599 
1600 
1601 /***********************************************************************
1602  *		FCIFlushFolder (CABINET.12)
1603  *
1604  * FCIFlushFolder completes the CFFolder structure under construction.
1605  *
1606  * All further data which is added by FCIAddFile will be associated to
1607  * the next CFFolder structure.
1608  *
1609  * FCIFlushFolder will be called by FCIAddFile automatically if the
1610  * threshold (stored in the member cbFolderThresh of the CCAB structure
1611  * pccab passed to FCICreate) is exceeded.
1612  *
1613  * FCIFlushFolder will be called by FCIFlushFolder automatically before
1614  * any data will be written into the cabinet file.
1615  *
1616  * PARAMS
1617  *   hfci          [I]  An HFCI from FCICreate
1618  *   pfnfcignc     [I]  A pointer to a function which gets information about
1619  *                      the next cabinet
1620  *   pfnfcis      [IO]  A pointer to a function which will report status
1621  *                      information about the compression process
1622  *
1623  * RETURNS
1624  *   On success, returns TRUE
1625  *   On failure, returns FALSE
1626  *
1627  * INCLUDES
1628  *   fci.h
1629  *
1630  */
1631 BOOL __cdecl FCIFlushFolder(
1632 	HFCI                  hfci,
1633 	PFNFCIGETNEXTCABINET  pfnfcignc,
1634 	PFNFCISTATUS          pfnfcis)
1635 {
1636     FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1637 
1638     if (!p_fci_internal) return FALSE;
1639     return fci_flush_folder(p_fci_internal,FALSE,pfnfcignc,pfnfcis);
1640 }
1641 
1642 
1643 
1644 /***********************************************************************
1645  *		FCIFlushCabinet (CABINET.13)
1646  *
1647  * FCIFlushCabinet stores the data which has been added by FCIAddFile
1648  * into the cabinet file. If the maximum cabinet size (stored in the
1649  * member cb of the CCAB structure pccab passed to FCICreate) has been
1650  * exceeded FCIFlushCabinet will be called automatic by FCIAddFile.
1651  * The remaining data still has to be flushed manually by calling
1652  * FCIFlushCabinet.
1653  *
1654  * After FCIFlushCabinet has been called (manually) FCIAddFile must
1655  * NOT be called again. Then hfci has to be released by FCIDestroy.
1656  *
1657  * PARAMS
1658  *   hfci          [I]  An HFCI from FCICreate
1659  *   fGetNextCab   [I]  Whether you want to add additional files to a
1660  *                      cabinet set (TRUE) or whether you want to
1661  *                      finalize it (FALSE)
1662  *   pfnfcignc     [I]  A pointer to a function which gets information about
1663  *                      the next cabinet
1664  *   pfnfcis      [IO]  A pointer to a function which will report status
1665  *                      information about the compression process
1666  *
1667  * RETURNS
1668  *   On success, returns TRUE
1669  *   On failure, returns FALSE
1670  *
1671  * INCLUDES
1672  *   fci.h
1673  *
1674  */
1675 BOOL __cdecl FCIFlushCabinet(
1676 	HFCI                  hfci,
1677 	BOOL                  fGetNextCab,
1678 	PFNFCIGETNEXTCABINET  pfnfcignc,
1679 	PFNFCISTATUS          pfnfcis)
1680 {
1681   FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1682 
1683   if (!p_fci_internal) return FALSE;
1684 
1685   if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE;
1686 
1687   while( p_fci_internal->files_size>0 ||
1688          p_fci_internal->placed_files_size>0 ) {
1689     if(!fci_flush_cabinet(p_fci_internal,fGetNextCab,pfnfcignc,pfnfcis)) return FALSE;
1690   }
1691 
1692   return TRUE;
1693 }
1694 
1695 
1696 /***********************************************************************
1697  *		FCIDestroy (CABINET.14)
1698  *
1699  * Frees a handle created by FCICreate.
1700  * Only reason for failure would be an invalid handle.
1701  *
1702  * PARAMS
1703  *   hfci [I] The HFCI to free
1704  *
1705  * RETURNS
1706  *   TRUE for success
1707  *   FALSE for failure
1708  */
1709 BOOL __cdecl FCIDestroy(HFCI hfci)
1710 {
1711     struct folder *folder, *folder_next;
1712     struct file *file, *file_next;
1713     struct data_block *block, *block_next;
1714     FCI_Int *p_fci_internal = get_fci_ptr( hfci );
1715 
1716     if (!p_fci_internal) return FALSE;
1717 
1718     /* before hfci can be removed all temporary files must be closed */
1719     /* and deleted */
1720     p_fci_internal->magic = 0;
1721 
1722     LIST_FOR_EACH_ENTRY_SAFE( folder, folder_next, &p_fci_internal->folders_list, struct folder, entry )
1723     {
1724         free_folder( p_fci_internal, folder );
1725     }
1726     LIST_FOR_EACH_ENTRY_SAFE( file, file_next, &p_fci_internal->files_list, struct file, entry )
1727     {
1728         free_file( p_fci_internal, file );
1729     }
1730     LIST_FOR_EACH_ENTRY_SAFE( block, block_next, &p_fci_internal->blocks_list, struct data_block, entry )
1731     {
1732         free_data_block( p_fci_internal, block );
1733     }
1734 
1735     close_temp_file( p_fci_internal, &p_fci_internal->data );
1736 
1737     /* hfci can now be removed */
1738     p_fci_internal->free(hfci);
1739     return TRUE;
1740 }
1741