mozglue/linker/szip.cpp

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include <algorithm>
michael@0 6 #include <map>
michael@0 7 #include <sys/stat.h>
michael@0 8 #include <string>
michael@0 9 #include <sstream>
michael@0 10 #include <cstring>
michael@0 11 #include <cstdlib>
michael@0 12 #include <zlib.h>
michael@0 13 #include <fcntl.h>
michael@0 14 #include <errno.h>
michael@0 15 #include "mozilla/Assertions.h"
michael@0 16 #include "mozilla/Scoped.h"
michael@0 17 #include "SeekableZStream.h"
michael@0 18 #include "Utils.h"
michael@0 19 #include "Logging.h"
michael@0 20
michael@0 21 Logging Logging::Singleton;
michael@0 22
michael@0 23 const char *filterName[] = {
michael@0 24 "none",
michael@0 25 "thumb",
michael@0 26 "arm",
michael@0 27 "x86",
michael@0 28 "auto"
michael@0 29 };
michael@0 30
michael@0 31 /* Maximum supported size for chunkSize */
michael@0 32 static const size_t maxChunkSize =
michael@0 33 1 << (8 * std::min(sizeof(((SeekableZStreamHeader *)nullptr)->chunkSize),
michael@0 34 sizeof(((SeekableZStreamHeader *)nullptr)->lastChunkSize)) - 1);
michael@0 35
michael@0 36 class Buffer: public MappedPtr
michael@0 37 {
michael@0 38 public:
michael@0 39 virtual ~Buffer() { }
michael@0 40
michael@0 41 virtual bool Resize(size_t size)
michael@0 42 {
michael@0 43 MemoryRange buf = mmap(nullptr, size, PROT_READ | PROT_WRITE,
michael@0 44 MAP_PRIVATE | MAP_ANON, -1, 0);
michael@0 45 if (buf == MAP_FAILED)
michael@0 46 return false;
michael@0 47 if (*this != MAP_FAILED)
michael@0 48 memcpy(buf, *this, std::min(size, GetLength()));
michael@0 49 Assign(buf);
michael@0 50 return true;
michael@0 51 }
michael@0 52
michael@0 53 bool Fill(Buffer &other)
michael@0 54 {
michael@0 55 size_t size = other.GetLength();
michael@0 56 if (!size || !Resize(size))
michael@0 57 return false;
michael@0 58 memcpy(static_cast<void *>(*this), static_cast<void *>(other), size);
michael@0 59 return true;
michael@0 60 }
michael@0 61 };
michael@0 62
michael@0 63 class FileBuffer: public Buffer
michael@0 64 {
michael@0 65 public:
michael@0 66 bool Init(const char *name, bool writable_ = false)
michael@0 67 {
michael@0 68 fd = open(name, writable_ ? O_RDWR | O_CREAT | O_TRUNC : O_RDONLY, 0666);
michael@0 69 if (fd == -1)
michael@0 70 return false;
michael@0 71 writable = writable_;
michael@0 72 return true;
michael@0 73 }
michael@0 74
michael@0 75 virtual bool Resize(size_t size)
michael@0 76 {
michael@0 77 if (writable) {
michael@0 78 if (ftruncate(fd, size) == -1)
michael@0 79 return false;
michael@0 80 }
michael@0 81 Assign(MemoryRange::mmap(nullptr, size,
michael@0 82 PROT_READ | (writable ? PROT_WRITE : 0),
michael@0 83 writable ? MAP_SHARED : MAP_PRIVATE, fd, 0));
michael@0 84 return this != MAP_FAILED;
michael@0 85 }
michael@0 86
michael@0 87 int getFd()
michael@0 88 {
michael@0 89 return fd;
michael@0 90 }
michael@0 91
michael@0 92 private:
michael@0 93 AutoCloseFD fd;
michael@0 94 bool writable;
michael@0 95 };
michael@0 96
michael@0 97 class FilteredBuffer: public Buffer
michael@0 98 {
michael@0 99 public:
michael@0 100 void Filter(Buffer &other, SeekableZStream::FilterId filter, size_t chunkSize)
michael@0 101 {
michael@0 102 SeekableZStream::ZStreamFilter filterCB =
michael@0 103 SeekableZStream::GetFilter(filter);
michael@0 104 MOZ_ASSERT(filterCB);
michael@0 105 Fill(other);
michael@0 106 size_t size = other.GetLength();
michael@0 107 Bytef *data = reinterpret_cast<Bytef *>(static_cast<void *>(*this));
michael@0 108 size_t avail = 0;
michael@0 109 /* Filter needs to be applied in chunks. */
michael@0 110 while (size) {
michael@0 111 avail = std::min(size, chunkSize);
michael@0 112 filterCB(data - static_cast<unsigned char *>(static_cast<void *>(*this)),
michael@0 113 SeekableZStream::FILTER, data, avail);
michael@0 114 size -= avail;
michael@0 115 data += avail;
michael@0 116 }
michael@0 117 }
michael@0 118 };
michael@0 119
michael@0 120 template <typename T>
michael@0 121 class Dictionary: public Buffer
michael@0 122 {
michael@0 123 typedef T piece;
michael@0 124 typedef std::pair<piece, int> stat_pair;
michael@0 125
michael@0 126 static bool stat_cmp(stat_pair a, stat_pair b)
michael@0 127 {
michael@0 128 return a.second < b.second;
michael@0 129 }
michael@0 130
michael@0 131 public:
michael@0 132 Dictionary(Buffer &inBuf, size_t size)
michael@0 133 {
michael@0 134 if (!size || !Resize(size))
michael@0 135 return;
michael@0 136 DEBUG_LOG("Creating dictionary");
michael@0 137 piece *origBufPieces = reinterpret_cast<piece *>(
michael@0 138 static_cast<void *>(inBuf));
michael@0 139 std::map<piece, int> stats;
michael@0 140 for (unsigned int i = 0; i < inBuf.GetLength() / sizeof(piece); i++) {
michael@0 141 stats[origBufPieces[i]]++;
michael@0 142 }
michael@0 143 std::vector<stat_pair> statsVec(stats.begin(), stats.end());
michael@0 144 std::sort(statsVec.begin(), statsVec.end(), stat_cmp);
michael@0 145
michael@0 146 piece *dictPieces = reinterpret_cast<piece *>(
michael@0 147 static_cast<void *>(*this));
michael@0 148 typename std::vector<stat_pair>::reverse_iterator it = statsVec.rbegin();
michael@0 149 for (int i = size / sizeof(piece); i > 0 && it < statsVec.rend();
michael@0 150 i--, ++it) {
michael@0 151 dictPieces[i - 1] = it->first;
michael@0 152 }
michael@0 153 }
michael@0 154 };
michael@0 155
michael@0 156 class SzipAction
michael@0 157 {
michael@0 158 public:
michael@0 159 virtual int run(const char *name, Buffer &origBuf,
michael@0 160 const char *outName, Buffer &outBuf) = 0;
michael@0 161
michael@0 162 virtual ~SzipAction() {}
michael@0 163 };
michael@0 164
michael@0 165 class SzipDecompress: public SzipAction
michael@0 166 {
michael@0 167 public:
michael@0 168 int run(const char *name, Buffer &origBuf,
michael@0 169 const char *outName, Buffer &outBuf);
michael@0 170 };
michael@0 171
michael@0 172
michael@0 173 class SzipCompress: public SzipAction
michael@0 174 {
michael@0 175 public:
michael@0 176 int run(const char *name, Buffer &origBuf,
michael@0 177 const char *outName, Buffer &outBuf);
michael@0 178
michael@0 179 SzipCompress(size_t aChunkSize, SeekableZStream::FilterId aFilter,
michael@0 180 size_t aDictSize)
michael@0 181 : chunkSize(aChunkSize ? aChunkSize : 16384)
michael@0 182 , filter(aFilter)
michael@0 183 , dictSize(aDictSize)
michael@0 184 {}
michael@0 185
michael@0 186 const static signed char winSizeLog = 15;
michael@0 187 const static size_t winSize = 1 << winSizeLog;
michael@0 188
michael@0 189 const static SeekableZStream::FilterId DEFAULT_FILTER =
michael@0 190 #if defined(TARGET_THUMB)
michael@0 191 SeekableZStream::BCJ_THUMB;
michael@0 192 #elif defined(TARGET_ARM)
michael@0 193 SeekableZStream::BCJ_ARM;
michael@0 194 #elif defined(TARGET_X86)
michael@0 195 SeekableZStream::BCJ_X86;
michael@0 196 #else
michael@0 197 SeekableZStream::NONE;
michael@0 198 #endif
michael@0 199
michael@0 200 private:
michael@0 201
michael@0 202 int do_compress(Buffer &origBuf, Buffer &outBuf, const unsigned char *aDict,
michael@0 203 size_t aDictSize, SeekableZStream::FilterId aFilter);
michael@0 204
michael@0 205 size_t chunkSize;
michael@0 206 SeekableZStream::FilterId filter;
michael@0 207 size_t dictSize;
michael@0 208 };
michael@0 209
michael@0 210 /* Decompress a seekable compressed stream */
michael@0 211 int SzipDecompress::run(const char *name, Buffer &origBuf,
michael@0 212 const char *outName, Buffer &outBuf)
michael@0 213 {
michael@0 214 size_t origSize = origBuf.GetLength();
michael@0 215 if (origSize < sizeof(SeekableZStreamHeader)) {
michael@0 216 LOG("%s is not compressed", name);
michael@0 217 return 0;
michael@0 218 }
michael@0 219
michael@0 220 SeekableZStream zstream;
michael@0 221 if (!zstream.Init(origBuf, origSize))
michael@0 222 return 0;
michael@0 223
michael@0 224 size_t size = zstream.GetUncompressedSize();
michael@0 225
michael@0 226 /* Give enough room for the uncompressed data */
michael@0 227 if (!outBuf.Resize(size)) {
michael@0 228 LOG("Error resizing %s: %s", outName, strerror(errno));
michael@0 229 return 1;
michael@0 230 }
michael@0 231
michael@0 232 if (!zstream.Decompress(outBuf, 0, size))
michael@0 233 return 1;
michael@0 234
michael@0 235 return 0;
michael@0 236 }
michael@0 237
michael@0 238 /* Generate a seekable compressed stream. */
michael@0 239 int SzipCompress::run(const char *name, Buffer &origBuf,
michael@0 240 const char *outName, Buffer &outBuf)
michael@0 241 {
michael@0 242 size_t origSize = origBuf.GetLength();
michael@0 243 if (origSize == 0) {
michael@0 244 LOG("Won't compress %s: it's empty", name);
michael@0 245 return 1;
michael@0 246 }
michael@0 247 if (SeekableZStreamHeader::validate(origBuf)) {
michael@0 248 LOG("Skipping %s: it's already a szip", name);
michael@0 249 return 0;
michael@0 250 }
michael@0 251 bool compressed = false;
michael@0 252 LOG("Size = %" PRIuSize, origSize);
michael@0 253
michael@0 254 /* Allocate a buffer the size of the uncompressed data: we don't want
michael@0 255 * a compressed file larger than that anyways. */
michael@0 256 if (!outBuf.Resize(origSize)) {
michael@0 257 LOG("Couldn't allocate output buffer: %s", strerror(errno));
michael@0 258 return 1;
michael@0 259 }
michael@0 260
michael@0 261 /* Find the most appropriate filter */
michael@0 262 SeekableZStream::FilterId firstFilter, lastFilter;
michael@0 263 bool scanFilters;
michael@0 264 if (filter == SeekableZStream::FILTER_MAX) {
michael@0 265 firstFilter = SeekableZStream::NONE;
michael@0 266 lastFilter = SeekableZStream::FILTER_MAX;
michael@0 267 scanFilters = true;
michael@0 268 } else {
michael@0 269 firstFilter = lastFilter = filter;
michael@0 270 ++lastFilter;
michael@0 271 scanFilters = false;
michael@0 272 }
michael@0 273
michael@0 274 mozilla::ScopedDeletePtr<Buffer> filteredBuf;
michael@0 275 Buffer *origData;
michael@0 276 for (SeekableZStream::FilterId f = firstFilter; f < lastFilter; ++f) {
michael@0 277 FilteredBuffer *filteredTmp = nullptr;
michael@0 278 Buffer tmpBuf;
michael@0 279 if (f != SeekableZStream::NONE) {
michael@0 280 DEBUG_LOG("Applying filter \"%s\"", filterName[f]);
michael@0 281 filteredTmp = new FilteredBuffer();
michael@0 282 filteredTmp->Filter(origBuf, f, chunkSize);
michael@0 283 origData = filteredTmp;
michael@0 284 } else {
michael@0 285 origData = &origBuf;
michael@0 286 }
michael@0 287 if (dictSize && !scanFilters) {
michael@0 288 filteredBuf = filteredTmp;
michael@0 289 break;
michael@0 290 }
michael@0 291 DEBUG_LOG("Compressing with no dictionary");
michael@0 292 if (do_compress(*origData, tmpBuf, nullptr, 0, f) == 0) {
michael@0 293 if (tmpBuf.GetLength() < outBuf.GetLength()) {
michael@0 294 outBuf.Fill(tmpBuf);
michael@0 295 compressed = true;
michael@0 296 filter = f;
michael@0 297 filteredBuf = filteredTmp;
michael@0 298 continue;
michael@0 299 }
michael@0 300 }
michael@0 301 delete filteredTmp;
michael@0 302 }
michael@0 303
michael@0 304 origData = filteredBuf ? filteredBuf : &origBuf;
michael@0 305
michael@0 306 if (dictSize) {
michael@0 307 Dictionary<uint64_t> dict(*origData, dictSize ? SzipCompress::winSize : 0);
michael@0 308
michael@0 309 /* Find the most appropriate dictionary size */
michael@0 310 size_t firstDictSize, lastDictSize;
michael@0 311 if (dictSize == (size_t) -1) {
michael@0 312 /* If we scanned for filters, we effectively already tried dictSize=0 */
michael@0 313 firstDictSize = scanFilters ? 4096 : 0;
michael@0 314 lastDictSize = SzipCompress::winSize;
michael@0 315 } else {
michael@0 316 firstDictSize = lastDictSize = dictSize;
michael@0 317 }
michael@0 318
michael@0 319 Buffer tmpBuf;
michael@0 320 for (size_t d = firstDictSize; d <= lastDictSize; d += 4096) {
michael@0 321 DEBUG_LOG("Compressing with dictionary of size %" PRIuSize, d);
michael@0 322 if (do_compress(*origData, tmpBuf, static_cast<unsigned char *>(dict)
michael@0 323 + SzipCompress::winSize - d, d, filter))
michael@0 324 continue;
michael@0 325 if (!compressed || tmpBuf.GetLength() < outBuf.GetLength()) {
michael@0 326 outBuf.Fill(tmpBuf);
michael@0 327 compressed = true;
michael@0 328 dictSize = d;
michael@0 329 }
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 if (!compressed) {
michael@0 334 outBuf.Fill(origBuf);
michael@0 335 LOG("Not compressed");
michael@0 336 return 0;
michael@0 337 }
michael@0 338
michael@0 339 if (dictSize == (size_t) -1)
michael@0 340 dictSize = 0;
michael@0 341
michael@0 342 DEBUG_LOG("Used filter \"%s\" and dictionary size of %" PRIuSize,
michael@0 343 filterName[filter], dictSize);
michael@0 344 LOG("Compressed size is %" PRIuSize, outBuf.GetLength());
michael@0 345
michael@0 346 /* Sanity check */
michael@0 347 Buffer tmpBuf;
michael@0 348 SzipDecompress decompress;
michael@0 349 if (decompress.run("buffer", outBuf, "buffer", tmpBuf))
michael@0 350 return 1;
michael@0 351
michael@0 352 size_t size = tmpBuf.GetLength();
michael@0 353 if (size != origSize) {
michael@0 354 LOG("Compression error: %" PRIuSize " != %" PRIuSize, size, origSize);
michael@0 355 return 1;
michael@0 356 }
michael@0 357 if (memcmp(static_cast<void *>(origBuf), static_cast<void *>(tmpBuf), size)) {
michael@0 358 LOG("Compression error: content mismatch");
michael@0 359 return 1;
michael@0 360 }
michael@0 361 return 0;
michael@0 362 }
michael@0 363
michael@0 364 int SzipCompress::do_compress(Buffer &origBuf, Buffer &outBuf,
michael@0 365 const unsigned char *aDict, size_t aDictSize,
michael@0 366 SeekableZStream::FilterId aFilter)
michael@0 367 {
michael@0 368 size_t origSize = origBuf.GetLength();
michael@0 369 MOZ_ASSERT(origSize != 0);
michael@0 370
michael@0 371 /* Expected total number of chunks */
michael@0 372 size_t nChunks = ((origSize + chunkSize - 1) / chunkSize);
michael@0 373
michael@0 374 /* The first chunk is going to be stored after the header, the dictionary
michael@0 375 * and the offset table */
michael@0 376 size_t offset = sizeof(SeekableZStreamHeader) + aDictSize
michael@0 377 + nChunks * sizeof(uint32_t);
michael@0 378
michael@0 379 if (offset >= origSize)
michael@0 380 return 1;
michael@0 381
michael@0 382 /* Allocate a buffer the size of the uncompressed data: we don't want
michael@0 383 * a compressed file larger than that anyways. */
michael@0 384 if (!outBuf.Resize(origSize)) {
michael@0 385 LOG("Couldn't allocate output buffer: %s", strerror(errno));
michael@0 386 return 1;
michael@0 387 }
michael@0 388
michael@0 389 SeekableZStreamHeader *header = new (outBuf) SeekableZStreamHeader;
michael@0 390 unsigned char *dictionary = static_cast<unsigned char *>(
michael@0 391 outBuf + sizeof(SeekableZStreamHeader));
michael@0 392 le_uint32 *entry =
michael@0 393 reinterpret_cast<le_uint32 *>(dictionary + aDictSize);
michael@0 394
michael@0 395 /* Initialize header */
michael@0 396 header->chunkSize = chunkSize;
michael@0 397 header->dictSize = aDictSize;
michael@0 398 header->totalSize = offset;
michael@0 399 header->windowBits = -SzipCompress::winSizeLog; // Raw stream,
michael@0 400 // window size of 32k.
michael@0 401 header->filter = aFilter;
michael@0 402 if (aDictSize)
michael@0 403 memcpy(dictionary, aDict, aDictSize);
michael@0 404
michael@0 405 /* Initialize zlib structure */
michael@0 406 z_stream zStream;
michael@0 407 memset(&zStream, 0, sizeof(zStream));
michael@0 408 zStream.avail_out = origSize - offset;
michael@0 409 zStream.next_out = static_cast<Bytef*>(outBuf) + offset;
michael@0 410
michael@0 411 size_t avail = 0;
michael@0 412 size_t size = origSize;
michael@0 413 unsigned char *data = reinterpret_cast<unsigned char *>(
michael@0 414 static_cast<void *>(origBuf));
michael@0 415 while (size) {
michael@0 416 avail = std::min(size, chunkSize);
michael@0 417
michael@0 418 /* Compress chunk */
michael@0 419 int ret = deflateInit2(&zStream, 9, Z_DEFLATED, header->windowBits,
michael@0 420 MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
michael@0 421 if (aDictSize)
michael@0 422 deflateSetDictionary(&zStream, dictionary, aDictSize);
michael@0 423 MOZ_ASSERT(ret == Z_OK);
michael@0 424 zStream.avail_in = avail;
michael@0 425 zStream.next_in = data;
michael@0 426 ret = deflate(&zStream, Z_FINISH);
michael@0 427 MOZ_ASSERT(ret == Z_STREAM_END);
michael@0 428 ret = deflateEnd(&zStream);
michael@0 429 MOZ_ASSERT(ret == Z_OK);
michael@0 430 if (zStream.avail_out <= 0)
michael@0 431 return 1;
michael@0 432
michael@0 433 size_t len = origSize - offset - zStream.avail_out;
michael@0 434
michael@0 435 /* Adjust headers */
michael@0 436 header->totalSize += len;
michael@0 437 *entry++ = offset;
michael@0 438 header->nChunks++;
michael@0 439
michael@0 440 /* Prepare for next iteration */
michael@0 441 size -= avail;
michael@0 442 data += avail;
michael@0 443 offset += len;
michael@0 444 }
michael@0 445 header->lastChunkSize = avail;
michael@0 446 MOZ_ASSERT(header->totalSize == offset);
michael@0 447 MOZ_ASSERT(header->nChunks == nChunks);
michael@0 448
michael@0 449 if (!outBuf.Resize(offset)) {
michael@0 450 LOG("Error truncating output: %s", strerror(errno));
michael@0 451 return 1;
michael@0 452 }
michael@0 453
michael@0 454 return 0;
michael@0 455
michael@0 456 }
michael@0 457
michael@0 458 bool GetSize(const char *str, size_t *out)
michael@0 459 {
michael@0 460 char *end;
michael@0 461 MOZ_ASSERT(out);
michael@0 462 errno = 0;
michael@0 463 *out = strtol(str, &end, 10);
michael@0 464 return (!errno && !*end);
michael@0 465 }
michael@0 466
michael@0 467 int main(int argc, char* argv[])
michael@0 468 {
michael@0 469 mozilla::ScopedDeletePtr<SzipAction> action;
michael@0 470 char **firstArg;
michael@0 471 bool compress = true;
michael@0 472 size_t chunkSize = 0;
michael@0 473 SeekableZStream::FilterId filter = SzipCompress::DEFAULT_FILTER;
michael@0 474 size_t dictSize = (size_t) 0;
michael@0 475
michael@0 476 Logging::Init();
michael@0 477
michael@0 478 for (firstArg = &argv[1]; argc > 2; argc--, firstArg++) {
michael@0 479 if (!firstArg[0] || firstArg[0][0] != '-')
michael@0 480 break;
michael@0 481 if (strcmp(firstArg[0], "-d") == 0) {
michael@0 482 compress = false;
michael@0 483 } else if (strcmp(firstArg[0], "-c") == 0) {
michael@0 484 firstArg++;
michael@0 485 argc--;
michael@0 486 if (!firstArg[0])
michael@0 487 break;
michael@0 488 if (!GetSize(firstArg[0], &chunkSize) || !chunkSize ||
michael@0 489 (chunkSize % 4096) || (chunkSize > maxChunkSize)) {
michael@0 490 LOG("Invalid chunk size");
michael@0 491 return 1;
michael@0 492 }
michael@0 493 } else if (strcmp(firstArg[0], "-f") == 0) {
michael@0 494 firstArg++;
michael@0 495 argc--;
michael@0 496 if (!firstArg[0])
michael@0 497 break;
michael@0 498 bool matched = false;
michael@0 499 for (unsigned int i = 0; i < sizeof(filterName) / sizeof(char *); ++i) {
michael@0 500 if (strcmp(firstArg[0], filterName[i]) == 0) {
michael@0 501 filter = static_cast<SeekableZStream::FilterId>(i);
michael@0 502 matched = true;
michael@0 503 break;
michael@0 504 }
michael@0 505 }
michael@0 506 if (!matched) {
michael@0 507 LOG("Invalid filter");
michael@0 508 return 1;
michael@0 509 }
michael@0 510 } else if (strcmp(firstArg[0], "-D") == 0) {
michael@0 511 firstArg++;
michael@0 512 argc--;
michael@0 513 if (!firstArg[0])
michael@0 514 break;
michael@0 515 if (strcmp(firstArg[0], "auto") == 0) {
michael@0 516 dictSize = -1;
michael@0 517 } else if (!GetSize(firstArg[0], &dictSize) || (dictSize >= 1 << 16)) {
michael@0 518 LOG("Invalid dictionary size");
michael@0 519 return 1;
michael@0 520 }
michael@0 521 }
michael@0 522 }
michael@0 523
michael@0 524 if (argc != 2 || !firstArg[0]) {
michael@0 525 LOG("usage: %s [-d] [-c CHUNKSIZE] [-f FILTER] [-D DICTSIZE] file",
michael@0 526 argv[0]);
michael@0 527 return 1;
michael@0 528 }
michael@0 529
michael@0 530 if (compress) {
michael@0 531 action = new SzipCompress(chunkSize, filter, dictSize);
michael@0 532 } else {
michael@0 533 if (chunkSize) {
michael@0 534 LOG("-c is incompatible with -d");
michael@0 535 return 1;
michael@0 536 }
michael@0 537 if (dictSize) {
michael@0 538 LOG("-D is incompatible with -d");
michael@0 539 return 1;
michael@0 540 }
michael@0 541 action = new SzipDecompress();
michael@0 542 }
michael@0 543
michael@0 544 std::stringstream tmpOutStream;
michael@0 545 tmpOutStream << firstArg[0] << ".sz." << getpid();
michael@0 546 std::string tmpOut(tmpOutStream.str());
michael@0 547 int ret;
michael@0 548 struct stat st;
michael@0 549 {
michael@0 550 FileBuffer origBuf;
michael@0 551 if (!origBuf.Init(firstArg[0])) {
michael@0 552 LOG("Couldn't open %s: %s", firstArg[0], strerror(errno));
michael@0 553 return 1;
michael@0 554 }
michael@0 555
michael@0 556 ret = fstat(origBuf.getFd(), &st);
michael@0 557 if (ret == -1) {
michael@0 558 LOG("Couldn't stat %s: %s", firstArg[0], strerror(errno));
michael@0 559 return 1;
michael@0 560 }
michael@0 561
michael@0 562 size_t origSize = st.st_size;
michael@0 563
michael@0 564 /* Mmap the original file */
michael@0 565 if (!origBuf.Resize(origSize)) {
michael@0 566 LOG("Couldn't mmap %s: %s", firstArg[0], strerror(errno));
michael@0 567 return 1;
michael@0 568 }
michael@0 569
michael@0 570 /* Create the compressed file */
michael@0 571 FileBuffer outBuf;
michael@0 572 if (!outBuf.Init(tmpOut.c_str(), true)) {
michael@0 573 LOG("Couldn't open %s: %s", tmpOut.c_str(), strerror(errno));
michael@0 574 return 1;
michael@0 575 }
michael@0 576
michael@0 577 ret = action->run(firstArg[0], origBuf, tmpOut.c_str(), outBuf);
michael@0 578 if ((ret == 0) && (fstat(outBuf.getFd(), &st) == -1)) {
michael@0 579 st.st_size = 0;
michael@0 580 }
michael@0 581 }
michael@0 582
michael@0 583 if ((ret == 0) && st.st_size) {
michael@0 584 rename(tmpOut.c_str(), firstArg[0]);
michael@0 585 } else {
michael@0 586 unlink(tmpOut.c_str());
michael@0 587 }
michael@0 588 return ret;
michael@0 589 }

mercurial