blob: f3168db7a7495c70c70f9437d312c52970aa88f9 [file] [log] [blame]
Amin Hassanic3e6b532017-03-07 17:47:25 -08001// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "puffin/src/include/puffin/utils.h"
6
7#include <inttypes.h>
8
9#include <string>
10#include <vector>
11
12#include "puffin/src/bit_reader.h"
13#include "puffin/src/include/puffin/common.h"
Amin Hassani26bcfdd2017-09-29 17:54:15 -070014#include "puffin/src/include/puffin/errors.h"
15#include "puffin/src/include/puffin/puffer.h"
Amin Hassanic3e6b532017-03-07 17:47:25 -080016#include "puffin/src/include/puffin/stream.h"
Amin Hassani26bcfdd2017-09-29 17:54:15 -070017#include "puffin/src/puff_writer.h"
Amin Hassanic3e6b532017-03-07 17:47:25 -080018#include "puffin/src/set_errors.h"
19
20namespace puffin {
21
22using std::string;
23using std::vector;
24
25string ByteExtentsToString(const vector<ByteExtent>& extents) {
26 string str;
27 for (const auto& extent : extents)
28 str += std::to_string(extent.offset) + ":" + std::to_string(extent.length) +
29 ",";
30 return str;
31}
32
33size_t BytesInByteExtents(const vector<ByteExtent>& extents) {
34 size_t bytes = 0;
35 for (const auto& extent : extents) {
36 bytes += extent.length;
37 }
38 return bytes;
39}
40
41// This function uses RFC1950 (https://www.ietf.org/rfc/rfc1950.txt) for the
42// definition of a zlib stream.
43bool LocateDeflatesInZlibBlocks(const UniqueStreamPtr& src,
44 const vector<ByteExtent>& zlibs,
45 vector<ByteExtent>* deflates) {
46 for (auto& zlib : zlibs) {
47 TEST_AND_RETURN_FALSE(src->Seek(zlib.offset));
48 uint16_t zlib_header;
49 TEST_AND_RETURN_FALSE(src->Read(&zlib_header, 2));
Amin Hassani26bcfdd2017-09-29 17:54:15 -070050 BufferBitReader bit_reader(reinterpret_cast<uint8_t*>(&zlib_header), 2);
Amin Hassanic3e6b532017-03-07 17:47:25 -080051
Amin Hassani26bcfdd2017-09-29 17:54:15 -070052 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(8));
53 auto cmf = bit_reader.ReadBits(8);
54 auto cm = bit_reader.ReadBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080055 if (cm != 8 && cm != 15) {
56 LOG(ERROR) << "Invalid compression method! cm: " << cm;
57 return false;
58 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070059 bit_reader.DropBits(4);
60 auto cinfo = bit_reader.ReadBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080061 if (cinfo > 7) {
62 LOG(ERROR) << "cinfo greater than 7 is not allowed in deflate";
63 return false;
64 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070065 bit_reader.DropBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080066
Amin Hassani26bcfdd2017-09-29 17:54:15 -070067 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(8));
68 auto flg = bit_reader.ReadBits(8);
Amin Hassanic3e6b532017-03-07 17:47:25 -080069 if (((cmf << 8) + flg) % 31) {
70 LOG(ERROR) << "Invalid zlib header on offset: " << zlib.offset;
71 return false;
72 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070073 bit_reader.ReadBits(5); // FCHECK
74 bit_reader.DropBits(5);
Amin Hassanic3e6b532017-03-07 17:47:25 -080075
Amin Hassani26bcfdd2017-09-29 17:54:15 -070076 auto fdict = bit_reader.ReadBits(1);
77 bit_reader.DropBits(1);
Amin Hassanic3e6b532017-03-07 17:47:25 -080078
Amin Hassani26bcfdd2017-09-29 17:54:15 -070079 bit_reader.ReadBits(2); // FLEVEL
80 bit_reader.DropBits(2);
Amin Hassanic3e6b532017-03-07 17:47:25 -080081
82 auto header_len = 2;
83 if (fdict) {
Amin Hassani26bcfdd2017-09-29 17:54:15 -070084 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(32));
85 bit_reader.DropBits(32);
Amin Hassanic3e6b532017-03-07 17:47:25 -080086 header_len += 4;
87 }
88
89 ByteExtent deflate;
90 deflate.offset = zlib.offset + header_len;
91 deflate.length = zlib.length - header_len - 4;
92 deflates->push_back(deflate);
93 }
94 return true;
95}
96
Amin Hassani26bcfdd2017-09-29 17:54:15 -070097bool FindPuffLocations(const UniqueStreamPtr& src,
98 const vector<ByteExtent>& deflates,
99 vector<ByteExtent>* puffs,
100 size_t* out_puff_size) {
101 Puffer puffer;
102 Buffer deflate_buffer;
103
104 // Here accumulate the size difference between each corresponding deflate and
105 // puff. At the end we add this cummulative size difference to the size of the
106 // deflate stream to get the size of the puff stream. We use signed size
107 // because puff size could be smaller than deflate size.
108 ssize_t total_size_difference = 0;
109 for (const auto& deflate : deflates) {
110 TEST_AND_RETURN_FALSE(src->Seek(deflate.offset));
111 // Read from src into deflate_buffer.
112 deflate_buffer.resize(deflate.length);
113 TEST_AND_RETURN_FALSE(src->Read(deflate_buffer.data(), deflate.length));
114
115 // Find the size of the puff.
116 BufferBitReader bit_reader(deflate_buffer.data(), deflate.length);
117 BufferPuffWriter puff_writer(nullptr, 0);
118 Error error;
119 TEST_AND_RETURN_FALSE(
120 puffer.PuffDeflate(&bit_reader, &puff_writer, &error));
121 TEST_AND_RETURN_FALSE(deflate.length == bit_reader.Offset());
122
123 // Add the location into puff.
124 auto puff_size = puff_writer.Size();
125 puffs->emplace_back(deflate.offset + total_size_difference, puff_size);
126 total_size_difference +=
127 static_cast<ssize_t>(puff_size) - static_cast<ssize_t>(deflate.length);
128 }
129
130 size_t src_size;
131 TEST_AND_RETURN_FALSE(src->GetSize(&src_size));
132 auto final_size = static_cast<ssize_t>(src_size) + total_size_difference;
133 TEST_AND_RETURN_FALSE(final_size >= 0);
134 *out_puff_size = final_size;
135 return true;
136}
137
Amin Hassanic3e6b532017-03-07 17:47:25 -0800138} // namespace puffin