133 uint64_t expected_uncompressed_size,
134 std::vector<unsigned char>& out,
135 std::string& error) {
138 error =
"xattr too small for decmpfs header";
143 error =
"decmpfs header magic mismatch";
147 error =
"compression type is not zlib-xattr";
150 if (h.
size != expected_uncompressed_size) {
156 if (payload_len == 0) {
157 error =
"decmpfs xattr has no payload";
162 if (payload[0] == 0xFF) {
163 const size_t raw_len = payload_len - 1;
164 if (raw_len < h.
size) {
167 out.assign(payload + 1, payload + 1 + raw_len);
169 out.assign(payload + 1, payload + 1 + h.
size);
175 out.resize(
static_cast<size_t>(h.
size));
177 zs.next_in =
const_cast<Bytef*
>(payload);
178 zs.avail_in =
static_cast<uInt
>(payload_len);
179 zs.next_out = out.data();
180 zs.avail_out =
static_cast<uInt
>(out.size());
181 int ret = inflateInit(&zs);
183 error =
"inflateInit failed";
186 ret = inflate(&zs, Z_FINISH);
187 if (ret != Z_STREAM_END && ret != Z_OK) {
188 error = std::string(
"inflate failed: ") +
189 (zs.msg ? zs.msg :
"unknown error");
193 out.resize(
static_cast<size_t>(zs.total_out));
200 uint64_t expected_uncompressed_size,
201 std::vector<unsigned char>& out,
202 std::string& error) {
205 error =
"resource fork too small for header";
213 const uint32_t dataOffset =
be32(rsrc_buf + 0);
215 const uint32_t dataLength =
be32(rsrc_buf + 8);
218 if (
static_cast<uint64_t
>(dataOffset) + dataLength > rsrc_len) {
219 error =
"resource fork data section out of range";
223 const unsigned char* data = rsrc_buf + dataOffset;
224 if (dataLength < 4) {
225 error =
"resource fork data section too small";
231 const uint32_t streamLen =
be32(data + 0);
232 if (streamLen + 4u > dataLength) {
233 error =
"resource fork stream length inconsistent";
236 const unsigned char* stream = data + 4;
243 error =
"stream too small for chunk count";
246 const uint32_t numChunks = le32(stream + 0);
247 if (numChunks == 0 || numChunks > 1'000'000) {
248 std::ostringstream m;
249 m <<
"implausible chunk count: " << numChunks;
254 const size_t chunk_table_size =
static_cast<size_t>(numChunks) * 8;
255 if (4u + chunk_table_size > streamLen) {
256 error =
"chunk table overruns stream";
259 const unsigned char* chunk_table = stream + 4;
262 out.reserve(
static_cast<size_t>(expected_uncompressed_size));
265 for (uint32_t i = 0; i < numChunks; ++i) {
266 const uint32_t c_off = le32(chunk_table + i * 8 + 0);
267 const uint32_t c_size = le32(chunk_table + i * 8 + 4);
268 if (c_off + c_size > streamLen) {
269 std::ostringstream m;
270 m <<
"chunk " << i <<
" out of stream bounds";
274 const unsigned char* c_data = stream + c_off;
280 if (!inflate_chunk(c_data, c_size, chunk_out.data(),
281 chunk_out.size(), produced, ce)) {
282 std::ostringstream m;
283 m <<
"chunk " << i <<
": " << ce;
287 out.insert(out.end(), chunk_out.begin(), chunk_out.begin() + produced);
288 if (out.size() > expected_uncompressed_size) {
291 out.resize(
static_cast<size_t>(expected_uncompressed_size));
296 if (out.size() != expected_uncompressed_size) {
297 std::ostringstream m;
298 m <<
"decompressed size " << out.size()
299 <<
" differs from expected " << expected_uncompressed_size;
306 if (out.empty())
return false;
bool decompress_xattr_zlib(const unsigned char *xattr_buf, size_t xattr_len, uint64_t expected_uncompressed_size, std::vector< unsigned char > &out, std::string &error)