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