1 1.1 christos /* 2 1.1 christos * Copyright (c) Meta Platforms, Inc. and affiliates. 3 1.1 christos * All rights reserved. 4 1.1 christos * 5 1.1 christos * This source code is licensed under both the BSD-style license (found in the 6 1.1 christos * LICENSE file in the root directory of this source tree) and the GPLv2 (found 7 1.1 christos * in the COPYING file in the root directory of this source tree). 8 1.1 christos * You may select, at your option, one of the above-listed licenses. 9 1.1 christos */ 10 1.1 christos 11 1.1 christos #include <string.h> 12 1.1 christos #include <stdio.h> 13 1.1 christos #include <stdlib.h> 14 1.1 christos 15 1.1 christos #define ZSTD_STATIC_LINKING_ONLY 16 1.1 christos #include "util.h" 17 1.1 christos #include "zstd.h" 18 1.1 christos 19 1.1 christos #define CHECK(cond, ...) \ 20 1.1 christos do { \ 21 1.1 christos if (!(cond)) { \ 22 1.1 christos fprintf(stderr, "%s:%d CHECK(%s) failed: ", __FILE__, __LINE__, #cond); \ 23 1.1 christos fprintf(stderr, "" __VA_ARGS__); \ 24 1.1 christos fprintf(stderr, "\n"); \ 25 1.1 christos exit(1); \ 26 1.1 christos } \ 27 1.1 christos } while (0) 28 1.1 christos 29 1.1 christos static void usage(char const *program) { 30 1.1 christos fprintf(stderr, "USAGE: %s FILE.zst PREFIX\n", program); 31 1.1 christos fprintf(stderr, "FILE.zst: A zstd compressed file with multiple frames\n"); 32 1.1 christos fprintf(stderr, "PREFIX: The output prefix. Uncompressed files will be " 33 1.1 christos "created named ${PREFIX}0 ${PREFIX}1...\n\n"); 34 1.1 christos fprintf(stderr, "This program takes concatenated zstd frames and " 35 1.1 christos "decompresses them into individual files.\n"); 36 1.1 christos fprintf(stderr, "E.g. files created with a command like: zstd -r directory " 37 1.1 christos "-o file.zst\n"); 38 1.1 christos } 39 1.1 christos 40 1.1 christos typedef struct { 41 1.1 christos char *data; 42 1.1 christos size_t size; 43 1.1 christos size_t frames; 44 1.1 christos size_t maxFrameSize; 45 1.1 christos } ZstdFrames; 46 1.1 christos 47 1.1 christos static ZstdFrames readFile(char const *fileName) { 48 1.1 christos U64 const fileSize = UTIL_getFileSize(fileName); 49 1.1 christos CHECK(fileSize != UTIL_FILESIZE_UNKNOWN, "Unknown file size!"); 50 1.1 christos 51 1.1 christos char *const data = (char *)malloc(fileSize); 52 1.1 christos CHECK(data != NULL, "Allocation failed"); 53 1.1 christos 54 1.1 christos FILE *file = fopen(fileName, "rb"); 55 1.1 christos CHECK(file != NULL, "fopen failed"); 56 1.1 christos 57 1.1 christos size_t const readSize = fread(data, 1, fileSize, file); 58 1.1 christos CHECK(readSize == fileSize, "fread failed"); 59 1.1 christos 60 1.1 christos fclose(file); 61 1.1 christos ZstdFrames frames; 62 1.1 christos frames.data = (char *)data; 63 1.1 christos frames.size = fileSize; 64 1.1 christos frames.frames = 0; 65 1.1 christos 66 1.1 christos size_t index; 67 1.1 christos size_t maxFrameSize = 0; 68 1.1 christos for (index = 0; index < fileSize;) { 69 1.1 christos size_t const frameSize = 70 1.1 christos ZSTD_findFrameCompressedSize(data + index, fileSize - index); 71 1.1 christos CHECK(!ZSTD_isError(frameSize), "Bad zstd frame: %s", 72 1.1 christos ZSTD_getErrorName(frameSize)); 73 1.1 christos if (frameSize > maxFrameSize) 74 1.1 christos maxFrameSize = frameSize; 75 1.1 christos frames.frames += 1; 76 1.1 christos index += frameSize; 77 1.1 christos } 78 1.1 christos CHECK(index == fileSize, "Zstd file corrupt!"); 79 1.1 christos frames.maxFrameSize = maxFrameSize; 80 1.1 christos 81 1.1 christos return frames; 82 1.1 christos } 83 1.1 christos 84 1.1 christos static int computePadding(size_t numFrames) { 85 1.1 christos return snprintf(NULL, 0, "%u", (unsigned)numFrames); 86 1.1 christos } 87 1.1 christos 88 1.1 christos int main(int argc, char **argv) { 89 1.1 christos if (argc != 3) { 90 1.1 christos usage(argv[0]); 91 1.1 christos exit(1); 92 1.1 christos } 93 1.1 christos char const *const zstdFile = argv[1]; 94 1.1 christos char const *const prefix = argv[2]; 95 1.1 christos 96 1.1 christos ZstdFrames frames = readFile(zstdFile); 97 1.1 christos 98 1.1 christos if (frames.frames <= 1) { 99 1.1 christos fprintf( 100 1.1 christos stderr, 101 1.1 christos "%s only has %u zstd frame. Simply use `zstd -d` to decompress it.\n", 102 1.1 christos zstdFile, (unsigned)frames.frames); 103 1.1 christos exit(1); 104 1.1 christos } 105 1.1 christos 106 1.1 christos int const padding = computePadding(frames.frames - 1); 107 1.1 christos 108 1.1 christos size_t const outFileNameSize = strlen(prefix) + padding + 1; 109 1.1 christos char* outFileName = malloc(outFileNameSize); 110 1.1 christos CHECK(outFileName != NULL, "Allocation failure"); 111 1.1 christos 112 1.1 christos size_t const bufferSize = 128 * 1024; 113 1.1 christos void *buffer = malloc(bufferSize); 114 1.1 christos CHECK(buffer != NULL, "Allocation failure"); 115 1.1 christos 116 1.1 christos ZSTD_DCtx* dctx = ZSTD_createDCtx(); 117 1.1 christos CHECK(dctx != NULL, "Allocation failure"); 118 1.1 christos 119 1.1 christos fprintf(stderr, "Recovering %u files...\n", (unsigned)frames.frames); 120 1.1 christos 121 1.1 christos size_t index; 122 1.1 christos size_t frame = 0; 123 1.1 christos for (index = 0; index < frames.size; ++frame) { 124 1.1 christos size_t const frameSize = 125 1.1 christos ZSTD_findFrameCompressedSize(frames.data + index, frames.size - index); 126 1.1 christos 127 1.1 christos int const ret = snprintf(outFileName, outFileNameSize, "%s%0*u", prefix, padding, (unsigned)frame); 128 1.1 christos CHECK(ret >= 0 && (size_t)ret <= outFileNameSize, "snprintf failed!"); 129 1.1 christos 130 1.1 christos FILE* outFile = fopen(outFileName, "wb"); 131 1.1 christos CHECK(outFile != NULL, "fopen failed"); 132 1.1 christos 133 1.1 christos ZSTD_DCtx_reset(dctx, ZSTD_reset_session_only); 134 1.1 christos ZSTD_inBuffer in = {frames.data + index, frameSize, 0}; 135 1.1 christos while (in.pos < in.size) { 136 1.1 christos ZSTD_outBuffer out = {buffer, bufferSize, 0}; 137 1.1 christos CHECK(!ZSTD_isError(ZSTD_decompressStream(dctx, &out, &in)), "decompression failed"); 138 1.1 christos size_t const writeSize = fwrite(out.dst, 1, out.pos, outFile); 139 1.1 christos CHECK(writeSize == out.pos, "fwrite failed"); 140 1.1 christos } 141 1.1 christos fclose(outFile); 142 1.1 christos fprintf(stderr, "Recovered %s\n", outFileName); 143 1.1 christos index += frameSize; 144 1.1 christos } 145 1.1 christos fprintf(stderr, "Complete\n"); 146 1.1 christos 147 1.1 christos free(outFileName); 148 1.1 christos ZSTD_freeDCtx(dctx); 149 1.1 christos free(buffer); 150 1.1 christos free(frames.data); 151 1.1 christos return 0; 152 1.1 christos } 153