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