1 /*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under both the BSD-style license (found in the
6 * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7 * in the COPYING file in the root directory of this source tree).
8 * You may select, at your option, one of the above-listed licenses.
9 */
10
11
12 /* *************************************
13 * Compiler Options
14 ***************************************/
15 #ifdef _MSC_VER /* Visual */
16 # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
17 # pragma warning(disable : 4204) /* non-constant aggregate initializer */
18 #endif
19 #if defined(__MINGW32__) && !defined(_POSIX_SOURCE)
20 # define _POSIX_SOURCE 1 /* disable %llu warnings with MinGW on Windows */
21 #endif
22
23 /*-*************************************
24 * Includes
25 ***************************************/
26 #include "platform.h" /* Large Files support, SET_BINARY_MODE */
27 #include "util.h" /* UTIL_getFileSize, UTIL_isRegularFile, UTIL_isSameFile */
28 #include <stdio.h> /* fprintf, open, fdopen, fread, _fileno, stdin, stdout */
29 #include <stdlib.h> /* malloc, free */
30 #include <string.h> /* strcmp, strlen */
31 #include <time.h> /* clock_t, to measure process time */
32 #include <fcntl.h> /* O_WRONLY */
33 #include <assert.h>
34 #include <errno.h> /* errno */
35 #include <limits.h> /* INT_MAX */
36 #include <signal.h>
37 #include "timefn.h" /* UTIL_getTime, UTIL_clockSpanMicro */
38
39 #if defined (_MSC_VER)
40 # include <sys/stat.h>
41 # include <io.h>
42 #endif
43
44 #include "fileio.h"
45 #include "fileio_asyncio.h"
46 #include "fileio_common.h"
47
48 FIO_display_prefs_t g_display_prefs = {2, FIO_ps_auto};
49 UTIL_time_t g_displayClock = UTIL_TIME_INITIALIZER;
50
51 #define ZSTD_STATIC_LINKING_ONLY /* ZSTD_magicNumber, ZSTD_frameHeaderSize_max */
52 #include "../lib/zstd.h"
53 #include "../lib/zstd_errors.h" /* ZSTD_error_frameParameter_windowTooLarge */
54
55 #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
56 # include <zlib.h>
57 # if !defined(z_const)
58 # define z_const
59 # endif
60 #endif
61
62 #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
63 # include <lzma.h>
64 #endif
65
66 #define LZ4_MAGICNUMBER 0x184D2204
67 #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
68 # define LZ4F_ENABLE_OBSOLETE_ENUMS
69 # include <lz4frame.h>
70 # include <lz4.h>
71 #endif
72
FIO_zlibVersion(void)73 char const* FIO_zlibVersion(void)
74 {
75 #if defined(ZSTD_GZCOMPRESS) || defined(ZSTD_GZDECOMPRESS)
76 return zlibVersion();
77 #else
78 return "Unsupported";
79 #endif
80 }
81
FIO_lz4Version(void)82 char const* FIO_lz4Version(void)
83 {
84 #if defined(ZSTD_LZ4COMPRESS) || defined(ZSTD_LZ4DECOMPRESS)
85 /* LZ4_versionString() added in v1.7.3 */
86 # if LZ4_VERSION_NUMBER >= 10703
87 return LZ4_versionString();
88 # else
89 # define ZSTD_LZ4_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
90 # define ZSTD_LZ4_VERSION_STRING ZSTD_EXPAND_AND_QUOTE(ZSTD_LZ4_VERSION)
91 return ZSTD_LZ4_VERSION_STRING;
92 # endif
93 #else
94 return "Unsupported";
95 #endif
96 }
97
FIO_lzmaVersion(void)98 char const* FIO_lzmaVersion(void)
99 {
100 #if defined(ZSTD_LZMACOMPRESS) || defined(ZSTD_LZMADECOMPRESS)
101 return lzma_version_string();
102 #else
103 return "Unsupported";
104 #endif
105 }
106
107
108 /*-*************************************
109 * Constants
110 ***************************************/
111 #define ADAPT_WINDOWLOG_DEFAULT 23 /* 8 MB */
112 #define DICTSIZE_MAX (32 MB) /* protection against large input (attack scenario) */
113
114 #define FNSPACE 30
115
116 /* Default file permissions 0666 (modulated by umask) */
117 /* Temporary restricted file permissions are used when we're going to
118 * chmod/chown at the end of the operation. */
119 #if !defined(_WIN32)
120 /* These macros aren't defined on windows. */
121 #define DEFAULT_FILE_PERMISSIONS (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
122 #define TEMPORARY_FILE_PERMISSIONS (S_IRUSR|S_IWUSR)
123 #else
124 #define DEFAULT_FILE_PERMISSIONS (0666)
125 #define TEMPORARY_FILE_PERMISSIONS (0600)
126 #endif
127
128 /*-************************************
129 * Signal (Ctrl-C trapping)
130 **************************************/
131 static const char* g_artefact = NULL;
INThandler(int sig)132 static void INThandler(int sig)
133 {
134 assert(sig==SIGINT); (void)sig;
135 #if !defined(_MSC_VER)
136 signal(sig, SIG_IGN); /* this invocation generates a buggy warning in Visual Studio */
137 #endif
138 if (g_artefact) {
139 assert(UTIL_isRegularFile(g_artefact));
140 remove(g_artefact);
141 }
142 DISPLAY("\n");
143 exit(2);
144 }
addHandler(char const * dstFileName)145 static void addHandler(char const* dstFileName)
146 {
147 if (UTIL_isRegularFile(dstFileName)) {
148 g_artefact = dstFileName;
149 signal(SIGINT, INThandler);
150 } else {
151 g_artefact = NULL;
152 }
153 }
154 /* Idempotent */
clearHandler(void)155 static void clearHandler(void)
156 {
157 if (g_artefact) signal(SIGINT, SIG_DFL);
158 g_artefact = NULL;
159 }
160
161
162 /*-*********************************************************
163 * Termination signal trapping (Print debug stack trace)
164 ***********************************************************/
165 #if defined(__has_feature) && !defined(BACKTRACE_ENABLE) /* Clang compiler */
166 # if (__has_feature(address_sanitizer))
167 # define BACKTRACE_ENABLE 0
168 # endif /* __has_feature(address_sanitizer) */
169 #elif defined(__SANITIZE_ADDRESS__) && !defined(BACKTRACE_ENABLE) /* GCC compiler */
170 # define BACKTRACE_ENABLE 0
171 #endif
172
173 #if !defined(BACKTRACE_ENABLE)
174 /* automatic detector : backtrace enabled by default on linux+glibc and osx */
175 # if (defined(__linux__) && (defined(__GLIBC__) && !defined(__UCLIBC__))) \
176 || (defined(__APPLE__) && defined(__MACH__))
177 # define BACKTRACE_ENABLE 1
178 # else
179 # define BACKTRACE_ENABLE 0
180 # endif
181 #endif
182
183 /* note : after this point, BACKTRACE_ENABLE is necessarily defined */
184
185
186 #if BACKTRACE_ENABLE
187
188 #include <execinfo.h> /* backtrace, backtrace_symbols */
189
190 #define MAX_STACK_FRAMES 50
191
ABRThandler(int sig)192 static void ABRThandler(int sig) {
193 const char* name;
194 void* addrlist[MAX_STACK_FRAMES];
195 char** symbollist;
196 int addrlen, i;
197
198 switch (sig) {
199 case SIGABRT: name = "SIGABRT"; break;
200 case SIGFPE: name = "SIGFPE"; break;
201 case SIGILL: name = "SIGILL"; break;
202 case SIGINT: name = "SIGINT"; break;
203 case SIGSEGV: name = "SIGSEGV"; break;
204 default: name = "UNKNOWN";
205 }
206
207 DISPLAY("Caught %s signal, printing stack:\n", name);
208 /* Retrieve current stack addresses. */
209 addrlen = backtrace(addrlist, MAX_STACK_FRAMES);
210 if (addrlen == 0) {
211 DISPLAY("\n");
212 return;
213 }
214 /* Create readable strings to each frame. */
215 symbollist = backtrace_symbols(addrlist, addrlen);
216 /* Print the stack trace, excluding calls handling the signal. */
217 for (i = ZSTD_START_SYMBOLLIST_FRAME; i < addrlen; i++) {
218 DISPLAY("%s\n", symbollist[i]);
219 }
220 free(symbollist);
221 /* Reset and raise the signal so default handler runs. */
222 signal(sig, SIG_DFL);
223 raise(sig);
224 }
225 #endif
226
FIO_addAbortHandler(void)227 void FIO_addAbortHandler(void)
228 {
229 #if BACKTRACE_ENABLE
230 signal(SIGABRT, ABRThandler);
231 signal(SIGFPE, ABRThandler);
232 signal(SIGILL, ABRThandler);
233 signal(SIGSEGV, ABRThandler);
234 signal(SIGBUS, ABRThandler);
235 #endif
236 }
237
238 /*-*************************************
239 * Parameters: FIO_ctx_t
240 ***************************************/
241
242 /* typedef'd to FIO_ctx_t within fileio.h */
243 struct FIO_ctx_s {
244
245 /* file i/o info */
246 int nbFilesTotal;
247 int hasStdinInput;
248 int hasStdoutOutput;
249
250 /* file i/o state */
251 int currFileIdx;
252 int nbFilesProcessed;
253 size_t totalBytesInput;
254 size_t totalBytesOutput;
255 };
256
FIO_shouldDisplayFileSummary(FIO_ctx_t const * fCtx)257 static int FIO_shouldDisplayFileSummary(FIO_ctx_t const* fCtx)
258 {
259 return fCtx->nbFilesTotal <= 1 || g_display_prefs.displayLevel >= 3;
260 }
261
FIO_shouldDisplayMultipleFileSummary(FIO_ctx_t const * fCtx)262 static int FIO_shouldDisplayMultipleFileSummary(FIO_ctx_t const* fCtx)
263 {
264 int const shouldDisplay = (fCtx->nbFilesProcessed >= 1 && fCtx->nbFilesTotal > 1);
265 assert(shouldDisplay || FIO_shouldDisplayFileSummary(fCtx) || fCtx->nbFilesProcessed == 0);
266 return shouldDisplay;
267 }
268
269
270 /*-*************************************
271 * Parameters: Initialization
272 ***************************************/
273
274 #define FIO_OVERLAP_LOG_NOTSET 9999
275 #define FIO_LDM_PARAM_NOTSET 9999
276
277
FIO_createPreferences(void)278 FIO_prefs_t* FIO_createPreferences(void)
279 {
280 FIO_prefs_t* const ret = (FIO_prefs_t*)malloc(sizeof(FIO_prefs_t));
281 if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
282
283 ret->compressionType = FIO_zstdCompression;
284 ret->overwrite = 0;
285 ret->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
286 ret->dictIDFlag = 1;
287 ret->checksumFlag = 1;
288 ret->removeSrcFile = 0;
289 ret->memLimit = 0;
290 ret->nbWorkers = 1;
291 ret->blockSize = 0;
292 ret->overlapLog = FIO_OVERLAP_LOG_NOTSET;
293 ret->adaptiveMode = 0;
294 ret->rsyncable = 0;
295 ret->minAdaptLevel = -50; /* initializing this value requires a constant, so ZSTD_minCLevel() doesn't work */
296 ret->maxAdaptLevel = 22; /* initializing this value requires a constant, so ZSTD_maxCLevel() doesn't work */
297 ret->ldmFlag = 0;
298 ret->ldmHashLog = 0;
299 ret->ldmMinMatch = 0;
300 ret->ldmBucketSizeLog = FIO_LDM_PARAM_NOTSET;
301 ret->ldmHashRateLog = FIO_LDM_PARAM_NOTSET;
302 ret->streamSrcSize = 0;
303 ret->targetCBlockSize = 0;
304 ret->srcSizeHint = 0;
305 ret->testMode = 0;
306 ret->literalCompressionMode = ZSTD_ps_auto;
307 ret->excludeCompressedFiles = 0;
308 ret->allowBlockDevices = 0;
309 ret->asyncIO = AIO_supported();
310 ret->passThrough = -1;
311 return ret;
312 }
313
FIO_createContext(void)314 FIO_ctx_t* FIO_createContext(void)
315 {
316 FIO_ctx_t* const ret = (FIO_ctx_t*)malloc(sizeof(FIO_ctx_t));
317 if (!ret) EXM_THROW(21, "Allocation error : not enough memory");
318
319 ret->currFileIdx = 0;
320 ret->hasStdinInput = 0;
321 ret->hasStdoutOutput = 0;
322 ret->nbFilesTotal = 1;
323 ret->nbFilesProcessed = 0;
324 ret->totalBytesInput = 0;
325 ret->totalBytesOutput = 0;
326 return ret;
327 }
328
FIO_freePreferences(FIO_prefs_t * const prefs)329 void FIO_freePreferences(FIO_prefs_t* const prefs)
330 {
331 free(prefs);
332 }
333
FIO_freeContext(FIO_ctx_t * const fCtx)334 void FIO_freeContext(FIO_ctx_t* const fCtx)
335 {
336 free(fCtx);
337 }
338
339
340 /*-*************************************
341 * Parameters: Display Options
342 ***************************************/
343
FIO_setNotificationLevel(int level)344 void FIO_setNotificationLevel(int level) { g_display_prefs.displayLevel=level; }
345
FIO_setProgressSetting(FIO_progressSetting_e setting)346 void FIO_setProgressSetting(FIO_progressSetting_e setting) { g_display_prefs.progressSetting = setting; }
347
348
349 /*-*************************************
350 * Parameters: Setters
351 ***************************************/
352
353 /* FIO_prefs_t functions */
354
FIO_setCompressionType(FIO_prefs_t * const prefs,FIO_compressionType_t compressionType)355 void FIO_setCompressionType(FIO_prefs_t* const prefs, FIO_compressionType_t compressionType) { prefs->compressionType = compressionType; }
356
FIO_overwriteMode(FIO_prefs_t * const prefs)357 void FIO_overwriteMode(FIO_prefs_t* const prefs) { prefs->overwrite = 1; }
358
FIO_setSparseWrite(FIO_prefs_t * const prefs,int sparse)359 void FIO_setSparseWrite(FIO_prefs_t* const prefs, int sparse) { prefs->sparseFileSupport = sparse; }
360
FIO_setDictIDFlag(FIO_prefs_t * const prefs,int dictIDFlag)361 void FIO_setDictIDFlag(FIO_prefs_t* const prefs, int dictIDFlag) { prefs->dictIDFlag = dictIDFlag; }
362
FIO_setChecksumFlag(FIO_prefs_t * const prefs,int checksumFlag)363 void FIO_setChecksumFlag(FIO_prefs_t* const prefs, int checksumFlag) { prefs->checksumFlag = checksumFlag; }
364
FIO_setRemoveSrcFile(FIO_prefs_t * const prefs,int flag)365 void FIO_setRemoveSrcFile(FIO_prefs_t* const prefs, int flag) { prefs->removeSrcFile = (flag!=0); }
366
FIO_setMemLimit(FIO_prefs_t * const prefs,unsigned memLimit)367 void FIO_setMemLimit(FIO_prefs_t* const prefs, unsigned memLimit) { prefs->memLimit = memLimit; }
368
FIO_setNbWorkers(FIO_prefs_t * const prefs,int nbWorkers)369 void FIO_setNbWorkers(FIO_prefs_t* const prefs, int nbWorkers) {
370 #ifndef ZSTD_MULTITHREAD
371 if (nbWorkers > 0) DISPLAYLEVEL(2, "Note : multi-threading is disabled \n");
372 #endif
373 prefs->nbWorkers = nbWorkers;
374 }
375
FIO_setExcludeCompressedFile(FIO_prefs_t * const prefs,int excludeCompressedFiles)376 void FIO_setExcludeCompressedFile(FIO_prefs_t* const prefs, int excludeCompressedFiles) { prefs->excludeCompressedFiles = excludeCompressedFiles; }
377
FIO_setAllowBlockDevices(FIO_prefs_t * const prefs,int allowBlockDevices)378 void FIO_setAllowBlockDevices(FIO_prefs_t* const prefs, int allowBlockDevices) { prefs->allowBlockDevices = allowBlockDevices; }
379
FIO_setBlockSize(FIO_prefs_t * const prefs,int blockSize)380 void FIO_setBlockSize(FIO_prefs_t* const prefs, int blockSize) {
381 if (blockSize && prefs->nbWorkers==0)
382 DISPLAYLEVEL(2, "Setting block size is useless in single-thread mode \n");
383 prefs->blockSize = blockSize;
384 }
385
FIO_setOverlapLog(FIO_prefs_t * const prefs,int overlapLog)386 void FIO_setOverlapLog(FIO_prefs_t* const prefs, int overlapLog){
387 if (overlapLog && prefs->nbWorkers==0)
388 DISPLAYLEVEL(2, "Setting overlapLog is useless in single-thread mode \n");
389 prefs->overlapLog = overlapLog;
390 }
391
FIO_setAdaptiveMode(FIO_prefs_t * const prefs,int adapt)392 void FIO_setAdaptiveMode(FIO_prefs_t* const prefs, int adapt) {
393 if ((adapt>0) && (prefs->nbWorkers==0))
394 EXM_THROW(1, "Adaptive mode is not compatible with single thread mode \n");
395 prefs->adaptiveMode = adapt;
396 }
397
FIO_setUseRowMatchFinder(FIO_prefs_t * const prefs,int useRowMatchFinder)398 void FIO_setUseRowMatchFinder(FIO_prefs_t* const prefs, int useRowMatchFinder) {
399 prefs->useRowMatchFinder = useRowMatchFinder;
400 }
401
FIO_setRsyncable(FIO_prefs_t * const prefs,int rsyncable)402 void FIO_setRsyncable(FIO_prefs_t* const prefs, int rsyncable) {
403 if ((rsyncable>0) && (prefs->nbWorkers==0))
404 EXM_THROW(1, "Rsyncable mode is not compatible with single thread mode \n");
405 prefs->rsyncable = rsyncable;
406 }
407
FIO_setStreamSrcSize(FIO_prefs_t * const prefs,size_t streamSrcSize)408 void FIO_setStreamSrcSize(FIO_prefs_t* const prefs, size_t streamSrcSize) {
409 prefs->streamSrcSize = streamSrcSize;
410 }
411
FIO_setTargetCBlockSize(FIO_prefs_t * const prefs,size_t targetCBlockSize)412 void FIO_setTargetCBlockSize(FIO_prefs_t* const prefs, size_t targetCBlockSize) {
413 prefs->targetCBlockSize = targetCBlockSize;
414 }
415
FIO_setSrcSizeHint(FIO_prefs_t * const prefs,size_t srcSizeHint)416 void FIO_setSrcSizeHint(FIO_prefs_t* const prefs, size_t srcSizeHint) {
417 prefs->srcSizeHint = (int)MIN((size_t)INT_MAX, srcSizeHint);
418 }
419
FIO_setTestMode(FIO_prefs_t * const prefs,int testMode)420 void FIO_setTestMode(FIO_prefs_t* const prefs, int testMode) {
421 prefs->testMode = (testMode!=0);
422 }
423
FIO_setLiteralCompressionMode(FIO_prefs_t * const prefs,ZSTD_ParamSwitch_e mode)424 void FIO_setLiteralCompressionMode(
425 FIO_prefs_t* const prefs,
426 ZSTD_ParamSwitch_e mode) {
427 prefs->literalCompressionMode = mode;
428 }
429
FIO_setAdaptMin(FIO_prefs_t * const prefs,int minCLevel)430 void FIO_setAdaptMin(FIO_prefs_t* const prefs, int minCLevel)
431 {
432 #ifndef ZSTD_NOCOMPRESS
433 assert(minCLevel >= ZSTD_minCLevel());
434 #endif
435 prefs->minAdaptLevel = minCLevel;
436 }
437
FIO_setAdaptMax(FIO_prefs_t * const prefs,int maxCLevel)438 void FIO_setAdaptMax(FIO_prefs_t* const prefs, int maxCLevel)
439 {
440 prefs->maxAdaptLevel = maxCLevel;
441 }
442
FIO_setLdmFlag(FIO_prefs_t * const prefs,unsigned ldmFlag)443 void FIO_setLdmFlag(FIO_prefs_t* const prefs, unsigned ldmFlag) {
444 prefs->ldmFlag = (ldmFlag>0);
445 }
446
FIO_setLdmHashLog(FIO_prefs_t * const prefs,int ldmHashLog)447 void FIO_setLdmHashLog(FIO_prefs_t* const prefs, int ldmHashLog) {
448 prefs->ldmHashLog = ldmHashLog;
449 }
450
FIO_setLdmMinMatch(FIO_prefs_t * const prefs,int ldmMinMatch)451 void FIO_setLdmMinMatch(FIO_prefs_t* const prefs, int ldmMinMatch) {
452 prefs->ldmMinMatch = ldmMinMatch;
453 }
454
FIO_setLdmBucketSizeLog(FIO_prefs_t * const prefs,int ldmBucketSizeLog)455 void FIO_setLdmBucketSizeLog(FIO_prefs_t* const prefs, int ldmBucketSizeLog) {
456 prefs->ldmBucketSizeLog = ldmBucketSizeLog;
457 }
458
459
FIO_setLdmHashRateLog(FIO_prefs_t * const prefs,int ldmHashRateLog)460 void FIO_setLdmHashRateLog(FIO_prefs_t* const prefs, int ldmHashRateLog) {
461 prefs->ldmHashRateLog = ldmHashRateLog;
462 }
463
FIO_setPatchFromMode(FIO_prefs_t * const prefs,int value)464 void FIO_setPatchFromMode(FIO_prefs_t* const prefs, int value)
465 {
466 prefs->patchFromMode = value != 0;
467 }
468
FIO_setContentSize(FIO_prefs_t * const prefs,int value)469 void FIO_setContentSize(FIO_prefs_t* const prefs, int value)
470 {
471 prefs->contentSize = value != 0;
472 }
473
FIO_setAsyncIOFlag(FIO_prefs_t * const prefs,int value)474 void FIO_setAsyncIOFlag(FIO_prefs_t* const prefs, int value) {
475 #ifdef ZSTD_MULTITHREAD
476 prefs->asyncIO = value;
477 #else
478 (void) prefs;
479 (void) value;
480 DISPLAYLEVEL(2, "Note : asyncio is disabled (lack of multithreading support) \n");
481 #endif
482 }
483
FIO_setPassThroughFlag(FIO_prefs_t * const prefs,int value)484 void FIO_setPassThroughFlag(FIO_prefs_t* const prefs, int value) {
485 prefs->passThrough = (value != 0);
486 }
487
FIO_setMMapDict(FIO_prefs_t * const prefs,ZSTD_ParamSwitch_e value)488 void FIO_setMMapDict(FIO_prefs_t* const prefs, ZSTD_ParamSwitch_e value)
489 {
490 prefs->mmapDict = value;
491 }
492
493 /* FIO_ctx_t functions */
494
FIO_setHasStdoutOutput(FIO_ctx_t * const fCtx,int value)495 void FIO_setHasStdoutOutput(FIO_ctx_t* const fCtx, int value) {
496 fCtx->hasStdoutOutput = value;
497 }
498
FIO_setNbFilesTotal(FIO_ctx_t * const fCtx,int value)499 void FIO_setNbFilesTotal(FIO_ctx_t* const fCtx, int value)
500 {
501 fCtx->nbFilesTotal = value;
502 }
503
FIO_determineHasStdinInput(FIO_ctx_t * const fCtx,const FileNamesTable * const filenames)504 void FIO_determineHasStdinInput(FIO_ctx_t* const fCtx, const FileNamesTable* const filenames) {
505 size_t i = 0;
506 for ( ; i < filenames->tableSize; ++i) {
507 if (!strcmp(stdinmark, filenames->fileNames[i])) {
508 fCtx->hasStdinInput = 1;
509 return;
510 }
511 }
512 }
513
514 /*-*************************************
515 * Functions
516 ***************************************/
517 /** FIO_removeFile() :
518 * @result : Unlink `fileName`, even if it's read-only */
FIO_removeFile(const char * path)519 static int FIO_removeFile(const char* path)
520 {
521 stat_t statbuf;
522 if (!UTIL_stat(path, &statbuf)) {
523 DISPLAYLEVEL(2, "zstd: Failed to stat %s while trying to remove it\n", path);
524 return 0;
525 }
526 if (!UTIL_isRegularFileStat(&statbuf)) {
527 DISPLAYLEVEL(2, "zstd: Refusing to remove non-regular file %s\n", path);
528 return 0;
529 }
530 #if defined(_WIN32)
531 /* windows doesn't allow remove read-only files,
532 * so try to make it writable first */
533 if (!(statbuf.st_mode & _S_IWRITE)) {
534 UTIL_chmod(path, &statbuf, _S_IWRITE);
535 }
536 #endif
537 return remove(path);
538 }
539
540 /** FIO_openSrcFile() :
541 * condition : `srcFileName` must be non-NULL. `prefs` may be NULL.
542 * @result : FILE* to `srcFileName`, or NULL if it fails */
FIO_openSrcFile(const FIO_prefs_t * const prefs,const char * srcFileName,stat_t * statbuf)543 static FILE* FIO_openSrcFile(const FIO_prefs_t* const prefs, const char* srcFileName, stat_t* statbuf)
544 {
545 int allowBlockDevices = prefs != NULL ? prefs->allowBlockDevices : 0;
546 assert(srcFileName != NULL);
547 assert(statbuf != NULL);
548 if (!strcmp (srcFileName, stdinmark)) {
549 DISPLAYLEVEL(4,"Using stdin for input \n");
550 SET_BINARY_MODE(stdin);
551 return stdin;
552 }
553
554 if (!UTIL_stat(srcFileName, statbuf)) {
555 DISPLAYLEVEL(1, "zstd: can't stat %s : %s -- ignored \n",
556 srcFileName, strerror(errno));
557 return NULL;
558 }
559
560 if (!UTIL_isRegularFileStat(statbuf)
561 && !UTIL_isFIFOStat(statbuf)
562 && !(allowBlockDevices && UTIL_isBlockDevStat(statbuf))
563 ) {
564 DISPLAYLEVEL(1, "zstd: %s is not a regular file -- ignored \n",
565 srcFileName);
566 return NULL;
567 }
568
569 { FILE* const f = fopen(srcFileName, "rb");
570 if (f == NULL)
571 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
572 return f;
573 }
574 }
575
576 /** FIO_openDstFile() :
577 * condition : `dstFileName` must be non-NULL.
578 * @result : FILE* to `dstFileName`, or NULL if it fails */
579 static FILE*
FIO_openDstFile(FIO_ctx_t * fCtx,FIO_prefs_t * const prefs,const char * srcFileName,const char * dstFileName,const int mode)580 FIO_openDstFile(FIO_ctx_t* fCtx, FIO_prefs_t* const prefs,
581 const char* srcFileName, const char* dstFileName,
582 const int mode)
583 {
584 int isDstRegFile;
585
586 if (prefs->testMode) return NULL; /* do not open file in test mode */
587
588 assert(dstFileName != NULL);
589 if (!strcmp (dstFileName, stdoutmark)) {
590 DISPLAYLEVEL(4,"Using stdout for output \n");
591 SET_BINARY_MODE(stdout);
592 if (prefs->sparseFileSupport == 1) {
593 prefs->sparseFileSupport = 0;
594 DISPLAYLEVEL(4, "Sparse File Support is automatically disabled on stdout ; try --sparse \n");
595 }
596 return stdout;
597 }
598
599 /* ensure dst is not the same as src */
600 if (srcFileName != NULL && UTIL_isSameFile(srcFileName, dstFileName)) {
601 DISPLAYLEVEL(1, "zstd: Refusing to open an output file which will overwrite the input file \n");
602 return NULL;
603 }
604
605 isDstRegFile = UTIL_isRegularFile(dstFileName); /* invoke once */
606 if (prefs->sparseFileSupport == 1) {
607 prefs->sparseFileSupport = ZSTD_SPARSE_DEFAULT;
608 if (!isDstRegFile) {
609 prefs->sparseFileSupport = 0;
610 DISPLAYLEVEL(4, "Sparse File Support is disabled when output is not a file \n");
611 }
612 }
613
614 if (isDstRegFile) {
615 /* Check if destination file already exists */
616 #if !defined(_WIN32)
617 /* this test does not work on Windows :
618 * `NUL` and `nul` are detected as regular files */
619 if (!strcmp(dstFileName, nulmark)) {
620 EXM_THROW(40, "%s is unexpectedly categorized as a regular file",
621 dstFileName);
622 }
623 #endif
624 if (!prefs->overwrite) {
625 if (g_display_prefs.displayLevel <= 1) {
626 /* No interaction possible */
627 DISPLAYLEVEL(1, "zstd: %s already exists; not overwritten \n",
628 dstFileName);
629 return NULL;
630 }
631 DISPLAY("zstd: %s already exists; ", dstFileName);
632 if (UTIL_requireUserConfirmation("overwrite (y/n) ? ", "Not overwritten \n", "yY", fCtx->hasStdinInput))
633 return NULL;
634 }
635 /* need to unlink */
636 FIO_removeFile(dstFileName);
637 }
638
639 {
640 #if defined(_WIN32)
641 /* Windows requires opening the file as a "binary" file to avoid
642 * mangling. This macro doesn't exist on unix. */
643 const int openflags = O_WRONLY|O_CREAT|O_TRUNC|O_BINARY;
644 const int fd = _open(dstFileName, openflags, mode);
645 FILE* f = NULL;
646 if (fd != -1) {
647 f = _fdopen(fd, "wb");
648 }
649 #else
650 const int openflags = O_WRONLY|O_CREAT|O_TRUNC;
651 const int fd = open(dstFileName, openflags, mode);
652 FILE* f = NULL;
653 if (fd != -1) {
654 f = fdopen(fd, "wb");
655 }
656 #endif
657 if (f == NULL) {
658 DISPLAYLEVEL(1, "zstd: %s: %s\n", dstFileName, strerror(errno));
659 } else {
660 /* An increased buffer size can provide a significant performance
661 * boost on some platforms. Note that providing a NULL buf with a
662 * size that's not 0 is not defined in ANSI C, but is defined in an
663 * extension. There are three possibilities here:
664 * 1. Libc supports the extended version and everything is good.
665 * 2. Libc ignores the size when buf is NULL, in which case
666 * everything will continue as if we didn't call `setvbuf()`.
667 * 3. We fail the call and execution continues but a warning
668 * message might be shown.
669 * In all cases due execution continues. For now, I believe that
670 * this is a more cost-effective solution than managing the buffers
671 * allocations ourselves (will require an API change).
672 */
673 if (setvbuf(f, NULL, _IOFBF, 1 MB)) {
674 DISPLAYLEVEL(2, "Warning: setvbuf failed for %s\n", dstFileName);
675 }
676 }
677 return f;
678 }
679 }
680
681
682 /* FIO_getDictFileStat() :
683 */
FIO_getDictFileStat(const char * fileName,stat_t * dictFileStat)684 static void FIO_getDictFileStat(const char* fileName, stat_t* dictFileStat) {
685 assert(dictFileStat != NULL);
686 if (fileName == NULL) return;
687
688 if (!UTIL_stat(fileName, dictFileStat)) {
689 EXM_THROW(31, "Stat failed on dictionary file %s: %s", fileName, strerror(errno));
690 }
691
692 if (!UTIL_isRegularFileStat(dictFileStat)) {
693 EXM_THROW(32, "Dictionary %s must be a regular file.", fileName);
694 }
695 }
696
697 /* FIO_setDictBufferMalloc() :
698 * allocates a buffer, pointed by `dict->dictBuffer`,
699 * loads `filename` content into it, up to DICTSIZE_MAX bytes.
700 * @return : loaded size
701 * if fileName==NULL, returns 0 and a NULL pointer
702 */
FIO_setDictBufferMalloc(FIO_Dict_t * dict,const char * fileName,FIO_prefs_t * const prefs,stat_t * dictFileStat)703 static size_t FIO_setDictBufferMalloc(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
704 {
705 FILE* fileHandle;
706 U64 fileSize;
707 void** bufferPtr = &dict->dictBuffer;
708
709 assert(bufferPtr != NULL);
710 assert(dictFileStat != NULL);
711 *bufferPtr = NULL;
712 if (fileName == NULL) return 0;
713
714 DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
715
716 fileHandle = fopen(fileName, "rb");
717
718 if (fileHandle == NULL) {
719 EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
720 }
721
722 fileSize = UTIL_getFileSizeStat(dictFileStat);
723 {
724 size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
725 if (fileSize > dictSizeMax) {
726 EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
727 fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
728 }
729 }
730 *bufferPtr = malloc((size_t)fileSize);
731 if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
732 { size_t const readSize = fread(*bufferPtr, 1, (size_t)fileSize, fileHandle);
733 if (readSize != fileSize) {
734 EXM_THROW(35, "Error reading dictionary file %s : %s",
735 fileName, strerror(errno));
736 }
737 }
738 fclose(fileHandle);
739 return (size_t)fileSize;
740 }
741
742 #if (PLATFORM_POSIX_VERSION > 0)
743 #include <sys/mman.h>
FIO_munmap(FIO_Dict_t * dict)744 static void FIO_munmap(FIO_Dict_t* dict)
745 {
746 munmap(dict->dictBuffer, dict->dictBufferSize);
747 dict->dictBuffer = NULL;
748 dict->dictBufferSize = 0;
749 }
FIO_setDictBufferMMap(FIO_Dict_t * dict,const char * fileName,FIO_prefs_t * const prefs,stat_t * dictFileStat)750 static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
751 {
752 int fileHandle;
753 U64 fileSize;
754 void** bufferPtr = &dict->dictBuffer;
755
756 assert(bufferPtr != NULL);
757 assert(dictFileStat != NULL);
758 *bufferPtr = NULL;
759 if (fileName == NULL) return 0;
760
761 DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
762
763 fileHandle = open(fileName, O_RDONLY);
764
765 if (fileHandle == -1) {
766 EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
767 }
768
769 fileSize = UTIL_getFileSizeStat(dictFileStat);
770 {
771 size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
772 if (fileSize > dictSizeMax) {
773 EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
774 fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
775 }
776 }
777
778 *bufferPtr = mmap(NULL, (size_t)fileSize, PROT_READ, MAP_PRIVATE, fileHandle, 0);
779 if (*bufferPtr==NULL) EXM_THROW(34, "%s", strerror(errno));
780
781 close(fileHandle);
782 return (size_t)fileSize;
783 }
784 #elif defined(_MSC_VER) || defined(_WIN32)
785 #include <windows.h>
FIO_munmap(FIO_Dict_t * dict)786 static void FIO_munmap(FIO_Dict_t* dict)
787 {
788 UnmapViewOfFile(dict->dictBuffer);
789 CloseHandle(dict->dictHandle);
790 dict->dictBuffer = NULL;
791 dict->dictBufferSize = 0;
792 }
FIO_setDictBufferMMap(FIO_Dict_t * dict,const char * fileName,FIO_prefs_t * const prefs,stat_t * dictFileStat)793 static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
794 {
795 HANDLE fileHandle, mapping;
796 U64 fileSize;
797 void** bufferPtr = &dict->dictBuffer;
798
799 assert(bufferPtr != NULL);
800 assert(dictFileStat != NULL);
801 *bufferPtr = NULL;
802 if (fileName == NULL) return 0;
803
804 DISPLAYLEVEL(4,"Loading %s as dictionary \n", fileName);
805
806 fileHandle = CreateFileA(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
807
808 if (fileHandle == INVALID_HANDLE_VALUE) {
809 EXM_THROW(33, "Couldn't open dictionary %s: %s", fileName, strerror(errno));
810 }
811
812 fileSize = UTIL_getFileSizeStat(dictFileStat);
813 {
814 size_t const dictSizeMax = prefs->patchFromMode ? prefs->memLimit : DICTSIZE_MAX;
815 if (fileSize > dictSizeMax) {
816 EXM_THROW(34, "Dictionary file %s is too large (> %u bytes)",
817 fileName, (unsigned)dictSizeMax); /* avoid extreme cases */
818 }
819 }
820
821 mapping = CreateFileMapping(fileHandle, NULL, PAGE_READONLY, 0, 0, NULL);
822 if (mapping == NULL) {
823 EXM_THROW(35, "Couldn't map dictionary %s: %s", fileName, strerror(errno));
824 }
825
826 *bufferPtr = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, (DWORD)fileSize); /* we can only cast to DWORD here because dictSize <= 2GB */
827 if (*bufferPtr==NULL) EXM_THROW(36, "%s", strerror(errno));
828
829 dict->dictHandle = fileHandle;
830 return (size_t)fileSize;
831 }
832 #else
FIO_setDictBufferMMap(FIO_Dict_t * dict,const char * fileName,FIO_prefs_t * const prefs,stat_t * dictFileStat)833 static size_t FIO_setDictBufferMMap(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat)
834 {
835 return FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
836 }
FIO_munmap(FIO_Dict_t * dict)837 static void FIO_munmap(FIO_Dict_t* dict) {
838 free(dict->dictBuffer);
839 dict->dictBuffer = NULL;
840 dict->dictBufferSize = 0;
841 }
842 #endif
843
FIO_freeDict(FIO_Dict_t * dict)844 static void FIO_freeDict(FIO_Dict_t* dict) {
845 if (dict->dictBufferType == FIO_mallocDict) {
846 free(dict->dictBuffer);
847 dict->dictBuffer = NULL;
848 dict->dictBufferSize = 0;
849 } else if (dict->dictBufferType == FIO_mmapDict) {
850 FIO_munmap(dict);
851 } else {
852 assert(0); /* Should not reach this case */
853 }
854 }
855
FIO_initDict(FIO_Dict_t * dict,const char * fileName,FIO_prefs_t * const prefs,stat_t * dictFileStat,FIO_dictBufferType_t dictBufferType)856 static void FIO_initDict(FIO_Dict_t* dict, const char* fileName, FIO_prefs_t* const prefs, stat_t* dictFileStat, FIO_dictBufferType_t dictBufferType) {
857 dict->dictBufferType = dictBufferType;
858 if (dict->dictBufferType == FIO_mallocDict) {
859 dict->dictBufferSize = FIO_setDictBufferMalloc(dict, fileName, prefs, dictFileStat);
860 } else if (dict->dictBufferType == FIO_mmapDict) {
861 dict->dictBufferSize = FIO_setDictBufferMMap(dict, fileName, prefs, dictFileStat);
862 } else {
863 assert(0); /* Should not reach this case */
864 }
865 }
866
867
868 /* FIO_checkFilenameCollisions() :
869 * Checks for and warns if there are any files that would have the same output path
870 */
FIO_checkFilenameCollisions(const char ** filenameTable,unsigned nbFiles)871 int FIO_checkFilenameCollisions(const char** filenameTable, unsigned nbFiles) {
872 const char **filenameTableSorted, *prevElem, *filename;
873 unsigned u;
874
875 filenameTableSorted = (const char**) malloc(sizeof(char*) * nbFiles);
876 if (!filenameTableSorted) {
877 DISPLAYLEVEL(1, "Allocation error during filename collision checking \n");
878 return 1;
879 }
880
881 for (u = 0; u < nbFiles; ++u) {
882 filename = strrchr(filenameTable[u], PATH_SEP);
883 if (filename == NULL) {
884 filenameTableSorted[u] = filenameTable[u];
885 } else {
886 filenameTableSorted[u] = filename+1;
887 }
888 }
889
890 qsort((void*)filenameTableSorted, nbFiles, sizeof(char*), UTIL_compareStr);
891 prevElem = filenameTableSorted[0];
892 for (u = 1; u < nbFiles; ++u) {
893 if (strcmp(prevElem, filenameTableSorted[u]) == 0) {
894 DISPLAYLEVEL(2, "WARNING: Two files have same filename: %s\n", prevElem);
895 }
896 prevElem = filenameTableSorted[u];
897 }
898
899 free((void*)filenameTableSorted);
900 return 0;
901 }
902
903 static const char*
extractFilename(const char * path,char separator)904 extractFilename(const char* path, char separator)
905 {
906 const char* search = strrchr(path, separator);
907 if (search == NULL) return path;
908 return search+1;
909 }
910
911 /* FIO_createFilename_fromOutDir() :
912 * Takes a source file name and specified output directory, and
913 * allocates memory for and returns a pointer to final path.
914 * This function never returns an error (it may abort() in case of pb)
915 */
916 static char*
FIO_createFilename_fromOutDir(const char * path,const char * outDirName,const size_t suffixLen)917 FIO_createFilename_fromOutDir(const char* path, const char* outDirName, const size_t suffixLen)
918 {
919 const char* filenameStart;
920 char separator;
921 char* result;
922
923 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
924 separator = '\\';
925 #else
926 separator = '/';
927 #endif
928
929 filenameStart = extractFilename(path, separator);
930 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
931 filenameStart = extractFilename(filenameStart, '/'); /* sometimes, '/' separator is also used on Windows (mingw+msys2) */
932 #endif
933
934 result = (char*) calloc(1, strlen(outDirName) + 1 + strlen(filenameStart) + suffixLen + 1);
935 if (!result) {
936 EXM_THROW(30, "zstd: FIO_createFilename_fromOutDir: %s", strerror(errno));
937 }
938
939 memcpy(result, outDirName, strlen(outDirName));
940 if (outDirName[strlen(outDirName)-1] == separator) {
941 memcpy(result + strlen(outDirName), filenameStart, strlen(filenameStart));
942 } else {
943 memcpy(result + strlen(outDirName), &separator, 1);
944 memcpy(result + strlen(outDirName) + 1, filenameStart, strlen(filenameStart));
945 }
946
947 return result;
948 }
949
950 /* FIO_highbit64() :
951 * gives position of highest bit.
952 * note : only works for v > 0 !
953 */
FIO_highbit64(unsigned long long v)954 static unsigned FIO_highbit64(unsigned long long v)
955 {
956 unsigned count = 0;
957 assert(v != 0);
958 v >>= 1;
959 while (v) { v >>= 1; count++; }
960 return count;
961 }
962
FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t * const prefs,unsigned long long const dictSize,unsigned long long const maxSrcFileSize)963 static void FIO_adjustMemLimitForPatchFromMode(FIO_prefs_t* const prefs,
964 unsigned long long const dictSize,
965 unsigned long long const maxSrcFileSize)
966 {
967 unsigned long long maxSize = MAX(prefs->memLimit, MAX(dictSize, maxSrcFileSize));
968 unsigned const maxWindowSize = (1U << ZSTD_WINDOWLOG_MAX);
969 if (maxSize == UTIL_FILESIZE_UNKNOWN)
970 EXM_THROW(42, "Using --patch-from with stdin requires --stream-size");
971 assert(maxSize != UTIL_FILESIZE_UNKNOWN);
972 if (maxSize > maxWindowSize)
973 EXM_THROW(42, "Can't handle files larger than %u GB\n", maxWindowSize/(1 GB));
974 FIO_setMemLimit(prefs, (unsigned)maxSize);
975 }
976
977 /* FIO_multiFilesConcatWarning() :
978 * This function handles logic when processing multiple files with -o or -c, displaying the appropriate warnings/prompts.
979 * Returns 1 if the console should abort, 0 if console should proceed.
980 *
981 * If output is stdout or test mode is active, check that `--rm` disabled.
982 *
983 * If there is just 1 file to process, zstd will proceed as usual.
984 * If each file get processed into its own separate destination file, proceed as usual.
985 *
986 * When multiple files are processed into a single output,
987 * display a warning message, then disable --rm if it's set.
988 *
989 * If -f is specified or if output is stdout, just proceed.
990 * If output is set with -o, prompt for confirmation.
991 */
FIO_multiFilesConcatWarning(const FIO_ctx_t * fCtx,FIO_prefs_t * prefs,const char * outFileName,int displayLevelCutoff)992 static int FIO_multiFilesConcatWarning(const FIO_ctx_t* fCtx, FIO_prefs_t* prefs, const char* outFileName, int displayLevelCutoff)
993 {
994 if (fCtx->hasStdoutOutput) {
995 if (prefs->removeSrcFile)
996 /* this should not happen ; hard fail, to protect user's data
997 * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
998 EXM_THROW(43, "It's not allowed to remove input files when processed output is piped to stdout. "
999 "This scenario is not supposed to be possible. "
1000 "This is a programming error. File an issue for it to be fixed.");
1001 }
1002 if (prefs->testMode) {
1003 if (prefs->removeSrcFile)
1004 /* this should not happen ; hard fail, to protect user's data
1005 * note: this should rather be an assert(), but we want to be certain that user's data will not be wiped out in case it nonetheless happen */
1006 EXM_THROW(43, "Test mode shall not remove input files! "
1007 "This scenario is not supposed to be possible. "
1008 "This is a programming error. File an issue for it to be fixed.");
1009 return 0;
1010 }
1011
1012 if (fCtx->nbFilesTotal == 1) return 0;
1013 assert(fCtx->nbFilesTotal > 1);
1014
1015 if (!outFileName) return 0;
1016
1017 if (fCtx->hasStdoutOutput) {
1018 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into stdout. \n");
1019 } else {
1020 DISPLAYLEVEL(2, "zstd: WARNING: all input files will be processed and concatenated into a single output file: %s \n", outFileName);
1021 }
1022 DISPLAYLEVEL(2, "The concatenated output CANNOT regenerate original file names nor directory structure. \n")
1023
1024 /* multi-input into single output : --rm is not allowed */
1025 if (prefs->removeSrcFile) {
1026 DISPLAYLEVEL(2, "Since it's a destructive operation, input files will not be removed. \n");
1027 prefs->removeSrcFile = 0;
1028 }
1029
1030 if (fCtx->hasStdoutOutput) return 0;
1031 if (prefs->overwrite) return 0;
1032
1033 /* multiple files concatenated into single destination file using -o without -f */
1034 if (g_display_prefs.displayLevel <= displayLevelCutoff) {
1035 /* quiet mode => no prompt => fail automatically */
1036 DISPLAYLEVEL(1, "Concatenating multiple processed inputs into a single output loses file metadata. \n");
1037 DISPLAYLEVEL(1, "Aborting. \n");
1038 return 1;
1039 }
1040 /* normal mode => prompt */
1041 return UTIL_requireUserConfirmation("Proceed? (y/n): ", "Aborting...", "yY", fCtx->hasStdinInput);
1042 }
1043
setInBuffer(const void * buf,size_t s,size_t pos)1044 static ZSTD_inBuffer setInBuffer(const void* buf, size_t s, size_t pos)
1045 {
1046 ZSTD_inBuffer i;
1047 i.src = buf;
1048 i.size = s;
1049 i.pos = pos;
1050 return i;
1051 }
1052
setOutBuffer(void * buf,size_t s,size_t pos)1053 static ZSTD_outBuffer setOutBuffer(void* buf, size_t s, size_t pos)
1054 {
1055 ZSTD_outBuffer o;
1056 o.dst = buf;
1057 o.size = s;
1058 o.pos = pos;
1059 return o;
1060 }
1061
1062 #ifndef ZSTD_NOCOMPRESS
1063
1064 /* **********************************************************************
1065 * Compression
1066 ************************************************************************/
1067 typedef struct {
1068 FIO_Dict_t dict;
1069 const char* dictFileName;
1070 stat_t dictFileStat;
1071 ZSTD_CStream* cctx;
1072 WritePoolCtx_t *writeCtx;
1073 ReadPoolCtx_t *readCtx;
1074 } cRess_t;
1075
1076 /** ZSTD_cycleLog() :
1077 * condition for correct operation : hashLog > 1 */
ZSTD_cycleLog(U32 hashLog,ZSTD_strategy strat)1078 static U32 ZSTD_cycleLog(U32 hashLog, ZSTD_strategy strat)
1079 {
1080 U32 const btScale = ((U32)strat >= (U32)ZSTD_btlazy2);
1081 assert(hashLog > 1);
1082 return hashLog - btScale;
1083 }
1084
FIO_adjustParamsForPatchFromMode(FIO_prefs_t * const prefs,ZSTD_compressionParameters * comprParams,unsigned long long const dictSize,unsigned long long const maxSrcFileSize,int cLevel)1085 static void FIO_adjustParamsForPatchFromMode(FIO_prefs_t* const prefs,
1086 ZSTD_compressionParameters* comprParams,
1087 unsigned long long const dictSize,
1088 unsigned long long const maxSrcFileSize,
1089 int cLevel)
1090 {
1091 unsigned const fileWindowLog = FIO_highbit64(maxSrcFileSize) + 1;
1092 ZSTD_compressionParameters const cParams = ZSTD_getCParams(cLevel, (size_t)maxSrcFileSize, (size_t)dictSize);
1093 FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, maxSrcFileSize);
1094 if (fileWindowLog > ZSTD_WINDOWLOG_MAX)
1095 DISPLAYLEVEL(1, "Max window log exceeded by file (compression ratio will suffer)\n");
1096 comprParams->windowLog = MAX(ZSTD_WINDOWLOG_MIN, MIN(ZSTD_WINDOWLOG_MAX, fileWindowLog));
1097 if (fileWindowLog > ZSTD_cycleLog(cParams.chainLog, cParams.strategy)) {
1098 if (!prefs->ldmFlag)
1099 DISPLAYLEVEL(2, "long mode automatically triggered\n");
1100 FIO_setLdmFlag(prefs, 1);
1101 }
1102 if (cParams.strategy >= ZSTD_btopt) {
1103 DISPLAYLEVEL(4, "[Optimal parser notes] Consider the following to improve patch size at the cost of speed:\n");
1104 DISPLAYLEVEL(4, "- Set a larger targetLength (e.g. --zstd=targetLength=4096)\n");
1105 DISPLAYLEVEL(4, "- Set a larger chainLog (e.g. --zstd=chainLog=%u)\n", ZSTD_CHAINLOG_MAX);
1106 DISPLAYLEVEL(4, "- Set a larger LDM hashLog (e.g. --zstd=ldmHashLog=%u)\n", ZSTD_LDM_HASHLOG_MAX);
1107 DISPLAYLEVEL(4, "- Set a smaller LDM rateLog (e.g. --zstd=ldmHashRateLog=%u)\n", ZSTD_LDM_HASHRATELOG_MIN);
1108 DISPLAYLEVEL(4, "Also consider playing around with searchLog and hashLog\n");
1109 }
1110 }
1111
FIO_createCResources(FIO_prefs_t * const prefs,const char * dictFileName,unsigned long long const maxSrcFileSize,int cLevel,ZSTD_compressionParameters comprParams)1112 static cRess_t FIO_createCResources(FIO_prefs_t* const prefs,
1113 const char* dictFileName, unsigned long long const maxSrcFileSize,
1114 int cLevel, ZSTD_compressionParameters comprParams) {
1115 int useMMap = prefs->mmapDict == ZSTD_ps_enable;
1116 int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable;
1117 FIO_dictBufferType_t dictBufferType;
1118 cRess_t ress;
1119 memset(&ress, 0, sizeof(ress));
1120
1121 DISPLAYLEVEL(6, "FIO_createCResources \n");
1122 ress.cctx = ZSTD_createCCtx();
1123 if (ress.cctx == NULL)
1124 EXM_THROW(30, "allocation error (%s): can't create ZSTD_CCtx",
1125 strerror(errno));
1126
1127 FIO_getDictFileStat(dictFileName, &ress.dictFileStat);
1128
1129 /* need to update memLimit before calling createDictBuffer
1130 * because of memLimit check inside it */
1131 if (prefs->patchFromMode) {
1132 U64 const dictSize = UTIL_getFileSizeStat(&ress.dictFileStat);
1133 unsigned long long const ssSize = (unsigned long long)prefs->streamSrcSize;
1134 useMMap |= dictSize > prefs->memLimit;
1135 FIO_adjustParamsForPatchFromMode(prefs, &comprParams, dictSize, ssSize > 0 ? ssSize : maxSrcFileSize, cLevel);
1136 }
1137
1138 dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict;
1139 FIO_initDict(&ress.dict, dictFileName, prefs, &ress.dictFileStat, dictBufferType); /* works with dictFileName==NULL */
1140
1141 ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_CStreamOutSize());
1142 ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_CStreamInSize());
1143
1144 /* Advanced parameters, including dictionary */
1145 if (dictFileName && (ress.dict.dictBuffer==NULL))
1146 EXM_THROW(32, "allocation error : can't create dictBuffer");
1147 ress.dictFileName = dictFileName;
1148
1149 if (prefs->adaptiveMode && !prefs->ldmFlag && !comprParams.windowLog)
1150 comprParams.windowLog = ADAPT_WINDOWLOG_DEFAULT;
1151
1152 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_contentSizeFlag, prefs->contentSize) ); /* always enable content size when available (note: supposed to be default) */
1153 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_dictIDFlag, prefs->dictIDFlag) );
1154 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_checksumFlag, prefs->checksumFlag) );
1155 /* compression level */
1156 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, cLevel) );
1157 /* max compressed block size */
1158 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetCBlockSize, (int)prefs->targetCBlockSize) );
1159 /* source size hint */
1160 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_srcSizeHint, (int)prefs->srcSizeHint) );
1161 /* long distance matching */
1162 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableLongDistanceMatching, prefs->ldmFlag) );
1163 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashLog, prefs->ldmHashLog) );
1164 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmMinMatch, prefs->ldmMinMatch) );
1165 if (prefs->ldmBucketSizeLog != FIO_LDM_PARAM_NOTSET) {
1166 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmBucketSizeLog, prefs->ldmBucketSizeLog) );
1167 }
1168 if (prefs->ldmHashRateLog != FIO_LDM_PARAM_NOTSET) {
1169 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_ldmHashRateLog, prefs->ldmHashRateLog) );
1170 }
1171 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_useRowMatchFinder, prefs->useRowMatchFinder));
1172 /* compression parameters */
1173 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_windowLog, (int)comprParams.windowLog) );
1174 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_chainLog, (int)comprParams.chainLog) );
1175 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_hashLog, (int)comprParams.hashLog) );
1176 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_searchLog, (int)comprParams.searchLog) );
1177 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_minMatch, (int)comprParams.minMatch) );
1178 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_targetLength, (int)comprParams.targetLength) );
1179 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_strategy, (int)comprParams.strategy) );
1180 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_literalCompressionMode, (int)prefs->literalCompressionMode) );
1181 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_enableDedicatedDictSearch, 1) );
1182 /* multi-threading */
1183 #ifdef ZSTD_MULTITHREAD
1184 DISPLAYLEVEL(5,"set nb workers = %u \n", prefs->nbWorkers);
1185 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_nbWorkers, prefs->nbWorkers) );
1186 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_jobSize, prefs->blockSize) );
1187 if (prefs->overlapLog != FIO_OVERLAP_LOG_NOTSET) {
1188 DISPLAYLEVEL(3,"set overlapLog = %u \n", prefs->overlapLog);
1189 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_overlapLog, prefs->overlapLog) );
1190 }
1191 CHECK( ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_rsyncable, prefs->rsyncable) );
1192 #endif
1193 /* dictionary */
1194 if (prefs->patchFromMode) {
1195 CHECK( ZSTD_CCtx_refPrefix(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) );
1196 } else {
1197 CHECK( ZSTD_CCtx_loadDictionary_byReference(ress.cctx, ress.dict.dictBuffer, ress.dict.dictBufferSize) );
1198 }
1199
1200 return ress;
1201 }
1202
FIO_freeCResources(cRess_t * const ress)1203 static void FIO_freeCResources(cRess_t* const ress)
1204 {
1205 FIO_freeDict(&(ress->dict));
1206 AIO_WritePool_free(ress->writeCtx);
1207 AIO_ReadPool_free(ress->readCtx);
1208 ZSTD_freeCStream(ress->cctx); /* never fails */
1209 }
1210
1211
1212 #ifdef ZSTD_GZCOMPRESS
1213 static unsigned long long
FIO_compressGzFrame(const cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,U64 * readsize)1214 FIO_compressGzFrame(const cRess_t* ress, /* buffers & handlers are used, but not changed */
1215 const char* srcFileName, U64 const srcFileSize,
1216 int compressionLevel, U64* readsize)
1217 {
1218 unsigned long long inFileSize = 0, outFileSize = 0;
1219 z_stream strm;
1220 IOJob_t *writeJob = NULL;
1221
1222 if (compressionLevel > Z_BEST_COMPRESSION)
1223 compressionLevel = Z_BEST_COMPRESSION;
1224
1225 strm.zalloc = Z_NULL;
1226 strm.zfree = Z_NULL;
1227 strm.opaque = Z_NULL;
1228
1229 { int const ret = deflateInit2(&strm, compressionLevel, Z_DEFLATED,
1230 15 /* maxWindowLogSize */ + 16 /* gzip only */,
1231 8, Z_DEFAULT_STRATEGY); /* see https://www.zlib.net/manual.html */
1232 if (ret != Z_OK) {
1233 EXM_THROW(71, "zstd: %s: deflateInit2 error %d \n", srcFileName, ret);
1234 } }
1235
1236 writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
1237 strm.next_in = 0;
1238 strm.avail_in = 0;
1239 strm.next_out = (Bytef*)writeJob->buffer;
1240 strm.avail_out = (uInt)writeJob->bufferSize;
1241
1242 while (1) {
1243 int ret;
1244 if (strm.avail_in == 0) {
1245 AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
1246 if (ress->readCtx->srcBufferLoaded == 0) break;
1247 inFileSize += ress->readCtx->srcBufferLoaded;
1248 strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
1249 strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
1250 }
1251
1252 {
1253 size_t const availBefore = strm.avail_in;
1254 ret = deflate(&strm, Z_NO_FLUSH);
1255 AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
1256 }
1257
1258 if (ret != Z_OK)
1259 EXM_THROW(72, "zstd: %s: deflate error %d \n", srcFileName, ret);
1260 { size_t const cSize = writeJob->bufferSize - strm.avail_out;
1261 if (cSize) {
1262 writeJob->usedBufferSize = cSize;
1263 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1264 outFileSize += cSize;
1265 strm.next_out = (Bytef*)writeJob->buffer;
1266 strm.avail_out = (uInt)writeJob->bufferSize;
1267 } }
1268 if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
1269 DISPLAYUPDATE_PROGRESS(
1270 "\rRead : %u MB ==> %.2f%% ",
1271 (unsigned)(inFileSize>>20),
1272 (double)outFileSize/(double)inFileSize*100)
1273 } else {
1274 DISPLAYUPDATE_PROGRESS(
1275 "\rRead : %u / %u MB ==> %.2f%% ",
1276 (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
1277 (double)outFileSize/(double)inFileSize*100);
1278 } }
1279
1280 while (1) {
1281 int const ret = deflate(&strm, Z_FINISH);
1282 { size_t const cSize = writeJob->bufferSize - strm.avail_out;
1283 if (cSize) {
1284 writeJob->usedBufferSize = cSize;
1285 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1286 outFileSize += cSize;
1287 strm.next_out = (Bytef*)writeJob->buffer;
1288 strm.avail_out = (uInt)writeJob->bufferSize;
1289 } }
1290 if (ret == Z_STREAM_END) break;
1291 if (ret != Z_BUF_ERROR)
1292 EXM_THROW(77, "zstd: %s: deflate error %d \n", srcFileName, ret);
1293 }
1294
1295 { int const ret = deflateEnd(&strm);
1296 if (ret != Z_OK) {
1297 EXM_THROW(79, "zstd: %s: deflateEnd error %d \n", srcFileName, ret);
1298 } }
1299 *readsize = inFileSize;
1300 AIO_WritePool_releaseIoJob(writeJob);
1301 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
1302 return outFileSize;
1303 }
1304 #endif
1305
1306
1307 #ifdef ZSTD_LZMACOMPRESS
1308 static unsigned long long
FIO_compressLzmaFrame(cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,U64 * readsize,int plain_lzma)1309 FIO_compressLzmaFrame(cRess_t* ress,
1310 const char* srcFileName, U64 const srcFileSize,
1311 int compressionLevel, U64* readsize, int plain_lzma)
1312 {
1313 unsigned long long inFileSize = 0, outFileSize = 0;
1314 lzma_stream strm = LZMA_STREAM_INIT;
1315 lzma_action action = LZMA_RUN;
1316 lzma_ret ret;
1317 IOJob_t *writeJob = NULL;
1318
1319 if (compressionLevel < 0) compressionLevel = 0;
1320 if (compressionLevel > 9) compressionLevel = 9;
1321
1322 if (plain_lzma) {
1323 lzma_options_lzma opt_lzma;
1324 if (lzma_lzma_preset(&opt_lzma, compressionLevel))
1325 EXM_THROW(81, "zstd: %s: lzma_lzma_preset error", srcFileName);
1326 ret = lzma_alone_encoder(&strm, &opt_lzma); /* LZMA */
1327 if (ret != LZMA_OK)
1328 EXM_THROW(82, "zstd: %s: lzma_alone_encoder error %d", srcFileName, ret);
1329 } else {
1330 ret = lzma_easy_encoder(&strm, compressionLevel, LZMA_CHECK_CRC64); /* XZ */
1331 if (ret != LZMA_OK)
1332 EXM_THROW(83, "zstd: %s: lzma_easy_encoder error %d", srcFileName, ret);
1333 }
1334
1335 writeJob =AIO_WritePool_acquireJob(ress->writeCtx);
1336 strm.next_out = (BYTE*)writeJob->buffer;
1337 strm.avail_out = writeJob->bufferSize;
1338 strm.next_in = 0;
1339 strm.avail_in = 0;
1340
1341 while (1) {
1342 if (strm.avail_in == 0) {
1343 size_t const inSize = AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_CStreamInSize());
1344 if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
1345 inFileSize += inSize;
1346 strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
1347 strm.avail_in = ress->readCtx->srcBufferLoaded;
1348 }
1349
1350 {
1351 size_t const availBefore = strm.avail_in;
1352 ret = lzma_code(&strm, action);
1353 AIO_ReadPool_consumeBytes(ress->readCtx, availBefore - strm.avail_in);
1354 }
1355
1356
1357 if (ret != LZMA_OK && ret != LZMA_STREAM_END)
1358 EXM_THROW(84, "zstd: %s: lzma_code encoding error %d", srcFileName, ret);
1359 { size_t const compBytes = writeJob->bufferSize - strm.avail_out;
1360 if (compBytes) {
1361 writeJob->usedBufferSize = compBytes;
1362 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1363 outFileSize += compBytes;
1364 strm.next_out = (BYTE*)writeJob->buffer;
1365 strm.avail_out = writeJob->bufferSize;
1366 } }
1367 if (srcFileSize == UTIL_FILESIZE_UNKNOWN)
1368 DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
1369 (unsigned)(inFileSize>>20),
1370 (double)outFileSize/(double)inFileSize*100)
1371 else
1372 DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
1373 (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
1374 (double)outFileSize/(double)inFileSize*100);
1375 if (ret == LZMA_STREAM_END) break;
1376 }
1377
1378 lzma_end(&strm);
1379 *readsize = inFileSize;
1380
1381 AIO_WritePool_releaseIoJob(writeJob);
1382 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
1383
1384 return outFileSize;
1385 }
1386 #endif
1387
1388 #ifdef ZSTD_LZ4COMPRESS
1389
1390 #if LZ4_VERSION_NUMBER <= 10600
1391 #define LZ4F_blockLinked blockLinked
1392 #define LZ4F_max64KB max64KB
1393 #endif
1394
FIO_LZ4_GetBlockSize_FromBlockId(int id)1395 static int FIO_LZ4_GetBlockSize_FromBlockId (int id) { return (1 << (8 + (2 * id))); }
1396
1397 static unsigned long long
FIO_compressLz4Frame(cRess_t * ress,const char * srcFileName,U64 const srcFileSize,int compressionLevel,int checksumFlag,U64 * readsize)1398 FIO_compressLz4Frame(cRess_t* ress,
1399 const char* srcFileName, U64 const srcFileSize,
1400 int compressionLevel, int checksumFlag,
1401 U64* readsize)
1402 {
1403 const size_t blockSize = FIO_LZ4_GetBlockSize_FromBlockId(LZ4F_max64KB);
1404 unsigned long long inFileSize = 0, outFileSize = 0;
1405
1406 LZ4F_preferences_t prefs;
1407 LZ4F_compressionContext_t ctx;
1408
1409 IOJob_t* writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
1410
1411 LZ4F_errorCode_t const errorCode = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
1412 if (LZ4F_isError(errorCode))
1413 EXM_THROW(31, "zstd: failed to create lz4 compression context");
1414
1415 memset(&prefs, 0, sizeof(prefs));
1416
1417 assert(blockSize <= ress->readCtx->base.jobBufferSize);
1418
1419 /* autoflush off to mitigate a bug in lz4<=1.9.3 for compression level 12 */
1420 prefs.autoFlush = 0;
1421 prefs.compressionLevel = compressionLevel;
1422 prefs.frameInfo.blockMode = LZ4F_blockLinked;
1423 prefs.frameInfo.blockSizeID = LZ4F_max64KB;
1424 prefs.frameInfo.contentChecksumFlag = (contentChecksum_t)checksumFlag;
1425 #if LZ4_VERSION_NUMBER >= 10600
1426 prefs.frameInfo.contentSize = (srcFileSize==UTIL_FILESIZE_UNKNOWN) ? 0 : srcFileSize;
1427 #endif
1428 assert(LZ4F_compressBound(blockSize, &prefs) <= writeJob->bufferSize);
1429
1430 {
1431 size_t headerSize = LZ4F_compressBegin(ctx, writeJob->buffer, writeJob->bufferSize, &prefs);
1432 if (LZ4F_isError(headerSize))
1433 EXM_THROW(33, "File header generation failed : %s",
1434 LZ4F_getErrorName(headerSize));
1435 writeJob->usedBufferSize = headerSize;
1436 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1437 outFileSize += headerSize;
1438
1439 /* Read first block */
1440 inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
1441
1442 /* Main Loop */
1443 while (ress->readCtx->srcBufferLoaded) {
1444 size_t inSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
1445 size_t const outSize = LZ4F_compressUpdate(ctx, writeJob->buffer, writeJob->bufferSize,
1446 ress->readCtx->srcBuffer, inSize, NULL);
1447 if (LZ4F_isError(outSize))
1448 EXM_THROW(35, "zstd: %s: lz4 compression failed : %s",
1449 srcFileName, LZ4F_getErrorName(outSize));
1450 outFileSize += outSize;
1451 if (srcFileSize == UTIL_FILESIZE_UNKNOWN) {
1452 DISPLAYUPDATE_PROGRESS("\rRead : %u MB ==> %.2f%%",
1453 (unsigned)(inFileSize>>20),
1454 (double)outFileSize/(double)inFileSize*100)
1455 } else {
1456 DISPLAYUPDATE_PROGRESS("\rRead : %u / %u MB ==> %.2f%%",
1457 (unsigned)(inFileSize>>20), (unsigned)(srcFileSize>>20),
1458 (double)outFileSize/(double)inFileSize*100);
1459 }
1460
1461 /* Write Block */
1462 writeJob->usedBufferSize = outSize;
1463 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1464
1465 /* Read next block */
1466 AIO_ReadPool_consumeBytes(ress->readCtx, inSize);
1467 inFileSize += AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
1468 }
1469
1470 /* End of Stream mark */
1471 headerSize = LZ4F_compressEnd(ctx, writeJob->buffer, writeJob->bufferSize, NULL);
1472 if (LZ4F_isError(headerSize))
1473 EXM_THROW(38, "zstd: %s: lz4 end of file generation failed : %s",
1474 srcFileName, LZ4F_getErrorName(headerSize));
1475
1476 writeJob->usedBufferSize = headerSize;
1477 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1478 outFileSize += headerSize;
1479 }
1480
1481 *readsize = inFileSize;
1482 LZ4F_freeCompressionContext(ctx);
1483 AIO_WritePool_releaseIoJob(writeJob);
1484 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
1485
1486 return outFileSize;
1487 }
1488 #endif
1489
1490 static unsigned long long
FIO_compressZstdFrame(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const cRess_t * ressPtr,const char * srcFileName,U64 fileSize,int compressionLevel,U64 * readsize)1491 FIO_compressZstdFrame(FIO_ctx_t* const fCtx,
1492 FIO_prefs_t* const prefs,
1493 const cRess_t* ressPtr,
1494 const char* srcFileName, U64 fileSize,
1495 int compressionLevel, U64* readsize)
1496 {
1497 cRess_t const ress = *ressPtr;
1498 IOJob_t* writeJob = AIO_WritePool_acquireJob(ressPtr->writeCtx);
1499
1500 U64 compressedfilesize = 0;
1501 ZSTD_EndDirective directive = ZSTD_e_continue;
1502 U64 pledgedSrcSize = ZSTD_CONTENTSIZE_UNKNOWN;
1503
1504 /* stats */
1505 ZSTD_frameProgression previous_zfp_update = { 0, 0, 0, 0, 0, 0 };
1506 ZSTD_frameProgression previous_zfp_correction = { 0, 0, 0, 0, 0, 0 };
1507 typedef enum { noChange, slower, faster } speedChange_e;
1508 speedChange_e speedChange = noChange;
1509 unsigned flushWaiting = 0;
1510 unsigned inputPresented = 0;
1511 unsigned inputBlocked = 0;
1512 unsigned lastJobID = 0;
1513 UTIL_time_t lastAdaptTime = UTIL_getTime();
1514 U64 const adaptEveryMicro = REFRESH_RATE;
1515
1516 UTIL_HumanReadableSize_t const file_hrs = UTIL_makeHumanReadableSize(fileSize);
1517
1518 DISPLAYLEVEL(6, "compression using zstd format \n");
1519
1520 /* init */
1521 if (fileSize != UTIL_FILESIZE_UNKNOWN) {
1522 pledgedSrcSize = fileSize;
1523 CHECK(ZSTD_CCtx_setPledgedSrcSize(ress.cctx, fileSize));
1524 } else if (prefs->streamSrcSize > 0) {
1525 /* unknown source size; use the declared stream size */
1526 pledgedSrcSize = prefs->streamSrcSize;
1527 CHECK( ZSTD_CCtx_setPledgedSrcSize(ress.cctx, prefs->streamSrcSize) );
1528 }
1529
1530 { int windowLog;
1531 UTIL_HumanReadableSize_t windowSize;
1532 CHECK(ZSTD_CCtx_getParameter(ress.cctx, ZSTD_c_windowLog, &windowLog));
1533 if (windowLog == 0) {
1534 if (prefs->ldmFlag) {
1535 /* If long mode is set without a window size libzstd will set this size internally */
1536 windowLog = ZSTD_WINDOWLOG_LIMIT_DEFAULT;
1537 } else {
1538 const ZSTD_compressionParameters cParams = ZSTD_getCParams(compressionLevel, fileSize, 0);
1539 windowLog = (int)cParams.windowLog;
1540 }
1541 }
1542 windowSize = UTIL_makeHumanReadableSize(MAX(1ULL, MIN(1ULL << windowLog, pledgedSrcSize)));
1543 DISPLAYLEVEL(4, "Decompression will require %.*f%s of memory\n", windowSize.precision, windowSize.value, windowSize.suffix);
1544 }
1545
1546 /* Main compression loop */
1547 do {
1548 size_t stillToFlush;
1549 /* Fill input Buffer */
1550 size_t const inSize = AIO_ReadPool_fillBuffer(ress.readCtx, ZSTD_CStreamInSize());
1551 ZSTD_inBuffer inBuff = setInBuffer( ress.readCtx->srcBuffer, ress.readCtx->srcBufferLoaded, 0 );
1552 DISPLAYLEVEL(6, "fread %u bytes from source \n", (unsigned)inSize);
1553 *readsize += inSize;
1554
1555 if ((ress.readCtx->srcBufferLoaded == 0) || (*readsize == fileSize))
1556 directive = ZSTD_e_end;
1557
1558 stillToFlush = 1;
1559 while ((inBuff.pos != inBuff.size) /* input buffer must be entirely ingested */
1560 || (directive == ZSTD_e_end && stillToFlush != 0) ) {
1561
1562 size_t const oldIPos = inBuff.pos;
1563 ZSTD_outBuffer outBuff = setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
1564 size_t const toFlushNow = ZSTD_toFlushNow(ress.cctx);
1565 CHECK_V(stillToFlush, ZSTD_compressStream2(ress.cctx, &outBuff, &inBuff, directive));
1566 AIO_ReadPool_consumeBytes(ress.readCtx, inBuff.pos - oldIPos);
1567
1568 /* count stats */
1569 inputPresented++;
1570 if (oldIPos == inBuff.pos) inputBlocked++; /* input buffer is full and can't take any more : input speed is faster than consumption rate */
1571 if (!toFlushNow) flushWaiting = 1;
1572
1573 /* Write compressed stream */
1574 DISPLAYLEVEL(6, "ZSTD_compress_generic(end:%u) => input pos(%u)<=(%u)size ; output generated %u bytes \n",
1575 (unsigned)directive, (unsigned)inBuff.pos, (unsigned)inBuff.size, (unsigned)outBuff.pos);
1576 if (outBuff.pos) {
1577 writeJob->usedBufferSize = outBuff.pos;
1578 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
1579 compressedfilesize += outBuff.pos;
1580 }
1581
1582 /* adaptive mode : statistics measurement and speed correction */
1583 if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) {
1584 ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
1585
1586 lastAdaptTime = UTIL_getTime();
1587
1588 /* check output speed */
1589 if (zfp.currentJobID > 1) { /* only possible if nbWorkers >= 1 */
1590
1591 unsigned long long newlyProduced = zfp.produced - previous_zfp_update.produced;
1592 unsigned long long newlyFlushed = zfp.flushed - previous_zfp_update.flushed;
1593 assert(zfp.produced >= previous_zfp_update.produced);
1594 assert(prefs->nbWorkers >= 1);
1595
1596 /* test if compression is blocked
1597 * either because output is slow and all buffers are full
1598 * or because input is slow and no job can start while waiting for at least one buffer to be filled.
1599 * note : exclude starting part, since currentJobID > 1 */
1600 if ( (zfp.consumed == previous_zfp_update.consumed) /* no data compressed : no data available, or no more buffer to compress to, OR compression is really slow (compression of a single block is slower than update rate)*/
1601 && (zfp.nbActiveWorkers == 0) /* confirmed : no compression ongoing */
1602 ) {
1603 DISPLAYLEVEL(6, "all buffers full : compression stopped => slow down \n")
1604 speedChange = slower;
1605 }
1606
1607 previous_zfp_update = zfp;
1608
1609 if ( (newlyProduced > (newlyFlushed * 9 / 8)) /* compression produces more data than output can flush (though production can be spiky, due to work unit : (N==4)*block sizes) */
1610 && (flushWaiting == 0) /* flush speed was never slowed by lack of production, so it's operating at max capacity */
1611 ) {
1612 DISPLAYLEVEL(6, "compression faster than flush (%llu > %llu), and flushed was never slowed down by lack of production => slow down \n", newlyProduced, newlyFlushed);
1613 speedChange = slower;
1614 }
1615 flushWaiting = 0;
1616 }
1617
1618 /* course correct only if there is at least one new job completed */
1619 if (zfp.currentJobID > lastJobID) {
1620 DISPLAYLEVEL(6, "compression level adaptation check \n")
1621
1622 /* check input speed */
1623 if (zfp.currentJobID > (unsigned)(prefs->nbWorkers+1)) { /* warm up period, to fill all workers */
1624 if (inputBlocked <= 0) {
1625 DISPLAYLEVEL(6, "input is never blocked => input is slower than ingestion \n");
1626 speedChange = slower;
1627 } else if (speedChange == noChange) {
1628 unsigned long long newlyIngested = zfp.ingested - previous_zfp_correction.ingested;
1629 unsigned long long newlyConsumed = zfp.consumed - previous_zfp_correction.consumed;
1630 unsigned long long newlyProduced = zfp.produced - previous_zfp_correction.produced;
1631 unsigned long long newlyFlushed = zfp.flushed - previous_zfp_correction.flushed;
1632 previous_zfp_correction = zfp;
1633 assert(inputPresented > 0);
1634 DISPLAYLEVEL(6, "input blocked %u/%u(%.2f) - ingested:%u vs %u:consumed - flushed:%u vs %u:produced \n",
1635 inputBlocked, inputPresented, (double)inputBlocked/inputPresented*100,
1636 (unsigned)newlyIngested, (unsigned)newlyConsumed,
1637 (unsigned)newlyFlushed, (unsigned)newlyProduced);
1638 if ( (inputBlocked > inputPresented / 8) /* input is waiting often, because input buffers is full : compression or output too slow */
1639 && (newlyFlushed * 33 / 32 > newlyProduced) /* flush everything that is produced */
1640 && (newlyIngested * 33 / 32 > newlyConsumed) /* input speed as fast or faster than compression speed */
1641 ) {
1642 DISPLAYLEVEL(6, "recommend faster as in(%llu) >= (%llu)comp(%llu) <= out(%llu) \n",
1643 newlyIngested, newlyConsumed, newlyProduced, newlyFlushed);
1644 speedChange = faster;
1645 }
1646 }
1647 inputBlocked = 0;
1648 inputPresented = 0;
1649 }
1650
1651 if (speedChange == slower) {
1652 DISPLAYLEVEL(6, "slower speed , higher compression \n")
1653 compressionLevel ++;
1654 if (compressionLevel > ZSTD_maxCLevel()) compressionLevel = ZSTD_maxCLevel();
1655 if (compressionLevel > prefs->maxAdaptLevel) compressionLevel = prefs->maxAdaptLevel;
1656 compressionLevel += (compressionLevel == 0); /* skip 0 */
1657 ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
1658 }
1659 if (speedChange == faster) {
1660 DISPLAYLEVEL(6, "faster speed , lighter compression \n")
1661 compressionLevel --;
1662 if (compressionLevel < prefs->minAdaptLevel) compressionLevel = prefs->minAdaptLevel;
1663 compressionLevel -= (compressionLevel == 0); /* skip 0 */
1664 ZSTD_CCtx_setParameter(ress.cctx, ZSTD_c_compressionLevel, compressionLevel);
1665 }
1666 speedChange = noChange;
1667
1668 lastJobID = zfp.currentJobID;
1669 } /* if (zfp.currentJobID > lastJobID) */
1670 } /* if (prefs->adaptiveMode && UTIL_clockSpanMicro(lastAdaptTime) > adaptEveryMicro) */
1671
1672 /* display notification */
1673 if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) {
1674 ZSTD_frameProgression const zfp = ZSTD_getFrameProgression(ress.cctx);
1675 double const cShare = (double)zfp.produced / (double)(zfp.consumed + !zfp.consumed/*avoid div0*/) * 100;
1676 UTIL_HumanReadableSize_t const buffered_hrs = UTIL_makeHumanReadableSize(zfp.ingested - zfp.consumed);
1677 UTIL_HumanReadableSize_t const consumed_hrs = UTIL_makeHumanReadableSize(zfp.consumed);
1678 UTIL_HumanReadableSize_t const produced_hrs = UTIL_makeHumanReadableSize(zfp.produced);
1679
1680 DELAY_NEXT_UPDATE();
1681
1682 /* display progress notifications */
1683 DISPLAY_PROGRESS("\r%79s\r", ""); /* Clear out the current displayed line */
1684 if (g_display_prefs.displayLevel >= 3) {
1685 /* Verbose progress update */
1686 DISPLAY_PROGRESS(
1687 "(L%i) Buffered:%5.*f%s - Consumed:%5.*f%s - Compressed:%5.*f%s => %.2f%% ",
1688 compressionLevel,
1689 buffered_hrs.precision, buffered_hrs.value, buffered_hrs.suffix,
1690 consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix,
1691 produced_hrs.precision, produced_hrs.value, produced_hrs.suffix,
1692 cShare );
1693 } else {
1694 /* Require level 2 or forcibly displayed progress counter for summarized updates */
1695 if (fCtx->nbFilesTotal > 1) {
1696 size_t srcFileNameSize = strlen(srcFileName);
1697 /* Ensure that the string we print is roughly the same size each time */
1698 if (srcFileNameSize > 18) {
1699 const char* truncatedSrcFileName = srcFileName + srcFileNameSize - 15;
1700 DISPLAY_PROGRESS("Compress: %u/%u files. Current: ...%s ",
1701 fCtx->currFileIdx+1, fCtx->nbFilesTotal, truncatedSrcFileName);
1702 } else {
1703 DISPLAY_PROGRESS("Compress: %u/%u files. Current: %*s ",
1704 fCtx->currFileIdx+1, fCtx->nbFilesTotal, (int)(18-srcFileNameSize), srcFileName);
1705 }
1706 }
1707 DISPLAY_PROGRESS("Read:%6.*f%4s ", consumed_hrs.precision, consumed_hrs.value, consumed_hrs.suffix);
1708 if (fileSize != UTIL_FILESIZE_UNKNOWN)
1709 DISPLAY_PROGRESS("/%6.*f%4s", file_hrs.precision, file_hrs.value, file_hrs.suffix);
1710 DISPLAY_PROGRESS(" ==> %2.f%%", cShare);
1711 }
1712 } /* if (SHOULD_DISPLAY_PROGRESS() && READY_FOR_UPDATE()) */
1713 } /* while ((inBuff.pos != inBuff.size) */
1714 } while (directive != ZSTD_e_end);
1715
1716 if (fileSize != UTIL_FILESIZE_UNKNOWN && *readsize != fileSize) {
1717 EXM_THROW(27, "Read error : Incomplete read : %llu / %llu B",
1718 (unsigned long long)*readsize, (unsigned long long)fileSize);
1719 }
1720
1721 AIO_WritePool_releaseIoJob(writeJob);
1722 AIO_WritePool_sparseWriteEnd(ressPtr->writeCtx);
1723
1724 return compressedfilesize;
1725 }
1726
1727 /*! FIO_compressFilename_internal() :
1728 * same as FIO_compressFilename_extRess(), with `ress.desFile` already opened.
1729 * @return : 0 : compression completed correctly,
1730 * 1 : missing or pb opening srcFileName
1731 */
1732 static int
FIO_compressFilename_internal(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,int compressionLevel)1733 FIO_compressFilename_internal(FIO_ctx_t* const fCtx,
1734 FIO_prefs_t* const prefs,
1735 cRess_t ress,
1736 const char* dstFileName, const char* srcFileName,
1737 int compressionLevel)
1738 {
1739 UTIL_time_t const timeStart = UTIL_getTime();
1740 clock_t const cpuStart = clock();
1741 U64 readsize = 0;
1742 U64 compressedfilesize = 0;
1743 U64 const fileSize = UTIL_getFileSize(srcFileName);
1744 DISPLAYLEVEL(5, "%s: %llu bytes \n", srcFileName, (unsigned long long)fileSize);
1745
1746 /* compression format selection */
1747 switch (prefs->compressionType) {
1748 default:
1749 case FIO_zstdCompression:
1750 compressedfilesize = FIO_compressZstdFrame(fCtx, prefs, &ress, srcFileName, fileSize, compressionLevel, &readsize);
1751 break;
1752
1753 case FIO_gzipCompression:
1754 #ifdef ZSTD_GZCOMPRESS
1755 compressedfilesize = FIO_compressGzFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize);
1756 #else
1757 (void)compressionLevel;
1758 EXM_THROW(20, "zstd: %s: file cannot be compressed as gzip (zstd compiled without ZSTD_GZCOMPRESS) -- ignored \n",
1759 srcFileName);
1760 #endif
1761 break;
1762
1763 case FIO_xzCompression:
1764 case FIO_lzmaCompression:
1765 #ifdef ZSTD_LZMACOMPRESS
1766 compressedfilesize = FIO_compressLzmaFrame(&ress, srcFileName, fileSize, compressionLevel, &readsize, prefs->compressionType==FIO_lzmaCompression);
1767 #else
1768 (void)compressionLevel;
1769 EXM_THROW(20, "zstd: %s: file cannot be compressed as xz/lzma (zstd compiled without ZSTD_LZMACOMPRESS) -- ignored \n",
1770 srcFileName);
1771 #endif
1772 break;
1773
1774 case FIO_lz4Compression:
1775 #ifdef ZSTD_LZ4COMPRESS
1776 compressedfilesize = FIO_compressLz4Frame(&ress, srcFileName, fileSize, compressionLevel, prefs->checksumFlag, &readsize);
1777 #else
1778 (void)compressionLevel;
1779 EXM_THROW(20, "zstd: %s: file cannot be compressed as lz4 (zstd compiled without ZSTD_LZ4COMPRESS) -- ignored \n",
1780 srcFileName);
1781 #endif
1782 break;
1783 }
1784
1785 /* Status */
1786 fCtx->totalBytesInput += (size_t)readsize;
1787 fCtx->totalBytesOutput += (size_t)compressedfilesize;
1788 DISPLAY_PROGRESS("\r%79s\r", "");
1789 if (FIO_shouldDisplayFileSummary(fCtx)) {
1790 UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) readsize);
1791 UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) compressedfilesize);
1792 if (readsize == 0) {
1793 DISPLAY_SUMMARY("%-20s : (%6.*f%s => %6.*f%s, %s) \n",
1794 srcFileName,
1795 hr_isize.precision, hr_isize.value, hr_isize.suffix,
1796 hr_osize.precision, hr_osize.value, hr_osize.suffix,
1797 dstFileName);
1798 } else {
1799 DISPLAY_SUMMARY("%-20s :%6.2f%% (%6.*f%s => %6.*f%s, %s) \n",
1800 srcFileName,
1801 (double)compressedfilesize / (double)readsize * 100,
1802 hr_isize.precision, hr_isize.value, hr_isize.suffix,
1803 hr_osize.precision, hr_osize.value, hr_osize.suffix,
1804 dstFileName);
1805 }
1806 }
1807
1808 /* Elapsed Time and CPU Load */
1809 { clock_t const cpuEnd = clock();
1810 double const cpuLoad_s = (double)(cpuEnd - cpuStart) / CLOCKS_PER_SEC;
1811 U64 const timeLength_ns = UTIL_clockSpanNano(timeStart);
1812 double const timeLength_s = (double)timeLength_ns / 1000000000;
1813 double const cpuLoad_pct = (cpuLoad_s / timeLength_s) * 100;
1814 DISPLAYLEVEL(4, "%-20s : Completed in %.2f sec (cpu load : %.0f%%)\n",
1815 srcFileName, timeLength_s, cpuLoad_pct);
1816 }
1817 return 0;
1818 }
1819
1820
1821 /*! FIO_compressFilename_dstFile() :
1822 * open dstFileName, or pass-through if ress.file != NULL,
1823 * then start compression with FIO_compressFilename_internal().
1824 * Manages source removal (--rm) and file permissions transfer.
1825 * note : ress.srcFile must be != NULL,
1826 * so reach this function through FIO_compressFilename_srcFile().
1827 * @return : 0 : compression completed correctly,
1828 * 1 : pb
1829 */
FIO_compressFilename_dstFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,const stat_t * srcFileStat,int compressionLevel)1830 static int FIO_compressFilename_dstFile(FIO_ctx_t* const fCtx,
1831 FIO_prefs_t* const prefs,
1832 cRess_t ress,
1833 const char* dstFileName,
1834 const char* srcFileName,
1835 const stat_t* srcFileStat,
1836 int compressionLevel)
1837 {
1838 int closeDstFile = 0;
1839 int result;
1840 int transferStat = 0;
1841 int dstFd = -1;
1842
1843 assert(AIO_ReadPool_getFile(ress.readCtx) != NULL);
1844 if (AIO_WritePool_getFile(ress.writeCtx) == NULL) {
1845 int dstFileInitialPermissions = DEFAULT_FILE_PERMISSIONS;
1846 if ( strcmp (srcFileName, stdinmark)
1847 && strcmp (dstFileName, stdoutmark)
1848 && UTIL_isRegularFileStat(srcFileStat) ) {
1849 transferStat = 1;
1850 dstFileInitialPermissions = TEMPORARY_FILE_PERMISSIONS;
1851 }
1852
1853 closeDstFile = 1;
1854 DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: opening dst: %s \n", dstFileName);
1855 { FILE *dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFileInitialPermissions);
1856 if (dstFile==NULL) return 1; /* could not open dstFileName */
1857 dstFd = fileno(dstFile);
1858 AIO_WritePool_setFile(ress.writeCtx, dstFile);
1859 }
1860 /* Must only be added after FIO_openDstFile() succeeds.
1861 * Otherwise we may delete the destination file if it already exists,
1862 * and the user presses Ctrl-C when asked if they wish to overwrite.
1863 */
1864 addHandler(dstFileName);
1865 }
1866
1867 result = FIO_compressFilename_internal(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
1868
1869 if (closeDstFile) {
1870 clearHandler();
1871
1872 if (transferStat) {
1873 UTIL_setFDStat(dstFd, dstFileName, srcFileStat);
1874 }
1875
1876 DISPLAYLEVEL(6, "FIO_compressFilename_dstFile: closing dst: %s \n", dstFileName);
1877 if (AIO_WritePool_closeFile(ress.writeCtx)) { /* error closing file */
1878 DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
1879 result=1;
1880 }
1881
1882 if (transferStat) {
1883 UTIL_utime(dstFileName, srcFileStat);
1884 }
1885
1886 if ( (result != 0) /* operation failure */
1887 && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */
1888 ) {
1889 FIO_removeFile(dstFileName); /* remove compression artefact; note don't do anything special if remove() fails */
1890 }
1891 }
1892
1893 return result;
1894 }
1895
1896 /* List used to compare file extensions (used with --exclude-compressed flag)
1897 * Different from the suffixList and should only apply to ZSTD compress operationResult
1898 */
1899 static const char *compressedFileExtensions[] = {
1900 ZSTD_EXTENSION,
1901 TZSTD_EXTENSION,
1902 GZ_EXTENSION,
1903 TGZ_EXTENSION,
1904 LZMA_EXTENSION,
1905 XZ_EXTENSION,
1906 TXZ_EXTENSION,
1907 LZ4_EXTENSION,
1908 TLZ4_EXTENSION,
1909 ".7z",
1910 ".aa3",
1911 ".aac",
1912 ".aar",
1913 ".ace",
1914 ".alac",
1915 ".ape",
1916 ".apk",
1917 ".apng",
1918 ".arc",
1919 ".archive",
1920 ".arj",
1921 ".ark",
1922 ".asf",
1923 ".avi",
1924 ".avif",
1925 ".ba",
1926 ".br",
1927 ".bz2",
1928 ".cab",
1929 ".cdx",
1930 ".chm",
1931 ".cr2",
1932 ".divx",
1933 ".dmg",
1934 ".dng",
1935 ".docm",
1936 ".docx",
1937 ".dotm",
1938 ".dotx",
1939 ".dsft",
1940 ".ear",
1941 ".eftx",
1942 ".emz",
1943 ".eot",
1944 ".epub",
1945 ".f4v",
1946 ".flac",
1947 ".flv",
1948 ".gho",
1949 ".gif",
1950 ".gifv",
1951 ".gnp",
1952 ".iso",
1953 ".jar",
1954 ".jpeg",
1955 ".jpg",
1956 ".jxl",
1957 ".lz",
1958 ".lzh",
1959 ".m4a",
1960 ".m4v",
1961 ".mkv",
1962 ".mov",
1963 ".mp2",
1964 ".mp3",
1965 ".mp4",
1966 ".mpa",
1967 ".mpc",
1968 ".mpe",
1969 ".mpeg",
1970 ".mpg",
1971 ".mpl",
1972 ".mpv",
1973 ".msi",
1974 ".odp",
1975 ".ods",
1976 ".odt",
1977 ".ogg",
1978 ".ogv",
1979 ".otp",
1980 ".ots",
1981 ".ott",
1982 ".pea",
1983 ".png",
1984 ".pptx",
1985 ".qt",
1986 ".rar",
1987 ".s7z",
1988 ".sfx",
1989 ".sit",
1990 ".sitx",
1991 ".sqx",
1992 ".svgz",
1993 ".swf",
1994 ".tbz2",
1995 ".tib",
1996 ".tlz",
1997 ".vob",
1998 ".war",
1999 ".webm",
2000 ".webp",
2001 ".wma",
2002 ".wmv",
2003 ".woff",
2004 ".woff2",
2005 ".wvl",
2006 ".xlsx",
2007 ".xpi",
2008 ".xps",
2009 ".zip",
2010 ".zipx",
2011 ".zoo",
2012 ".zpaq",
2013 NULL
2014 };
2015
2016 /*! FIO_compressFilename_srcFile() :
2017 * @return : 0 : compression completed correctly,
2018 * 1 : missing or pb opening srcFileName
2019 */
2020 static int
FIO_compressFilename_srcFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,cRess_t ress,const char * dstFileName,const char * srcFileName,int compressionLevel)2021 FIO_compressFilename_srcFile(FIO_ctx_t* const fCtx,
2022 FIO_prefs_t* const prefs,
2023 cRess_t ress,
2024 const char* dstFileName,
2025 const char* srcFileName,
2026 int compressionLevel)
2027 {
2028 int result;
2029 FILE* srcFile;
2030 stat_t srcFileStat;
2031 U64 fileSize = UTIL_FILESIZE_UNKNOWN;
2032 DISPLAYLEVEL(6, "FIO_compressFilename_srcFile: %s \n", srcFileName);
2033
2034 if (strcmp(srcFileName, stdinmark)) {
2035 if (UTIL_stat(srcFileName, &srcFileStat)) {
2036 /* failure to stat at all is handled during opening */
2037
2038 /* ensure src is not a directory */
2039 if (UTIL_isDirectoryStat(&srcFileStat)) {
2040 DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
2041 return 1;
2042 }
2043
2044 /* ensure src is not the same as dict (if present) */
2045 if (ress.dictFileName != NULL && UTIL_isSameFileStat(srcFileName, ress.dictFileName, &srcFileStat, &ress.dictFileStat)) {
2046 DISPLAYLEVEL(1, "zstd: cannot use %s as an input file and dictionary \n", srcFileName);
2047 return 1;
2048 }
2049 }
2050 }
2051
2052 /* Check if "srcFile" is compressed. Only done if --exclude-compressed flag is used
2053 * YES => ZSTD will skip compression of the file and will return 0.
2054 * NO => ZSTD will resume with compress operation.
2055 */
2056 if (prefs->excludeCompressedFiles == 1 && UTIL_isCompressedFile(srcFileName, compressedFileExtensions)) {
2057 DISPLAYLEVEL(4, "File is already compressed : %s \n", srcFileName);
2058 return 0;
2059 }
2060
2061 srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
2062 if (srcFile == NULL) return 1; /* srcFile could not be opened */
2063
2064 /* Don't use AsyncIO for small files */
2065 if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
2066 fileSize = UTIL_getFileSizeStat(&srcFileStat);
2067 if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
2068 AIO_ReadPool_setAsync(ress.readCtx, 0);
2069 AIO_WritePool_setAsync(ress.writeCtx, 0);
2070 } else {
2071 AIO_ReadPool_setAsync(ress.readCtx, 1);
2072 AIO_WritePool_setAsync(ress.writeCtx, 1);
2073 }
2074
2075 AIO_ReadPool_setFile(ress.readCtx, srcFile);
2076 result = FIO_compressFilename_dstFile(
2077 fCtx, prefs, ress,
2078 dstFileName, srcFileName,
2079 &srcFileStat, compressionLevel);
2080 AIO_ReadPool_closeFile(ress.readCtx);
2081
2082 if ( prefs->removeSrcFile /* --rm */
2083 && result == 0 /* success */
2084 && strcmp(srcFileName, stdinmark) /* exception : don't erase stdin */
2085 ) {
2086 /* We must clear the handler, since after this point calling it would
2087 * delete both the source and destination files.
2088 */
2089 clearHandler();
2090 if (FIO_removeFile(srcFileName))
2091 EXM_THROW(1, "zstd: %s: %s", srcFileName, strerror(errno));
2092 }
2093 return result;
2094 }
2095
2096 static const char*
checked_index(const char * options[],size_t length,size_t index)2097 checked_index(const char* options[], size_t length, size_t index) {
2098 assert(index < length);
2099 /* Necessary to avoid warnings since -O3 will omit the above `assert` */
2100 (void) length;
2101 return options[index];
2102 }
2103
2104 #define INDEX(options, index) checked_index((options), sizeof(options) / sizeof(char*), (size_t)(index))
2105
FIO_displayCompressionParameters(const FIO_prefs_t * prefs)2106 void FIO_displayCompressionParameters(const FIO_prefs_t* prefs)
2107 {
2108 static const char* formatOptions[5] = {ZSTD_EXTENSION, GZ_EXTENSION, XZ_EXTENSION,
2109 LZMA_EXTENSION, LZ4_EXTENSION};
2110 static const char* sparseOptions[3] = {" --no-sparse", "", " --sparse"};
2111 static const char* checkSumOptions[3] = {" --no-check", "", " --check"};
2112 static const char* rowMatchFinderOptions[3] = {"", " --no-row-match-finder", " --row-match-finder"};
2113 static const char* compressLiteralsOptions[3] = {"", " --compress-literals", " --no-compress-literals"};
2114
2115 assert(g_display_prefs.displayLevel >= 4);
2116
2117 DISPLAY("--format=%s", formatOptions[prefs->compressionType]);
2118 DISPLAY("%s", INDEX(sparseOptions, prefs->sparseFileSupport));
2119 DISPLAY("%s", prefs->dictIDFlag ? "" : " --no-dictID");
2120 DISPLAY("%s", INDEX(checkSumOptions, prefs->checksumFlag));
2121 DISPLAY(" --block-size=%d", prefs->blockSize);
2122 if (prefs->adaptiveMode)
2123 DISPLAY(" --adapt=min=%d,max=%d", prefs->minAdaptLevel, prefs->maxAdaptLevel);
2124 DISPLAY("%s", INDEX(rowMatchFinderOptions, prefs->useRowMatchFinder));
2125 DISPLAY("%s", prefs->rsyncable ? " --rsyncable" : "");
2126 if (prefs->streamSrcSize)
2127 DISPLAY(" --stream-size=%u", (unsigned) prefs->streamSrcSize);
2128 if (prefs->srcSizeHint)
2129 DISPLAY(" --size-hint=%d", prefs->srcSizeHint);
2130 if (prefs->targetCBlockSize)
2131 DISPLAY(" --target-compressed-block-size=%u", (unsigned) prefs->targetCBlockSize);
2132 DISPLAY("%s", INDEX(compressLiteralsOptions, prefs->literalCompressionMode));
2133 DISPLAY(" --memory=%u", prefs->memLimit ? prefs->memLimit : 128 MB);
2134 DISPLAY(" --threads=%d", prefs->nbWorkers);
2135 DISPLAY("%s", prefs->excludeCompressedFiles ? " --exclude-compressed" : "");
2136 DISPLAY(" --%scontent-size", prefs->contentSize ? "" : "no-");
2137 DISPLAY("\n");
2138 }
2139
2140 #undef INDEX
2141
FIO_compressFilename(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName,const char * dictFileName,int compressionLevel,ZSTD_compressionParameters comprParams)2142 int FIO_compressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, const char* dstFileName,
2143 const char* srcFileName, const char* dictFileName,
2144 int compressionLevel, ZSTD_compressionParameters comprParams)
2145 {
2146 cRess_t ress = FIO_createCResources(prefs, dictFileName, UTIL_getFileSize(srcFileName), compressionLevel, comprParams);
2147 int const result = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
2148
2149 #define DISPLAY_LEVEL_DEFAULT 2
2150
2151 FIO_freeCResources(&ress);
2152 return result;
2153 }
2154
2155 /* FIO_determineCompressedName() :
2156 * create a destination filename for compressed srcFileName.
2157 * @return a pointer to it.
2158 * This function never returns an error (it may abort() in case of pb)
2159 */
2160 static const char*
FIO_determineCompressedName(const char * srcFileName,const char * outDirName,const char * suffix)2161 FIO_determineCompressedName(const char* srcFileName, const char* outDirName, const char* suffix)
2162 {
2163 static size_t dfnbCapacity = 0;
2164 static char* dstFileNameBuffer = NULL; /* using static allocation : this function cannot be multi-threaded */
2165 char* outDirFilename = NULL;
2166 size_t sfnSize = strlen(srcFileName);
2167 size_t const srcSuffixLen = strlen(suffix);
2168
2169 if(!strcmp(srcFileName, stdinmark)) {
2170 return stdoutmark;
2171 }
2172
2173 if (outDirName) {
2174 outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, srcSuffixLen);
2175 sfnSize = strlen(outDirFilename);
2176 assert(outDirFilename != NULL);
2177 }
2178
2179 if (dfnbCapacity <= sfnSize+srcSuffixLen+1) {
2180 /* resize buffer for dstName */
2181 free(dstFileNameBuffer);
2182 dfnbCapacity = sfnSize + srcSuffixLen + 30;
2183 dstFileNameBuffer = (char*)malloc(dfnbCapacity);
2184 if (!dstFileNameBuffer) {
2185 EXM_THROW(30, "zstd: %s", strerror(errno));
2186 }
2187 }
2188 assert(dstFileNameBuffer != NULL);
2189
2190 if (outDirFilename) {
2191 memcpy(dstFileNameBuffer, outDirFilename, sfnSize);
2192 free(outDirFilename);
2193 } else {
2194 memcpy(dstFileNameBuffer, srcFileName, sfnSize);
2195 }
2196 memcpy(dstFileNameBuffer+sfnSize, suffix, srcSuffixLen+1 /* Include terminating null */);
2197 return dstFileNameBuffer;
2198 }
2199
FIO_getLargestFileSize(const char ** inFileNames,unsigned nbFiles)2200 static unsigned long long FIO_getLargestFileSize(const char** inFileNames, unsigned nbFiles)
2201 {
2202 size_t i;
2203 unsigned long long fileSize, maxFileSize = 0;
2204 for (i = 0; i < nbFiles; i++) {
2205 fileSize = UTIL_getFileSize(inFileNames[i]);
2206 maxFileSize = fileSize > maxFileSize ? fileSize : maxFileSize;
2207 }
2208 return maxFileSize;
2209 }
2210
2211 /* FIO_compressMultipleFilenames() :
2212 * compress nbFiles files
2213 * into either one destination (outFileName),
2214 * or into one file each (outFileName == NULL, but suffix != NULL),
2215 * or into a destination folder (specified with -O)
2216 */
FIO_compressMultipleFilenames(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char ** inFileNamesTable,const char * outMirroredRootDirName,const char * outDirName,const char * outFileName,const char * suffix,const char * dictFileName,int compressionLevel,ZSTD_compressionParameters comprParams)2217 int FIO_compressMultipleFilenames(FIO_ctx_t* const fCtx,
2218 FIO_prefs_t* const prefs,
2219 const char** inFileNamesTable,
2220 const char* outMirroredRootDirName,
2221 const char* outDirName,
2222 const char* outFileName, const char* suffix,
2223 const char* dictFileName, int compressionLevel,
2224 ZSTD_compressionParameters comprParams)
2225 {
2226 int status;
2227 int error = 0;
2228 cRess_t ress = FIO_createCResources(prefs, dictFileName,
2229 FIO_getLargestFileSize(inFileNamesTable, (unsigned)fCtx->nbFilesTotal),
2230 compressionLevel, comprParams);
2231
2232 /* init */
2233 assert(outFileName != NULL || suffix != NULL);
2234 if (outFileName != NULL) { /* output into a single destination (stdout typically) */
2235 FILE *dstFile;
2236 if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
2237 FIO_freeCResources(&ress);
2238 return 1;
2239 }
2240 dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
2241 if (dstFile == NULL) { /* could not open outFileName */
2242 error = 1;
2243 } else {
2244 AIO_WritePool_setFile(ress.writeCtx, dstFile);
2245 for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
2246 status = FIO_compressFilename_srcFile(fCtx, prefs, ress, outFileName, inFileNamesTable[fCtx->currFileIdx], compressionLevel);
2247 if (!status) fCtx->nbFilesProcessed++;
2248 error |= status;
2249 }
2250 if (AIO_WritePool_closeFile(ress.writeCtx))
2251 EXM_THROW(29, "Write error (%s) : cannot properly close %s",
2252 strerror(errno), outFileName);
2253 }
2254 } else {
2255 if (outMirroredRootDirName)
2256 UTIL_mirrorSourceFilesDirectories(inFileNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
2257
2258 for (; fCtx->currFileIdx < fCtx->nbFilesTotal; ++fCtx->currFileIdx) {
2259 const char* const srcFileName = inFileNamesTable[fCtx->currFileIdx];
2260 const char* dstFileName = NULL;
2261 if (outMirroredRootDirName) {
2262 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
2263 if (validMirroredDirName) {
2264 dstFileName = FIO_determineCompressedName(srcFileName, validMirroredDirName, suffix);
2265 free(validMirroredDirName);
2266 } else {
2267 DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot compress '%s' into '%s' \n", srcFileName, outMirroredRootDirName);
2268 error=1;
2269 continue;
2270 }
2271 } else {
2272 dstFileName = FIO_determineCompressedName(srcFileName, outDirName, suffix); /* cannot fail */
2273 }
2274 status = FIO_compressFilename_srcFile(fCtx, prefs, ress, dstFileName, srcFileName, compressionLevel);
2275 if (!status) fCtx->nbFilesProcessed++;
2276 error |= status;
2277 }
2278
2279 if (outDirName)
2280 FIO_checkFilenameCollisions(inFileNamesTable , (unsigned)fCtx->nbFilesTotal);
2281 }
2282
2283 if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
2284 UTIL_HumanReadableSize_t hr_isize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesInput);
2285 UTIL_HumanReadableSize_t hr_osize = UTIL_makeHumanReadableSize((U64) fCtx->totalBytesOutput);
2286
2287 DISPLAY_PROGRESS("\r%79s\r", "");
2288 if (fCtx->totalBytesInput == 0) {
2289 DISPLAY_SUMMARY("%3d files compressed : (%6.*f%4s => %6.*f%4s)\n",
2290 fCtx->nbFilesProcessed,
2291 hr_isize.precision, hr_isize.value, hr_isize.suffix,
2292 hr_osize.precision, hr_osize.value, hr_osize.suffix);
2293 } else {
2294 DISPLAY_SUMMARY("%3d files compressed : %.2f%% (%6.*f%4s => %6.*f%4s)\n",
2295 fCtx->nbFilesProcessed,
2296 (double)fCtx->totalBytesOutput/((double)fCtx->totalBytesInput)*100,
2297 hr_isize.precision, hr_isize.value, hr_isize.suffix,
2298 hr_osize.precision, hr_osize.value, hr_osize.suffix);
2299 }
2300 }
2301
2302 FIO_freeCResources(&ress);
2303 return error;
2304 }
2305
2306 #endif /* #ifndef ZSTD_NOCOMPRESS */
2307
2308
2309
2310 #ifndef ZSTD_NODECOMPRESS
2311
2312 /* **************************************************************************
2313 * Decompression
2314 ***************************************************************************/
2315 typedef struct {
2316 FIO_Dict_t dict;
2317 ZSTD_DStream* dctx;
2318 WritePoolCtx_t *writeCtx;
2319 ReadPoolCtx_t *readCtx;
2320 } dRess_t;
2321
FIO_createDResources(FIO_prefs_t * const prefs,const char * dictFileName)2322 static dRess_t FIO_createDResources(FIO_prefs_t* const prefs, const char* dictFileName)
2323 {
2324 int useMMap = prefs->mmapDict == ZSTD_ps_enable;
2325 int forceNoUseMMap = prefs->mmapDict == ZSTD_ps_disable;
2326 stat_t statbuf;
2327 dRess_t ress;
2328 memset(&statbuf, 0, sizeof(statbuf));
2329 memset(&ress, 0, sizeof(ress));
2330
2331 FIO_getDictFileStat(dictFileName, &statbuf);
2332
2333 if (prefs->patchFromMode){
2334 U64 const dictSize = UTIL_getFileSizeStat(&statbuf);
2335 useMMap |= dictSize > prefs->memLimit;
2336 FIO_adjustMemLimitForPatchFromMode(prefs, dictSize, 0 /* just use the dict size */);
2337 }
2338
2339 /* Allocation */
2340 ress.dctx = ZSTD_createDStream();
2341 if (ress.dctx==NULL)
2342 EXM_THROW(60, "Error: %s : can't create ZSTD_DStream", strerror(errno));
2343 CHECK( ZSTD_DCtx_setMaxWindowSize(ress.dctx, prefs->memLimit) );
2344 CHECK( ZSTD_DCtx_setParameter(ress.dctx, ZSTD_d_forceIgnoreChecksum, !prefs->checksumFlag));
2345
2346 /* dictionary */
2347 {
2348 FIO_dictBufferType_t dictBufferType = (useMMap && !forceNoUseMMap) ? FIO_mmapDict : FIO_mallocDict;
2349 FIO_initDict(&ress.dict, dictFileName, prefs, &statbuf, dictBufferType);
2350
2351 CHECK(ZSTD_DCtx_reset(ress.dctx, ZSTD_reset_session_only) );
2352
2353 if (prefs->patchFromMode){
2354 CHECK(ZSTD_DCtx_refPrefix(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
2355 } else {
2356 CHECK(ZSTD_DCtx_loadDictionary_byReference(ress.dctx, ress.dict.dictBuffer, ress.dict.dictBufferSize));
2357 }
2358 }
2359
2360 ress.writeCtx = AIO_WritePool_create(prefs, ZSTD_DStreamOutSize());
2361 ress.readCtx = AIO_ReadPool_create(prefs, ZSTD_DStreamInSize());
2362 return ress;
2363 }
2364
FIO_freeDResources(dRess_t ress)2365 static void FIO_freeDResources(dRess_t ress)
2366 {
2367 FIO_freeDict(&(ress.dict));
2368 CHECK( ZSTD_freeDStream(ress.dctx) );
2369 AIO_WritePool_free(ress.writeCtx);
2370 AIO_ReadPool_free(ress.readCtx);
2371 }
2372
2373 /* FIO_passThrough() : just copy input into output, for compatibility with gzip -df mode
2374 * @return : 0 (no error) */
FIO_passThrough(dRess_t * ress)2375 static int FIO_passThrough(dRess_t *ress)
2376 {
2377 size_t const blockSize = MIN(MIN(64 KB, ZSTD_DStreamInSize()), ZSTD_DStreamOutSize());
2378 IOJob_t *writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
2379 AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
2380
2381 while(ress->readCtx->srcBufferLoaded) {
2382 size_t writeSize;
2383 writeSize = MIN(blockSize, ress->readCtx->srcBufferLoaded);
2384 assert(writeSize <= writeJob->bufferSize);
2385 memcpy(writeJob->buffer, ress->readCtx->srcBuffer, writeSize);
2386 writeJob->usedBufferSize = writeSize;
2387 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
2388 AIO_ReadPool_consumeBytes(ress->readCtx, writeSize);
2389 AIO_ReadPool_fillBuffer(ress->readCtx, blockSize);
2390 }
2391 assert(ress->readCtx->reachedEof);
2392 AIO_WritePool_releaseIoJob(writeJob);
2393 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
2394 return 0;
2395 }
2396
2397 /* FIO_zstdErrorHelp() :
2398 * detailed error message when requested window size is too large */
2399 static void
FIO_zstdErrorHelp(const FIO_prefs_t * const prefs,const dRess_t * ress,size_t err,const char * srcFileName)2400 FIO_zstdErrorHelp(const FIO_prefs_t* const prefs,
2401 const dRess_t* ress,
2402 size_t err,
2403 const char* srcFileName)
2404 {
2405 ZSTD_FrameHeader header;
2406
2407 /* Help message only for one specific error */
2408 if (ZSTD_getErrorCode(err) != ZSTD_error_frameParameter_windowTooLarge)
2409 return;
2410
2411 /* Try to decode the frame header */
2412 err = ZSTD_getFrameHeader(&header, ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded);
2413 if (err == 0) {
2414 unsigned long long const windowSize = header.windowSize;
2415 unsigned const windowLog = FIO_highbit64(windowSize) + ((windowSize & (windowSize - 1)) != 0);
2416 assert(prefs->memLimit > 0);
2417 DISPLAYLEVEL(1, "%s : Window size larger than maximum : %llu > %u \n",
2418 srcFileName, windowSize, prefs->memLimit);
2419 if (windowLog <= ZSTD_WINDOWLOG_MAX) {
2420 unsigned const windowMB = (unsigned)((windowSize >> 20) + ((windowSize & ((1 MB) - 1)) != 0));
2421 assert(windowSize < (U64)(1ULL << 52)); /* ensure now overflow for windowMB */
2422 DISPLAYLEVEL(1, "%s : Use --long=%u or --memory=%uMB \n",
2423 srcFileName, windowLog, windowMB);
2424 return;
2425 } }
2426 DISPLAYLEVEL(1, "%s : Window log larger than ZSTD_WINDOWLOG_MAX=%u; not supported \n",
2427 srcFileName, ZSTD_WINDOWLOG_MAX);
2428 }
2429
2430 /** FIO_decompressFrame() :
2431 * @return : size of decoded zstd frame, or an error code
2432 */
2433 #define FIO_ERROR_FRAME_DECODING ((unsigned long long)(-2))
2434 static unsigned long long
FIO_decompressZstdFrame(FIO_ctx_t * const fCtx,dRess_t * ress,const FIO_prefs_t * const prefs,const char * srcFileName,U64 alreadyDecoded)2435 FIO_decompressZstdFrame(FIO_ctx_t* const fCtx, dRess_t* ress,
2436 const FIO_prefs_t* const prefs,
2437 const char* srcFileName,
2438 U64 alreadyDecoded) /* for multi-frames streams */
2439 {
2440 U64 frameSize = 0;
2441 const char* srcFName20 = srcFileName;
2442 IOJob_t* writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
2443 assert(writeJob);
2444
2445 /* display last 20 characters only when not --verbose */
2446 { size_t const srcFileLength = strlen(srcFileName);
2447 if ((srcFileLength>20) && (g_display_prefs.displayLevel<3))
2448 srcFName20 += srcFileLength-20;
2449 }
2450
2451 ZSTD_DCtx_reset(ress->dctx, ZSTD_reset_session_only);
2452
2453 /* Header loading : ensures ZSTD_getFrameHeader() will succeed */
2454 AIO_ReadPool_fillBuffer(ress->readCtx, ZSTD_FRAMEHEADERSIZE_MAX);
2455
2456 /* Main decompression Loop */
2457 while (1) {
2458 ZSTD_inBuffer inBuff = setInBuffer( ress->readCtx->srcBuffer, ress->readCtx->srcBufferLoaded, 0 );
2459 ZSTD_outBuffer outBuff= setOutBuffer( writeJob->buffer, writeJob->bufferSize, 0 );
2460 size_t const readSizeHint = ZSTD_decompressStream(ress->dctx, &outBuff, &inBuff);
2461 UTIL_HumanReadableSize_t const hrs = UTIL_makeHumanReadableSize(alreadyDecoded+frameSize);
2462 if (ZSTD_isError(readSizeHint)) {
2463 DISPLAYLEVEL(1, "%s : Decoding error (36) : %s \n",
2464 srcFileName, ZSTD_getErrorName(readSizeHint));
2465 FIO_zstdErrorHelp(prefs, ress, readSizeHint, srcFileName);
2466 AIO_WritePool_releaseIoJob(writeJob);
2467 return FIO_ERROR_FRAME_DECODING;
2468 }
2469
2470 /* Write block */
2471 writeJob->usedBufferSize = outBuff.pos;
2472 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
2473 frameSize += outBuff.pos;
2474 if (fCtx->nbFilesTotal > 1) {
2475 DISPLAYUPDATE_PROGRESS(
2476 "\rDecompress: %2u/%2u files. Current: %s : %.*f%s... ",
2477 fCtx->currFileIdx+1, fCtx->nbFilesTotal, srcFName20, hrs.precision, hrs.value, hrs.suffix);
2478 } else {
2479 DISPLAYUPDATE_PROGRESS("\r%-20.20s : %.*f%s... ",
2480 srcFName20, hrs.precision, hrs.value, hrs.suffix);
2481 }
2482
2483 AIO_ReadPool_consumeBytes(ress->readCtx, inBuff.pos);
2484
2485 if (readSizeHint == 0) break; /* end of frame */
2486
2487 /* Fill input buffer */
2488 { size_t const toDecode = MIN(readSizeHint, ZSTD_DStreamInSize()); /* support large skippable frames */
2489 if (ress->readCtx->srcBufferLoaded < toDecode) {
2490 size_t const readSize = AIO_ReadPool_fillBuffer(ress->readCtx, toDecode);
2491 if (readSize==0) {
2492 DISPLAYLEVEL(1, "%s : Read error (39) : premature end \n",
2493 srcFileName);
2494 AIO_WritePool_releaseIoJob(writeJob);
2495 return FIO_ERROR_FRAME_DECODING;
2496 }
2497 } } }
2498
2499 AIO_WritePool_releaseIoJob(writeJob);
2500 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
2501
2502 return frameSize;
2503 }
2504
2505
2506 #ifdef ZSTD_GZDECOMPRESS
2507 static unsigned long long
FIO_decompressGzFrame(dRess_t * ress,const char * srcFileName)2508 FIO_decompressGzFrame(dRess_t* ress, const char* srcFileName)
2509 {
2510 unsigned long long outFileSize = 0;
2511 z_stream strm;
2512 int flush = Z_NO_FLUSH;
2513 int decodingError = 0;
2514 IOJob_t *writeJob = NULL;
2515
2516 strm.zalloc = Z_NULL;
2517 strm.zfree = Z_NULL;
2518 strm.opaque = Z_NULL;
2519 strm.next_in = 0;
2520 strm.avail_in = 0;
2521 /* see https://www.zlib.net/manual.html */
2522 if (inflateInit2(&strm, 15 /* maxWindowLogSize */ + 16 /* gzip only */) != Z_OK)
2523 return FIO_ERROR_FRAME_DECODING;
2524
2525 writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
2526 strm.next_out = (Bytef*)writeJob->buffer;
2527 strm.avail_out = (uInt)writeJob->bufferSize;
2528 strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
2529 strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
2530
2531 for ( ; ; ) {
2532 int ret;
2533 if (strm.avail_in == 0) {
2534 AIO_ReadPool_consumeAndRefill(ress->readCtx);
2535 if (ress->readCtx->srcBufferLoaded == 0) flush = Z_FINISH;
2536 strm.next_in = (z_const unsigned char*)ress->readCtx->srcBuffer;
2537 strm.avail_in = (uInt)ress->readCtx->srcBufferLoaded;
2538 }
2539 ret = inflate(&strm, flush);
2540 if (ret == Z_BUF_ERROR) {
2541 DISPLAYLEVEL(1, "zstd: %s: premature gz end \n", srcFileName);
2542 decodingError = 1; break;
2543 }
2544 if (ret != Z_OK && ret != Z_STREAM_END) {
2545 DISPLAYLEVEL(1, "zstd: %s: inflate error %d \n", srcFileName, ret);
2546 decodingError = 1; break;
2547 }
2548 { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
2549 if (decompBytes) {
2550 writeJob->usedBufferSize = decompBytes;
2551 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
2552 outFileSize += decompBytes;
2553 strm.next_out = (Bytef*)writeJob->buffer;
2554 strm.avail_out = (uInt)writeJob->bufferSize;
2555 }
2556 }
2557 if (ret == Z_STREAM_END) break;
2558 }
2559
2560 AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
2561
2562 if ( (inflateEnd(&strm) != Z_OK) /* release resources ; error detected */
2563 && (decodingError==0) ) {
2564 DISPLAYLEVEL(1, "zstd: %s: inflateEnd error \n", srcFileName);
2565 decodingError = 1;
2566 }
2567 AIO_WritePool_releaseIoJob(writeJob);
2568 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
2569 return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
2570 }
2571 #endif
2572
2573 #ifdef ZSTD_LZMADECOMPRESS
2574 static unsigned long long
FIO_decompressLzmaFrame(dRess_t * ress,const char * srcFileName,int plain_lzma)2575 FIO_decompressLzmaFrame(dRess_t* ress,
2576 const char* srcFileName, int plain_lzma)
2577 {
2578 unsigned long long outFileSize = 0;
2579 lzma_stream strm = LZMA_STREAM_INIT;
2580 lzma_action action = LZMA_RUN;
2581 lzma_ret initRet;
2582 int decodingError = 0;
2583 IOJob_t *writeJob = NULL;
2584
2585 strm.next_in = 0;
2586 strm.avail_in = 0;
2587 if (plain_lzma) {
2588 initRet = lzma_alone_decoder(&strm, UINT64_MAX); /* LZMA */
2589 } else {
2590 initRet = lzma_stream_decoder(&strm, UINT64_MAX, 0); /* XZ */
2591 }
2592
2593 if (initRet != LZMA_OK) {
2594 DISPLAYLEVEL(1, "zstd: %s: %s error %d \n",
2595 plain_lzma ? "lzma_alone_decoder" : "lzma_stream_decoder",
2596 srcFileName, initRet);
2597 return FIO_ERROR_FRAME_DECODING;
2598 }
2599
2600 writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
2601 strm.next_out = (BYTE*)writeJob->buffer;
2602 strm.avail_out = writeJob->bufferSize;
2603 strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
2604 strm.avail_in = ress->readCtx->srcBufferLoaded;
2605
2606 for ( ; ; ) {
2607 lzma_ret ret;
2608 if (strm.avail_in == 0) {
2609 AIO_ReadPool_consumeAndRefill(ress->readCtx);
2610 if (ress->readCtx->srcBufferLoaded == 0) action = LZMA_FINISH;
2611 strm.next_in = (BYTE const*)ress->readCtx->srcBuffer;
2612 strm.avail_in = ress->readCtx->srcBufferLoaded;
2613 }
2614 ret = lzma_code(&strm, action);
2615
2616 if (ret == LZMA_BUF_ERROR) {
2617 DISPLAYLEVEL(1, "zstd: %s: premature lzma end \n", srcFileName);
2618 decodingError = 1; break;
2619 }
2620 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
2621 DISPLAYLEVEL(1, "zstd: %s: lzma_code decoding error %d \n",
2622 srcFileName, ret);
2623 decodingError = 1; break;
2624 }
2625 { size_t const decompBytes = writeJob->bufferSize - strm.avail_out;
2626 if (decompBytes) {
2627 writeJob->usedBufferSize = decompBytes;
2628 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
2629 outFileSize += decompBytes;
2630 strm.next_out = (BYTE*)writeJob->buffer;
2631 strm.avail_out = writeJob->bufferSize;
2632 } }
2633 if (ret == LZMA_STREAM_END) break;
2634 }
2635
2636 AIO_ReadPool_consumeBytes(ress->readCtx, ress->readCtx->srcBufferLoaded - strm.avail_in);
2637 lzma_end(&strm);
2638 AIO_WritePool_releaseIoJob(writeJob);
2639 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
2640 return decodingError ? FIO_ERROR_FRAME_DECODING : outFileSize;
2641 }
2642 #endif
2643
2644 #ifdef ZSTD_LZ4DECOMPRESS
2645 static unsigned long long
FIO_decompressLz4Frame(dRess_t * ress,const char * srcFileName)2646 FIO_decompressLz4Frame(dRess_t* ress, const char* srcFileName)
2647 {
2648 unsigned long long filesize = 0;
2649 LZ4F_errorCode_t nextToLoad = 4;
2650 LZ4F_decompressionContext_t dCtx;
2651 LZ4F_errorCode_t const errorCode = LZ4F_createDecompressionContext(&dCtx, LZ4F_VERSION);
2652 int decodingError = 0;
2653 IOJob_t *writeJob = NULL;
2654
2655 if (LZ4F_isError(errorCode)) {
2656 DISPLAYLEVEL(1, "zstd: failed to create lz4 decompression context \n");
2657 return FIO_ERROR_FRAME_DECODING;
2658 }
2659
2660 writeJob = AIO_WritePool_acquireJob(ress->writeCtx);
2661
2662 /* Main Loop */
2663 for (;nextToLoad;) {
2664 size_t pos = 0;
2665 size_t decodedBytes = writeJob->bufferSize;
2666 int fullBufferDecoded = 0;
2667
2668 /* Read input */
2669 AIO_ReadPool_fillBuffer(ress->readCtx, nextToLoad);
2670 if(!ress->readCtx->srcBufferLoaded) break; /* reached end of file */
2671
2672 while ((pos < ress->readCtx->srcBufferLoaded) || fullBufferDecoded) { /* still to read, or still to flush */
2673 /* Decode Input (at least partially) */
2674 size_t remaining = ress->readCtx->srcBufferLoaded - pos;
2675 decodedBytes = writeJob->bufferSize;
2676 nextToLoad = LZ4F_decompress(dCtx, writeJob->buffer, &decodedBytes, (char*)(ress->readCtx->srcBuffer)+pos,
2677 &remaining, NULL);
2678 if (LZ4F_isError(nextToLoad)) {
2679 DISPLAYLEVEL(1, "zstd: %s: lz4 decompression error : %s \n",
2680 srcFileName, LZ4F_getErrorName(nextToLoad));
2681 decodingError = 1; nextToLoad = 0; break;
2682 }
2683 pos += remaining;
2684 assert(pos <= ress->readCtx->srcBufferLoaded);
2685 fullBufferDecoded = decodedBytes == writeJob->bufferSize;
2686
2687 /* Write Block */
2688 if (decodedBytes) {
2689 UTIL_HumanReadableSize_t hrs;
2690 writeJob->usedBufferSize = decodedBytes;
2691 AIO_WritePool_enqueueAndReacquireWriteJob(&writeJob);
2692 filesize += decodedBytes;
2693 hrs = UTIL_makeHumanReadableSize(filesize);
2694 DISPLAYUPDATE_PROGRESS("\rDecompressed : %.*f%s ", hrs.precision, hrs.value, hrs.suffix);
2695 }
2696
2697 if (!nextToLoad) break;
2698 }
2699 AIO_ReadPool_consumeBytes(ress->readCtx, pos);
2700 }
2701 if (nextToLoad!=0) {
2702 DISPLAYLEVEL(1, "zstd: %s: unfinished lz4 stream \n", srcFileName);
2703 decodingError=1;
2704 }
2705
2706 LZ4F_freeDecompressionContext(dCtx);
2707 AIO_WritePool_releaseIoJob(writeJob);
2708 AIO_WritePool_sparseWriteEnd(ress->writeCtx);
2709
2710 return decodingError ? FIO_ERROR_FRAME_DECODING : filesize;
2711 }
2712 #endif
2713
2714
2715
2716 /** FIO_decompressFrames() :
2717 * Find and decode frames inside srcFile
2718 * srcFile presumed opened and valid
2719 * @return : 0 : OK
2720 * 1 : error
2721 */
FIO_decompressFrames(FIO_ctx_t * const fCtx,dRess_t ress,const FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName)2722 static int FIO_decompressFrames(FIO_ctx_t* const fCtx,
2723 dRess_t ress, const FIO_prefs_t* const prefs,
2724 const char* dstFileName, const char* srcFileName)
2725 {
2726 unsigned readSomething = 0;
2727 unsigned long long filesize = 0;
2728 int passThrough = prefs->passThrough;
2729
2730 if (passThrough == -1) {
2731 /* If pass-through mode is not explicitly enabled or disabled,
2732 * default to the legacy behavior of enabling it if we are writing
2733 * to stdout with the overwrite flag enabled.
2734 */
2735 passThrough = prefs->overwrite && !strcmp(dstFileName, stdoutmark);
2736 }
2737 assert(passThrough == 0 || passThrough == 1);
2738
2739 /* for each frame */
2740 for ( ; ; ) {
2741 /* check magic number -> version */
2742 size_t const toRead = 4;
2743 const BYTE* buf;
2744 AIO_ReadPool_fillBuffer(ress.readCtx, toRead);
2745 buf = (const BYTE*)ress.readCtx->srcBuffer;
2746 if (ress.readCtx->srcBufferLoaded==0) {
2747 if (readSomething==0) { /* srcFile is empty (which is invalid) */
2748 DISPLAYLEVEL(1, "zstd: %s: unexpected end of file \n", srcFileName);
2749 return 1;
2750 } /* else, just reached frame boundary */
2751 break; /* no more input */
2752 }
2753 readSomething = 1; /* there is at least 1 byte in srcFile */
2754 if (ress.readCtx->srcBufferLoaded < toRead) { /* not enough input to check magic number */
2755 if (passThrough) {
2756 return FIO_passThrough(&ress);
2757 }
2758 DISPLAYLEVEL(1, "zstd: %s: unknown header \n", srcFileName);
2759 return 1;
2760 }
2761 if (ZSTD_isFrame(buf, ress.readCtx->srcBufferLoaded)) {
2762 unsigned long long const frameSize = FIO_decompressZstdFrame(fCtx, &ress, prefs, srcFileName, filesize);
2763 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
2764 filesize += frameSize;
2765 } else if (buf[0] == 31 && buf[1] == 139) { /* gz magic number */
2766 #ifdef ZSTD_GZDECOMPRESS
2767 unsigned long long const frameSize = FIO_decompressGzFrame(&ress, srcFileName);
2768 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
2769 filesize += frameSize;
2770 #else
2771 DISPLAYLEVEL(1, "zstd: %s: gzip file cannot be uncompressed (zstd compiled without HAVE_ZLIB) -- ignored \n", srcFileName);
2772 return 1;
2773 #endif
2774 } else if ((buf[0] == 0xFD && buf[1] == 0x37) /* xz magic number */
2775 || (buf[0] == 0x5D && buf[1] == 0x00)) { /* lzma header (no magic number) */
2776 #ifdef ZSTD_LZMADECOMPRESS
2777 unsigned long long const frameSize = FIO_decompressLzmaFrame(&ress, srcFileName, buf[0] != 0xFD);
2778 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
2779 filesize += frameSize;
2780 #else
2781 DISPLAYLEVEL(1, "zstd: %s: xz/lzma file cannot be uncompressed (zstd compiled without HAVE_LZMA) -- ignored \n", srcFileName);
2782 return 1;
2783 #endif
2784 } else if (MEM_readLE32(buf) == LZ4_MAGICNUMBER) {
2785 #ifdef ZSTD_LZ4DECOMPRESS
2786 unsigned long long const frameSize = FIO_decompressLz4Frame(&ress, srcFileName);
2787 if (frameSize == FIO_ERROR_FRAME_DECODING) return 1;
2788 filesize += frameSize;
2789 #else
2790 DISPLAYLEVEL(1, "zstd: %s: lz4 file cannot be uncompressed (zstd compiled without HAVE_LZ4) -- ignored \n", srcFileName);
2791 return 1;
2792 #endif
2793 } else if (passThrough) {
2794 return FIO_passThrough(&ress);
2795 } else {
2796 DISPLAYLEVEL(1, "zstd: %s: unsupported format \n", srcFileName);
2797 return 1;
2798 } } /* for each frame */
2799
2800 /* Final Status */
2801 fCtx->totalBytesOutput += (size_t)filesize;
2802 DISPLAY_PROGRESS("\r%79s\r", "");
2803 if (FIO_shouldDisplayFileSummary(fCtx))
2804 DISPLAY_SUMMARY("%-20s: %llu bytes \n", srcFileName, filesize);
2805
2806 return 0;
2807 }
2808
2809 /** FIO_decompressDstFile() :
2810 open `dstFileName`, or pass-through if writeCtx's file is already != 0,
2811 then start decompression process (FIO_decompressFrames()).
2812 @return : 0 : OK
2813 1 : operation aborted
2814 */
FIO_decompressDstFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,dRess_t ress,const char * dstFileName,const char * srcFileName,const stat_t * srcFileStat)2815 static int FIO_decompressDstFile(FIO_ctx_t* const fCtx,
2816 FIO_prefs_t* const prefs,
2817 dRess_t ress,
2818 const char* dstFileName,
2819 const char* srcFileName,
2820 const stat_t* srcFileStat)
2821 {
2822 int result;
2823 int releaseDstFile = 0;
2824 int transferStat = 0;
2825 int dstFd = 0;
2826
2827 if ((AIO_WritePool_getFile(ress.writeCtx) == NULL) && (prefs->testMode == 0)) {
2828 FILE *dstFile;
2829 int dstFilePermissions = DEFAULT_FILE_PERMISSIONS;
2830 if ( strcmp(srcFileName, stdinmark) /* special case : don't transfer permissions from stdin */
2831 && strcmp(dstFileName, stdoutmark)
2832 && UTIL_isRegularFileStat(srcFileStat) ) {
2833 transferStat = 1;
2834 dstFilePermissions = TEMPORARY_FILE_PERMISSIONS;
2835 }
2836
2837 releaseDstFile = 1;
2838
2839 dstFile = FIO_openDstFile(fCtx, prefs, srcFileName, dstFileName, dstFilePermissions);
2840 if (dstFile==NULL) return 1;
2841 dstFd = fileno(dstFile);
2842 AIO_WritePool_setFile(ress.writeCtx, dstFile);
2843
2844 /* Must only be added after FIO_openDstFile() succeeds.
2845 * Otherwise we may delete the destination file if it already exists,
2846 * and the user presses Ctrl-C when asked if they wish to overwrite.
2847 */
2848 addHandler(dstFileName);
2849 }
2850
2851 result = FIO_decompressFrames(fCtx, ress, prefs, dstFileName, srcFileName);
2852
2853 if (releaseDstFile) {
2854 clearHandler();
2855
2856 if (transferStat) {
2857 UTIL_setFDStat(dstFd, dstFileName, srcFileStat);
2858 }
2859
2860 if (AIO_WritePool_closeFile(ress.writeCtx)) {
2861 DISPLAYLEVEL(1, "zstd: %s: %s \n", dstFileName, strerror(errno));
2862 result = 1;
2863 }
2864
2865 if (transferStat) {
2866 UTIL_utime(dstFileName, srcFileStat);
2867 }
2868
2869 if ( (result != 0) /* operation failure */
2870 && strcmp(dstFileName, stdoutmark) /* special case : don't remove() stdout */
2871 ) {
2872 FIO_removeFile(dstFileName); /* remove decompression artefact; note: don't do anything special if remove() fails */
2873 }
2874 }
2875
2876 return result;
2877 }
2878
2879
2880 /** FIO_decompressSrcFile() :
2881 Open `srcFileName`, transfer control to decompressDstFile()
2882 @return : 0 : OK
2883 1 : error
2884 */
FIO_decompressSrcFile(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,dRess_t ress,const char * dstFileName,const char * srcFileName)2885 static int FIO_decompressSrcFile(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs, dRess_t ress, const char* dstFileName, const char* srcFileName)
2886 {
2887 FILE* srcFile;
2888 stat_t srcFileStat;
2889 int result;
2890 U64 fileSize = UTIL_FILESIZE_UNKNOWN;
2891
2892 if (UTIL_isDirectory(srcFileName)) {
2893 DISPLAYLEVEL(1, "zstd: %s is a directory -- ignored \n", srcFileName);
2894 return 1;
2895 }
2896
2897 srcFile = FIO_openSrcFile(prefs, srcFileName, &srcFileStat);
2898 if (srcFile==NULL) return 1;
2899
2900 /* Don't use AsyncIO for small files */
2901 if (strcmp(srcFileName, stdinmark)) /* Stdin doesn't have stats */
2902 fileSize = UTIL_getFileSizeStat(&srcFileStat);
2903 if(fileSize != UTIL_FILESIZE_UNKNOWN && fileSize < ZSTD_BLOCKSIZE_MAX * 3) {
2904 AIO_ReadPool_setAsync(ress.readCtx, 0);
2905 AIO_WritePool_setAsync(ress.writeCtx, 0);
2906 } else {
2907 AIO_ReadPool_setAsync(ress.readCtx, 1);
2908 AIO_WritePool_setAsync(ress.writeCtx, 1);
2909 }
2910
2911 AIO_ReadPool_setFile(ress.readCtx, srcFile);
2912
2913 result = FIO_decompressDstFile(fCtx, prefs, ress, dstFileName, srcFileName, &srcFileStat);
2914
2915 AIO_ReadPool_setFile(ress.readCtx, NULL);
2916
2917 /* Close file */
2918 if (fclose(srcFile)) {
2919 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno)); /* error should not happen */
2920 return 1;
2921 }
2922 if ( prefs->removeSrcFile /* --rm */
2923 && (result==0) /* decompression successful */
2924 && strcmp(srcFileName, stdinmark) ) /* not stdin */ {
2925 /* We must clear the handler, since after this point calling it would
2926 * delete both the source and destination files.
2927 */
2928 clearHandler();
2929 if (FIO_removeFile(srcFileName)) {
2930 /* failed to remove src file */
2931 DISPLAYLEVEL(1, "zstd: %s: %s \n", srcFileName, strerror(errno));
2932 return 1;
2933 } }
2934 return result;
2935 }
2936
2937
2938
FIO_decompressFilename(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char * dstFileName,const char * srcFileName,const char * dictFileName)2939 int FIO_decompressFilename(FIO_ctx_t* const fCtx, FIO_prefs_t* const prefs,
2940 const char* dstFileName, const char* srcFileName,
2941 const char* dictFileName)
2942 {
2943 dRess_t const ress = FIO_createDResources(prefs, dictFileName);
2944
2945 int const decodingError = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
2946
2947
2948
2949 FIO_freeDResources(ress);
2950 return decodingError;
2951 }
2952
2953 static const char *suffixList[] = {
2954 ZSTD_EXTENSION,
2955 TZSTD_EXTENSION,
2956 #ifndef ZSTD_NODECOMPRESS
2957 ZSTD_ALT_EXTENSION,
2958 #endif
2959 #ifdef ZSTD_GZDECOMPRESS
2960 GZ_EXTENSION,
2961 TGZ_EXTENSION,
2962 #endif
2963 #ifdef ZSTD_LZMADECOMPRESS
2964 LZMA_EXTENSION,
2965 XZ_EXTENSION,
2966 TXZ_EXTENSION,
2967 #endif
2968 #ifdef ZSTD_LZ4DECOMPRESS
2969 LZ4_EXTENSION,
2970 TLZ4_EXTENSION,
2971 #endif
2972 NULL
2973 };
2974
2975 static const char *suffixListStr =
2976 ZSTD_EXTENSION "/" TZSTD_EXTENSION
2977 #ifdef ZSTD_GZDECOMPRESS
2978 "/" GZ_EXTENSION "/" TGZ_EXTENSION
2979 #endif
2980 #ifdef ZSTD_LZMADECOMPRESS
2981 "/" LZMA_EXTENSION "/" XZ_EXTENSION "/" TXZ_EXTENSION
2982 #endif
2983 #ifdef ZSTD_LZ4DECOMPRESS
2984 "/" LZ4_EXTENSION "/" TLZ4_EXTENSION
2985 #endif
2986 ;
2987
2988 /* FIO_determineDstName() :
2989 * create a destination filename from a srcFileName.
2990 * @return a pointer to it.
2991 * @return == NULL if there is an error */
2992 static const char*
FIO_determineDstName(const char * srcFileName,const char * outDirName)2993 FIO_determineDstName(const char* srcFileName, const char* outDirName)
2994 {
2995 static size_t dfnbCapacity = 0;
2996 static char* dstFileNameBuffer = NULL; /* using static allocation : this function cannot be multi-threaded */
2997 size_t dstFileNameEndPos;
2998 char* outDirFilename = NULL;
2999 const char* dstSuffix = "";
3000 size_t dstSuffixLen = 0;
3001
3002 size_t sfnSize = strlen(srcFileName);
3003
3004 size_t srcSuffixLen;
3005 const char* const srcSuffix = strrchr(srcFileName, '.');
3006
3007 if(!strcmp(srcFileName, stdinmark)) {
3008 return stdoutmark;
3009 }
3010
3011 if (srcSuffix == NULL) {
3012 DISPLAYLEVEL(1,
3013 "zstd: %s: unknown suffix (%s expected). "
3014 "Can't derive the output file name. "
3015 "Specify it with -o dstFileName. Ignoring.\n",
3016 srcFileName, suffixListStr);
3017 return NULL;
3018 }
3019 srcSuffixLen = strlen(srcSuffix);
3020
3021 {
3022 const char** matchedSuffixPtr;
3023 for (matchedSuffixPtr = suffixList; *matchedSuffixPtr != NULL; matchedSuffixPtr++) {
3024 if (!strcmp(*matchedSuffixPtr, srcSuffix)) {
3025 break;
3026 }
3027 }
3028
3029 /* check suffix is authorized */
3030 if (sfnSize <= srcSuffixLen || *matchedSuffixPtr == NULL) {
3031 DISPLAYLEVEL(1,
3032 "zstd: %s: unknown suffix (%s expected). "
3033 "Can't derive the output file name. "
3034 "Specify it with -o dstFileName. Ignoring.\n",
3035 srcFileName, suffixListStr);
3036 return NULL;
3037 }
3038
3039 if ((*matchedSuffixPtr)[1] == 't') {
3040 dstSuffix = ".tar";
3041 dstSuffixLen = strlen(dstSuffix);
3042 }
3043 }
3044
3045 if (outDirName) {
3046 outDirFilename = FIO_createFilename_fromOutDir(srcFileName, outDirName, 0);
3047 sfnSize = strlen(outDirFilename);
3048 assert(outDirFilename != NULL);
3049 }
3050
3051 if (dfnbCapacity+srcSuffixLen <= sfnSize+1+dstSuffixLen) {
3052 /* allocate enough space to write dstFilename into it */
3053 free(dstFileNameBuffer);
3054 dfnbCapacity = sfnSize + 20;
3055 dstFileNameBuffer = (char*)malloc(dfnbCapacity);
3056 if (dstFileNameBuffer==NULL)
3057 EXM_THROW(74, "%s : not enough memory for dstFileName",
3058 strerror(errno));
3059 }
3060
3061 /* return dst name == src name truncated from suffix */
3062 assert(dstFileNameBuffer != NULL);
3063 dstFileNameEndPos = sfnSize - srcSuffixLen;
3064 if (outDirFilename) {
3065 memcpy(dstFileNameBuffer, outDirFilename, dstFileNameEndPos);
3066 free(outDirFilename);
3067 } else {
3068 memcpy(dstFileNameBuffer, srcFileName, dstFileNameEndPos);
3069 }
3070
3071 /* The short tar extensions tzst, tgz, txz and tlz4 files should have "tar"
3072 * extension on decompression. Also writes terminating null. */
3073 strcpy(dstFileNameBuffer + dstFileNameEndPos, dstSuffix);
3074 return dstFileNameBuffer;
3075
3076 /* note : dstFileNameBuffer memory is not going to be free */
3077 }
3078
3079 int
FIO_decompressMultipleFilenames(FIO_ctx_t * const fCtx,FIO_prefs_t * const prefs,const char ** srcNamesTable,const char * outMirroredRootDirName,const char * outDirName,const char * outFileName,const char * dictFileName)3080 FIO_decompressMultipleFilenames(FIO_ctx_t* const fCtx,
3081 FIO_prefs_t* const prefs,
3082 const char** srcNamesTable,
3083 const char* outMirroredRootDirName,
3084 const char* outDirName, const char* outFileName,
3085 const char* dictFileName)
3086 {
3087 int status;
3088 int error = 0;
3089 dRess_t ress = FIO_createDResources(prefs, dictFileName);
3090
3091 if (outFileName) {
3092 if (FIO_multiFilesConcatWarning(fCtx, prefs, outFileName, 1 /* displayLevelCutoff */)) {
3093 FIO_freeDResources(ress);
3094 return 1;
3095 }
3096 if (!prefs->testMode) {
3097 FILE* dstFile = FIO_openDstFile(fCtx, prefs, NULL, outFileName, DEFAULT_FILE_PERMISSIONS);
3098 if (dstFile == 0) EXM_THROW(19, "cannot open %s", outFileName);
3099 AIO_WritePool_setFile(ress.writeCtx, dstFile);
3100 }
3101 for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) {
3102 status = FIO_decompressSrcFile(fCtx, prefs, ress, outFileName, srcNamesTable[fCtx->currFileIdx]);
3103 if (!status) fCtx->nbFilesProcessed++;
3104 error |= status;
3105 }
3106 if ((!prefs->testMode) && (AIO_WritePool_closeFile(ress.writeCtx)))
3107 EXM_THROW(72, "Write error : %s : cannot properly close output file",
3108 strerror(errno));
3109 } else {
3110 if (outMirroredRootDirName)
3111 UTIL_mirrorSourceFilesDirectories(srcNamesTable, (unsigned)fCtx->nbFilesTotal, outMirroredRootDirName);
3112
3113 for (; fCtx->currFileIdx < fCtx->nbFilesTotal; fCtx->currFileIdx++) { /* create dstFileName */
3114 const char* const srcFileName = srcNamesTable[fCtx->currFileIdx];
3115 const char* dstFileName = NULL;
3116 if (outMirroredRootDirName) {
3117 char* validMirroredDirName = UTIL_createMirroredDestDirName(srcFileName, outMirroredRootDirName);
3118 if (validMirroredDirName) {
3119 dstFileName = FIO_determineDstName(srcFileName, validMirroredDirName);
3120 free(validMirroredDirName);
3121 } else {
3122 DISPLAYLEVEL(2, "zstd: --output-dir-mirror cannot decompress '%s' into '%s'\n", srcFileName, outMirroredRootDirName);
3123 }
3124 } else {
3125 dstFileName = FIO_determineDstName(srcFileName, outDirName);
3126 }
3127 if (dstFileName == NULL) { error=1; continue; }
3128 status = FIO_decompressSrcFile(fCtx, prefs, ress, dstFileName, srcFileName);
3129 if (!status) fCtx->nbFilesProcessed++;
3130 error |= status;
3131 }
3132 if (outDirName)
3133 FIO_checkFilenameCollisions(srcNamesTable , (unsigned)fCtx->nbFilesTotal);
3134 }
3135
3136 if (FIO_shouldDisplayMultipleFileSummary(fCtx)) {
3137 DISPLAY_PROGRESS("\r%79s\r", "");
3138 DISPLAY_SUMMARY("%d files decompressed : %6llu bytes total \n",
3139 fCtx->nbFilesProcessed, (unsigned long long)fCtx->totalBytesOutput);
3140 }
3141
3142 FIO_freeDResources(ress);
3143 return error;
3144 }
3145
3146 /* **************************************************************************
3147 * .zst file info (--list command)
3148 ***************************************************************************/
3149
3150 typedef struct {
3151 U64 decompressedSize;
3152 U64 compressedSize;
3153 U64 windowSize;
3154 int numActualFrames;
3155 int numSkippableFrames;
3156 int decompUnavailable;
3157 int usesCheck;
3158 BYTE checksum[4];
3159 U32 nbFiles;
3160 unsigned dictID;
3161 } fileInfo_t;
3162
3163 typedef enum {
3164 info_success=0,
3165 info_frame_error=1,
3166 info_not_zstd=2,
3167 info_file_error=3,
3168 info_truncated_input=4
3169 } InfoError;
3170
3171 #define ERROR_IF(c,n,...) { \
3172 if (c) { \
3173 DISPLAYLEVEL(1, __VA_ARGS__); \
3174 DISPLAYLEVEL(1, " \n"); \
3175 return n; \
3176 } \
3177 }
3178
3179 static InfoError
FIO_analyzeFrames(fileInfo_t * info,FILE * const srcFile)3180 FIO_analyzeFrames(fileInfo_t* info, FILE* const srcFile)
3181 {
3182 /* begin analyzing frame */
3183 for ( ; ; ) {
3184 BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX];
3185 size_t const numBytesRead = fread(headerBuffer, 1, sizeof(headerBuffer), srcFile);
3186 if (numBytesRead < ZSTD_FRAMEHEADERSIZE_MIN(ZSTD_f_zstd1)) {
3187 if ( feof(srcFile)
3188 && (numBytesRead == 0)
3189 && (info->compressedSize > 0)
3190 && (info->compressedSize != UTIL_FILESIZE_UNKNOWN) ) {
3191 unsigned long long file_position = (unsigned long long) LONG_TELL(srcFile);
3192 unsigned long long file_size = (unsigned long long) info->compressedSize;
3193 ERROR_IF(file_position != file_size, info_truncated_input,
3194 "Error: seeked to position %llu, which is beyond file size of %llu\n",
3195 file_position,
3196 file_size);
3197 break; /* correct end of file => success */
3198 }
3199 ERROR_IF(feof(srcFile), info_not_zstd, "Error: reached end of file with incomplete frame");
3200 ERROR_IF(1, info_frame_error, "Error: did not reach end of file but ran out of frames");
3201 }
3202 { U32 const magicNumber = MEM_readLE32(headerBuffer);
3203 /* Zstandard frame */
3204 if (magicNumber == ZSTD_MAGICNUMBER) {
3205 ZSTD_FrameHeader header;
3206 U64 const frameContentSize = ZSTD_getFrameContentSize(headerBuffer, numBytesRead);
3207 if ( frameContentSize == ZSTD_CONTENTSIZE_ERROR
3208 || frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ) {
3209 info->decompUnavailable = 1;
3210 } else {
3211 info->decompressedSize += frameContentSize;
3212 }
3213 ERROR_IF(ZSTD_getFrameHeader(&header, headerBuffer, numBytesRead) != 0,
3214 info_frame_error, "Error: could not decode frame header");
3215 if (info->dictID != 0 && info->dictID != header.dictID) {
3216 DISPLAY("WARNING: File contains multiple frames with different dictionary IDs. Showing dictID 0 instead");
3217 info->dictID = 0;
3218 } else {
3219 info->dictID = header.dictID;
3220 }
3221 info->windowSize = header.windowSize;
3222 /* move to the end of the frame header */
3223 { size_t const headerSize = ZSTD_frameHeaderSize(headerBuffer, numBytesRead);
3224 ERROR_IF(ZSTD_isError(headerSize), info_frame_error, "Error: could not determine frame header size");
3225 ERROR_IF(fseek(srcFile, ((long)headerSize)-((long)numBytesRead), SEEK_CUR) != 0,
3226 info_frame_error, "Error: could not move to end of frame header");
3227 }
3228
3229 /* skip all blocks in the frame */
3230 { int lastBlock = 0;
3231 do {
3232 BYTE blockHeaderBuffer[3];
3233 ERROR_IF(fread(blockHeaderBuffer, 1, 3, srcFile) != 3,
3234 info_frame_error, "Error while reading block header");
3235 { U32 const blockHeader = MEM_readLE24(blockHeaderBuffer);
3236 U32 const blockTypeID = (blockHeader >> 1) & 3;
3237 U32 const isRLE = (blockTypeID == 1);
3238 U32 const isWrongBlock = (blockTypeID == 3);
3239 long const blockSize = isRLE ? 1 : (long)(blockHeader >> 3);
3240 ERROR_IF(isWrongBlock, info_frame_error, "Error: unsupported block type");
3241 lastBlock = blockHeader & 1;
3242 ERROR_IF(fseek(srcFile, blockSize, SEEK_CUR) != 0,
3243 info_frame_error, "Error: could not skip to end of block");
3244 }
3245 } while (lastBlock != 1);
3246 }
3247
3248 /* check if checksum is used */
3249 { BYTE const frameHeaderDescriptor = headerBuffer[4];
3250 int const contentChecksumFlag = (frameHeaderDescriptor & (1 << 2)) >> 2;
3251 if (contentChecksumFlag) {
3252 info->usesCheck = 1;
3253 ERROR_IF(fread(info->checksum, 1, 4, srcFile) != 4,
3254 info_frame_error, "Error: could not read checksum");
3255 } }
3256 info->numActualFrames++;
3257 }
3258 /* Skippable frame */
3259 else if ((magicNumber & ZSTD_MAGIC_SKIPPABLE_MASK) == ZSTD_MAGIC_SKIPPABLE_START) {
3260 U32 const frameSize = MEM_readLE32(headerBuffer + 4);
3261 long const seek = (long)(8 + frameSize - numBytesRead);
3262 ERROR_IF(LONG_SEEK(srcFile, seek, SEEK_CUR) != 0,
3263 info_frame_error, "Error: could not find end of skippable frame");
3264 info->numSkippableFrames++;
3265 }
3266 /* unknown content */
3267 else {
3268 return info_not_zstd;
3269 }
3270 } /* magic number analysis */
3271 } /* end analyzing frames */
3272 return info_success;
3273 }
3274
3275
3276 static InfoError
getFileInfo_fileConfirmed(fileInfo_t * info,const char * inFileName)3277 getFileInfo_fileConfirmed(fileInfo_t* info, const char* inFileName)
3278 {
3279 InfoError status;
3280 stat_t srcFileStat;
3281 FILE* const srcFile = FIO_openSrcFile(NULL, inFileName, &srcFileStat);
3282 ERROR_IF(srcFile == NULL, info_file_error, "Error: could not open source file %s", inFileName);
3283
3284 info->compressedSize = UTIL_getFileSizeStat(&srcFileStat);
3285 status = FIO_analyzeFrames(info, srcFile);
3286
3287 fclose(srcFile);
3288 info->nbFiles = 1;
3289 return status;
3290 }
3291
3292
3293 /** getFileInfo() :
3294 * Reads information from file, stores in *info
3295 * @return : InfoError status
3296 */
3297 static InfoError
getFileInfo(fileInfo_t * info,const char * srcFileName)3298 getFileInfo(fileInfo_t* info, const char* srcFileName)
3299 {
3300 ERROR_IF(!UTIL_isRegularFile(srcFileName),
3301 info_file_error, "Error : %s is not a file", srcFileName);
3302 return getFileInfo_fileConfirmed(info, srcFileName);
3303 }
3304
3305
3306 static void
displayInfo(const char * inFileName,const fileInfo_t * info,int displayLevel)3307 displayInfo(const char* inFileName, const fileInfo_t* info, int displayLevel)
3308 {
3309 UTIL_HumanReadableSize_t const window_hrs = UTIL_makeHumanReadableSize(info->windowSize);
3310 UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(info->compressedSize);
3311 UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(info->decompressedSize);
3312 double const ratio = (info->compressedSize == 0) ? 0 : ((double)info->decompressedSize)/(double)info->compressedSize;
3313 const char* const checkString = (info->usesCheck ? "XXH64" : "None");
3314 if (displayLevel <= 2) {
3315 if (!info->decompUnavailable) {
3316 DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %s\n",
3317 info->numSkippableFrames + info->numActualFrames,
3318 info->numSkippableFrames,
3319 compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3320 decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
3321 ratio, checkString, inFileName);
3322 } else {
3323 DISPLAYOUT("%6d %5d %6.*f%4s %5s %s\n",
3324 info->numSkippableFrames + info->numActualFrames,
3325 info->numSkippableFrames,
3326 compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3327 checkString, inFileName);
3328 }
3329 } else {
3330 DISPLAYOUT("%s \n", inFileName);
3331 DISPLAYOUT("# Zstandard Frames: %d\n", info->numActualFrames);
3332 if (info->numSkippableFrames)
3333 DISPLAYOUT("# Skippable Frames: %d\n", info->numSkippableFrames);
3334 DISPLAYOUT("DictID: %u\n", info->dictID);
3335 DISPLAYOUT("Window Size: %.*f%s (%llu B)\n",
3336 window_hrs.precision, window_hrs.value, window_hrs.suffix,
3337 (unsigned long long)info->windowSize);
3338 DISPLAYOUT("Compressed Size: %.*f%s (%llu B)\n",
3339 compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3340 (unsigned long long)info->compressedSize);
3341 if (!info->decompUnavailable) {
3342 DISPLAYOUT("Decompressed Size: %.*f%s (%llu B)\n",
3343 decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
3344 (unsigned long long)info->decompressedSize);
3345 DISPLAYOUT("Ratio: %.4f\n", ratio);
3346 }
3347
3348 if (info->usesCheck && info->numActualFrames == 1) {
3349 DISPLAYOUT("Check: %s %02x%02x%02x%02x\n", checkString,
3350 info->checksum[3], info->checksum[2],
3351 info->checksum[1], info->checksum[0]
3352 );
3353 } else {
3354 DISPLAYOUT("Check: %s\n", checkString);
3355 }
3356
3357 DISPLAYOUT("\n");
3358 }
3359 }
3360
FIO_addFInfo(fileInfo_t fi1,fileInfo_t fi2)3361 static fileInfo_t FIO_addFInfo(fileInfo_t fi1, fileInfo_t fi2)
3362 {
3363 fileInfo_t total;
3364 memset(&total, 0, sizeof(total));
3365 total.numActualFrames = fi1.numActualFrames + fi2.numActualFrames;
3366 total.numSkippableFrames = fi1.numSkippableFrames + fi2.numSkippableFrames;
3367 total.compressedSize = fi1.compressedSize + fi2.compressedSize;
3368 total.decompressedSize = fi1.decompressedSize + fi2.decompressedSize;
3369 total.decompUnavailable = fi1.decompUnavailable | fi2.decompUnavailable;
3370 total.usesCheck = fi1.usesCheck & fi2.usesCheck;
3371 total.nbFiles = fi1.nbFiles + fi2.nbFiles;
3372 return total;
3373 }
3374
3375 static int
FIO_listFile(fileInfo_t * total,const char * inFileName,int displayLevel)3376 FIO_listFile(fileInfo_t* total, const char* inFileName, int displayLevel)
3377 {
3378 fileInfo_t info;
3379 memset(&info, 0, sizeof(info));
3380 { InfoError const error = getFileInfo(&info, inFileName);
3381 switch (error) {
3382 case info_frame_error:
3383 /* display error, but provide output */
3384 DISPLAYLEVEL(1, "Error while parsing \"%s\" \n", inFileName);
3385 break;
3386 case info_not_zstd:
3387 DISPLAYOUT("File \"%s\" not compressed by zstd \n", inFileName);
3388 if (displayLevel > 2) DISPLAYOUT("\n");
3389 return 1;
3390 case info_file_error:
3391 /* error occurred while opening the file */
3392 if (displayLevel > 2) DISPLAYOUT("\n");
3393 return 1;
3394 case info_truncated_input:
3395 DISPLAYOUT("File \"%s\" is truncated \n", inFileName);
3396 if (displayLevel > 2) DISPLAYOUT("\n");
3397 return 1;
3398 case info_success:
3399 default:
3400 break;
3401 }
3402
3403 displayInfo(inFileName, &info, displayLevel);
3404 *total = FIO_addFInfo(*total, info);
3405 assert(error == info_success || error == info_frame_error);
3406 return (int)error;
3407 }
3408 }
3409
FIO_listMultipleFiles(unsigned numFiles,const char ** filenameTable,int displayLevel)3410 int FIO_listMultipleFiles(unsigned numFiles, const char** filenameTable, int displayLevel)
3411 {
3412 /* ensure no specified input is stdin (needs fseek() capability) */
3413 { unsigned u;
3414 for (u=0; u<numFiles;u++) {
3415 ERROR_IF(!strcmp (filenameTable[u], stdinmark),
3416 1, "zstd: --list does not support reading from standard input");
3417 } }
3418
3419 if (numFiles == 0) {
3420 if (!UTIL_isConsole(stdin)) {
3421 DISPLAYLEVEL(1, "zstd: --list does not support reading from standard input \n");
3422 }
3423 DISPLAYLEVEL(1, "No files given \n");
3424 return 1;
3425 }
3426
3427 if (displayLevel <= 2) {
3428 DISPLAYOUT("Frames Skips Compressed Uncompressed Ratio Check Filename\n");
3429 }
3430 { int error = 0;
3431 fileInfo_t total;
3432 memset(&total, 0, sizeof(total));
3433 total.usesCheck = 1;
3434 /* --list each file, and check for any error */
3435 { unsigned u;
3436 for (u=0; u<numFiles;u++) {
3437 error |= FIO_listFile(&total, filenameTable[u], displayLevel);
3438 } }
3439 if (numFiles > 1 && displayLevel <= 2) { /* display total */
3440 UTIL_HumanReadableSize_t const compressed_hrs = UTIL_makeHumanReadableSize(total.compressedSize);
3441 UTIL_HumanReadableSize_t const decompressed_hrs = UTIL_makeHumanReadableSize(total.decompressedSize);
3442 double const ratio = (total.compressedSize == 0) ? 0 : ((double)total.decompressedSize)/(double)total.compressedSize;
3443 const char* const checkString = (total.usesCheck ? "XXH64" : "");
3444 DISPLAYOUT("----------------------------------------------------------------- \n");
3445 if (total.decompUnavailable) {
3446 DISPLAYOUT("%6d %5d %6.*f%4s %5s %u files\n",
3447 total.numSkippableFrames + total.numActualFrames,
3448 total.numSkippableFrames,
3449 compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3450 checkString, (unsigned)total.nbFiles);
3451 } else {
3452 DISPLAYOUT("%6d %5d %6.*f%4s %8.*f%4s %5.3f %5s %u files\n",
3453 total.numSkippableFrames + total.numActualFrames,
3454 total.numSkippableFrames,
3455 compressed_hrs.precision, compressed_hrs.value, compressed_hrs.suffix,
3456 decompressed_hrs.precision, decompressed_hrs.value, decompressed_hrs.suffix,
3457 ratio, checkString, (unsigned)total.nbFiles);
3458 } }
3459 return error;
3460 }
3461 }
3462
3463
3464 #endif /* #ifndef ZSTD_NODECOMPRESS */
3465