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 <fcntl.h>
8 #include <lz4/lz4frame.h>
9 #include <string.h>
10 
11 #include <fbl/auto_call.h>
12 #include <fbl/string_buffer.h>
13 #include <fbl/vector.h>
14 #include <fbl/unique_fd.h>
15 #include <fvm/sparse-reader.h>
16 #include <fvm/fvm-sparse.h>
17 
18 #include "format.h"
19 #include "fvm-info.h"
20 #include "sparse-paver.h"
21 
22 // The number of additional slices a partition will need to become zxcrypt'd.
23 // TODO(planders): Replace this with a value supplied by ulib/zxcrypt.
24 constexpr size_t kZxcryptExtraSlices = 1;
25 
26 // A Container represents a method of storing multiple file system partitions in an
27 // FVM-recognizable format
28 class Container {
29 public:
30     // Returns a Container representation of the FVM within the given |path|, starting at |offset|
31     // bytes of length |length| bytes. Will return an error if the file does not exist or is not a
32     // valid Container type, or if flags is not zero or a valid combination of fvm::sparse_flags_t.
33     static zx_status_t Create(const char* path, off_t offset, off_t length, uint32_t flags,
34                               fbl::unique_ptr<Container>* out);
35 
36     Container(const char* path, size_t slice_size, uint32_t flags);
37 
38     virtual ~Container();
39 
40     // Resets the Container state so we are ready to add a new set of partitions
41     // Init must be called separately from the constructor, as it will overwrite data pertinent to
42     // an existing Container.
43     virtual zx_status_t Init() = 0;
44 
45     // Reports various information about the Container, e.g. number of partitions, and runs fsck on
46     // all supported partitions (blobfs, minfs)
47     virtual zx_status_t Verify() const = 0;
48 
49     // Commits the Container data to disk
50     virtual zx_status_t Commit() = 0;
51 
52     // Returns the Container's specified slice size (in bytes)
53     virtual size_t SliceSize() const = 0;
54 
55     // Given a path to a valid file system partition, adds that partition to the container
56     virtual zx_status_t AddPartition(const char* path, const char* type_name) = 0;
57 
58     // Calculates the minimum disk size required to hold the unpacked contents of the container.
59     virtual uint64_t CalculateDiskSize() const = 0;
60 protected:
61     // Returns the minimum disk size necessary to store |slice_count| slices of size |slice_size_|
62     // in an FVM.
63     uint64_t CalculateDiskSizeForSlices(size_t slice_count) const;
64 
65     fbl::StringBuffer<PATH_MAX> path_;
66     fbl::unique_fd fd_;
67     size_t slice_size_;
68     uint32_t flags_;
69 };
70 
71 class FvmContainer final : public Container {
72     struct FvmPartitionInfo {
73         uint32_t vpart_index;
74         uint32_t pslice_start;
75         uint32_t slice_count;
76         fbl::unique_ptr<Format> format;
77     };
78 
79 public:
80     // Creates an FVM container at the given path, creating a new file if one does not already
81     // exist. |offset| and |length| are provided to specify the offset (in bytes) and the length
82     // (in bytes) of the FVM within the file. For a file that has not yet been created, these
83     // should both be 0. For a file that exists, if not otherwise specified the offset should be 0
84     // and the length should be the size of the file.
85     static zx_status_t Create(const char* path, size_t slice_size, off_t offset, off_t length,
86                               fbl::unique_ptr<FvmContainer>* out);
87     FvmContainer(const char* path, size_t slice_size, off_t offset, off_t length);
88     ~FvmContainer();
89     zx_status_t Init() final;
90     zx_status_t Verify() const final;
91     zx_status_t Commit() final;
92 
93     // Extends the FVM container to the specified length
94     zx_status_t Extend(size_t length);
95     size_t SliceSize() const final;
96     zx_status_t AddPartition(const char* path, const char* type_name) final;
97 
98     uint64_t CalculateDiskSize() const final;
99 
100     // Returns the actual disk size.
101     uint64_t GetDiskSize() const;
102 private:
103     uint64_t disk_offset_;
104     uint64_t disk_size_;
105     fbl::Vector<FvmPartitionInfo> partitions_;
106     FvmInfo info_;
107 
108     // Write the |part_index|th partition to disk
109     zx_status_t WritePartition(unsigned part_index);
110     // Write a partition's |extent_index|th extent to disk. |*pslice| is the starting pslice, and
111     // is updated to reflect the latest written pslice.
112     zx_status_t WriteExtent(unsigned extent_index, Format* format, uint32_t* pslice);
113     // Write one data block of size |block_size| to disk at |block_offset| within pslice |pslice|
114     zx_status_t WriteData(uint32_t pslice, uint32_t block_offset, size_t block_size, void* data);
115 };
116 
117 class CompressionContext {
118 public:
CompressionContext()119     CompressionContext() {}
~CompressionContext()120     ~CompressionContext() {}
121     zx_status_t Setup(size_t max_len);
122     zx_status_t Compress(const void* data, size_t length);
123     zx_status_t Finish();
124 
GetData()125     const void* GetData() const { return data_.get(); }
GetLength()126     size_t GetLength() const { return offset_; }
127 
128 private:
IncreaseOffset(size_t value)129     void IncreaseOffset(size_t value) {
130         offset_ += value;
131         ZX_DEBUG_ASSERT(offset_ <= size_);
132     }
133 
GetRemaining()134     size_t GetRemaining() const {
135         return size_ - offset_;
136     }
137 
GetBuffer()138     void* GetBuffer() const {
139         return data_.get() + offset_;
140     }
141 
Reset(size_t size)142     void Reset(size_t size) {
143         data_.reset(new uint8_t[size]);
144         size_ = size;
145         offset_ = 0;
146     }
147 
148     LZ4F_compressionContext_t cctx_;
149     fbl::unique_ptr<uint8_t[]> data_;
150     size_t size_ = 0;
151     size_t offset_ = 0;
152 };
153 
154 class SparseContainer final : public Container {
155 public:
156     static zx_status_t Create(const char* path, size_t slice_size, uint32_t flags,
157                               fbl::unique_ptr<SparseContainer>* out);
158     SparseContainer(const char* path, uint64_t slice_size, uint32_t flags);
159     ~SparseContainer();
160     zx_status_t Init() final;
161     zx_status_t Verify() const final;
162     zx_status_t Commit() final;
163 
164     // Unpacks the sparse container and "paves" it to |path|.
165     zx_status_t Pave(const char* path, size_t disk_offset = 0, size_t disk_size = 0);
166     size_t SliceSize() const final;
167     size_t SliceCount() const;
168     zx_status_t AddPartition(const char* path, const char* type_name) final;
169 
170     // Decompresses the contents of the sparse file (if they are compressed), and writes the output
171     // to |path|.
172     zx_status_t Decompress(const char* path);
173 
174     uint64_t CalculateDiskSize() const final;
175 
176     // Checks whether the container will fit within a disk of size |target_size| (in bytes).
177     zx_status_t CheckDiskSize(uint64_t target_size) const;
178 private:
179     bool valid_;
180     bool dirty_;
181     size_t disk_size_;
182     size_t extent_size_;
183     fvm::sparse_image_t image_;
184     fbl::Vector<SparsePartitionInfo> partitions_;
185     CompressionContext compression_;
186     fbl::unique_ptr<fvm::SparseReader> reader_;
187 
188     zx_status_t AllocatePartition(fbl::unique_ptr<Format> format);
189     zx_status_t AllocateExtent(uint32_t part_index, uint64_t slice_start, uint64_t slice_count,
190                                uint64_t extent_length);
191 
192     zx_status_t PrepareWrite(size_t max_len);
193     zx_status_t WriteData(const void* data, size_t length);
194     zx_status_t CompleteWrite();
195     void CheckValid() const;
196 };
197