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