1 // Copyright 2017 The Fuchsia 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 #pragma once 6 7 #include <assert.h> 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdbool.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <unistd.h> 18 19 #include <lz4/lz4frame.h> 20 #include <fbl/auto_call.h> 21 #include <fbl/unique_fd.h> 22 #include "fvm/fvm-sparse.h" 23 24 #ifdef __Fuchsia__ 25 #include <zircon/syscalls.h> 26 #endif 27 28 #define LZ4_MAX_BLOCK_SIZE 65536 29 30 namespace fvm { 31 32 class SparseReader { 33 public: 34 static zx_status_t Create(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out); 35 static zx_status_t CreateSilent(fbl::unique_fd fd, fbl::unique_ptr<SparseReader>* out); 36 ~SparseReader(); 37 38 fvm::sparse_image_t* Image(); 39 fvm::partition_descriptor_t* Partitions(); 40 41 // Read requested data from sparse file into buffer 42 zx_status_t ReadData(uint8_t* data, size_t length, size_t *actual); 43 // Write decompressed data into new file 44 zx_status_t WriteDecompressed(fbl::unique_fd outfd); 45 private: 46 typedef struct buffer { 47 // Write |length| bytes from |indata| into buffer. is_emptybuffer48 bool is_empty() const { 49 return offset == 0 && size == 0; 50 } 51 writebuffer52 void write(uint8_t* indata, size_t length) { 53 ZX_ASSERT(length <= max_size); 54 // We should have read all previous data from buffer before writing more. 55 ZX_ASSERT(is_empty()); 56 57 if (length > 0) { 58 memcpy(data.get(), indata, length); 59 size = length; 60 } 61 } 62 63 // Read up to |length| bytes from buffer into |outdata|, returning |actual| bytes copied. readbuffer64 void read(uint8_t* outdata, size_t length, size_t* actual) { 65 size_t cp_sz = fbl::min(length, size); 66 67 if (cp_sz > 0) { 68 memcpy(outdata, data.get() + offset, cp_sz); 69 offset += cp_sz; 70 } 71 72 size -= cp_sz; 73 74 if (size == 0) { 75 offset = 0; 76 } 77 78 *actual = cp_sz; 79 } 80 81 // Data buffer 82 fbl::unique_ptr<uint8_t[]> data; 83 // Actual size of data contained within buffer 84 size_t size; 85 // Offset into buffer where valid data begins 86 size_t offset; 87 // Maximum size allocated for buffer 88 size_t max_size; 89 } buffer_t; 90 91 static zx_status_t CreateHelper(fbl::unique_fd fd, bool verbose, 92 fbl::unique_ptr<SparseReader>* out); 93 94 SparseReader(fbl::unique_fd fd, bool verbose); 95 // Read in header data, prepare buffers and decompression context if necessary 96 zx_status_t ReadMetadata(); 97 // Initialize buffer with a given |size| 98 static zx_status_t InitializeBuffer(size_t size, buffer_t* out_buf); 99 // Read |length| bytes of raw data from file directly into |data|. Return |actual| bytes read. 100 zx_status_t ReadRaw(uint8_t* data, size_t length, size_t* actual); 101 102 void PrintStats() const; 103 104 // True if sparse file is compressed 105 bool compressed_; 106 107 // If true, all logs are printed. 108 bool verbose_; 109 110 fbl::unique_fd fd_; 111 fbl::unique_ptr<uint8_t[]> metadata_; 112 LZ4F_decompressionContext_t dctx_; 113 // A hint of the size of the next compressed frame to be decompressed. 114 // May be an overestimate, but will not be an underestimate (0 indicates no more data left to 115 // decompress). 116 size_t to_read_; 117 118 // Buffer for compressed data read directly from file 119 buffer_t in_buf_; 120 // Buffer for decompressed data 121 buffer_t out_buf_; 122 123 #ifdef __Fuchsia__ 124 // Total time spent reading/decompressing data 125 zx_ticks_t total_time_ = 0; 126 // Total time spent reading data from fd 127 zx_ticks_t read_time_ = 0; 128 #endif 129 }; 130 131 } // namespace fvm 132