1 // Copyright 2018 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 <stdbool.h>
8 
9 #include <fbl/function.h>
10 #include <fbl/string.h>
11 #include <fbl/unique_fd.h>
12 #include <fbl/unique_ptr.h>
13 #include <fbl/vector.h>
14 #include <gpt/gpt.h>
15 #include <zircon/types.h>
16 
17 #include <utility>
18 
19 namespace paver {
20 
21 enum class Partition {
22     kUnknown,
23     kBootloader,
24     kKernelC,
25     kEfi,
26     kZirconA,
27     kZirconB,
28     kZirconR,
29     kVbMetaA,
30     kVbMetaB,
31     kFuchsiaVolumeManager,
32     // The following are only valid for WipePartition.
33     kInstallType,
34     kSystem,
35     kBlob,
36     kData,
37 };
38 
39 // A special filter for test injection.
40 // API should return true if device passed in should be filtered out.
41 extern bool (*TestBlockFilter)(const fbl::unique_fd&);
42 
43 // Abstract device partitioner definition.
44 // This class defines common APIs for interacting with a device partitioner.
45 class DevicePartitioner {
46 public:
47     // Factory method which automatically returns the correct DevicePartitioner
48     // implementation. Returns nullptr on failure.
49     static fbl::unique_ptr<DevicePartitioner> Create();
50 
51     virtual ~DevicePartitioner() = default;
52 
53     virtual bool IsCros() const = 0;
54 
55     // Whether to use skip block interface or block interface for non-FVM
56     // partitions.
57     virtual bool UseSkipBlockInterface() const = 0;
58 
59     // Returns a file descriptor to a partition of type |partition_type|, creating it.
60     // Assumes that the partition does not already exist.
61     virtual zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) = 0;
62 
63     // Returns a file descriptor to a partition of type |partition_type| if one exists.
64     virtual zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const = 0;
65 
66     // Finalizes the partition of type |partition_type| after it has been
67     // written.
68     virtual zx_status_t FinalizePartition(Partition partition_type) = 0;
69 
70     // Wipes Fuchsia specific partitions.
71     virtual zx_status_t WipePartitions() = 0;
72 
73     // Returns block size in bytes for specified device.
74     virtual zx_status_t GetBlockSize(const fbl::unique_fd& device_fd,
75                                      uint32_t* block_size) const = 0;
76 };
77 
78 // Useful for when a GPT table is available (e.g. x86 devices). Provides common
79 // utility functions.
80 class GptDevicePartitioner {
81 public:
82     using FilterCallback = fbl::Function<bool(const gpt_partition_t&)>;
83 
84     // Find and initialize a GPT based device.
85     static zx_status_t InitializeGpt(fbl::unique_fd devfs_root,
86                                      fbl::unique_ptr<GptDevicePartitioner>* gpt_out);
87 
~GptDevicePartitioner()88     virtual ~GptDevicePartitioner() {
89         if (gpt_) {
90             gpt_device_release(gpt_);
91         }
92     }
93 
94     // Returns block info for a specified block device.
GetBlockInfo(block_info_t * block_info)95     zx_status_t GetBlockInfo(block_info_t* block_info) const {
96         memcpy(block_info, &block_info_, sizeof(*block_info));
97         return ZX_OK;
98     }
99 
GetGpt()100     gpt_device_t* GetGpt() const { return gpt_; }
GetFd()101     int GetFd() const { return fd_.get(); }
102 
103     // Find the first spot that has at least |bytes_requested| of space.
104     //
105     // Returns the |start_out| block and |length_out| blocks, indicating
106     // how much space was found, on success. This may be larger than
107     // the number of bytes requested.
108     zx_status_t FindFirstFit(size_t bytes_requested, size_t* start_out, size_t* length_out) const;
109 
110     // Creates a partition, adds an entry to the GPT, and returns a file descriptor to it.
111     // Assumes that the partition does not already exist.
112     zx_status_t AddPartition(const char* name, uint8_t* type, size_t minimum_size_bytes,
113                              size_t optional_reserve_bytes, fbl::unique_fd* out_fd);
114 
115     // Returns a file descriptor to a partition which can be paved,
116     // if one exists.
117     zx_status_t FindPartition(FilterCallback filter, gpt_partition_t** out,
118                               fbl::unique_fd* out_fd);
119     zx_status_t FindPartition(FilterCallback filter, fbl::unique_fd* out_fd) const;
120 
121     // Wipes a specified partition from the GPT, and overwrites first 8KiB with
122     // nonsense.
123     zx_status_t WipePartitions(FilterCallback filter);
124 
125 private:
126     // Find and return the topological path of the GPT which we will pave.
127     static bool FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out);
128 
GptDevicePartitioner(fbl::unique_fd devfs_root,fbl::unique_fd fd,gpt_device_t * gpt,block_info_t block_info)129     GptDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd fd, gpt_device_t* gpt,
130                          block_info_t block_info)
131         : devfs_root_(std::move(devfs_root)), fd_(std::move(fd)), gpt_(gpt),
132           block_info_(block_info) {}
133 
134     zx_status_t CreateGptPartition(const char* name, uint8_t* type, uint64_t offset,
135                                    uint64_t blocks, uint8_t* out_guid);
136 
137     fbl::unique_fd devfs_root_;
138     fbl::unique_fd fd_;
139     gpt_device_t* gpt_;
140     block_info_t block_info_;
141 };
142 
143 // DevicePartitioner implementation for EFI based devices.
144 class EfiDevicePartitioner : public DevicePartitioner {
145 public:
146     static zx_status_t Initialize(fbl::unique_fd devfs_root,
147                                   fbl::unique_ptr<DevicePartitioner>* partitioner);
148 
IsCros()149     bool IsCros() const override { return false; }
150 
UseSkipBlockInterface()151     bool UseSkipBlockInterface() const override { return false; }
152 
153     zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
154 
155     zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
156 
FinalizePartition(Partition unused)157     zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
158 
159     zx_status_t WipePartitions() override;
160 
161     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
162 
163 private:
EfiDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)164     EfiDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
165         : gpt_(std::move(gpt)) {}
166 
167     fbl::unique_ptr<GptDevicePartitioner> gpt_;
168 };
169 
170 // DevicePartitioner implementation for ChromeOS devices.
171 class CrosDevicePartitioner : public DevicePartitioner {
172 public:
173     static zx_status_t Initialize(fbl::unique_fd devfs_root,
174                                   fbl::unique_ptr<DevicePartitioner>* partitioner);
175 
IsCros()176     bool IsCros() const override { return true; }
177 
UseSkipBlockInterface()178     bool UseSkipBlockInterface() const override { return false; }
179 
180     zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override;
181 
182     zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
183 
184     zx_status_t FinalizePartition(Partition unused) override;
185 
186     zx_status_t WipePartitions() override;
187 
188     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
189 
190 private:
CrosDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)191     CrosDevicePartitioner(fbl::unique_ptr<GptDevicePartitioner> gpt)
192         : gpt_(std::move(gpt)) {}
193 
194     fbl::unique_ptr<GptDevicePartitioner> gpt_;
195 };
196 
197 // DevicePartitioner implementation for devices which have fixed partition maps (e.g. ARM
198 // devices). It will not attempt to write a partition map of any kind to the device.
199 // Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
200 // ZIRCON-R).
201 class FixedDevicePartitioner : public DevicePartitioner {
202 public:
203     static zx_status_t Initialize(fbl::unique_fd devfs_root,
204                                   fbl::unique_ptr<DevicePartitioner>* partitioner);
205 
IsCros()206     bool IsCros() const override { return false; }
207 
UseSkipBlockInterface()208     bool UseSkipBlockInterface() const override { return false; }
209 
AddPartition(Partition partition_type,fbl::unique_fd * out_fd)210     zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
211         return ZX_ERR_NOT_SUPPORTED;
212     }
213 
214     zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
215 
FinalizePartition(Partition unused)216     zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
217 
218     zx_status_t WipePartitions() override;
219 
220     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
221 
222 private:
FixedDevicePartitioner(fbl::unique_fd devfs_root)223     FixedDevicePartitioner(fbl::unique_fd devfs_root)
224         : devfs_root_(std::move(devfs_root)) {}
225 
226     fbl::unique_fd devfs_root_;
227 };
228 
229 // DevicePartitioner implementation for devices which have fixed partition maps, but do not expose a
230 // block device interface. Instead they expose devices with skip-block IOCTL interfaces. Like the
231 // FixedDevicePartitioner, it will not attempt to write a partition map of any kind to the device.
232 // Assumes standardized partition layout structure (e.g. ZIRCON-A, ZIRCON-B,
233 // ZIRCON-R).
234 class SkipBlockDevicePartitioner : public DevicePartitioner {
235 public:
236     static zx_status_t Initialize(fbl::unique_fd devfs_root,
237                                   fbl::unique_ptr<DevicePartitioner>* partitioner);
238 
IsCros()239     bool IsCros() const override { return false; }
240 
UseSkipBlockInterface()241     bool UseSkipBlockInterface() const override { return true; }
242 
AddPartition(Partition partition_type,fbl::unique_fd * out_fd)243     zx_status_t AddPartition(Partition partition_type, fbl::unique_fd* out_fd) override {
244         return ZX_ERR_NOT_SUPPORTED;
245     }
246 
247     zx_status_t FindPartition(Partition partition_type, fbl::unique_fd* out_fd) const override;
248 
FinalizePartition(Partition unused)249     zx_status_t FinalizePartition(Partition unused) override { return ZX_OK; }
250 
251     zx_status_t WipePartitions() override;
252 
253     zx_status_t GetBlockSize(const fbl::unique_fd& device_fd, uint32_t* block_size) const override;
254 
255 private:
SkipBlockDevicePartitioner(fbl::unique_fd devfs_root,fbl::unique_fd block_devfs_root)256     SkipBlockDevicePartitioner(fbl::unique_fd devfs_root, fbl::unique_fd block_devfs_root)
257         : devfs_root_(std::move(devfs_root)), block_devfs_root_(std::move(block_devfs_root)) {}
258 
259     fbl::unique_fd devfs_root_;
260     fbl::unique_fd block_devfs_root_;
261 };
262 } // namespace paver
263