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