xref: /reactos/sdk/tools/cabman/cabinet.h (revision a0f8b40d)
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS cabinet manager
4  * FILE:        tools/cabman/cabinet.h
5  * PURPOSE:     Cabinet definitions
6  */
7 
8 #pragma once
9 
10 #if defined(_WIN32)
11     #define WIN32_LEAN_AND_MEAN
12     #include <intrin.h>
13     #include <windows.h>
14 #else
15     #include <typedefs.h>
16     #include <unistd.h>
17 #endif
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <sys/types.h>
22 #include <time.h>
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <limits.h>
28 #include <string>
29 #include <list>
30 
31 #ifndef PATH_MAX
32 #define PATH_MAX MAX_PATH
33 #endif
34 
35 #if !defined(C_ASSERT)
36 #define C_ASSERT(expr) extern char (*c_assert(void)) [(expr) ? 1 : -1]
37 #endif
38 
39 #ifndef _countof
40 #define _countof(_Array) (sizeof(_Array) / sizeof(_Array[0]))
41 #endif
42 
43 #if defined(_WIN32)
44 #define DIR_SEPARATOR_CHAR '\\'
45 #define DIR_SEPARATOR_STRING "\\"
46 
47 #define snprintf _snprintf
48 #define strcasecmp _stricmp
49 #define strdup _strdup
50 #else
51 #define DIR_SEPARATOR_CHAR '/'
52 #define DIR_SEPARATOR_STRING "/"
53 #endif // _WIN32
54 
GetSizeOfFile(FILE * handle)55 inline LONG GetSizeOfFile(FILE* handle)
56 {
57     LONG size;
58     LONG currentPos = ftell(handle);
59 
60     if (fseek(handle, 0, SEEK_END) != 0)
61         return (LONG)-1;
62 
63     size = ftell(handle);
64     fseek(handle, 0, SEEK_SET);
65     return size;
66 }
67 
68 /* Debugging */
69 
70 #define NORMAL_MASK    0x000000FF
71 #define SPECIAL_MASK   0xFFFFFF00
72 #define MIN_TRACE      0x00000001
73 #define MID_TRACE      0x00000002
74 #define MAX_TRACE      0x00000003
75 
76 #define DEBUG_MEMORY   0x00000100
77 
78 #if DBG
79 
80 extern ULONG DebugTraceLevel;
81 
82 #undef DPRINT
83 #define DPRINT(_t_, _x_) \
84     if ((_t_ > NORMAL_MASK) \
85         ? (DebugTraceLevel & _t_) > NORMAL_MASK \
86         : (DebugTraceLevel & NORMAL_MASK) >= _t_) { \
87         printf("(%s:%d)(%s) ", __FILE__, __LINE__, __FUNCTION__); \
88         printf _x_ ; \
89     }
90 
91 #undef ASSERT
92 #define ASSERT(_b_) { \
93     if (!(_b_)) { \
94         printf("(%s:%d)(%s) ASSERTION: ", __FILE__, __LINE__, __FUNCTION__); \
95         printf(#_b_); \
96         exit(0); \
97     } \
98 }
99 
100 #else /* DBG */
101 
102 #undef DPRINT
103 #define DPRINT(_t_, _x_) do { } while(0)
104 
105 #undef ASSERT
106 #define ASSERT(_x_)
107 
108 #endif /* DBG */
109 
110 
111 /* Cabinet constants */
112 
113 #define CAB_SIGNATURE        0x4643534D // "MSCF"
114 #define CAB_VERSION          0x0103
115 #define CAB_BLOCKSIZE        32768
116 
117 #define CAB_COMP_MASK        0x00FF
118 #define CAB_COMP_NONE        0x0000
119 #define CAB_COMP_MSZIP       0x0001
120 #define CAB_COMP_QUANTUM     0x0002
121 #define CAB_COMP_LZX         0x0003
122 
123 #define CAB_FLAG_HASPREV     0x0001
124 #define CAB_FLAG_HASNEXT     0x0002
125 #define CAB_FLAG_RESERVE     0x0004
126 
127 #define CAB_ATTRIB_READONLY  0x0001
128 #define CAB_ATTRIB_HIDDEN    0x0002
129 #define CAB_ATTRIB_SYSTEM    0x0004
130 #define CAB_ATTRIB_VOLUME    0x0008
131 #define CAB_ATTRIB_DIRECTORY 0x0010
132 #define CAB_ATTRIB_ARCHIVE   0x0020
133 #define CAB_ATTRIB_EXECUTE   0x0040
134 #define CAB_ATTRIB_UTF_NAME  0x0080
135 
136 #define CAB_FILE_MAX_FOLDER  0xFFFC
137 #define CAB_FILE_CONTINUED   0xFFFD
138 #define CAB_FILE_SPLIT       0xFFFE
139 #define CAB_FILE_PREV_NEXT   0xFFFF
140 
141 
142 /* Cabinet structures */
143 
144 typedef struct _CFHEADER
145 {
146     ULONG Signature;        // File signature 'MSCF' (CAB_SIGNATURE)
147     ULONG Reserved1;        // Reserved field
148     ULONG CabinetSize;      // Cabinet file size
149     ULONG Reserved2;        // Reserved field
150     ULONG FileTableOffset;  // Offset of first CFFILE
151     ULONG Reserved3;        // Reserved field
152     USHORT Version;          // Cabinet version (CAB_VERSION)
153     USHORT FolderCount;      // Number of folders
154     USHORT FileCount;        // Number of files
155     USHORT Flags;            // Cabinet flags (CAB_FLAG_*)
156     USHORT SetID;            // Cabinet set id
157     USHORT CabinetNumber;    // Zero-based cabinet number
158 /* Optional fields (depends on Flags)
159     USHORT CabinetResSize    // Per-cabinet reserved area size
160     char     FolderResSize     // Per-folder reserved area size
161     char     FileResSize       // Per-file reserved area size
162     char     CabinetReserved[] // Per-cabinet reserved area
163     char     CabinetPrev[]     // Name of previous cabinet file
164     char     DiskPrev[]        // Name of previous disk
165     char     CabinetNext[]     // Name of next cabinet file
166     char     DiskNext[]        // Name of next disk
167  */
168 } CFHEADER, *PCFHEADER;
169 
170 
171 typedef struct _CFFOLDER
172 {
173     ULONG DataOffset;       // Absolute offset of first CFDATA block in this folder
174     USHORT DataBlockCount;   // Number of CFDATA blocks in this folder in this cabinet
175     USHORT CompressionType;  // Type of compression used for all CFDATA blocks in this folder
176 /* Optional fields (depends on Flags)
177     char     FolderReserved[]  // Per-folder reserved area
178  */
179 } CFFOLDER, *PCFFOLDER;
180 
181 C_ASSERT(sizeof(CFFOLDER) == 8);
182 
183 
184 typedef struct _CFFILE
185 {
186     ULONG FileSize;         // Uncompressed file size in bytes
187     ULONG FileOffset;       // Uncompressed offset of file in the folder
188     USHORT FileControlID;    // File control ID (CAB_FILE_*)
189     USHORT FileDate;         // File date stamp, as used by DOS
190     USHORT FileTime;         // File time stamp, as used by DOS
191     USHORT Attributes;       // File attributes (CAB_ATTRIB_*)
192     /* After this is the NULL terminated filename */
193 } CFFILE, *PCFFILE;
194 
195 C_ASSERT(sizeof(CFFILE) == 16);
196 
197 typedef struct _CFDATA
198 {
199     ULONG Checksum;         // Checksum of CFDATA entry
200     USHORT CompSize;         // Number of compressed bytes in this block
201     USHORT UncompSize;       // Number of uncompressed bytes in this block
202 /* Optional fields (depends on Flags)
203     char  DataReserved[]    // Per-datablock reserved area
204  */
205 } CFDATA, *PCFDATA;
206 
207 C_ASSERT(sizeof(CFDATA) == 8);
208 
209 /* Application structures */
210 
211 typedef struct _CFDATA_NODE
212 {
213     ULONG       ScratchFilePosition = 0;    // Absolute offset in scratch file
214     ULONG       AbsoluteOffset = 0;         // Absolute offset in cabinet
215     ULONG       UncompOffset = 0;           // Uncompressed offset in folder
216     CFDATA      Data = { 0 };
217 } CFDATA_NODE, *PCFDATA_NODE;
218 
219 typedef struct _CFFOLDER_NODE
220 {
221     ULONG           UncompOffset = 0;       // File size accumulator
222     ULONG           AbsoluteOffset = 0;
223     ULONG           TotalFolderSize = 0;    // Total size of folder in current disk
224     std::list<PCFDATA_NODE> DataList;
225     ULONG           Index = 0;
226     bool            Commit = false;         // true if the folder should be committed
227     bool            Delete = false;         // true if marked for deletion
228     CFFOLDER        Folder = { 0 };
229 } CFFOLDER_NODE, *PCFFOLDER_NODE;
230 
231 typedef struct _CFFILE_NODE
232 {
233     CFFILE              File = { 0 };
234     std::string         FileName;
235     std::string         TargetFolder;
236     PCFDATA_NODE        DataBlock = nullptr;    // First data block of file. NULL if not known
237     bool                Commit = false;         // true if the file data should be committed
238     bool                Delete = false;         // true if marked for deletion
239     PCFFOLDER_NODE      FolderNode = nullptr;   // Folder this file belong to
240 } CFFILE_NODE, *PCFFILE_NODE;
241 
242 typedef struct _SEARCH_CRITERIA
243 {
244     std::string             Search;             // The actual search criteria
245     std::string             TargetFolder;       // The filename will be TargetFolder\file
246 } SEARCH_CRITERIA, *PSEARCH_CRITERIA;
247 
248 typedef struct _CAB_SEARCH
249 {
250     std::list<PCFFILE_NODE>::iterator      Next;   // Pointer to next node
251     PCFFILE           File = nullptr;   // Pointer to current CFFILE
252     std::string       FileName;         // Current filename
253 } CAB_SEARCH, *PCAB_SEARCH;
254 
255 
256 /* Constants */
257 
258 /* Status codes */
259 #define CAB_STATUS_SUCCESS       0x00000000
260 #define CAB_STATUS_FAILURE       0x00000001
261 #define CAB_STATUS_NOMEMORY      0x00000002
262 #define CAB_STATUS_CANNOT_OPEN   0x00000003
263 #define CAB_STATUS_CANNOT_CREATE 0x00000004
264 #define CAB_STATUS_CANNOT_READ   0x00000005
265 #define CAB_STATUS_CANNOT_WRITE  0x00000006
266 #define CAB_STATUS_FILE_EXISTS   0x00000007
267 #define CAB_STATUS_INVALID_CAB   0x00000008
268 #define CAB_STATUS_NOFILE        0x00000009
269 #define CAB_STATUS_UNSUPPCOMP    0x0000000A
270 
271 
272 
273 /* Codecs */
274 
275 class CCABCodec
276 {
277 public:
278     /* Default constructor */
CCABCodec()279     CCABCodec() {};
280     /* Default destructor */
~CCABCodec()281     virtual ~CCABCodec() {};
282     /* Compresses a data block */
283     virtual ULONG Compress(void* OutputBuffer,
284                            void* InputBuffer,
285                            ULONG InputLength,
286                            PULONG OutputLength) = 0;
287     /* Uncompresses a data block */
288     virtual ULONG Uncompress(void* OutputBuffer,
289                              void* InputBuffer,
290                              ULONG InputLength,
291                              PULONG OutputLength) = 0;
292 };
293 
294 
295 /* Codec status codes */
296 #define CS_SUCCESS      0x0000  /* All data consumed */
297 #define CS_NOMEMORY     0x0001  /* Not enough free memory */
298 #define CS_BADSTREAM    0x0002  /* Bad data stream */
299 
300 
301 /* Codec indentifiers */
302 #define CAB_CODEC_RAW   0x00
303 #define CAB_CODEC_LZX   0x01
304 #define CAB_CODEC_MSZIP 0x02
305 
306 
307 
308 /* Classes */
309 
310 class CCabinet
311 {
312 public:
313     /* Default constructor */
314     CCabinet();
315     /* Default destructor */
316     virtual ~CCabinet();
317     /* Determines if a character is a separator */
318     bool IsSeparator(char Char);
319     /* Replaces \ or / with the one used be the host environment */
320     void ConvertPath(std::string& Path);
321     /* Returns the filename part of a fully qualified filename */
322     std::string GetFileName(const std::string& Path);
323     /* Normalizes a path */
324     void NormalizePath(std::string& Path);
325     /* Returns name of cabinet file */
326     char* GetCabinetName();
327     /* Sets the name of the cabinet file */
328     void SetCabinetName(const char* FileName);
329     /* Sets destination path for extracted files */
330     void SetDestinationPath(const char* DestinationPath);
331     /* Sets cabinet reserved file */
332     bool SetCabinetReservedFile(const char* FileName);
333     /* Returns destination path */
334     const char* GetDestinationPath();
335     /* Returns zero-based current disk number */
336     ULONG GetCurrentDiskNumber();
337     /* Opens the current cabinet file */
338     ULONG Open();
339     /* Closes the current open cabinet file */
340     void Close();
341     /* Locates the first file in the current cabinet file that matches a search criteria */
342     ULONG FindFirst(PCAB_SEARCH Search);
343     /* Locates the next file in the current cabinet file */
344     ULONG FindNext(PCAB_SEARCH Search);
345     /* Extracts a file from the current cabinet file */
346     ULONG ExtractFile(const char* FileName);
347     /* Select codec engine to use */
348     void SelectCodec(LONG Id);
349     /* Returns whether a codec engine is selected */
350     bool IsCodecSelected();
351     /* Adds a search criteria for adding files to a simple cabinet, displaying files in a cabinet or extracting them */
352     ULONG AddSearchCriteria(const std::string& SearchCriteria, const std::string& TargetFolder);
353     /* Destroys the search criteria list */
354     void DestroySearchCriteria();
355     /* Returns whether we have search criteria */
356     bool HasSearchCriteria();
357 
358     std::string CreateCabFilename(PCFFILE_NODE Node);
359 
360 #ifndef CAB_READ_ONLY
361     /* Creates a simple cabinet based on the search criteria data */
362     bool CreateSimpleCabinet();
363     /* Sets the codec to use for compression (based on a string value) */
364     bool SetCompressionCodec(const char* CodecName);
365     /* Creates a new cabinet file */
366     ULONG NewCabinet();
367     /* Forces a new disk to be created */
368     ULONG NewDisk();
369     /* Forces a new folder to be created */
370     ULONG NewFolder();
371     /* Writes a file to scratch storage */
372     ULONG WriteFileToScratchStorage(PCFFILE_NODE FileNode);
373     /* Forces the current disk to be written */
374     ULONG WriteDisk(ULONG MoreDisks);
375     /* Commits the current disk */
376     ULONG CommitDisk(ULONG MoreDisks);
377     /* Closes the current disk */
378     ULONG CloseDisk();
379     /* Closes the current cabinet */
380     ULONG CloseCabinet();
381     /* Adds a file to the current disk */
382     ULONG AddFile(const std::string& FileName, const std::string& TargetFolder);
383     /* Sets the maximum size of the current disk */
384     void SetMaxDiskSize(ULONG Size);
385 #endif /* CAB_READ_ONLY */
386 
387     /* Default event handlers */
388 
389     /* Handler called when a file is about to be overridden */
390     virtual bool OnOverwrite(PCFFILE Entry, const char* FileName);
391     /* Handler called when a file is about to be extracted */
392     virtual void OnExtract(PCFFILE Entry, const char* FileName);
393     /* Handler called when a new disk is to be processed */
394     virtual void OnDiskChange(const char* CabinetName, const char* DiskLabel);
395 
396     virtual void OnVerboseMessage(const char* Message);
397 #ifndef CAB_READ_ONLY
398     /* Handler called when a file is about to be added */
399     virtual void OnAdd(PCFFILE Entry, const char* FileName);
400     /* Handler called when a cabinet need a name */
401     virtual bool OnCabinetName(ULONG Number, char* Name);
402     /* Handler called when a disk needs a label */
403     virtual bool OnDiskLabel(ULONG Number, char* Label);
404 #endif /* CAB_READ_ONLY */
405 private:
406     PCFFOLDER_NODE LocateFolderNode(ULONG Index);
407     ULONG GetAbsoluteOffset(PCFFILE_NODE File);
408     ULONG LocateFile(const char* FileName, PCFFILE_NODE *File);
409     ULONG ReadString(char* String, LONG MaxLength);
410     ULONG ReadFileTable();
411     ULONG ReadDataBlocks(PCFFOLDER_NODE FolderNode);
412     PCFFOLDER_NODE NewFolderNode();
413     PCFFILE_NODE NewFileNode();
414     PCFDATA_NODE NewDataNode(PCFFOLDER_NODE FolderNode);
415     void DestroyDataNodes(PCFFOLDER_NODE FolderNode);
416     void DestroyFileNodes();
417     void DestroyDeletedFileNodes();
418     void DestroyFolderNodes();
419     void DestroyDeletedFolderNodes();
420     ULONG ComputeChecksum(void* Buffer, ULONG Size, ULONG Seed);
421     ULONG ReadBlock(void* Buffer, ULONG Size, PULONG BytesRead);
422     bool MatchFileNamePattern(const char* FileName, const char* Pattern);
423 #ifndef CAB_READ_ONLY
424     ULONG InitCabinetHeader();
425     ULONG WriteCabinetHeader(bool MoreDisks);
426     ULONG WriteFolderEntries();
427     ULONG WriteFileEntries();
428     ULONG CommitDataBlocks(PCFFOLDER_NODE FolderNode);
429     ULONG WriteDataBlock();
430     ULONG GetAttributesOnFile(PCFFILE_NODE File);
431     ULONG SetAttributesOnFile(char* FileName, USHORT FileAttributes);
432     ULONG GetFileTimes(FILE* FileHandle, PCFFILE_NODE File);
433 #if !defined(_WIN32)
434     void ConvertDateAndTime(time_t* Time, PUSHORT DosDate, PUSHORT DosTime);
435 #endif
436 #endif /* CAB_READ_ONLY */
437     ULONG CurrentDiskNumber;    // Zero based disk number
438     char CabinetName[256];     // Filename of current cabinet
439     char CabinetPrev[256];     // Filename of previous cabinet
440     char DiskPrev[256];        // Label of cabinet in file CabinetPrev
441     char CabinetNext[256];     // Filename of next cabinet
442     char DiskNext[256];        // Label of cabinet in file CabinetNext
443     ULONG TotalHeaderSize;      // Size of header and optional fields
444     ULONG NextFieldsSize;       // Size of next cabinet name and next disk label
445     ULONG TotalFolderSize;      // Size of all folder entries
446     ULONG TotalFileSize;        // Size of all file entries
447     ULONG FolderUncompSize;     // Uncompressed size of folder
448     ULONG BytesLeftInBlock;     // Number of bytes left in current block
449     bool ReuseBlock;
450     std::string DestPath;
451     std::string CabinetReservedFile;
452     void* CabinetReservedFileBuffer;
453     ULONG CabinetReservedFileSize;
454     FILE* FileHandle;
455     bool FileOpen;
456     CFHEADER CABHeader;
457     ULONG CabinetReserved;
458     ULONG FolderReserved;
459     ULONG DataReserved;
460     std::list<PCFFOLDER_NODE> FolderList;
461     PCFFOLDER_NODE CurrentFolderNode;
462     PCFDATA_NODE CurrentDataNode;
463     std::list<PCFFILE_NODE> FileList;
464     std::list<PSEARCH_CRITERIA> CriteriaList;
465     CCABCodec *Codec;
466     LONG CodecId;
467     bool CodecSelected;
468     void* InputBuffer;
469     void* CurrentIBuffer;               // Current offset in input buffer
470     ULONG CurrentIBufferSize;   // Bytes left in input buffer
471     void* OutputBuffer;
472     ULONG TotalCompSize;        // Total size of current CFDATA block
473     void* CurrentOBuffer;               // Current offset in output buffer
474     ULONG CurrentOBufferSize;   // Bytes left in output buffer
475     ULONG BytesLeftInCabinet;
476     bool RestartSearch;
477     ULONG LastFileOffset;       // Uncompressed offset of last extracted file
478 #ifndef CAB_READ_ONLY
479     ULONG LastBlockStart;       // Uncompressed offset of last block in folder
480     ULONG MaxDiskSize;
481     ULONG DiskSize;
482     ULONG PrevCabinetNumber;    // Previous cabinet number (where split file starts)
483     bool CreateNewDisk;
484     bool CreateNewFolder;
485 
486     class CCFDATAStorage *ScratchFile;
487     FILE* SourceFile;
488     bool ContinueFile;
489     ULONG TotalBytesLeft;
490     bool BlockIsSplit;                  // true if current data block is split
491     ULONG NextFolderNumber;     // Zero based folder number
492 #endif /* CAB_READ_ONLY */
493 };
494 
495 /* EOF */
496