blob: 0f22905fc48080dc91b150a84307dcf3adf01e45 [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
Amin Hassanic3e6b532017-03-07 17:47:25 -080025size_t BytesInByteExtents(const vector<ByteExtent>& extents) {
26 size_t bytes = 0;
27 for (const auto& extent : extents) {
28 bytes += extent.length;
29 }
30 return bytes;
31}
32
33// This function uses RFC1950 (https://www.ietf.org/rfc/rfc1950.txt) for the
34// definition of a zlib stream.
35bool LocateDeflatesInZlibBlocks(const UniqueStreamPtr& src,
36 const vector<ByteExtent>& zlibs,
Amin Hassani7074da62017-09-30 17:14:06 -070037 vector<BitExtent>* deflates) {
Amin Hassanic3e6b532017-03-07 17:47:25 -080038 for (auto& zlib : zlibs) {
39 TEST_AND_RETURN_FALSE(src->Seek(zlib.offset));
40 uint16_t zlib_header;
41 TEST_AND_RETURN_FALSE(src->Read(&zlib_header, 2));
Amin Hassani26bcfdd2017-09-29 17:54:15 -070042 BufferBitReader bit_reader(reinterpret_cast<uint8_t*>(&zlib_header), 2);
Amin Hassanic3e6b532017-03-07 17:47:25 -080043
Amin Hassani26bcfdd2017-09-29 17:54:15 -070044 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(8));
45 auto cmf = bit_reader.ReadBits(8);
46 auto cm = bit_reader.ReadBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080047 if (cm != 8 && cm != 15) {
48 LOG(ERROR) << "Invalid compression method! cm: " << cm;
49 return false;
50 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070051 bit_reader.DropBits(4);
52 auto cinfo = bit_reader.ReadBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080053 if (cinfo > 7) {
54 LOG(ERROR) << "cinfo greater than 7 is not allowed in deflate";
55 return false;
56 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070057 bit_reader.DropBits(4);
Amin Hassanic3e6b532017-03-07 17:47:25 -080058
Amin Hassani26bcfdd2017-09-29 17:54:15 -070059 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(8));
60 auto flg = bit_reader.ReadBits(8);
Amin Hassanic3e6b532017-03-07 17:47:25 -080061 if (((cmf << 8) + flg) % 31) {
62 LOG(ERROR) << "Invalid zlib header on offset: " << zlib.offset;
63 return false;
64 }
Amin Hassani26bcfdd2017-09-29 17:54:15 -070065 bit_reader.ReadBits(5); // FCHECK
66 bit_reader.DropBits(5);
Amin Hassanic3e6b532017-03-07 17:47:25 -080067
Amin Hassani26bcfdd2017-09-29 17:54:15 -070068 auto fdict = bit_reader.ReadBits(1);
69 bit_reader.DropBits(1);
Amin Hassanic3e6b532017-03-07 17:47:25 -080070
Amin Hassani26bcfdd2017-09-29 17:54:15 -070071 bit_reader.ReadBits(2); // FLEVEL
72 bit_reader.DropBits(2);
Amin Hassanic3e6b532017-03-07 17:47:25 -080073
74 auto header_len = 2;
75 if (fdict) {
Amin Hassani26bcfdd2017-09-29 17:54:15 -070076 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(32));
77 bit_reader.DropBits(32);
Amin Hassanic3e6b532017-03-07 17:47:25 -080078 header_len += 4;
79 }
80
Amin Hassani7074da62017-09-30 17:14:06 -070081 ByteExtent deflate(zlib.offset + header_len, zlib.length - header_len - 4);
82 TEST_AND_RETURN_FALSE(FindDeflateSubBlocks(src, {deflate}, deflates));
83 }
84 return true;
85}
86
87bool FindDeflateSubBlocks(const UniqueStreamPtr& src,
88 const vector<ByteExtent>& deflates,
89 vector<BitExtent>* subblock_deflates) {
90 Puffer puffer;
91 Buffer deflate_buffer;
92 for (const auto& deflate : deflates) {
93 TEST_AND_RETURN_FALSE(src->Seek(deflate.offset));
94 // Read from src into deflate_buffer.
95 deflate_buffer.resize(deflate.length);
96 TEST_AND_RETURN_FALSE(src->Read(deflate_buffer.data(), deflate.length));
97
98 // Find all the subblocks.
99 BufferBitReader bit_reader(deflate_buffer.data(), deflate.length);
100 BufferPuffWriter puff_writer(nullptr, 0);
101 Error error;
102 vector<BitExtent> subblocks;
103 TEST_AND_RETURN_FALSE(
104 puffer.PuffDeflate(&bit_reader, &puff_writer, &subblocks, &error));
105 TEST_AND_RETURN_FALSE(deflate.length == bit_reader.Offset());
106 for (const auto& subblock : subblocks) {
107 subblock_deflates->emplace_back(subblock.offset + deflate.offset * 8,
108 subblock.length);
109 }
Amin Hassanic3e6b532017-03-07 17:47:25 -0800110 }
111 return true;
112}
113
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700114bool FindPuffLocations(const UniqueStreamPtr& src,
Amin Hassani7074da62017-09-30 17:14:06 -0700115 const vector<BitExtent>& deflates,
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700116 vector<ByteExtent>* puffs,
117 size_t* out_puff_size) {
118 Puffer puffer;
119 Buffer deflate_buffer;
120
121 // Here accumulate the size difference between each corresponding deflate and
122 // puff. At the end we add this cummulative size difference to the size of the
123 // deflate stream to get the size of the puff stream. We use signed size
124 // because puff size could be smaller than deflate size.
125 ssize_t total_size_difference = 0;
Amin Hassani7074da62017-09-30 17:14:06 -0700126 for (auto deflate = deflates.begin(); deflate != deflates.end(); ++deflate) {
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700127 // Read from src into deflate_buffer.
Amin Hassani7074da62017-09-30 17:14:06 -0700128 auto start_byte = deflate->offset / 8;
129 auto end_byte = (deflate->offset + deflate->length + 7) / 8;
130 deflate_buffer.resize(end_byte - start_byte);
131 TEST_AND_RETURN_FALSE(src->Seek(start_byte));
132 TEST_AND_RETURN_FALSE(
133 src->Read(deflate_buffer.data(), deflate_buffer.size()));
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700134 // Find the size of the puff.
Amin Hassani7074da62017-09-30 17:14:06 -0700135 BufferBitReader bit_reader(deflate_buffer.data(), deflate_buffer.size());
136 size_t bits_to_skip = deflate->offset % 8;
137 TEST_AND_RETURN_FALSE(bit_reader.CacheBits(bits_to_skip));
138 bit_reader.DropBits(bits_to_skip);
139
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700140 BufferPuffWriter puff_writer(nullptr, 0);
141 Error error;
142 TEST_AND_RETURN_FALSE(
Amin Hassani7074da62017-09-30 17:14:06 -0700143 puffer.PuffDeflate(&bit_reader, &puff_writer, nullptr, &error));
144 TEST_AND_RETURN_FALSE(deflate_buffer.size() == bit_reader.Offset());
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700145
Amin Hassani7074da62017-09-30 17:14:06 -0700146 // 1 if a deflate ends at the same byte that the next deflate starts and
147 // there is a few bits gap between them. In practice this may never happen,
148 // but it is a good idea to support it anyways. If there is a gap, the value
149 // of the gap will be saved as an integer byte to the puff stream. The parts
150 // of the byte that belogs to the deflates are shifted out.
151 int gap = 0;
152 if (deflate != deflates.begin()) {
153 auto prev_deflate = std::prev(deflate);
154 if ((prev_deflate->offset + prev_deflate->length == deflate->offset)
155 // If deflates are on byte boundary the gap will not be counted later,
156 // so we won't worry about it.
157 && (deflate->offset % 8 != 0)) {
158 gap = 1;
159 }
160 }
161
162 start_byte = ((deflate->offset + 7) / 8);
163 end_byte = (deflate->offset + deflate->length) / 8;
164 ssize_t deflate_length_in_bytes = end_byte - start_byte;
165
166 // If there was no gap bits between the current and previous deflates, there
167 // will be no extra gap byte, so the offset will be shifted one byte back.
168 auto puff_offset = start_byte - gap + total_size_difference;
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700169 auto puff_size = puff_writer.Size();
Amin Hassani7074da62017-09-30 17:14:06 -0700170 // Add the location into puff.
171 puffs->emplace_back(puff_offset, puff_size);
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700172 total_size_difference +=
Amin Hassani7074da62017-09-30 17:14:06 -0700173 static_cast<ssize_t>(puff_size) - deflate_length_in_bytes - gap;
Amin Hassani26bcfdd2017-09-29 17:54:15 -0700174 }
175
176 size_t src_size;
177 TEST_AND_RETURN_FALSE(src->GetSize(&src_size));
178 auto final_size = static_cast<ssize_t>(src_size) + total_size_difference;
179 TEST_AND_RETURN_FALSE(final_size >= 0);
180 *out_puff_size = final_size;
181 return true;
182}
183
Amin Hassanic3e6b532017-03-07 17:47:25 -0800184} // namespace puffin