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 #include <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <libgen.h>
9 
10 #include <chromeos-disk-setup/chromeos-disk-setup.h>
11 #include <fbl/auto_call.h>
12 #include <fbl/function.h>
13 #include <fs-management/fvm.h>
14 #include <fuchsia/hardware/block/c/fidl.h>
15 #include <fuchsia/hardware/skipblock/c/fidl.h>
16 #include <gpt/cros.h>
17 #include <lib/fdio/util.h>
18 #include <lib/fdio/watcher.h>
19 #include <lib/fzl/fdio.h>
20 #include <zircon/device/device.h>
21 #include <zircon/status.h>
22 #include <zxcrypt/volume.h>
23 
24 #include <utility>
25 
26 #include "device-partitioner.h"
27 #include "pave-logging.h"
28 #include "pave-utils.h"
29 
30 namespace paver {
31 
32 bool (*TestBlockFilter)(const fbl::unique_fd&) = nullptr;
33 
34 namespace {
35 
36 constexpr char kEfiName[] = "EFI Gigaboot";
37 constexpr char kGptDriverName[] = "/boot/driver/gpt.so";
38 constexpr char kFvmPartitionName[] = "fvm";
39 constexpr char kZirconAName[] = "ZIRCON-A";
40 constexpr char kZirconBName[] = "ZIRCON-B";
41 constexpr char kZirconRName[] = "ZIRCON-R";
42 
KernelFilterCallback(const gpt_partition_t & part,const uint8_t kern_type[GPT_GUID_LEN],fbl::StringPiece partition_name)43 bool KernelFilterCallback(const gpt_partition_t& part, const uint8_t kern_type[GPT_GUID_LEN], fbl::StringPiece partition_name) {
44     char cstring_name[GPT_NAME_LEN];
45     utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN);
46     return memcmp(part.type, kern_type, GPT_GUID_LEN) == 0 &&
47            strncmp(cstring_name, partition_name.data(), partition_name.length()) == 0;
48 }
49 
FvmFilterCallback(const gpt_partition_t & part)50 bool FvmFilterCallback(const gpt_partition_t& part) {
51     const uint8_t partition_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
52     return memcmp(part.type, partition_type, GPT_GUID_LEN) == 0;
53 }
54 
IsGigabootPartition(const gpt_partition_t & part)55 bool IsGigabootPartition(const gpt_partition_t& part) {
56     const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE;
57     char cstring_name[GPT_NAME_LEN];
58     utf16_to_cstring(cstring_name, reinterpret_cast<const uint16_t*>(part.name), GPT_NAME_LEN);
59     // Disk-paved EFI: Identified by "EFI Gigaboot" label.
60     const bool gigaboot_efi = strncmp(cstring_name, kEfiName, strlen(kEfiName)) == 0;
61     return memcmp(part.type, efi_type, GPT_GUID_LEN) == 0 && gigaboot_efi;
62 }
63 
WipeFilterCallback(const gpt_partition_t & part)64 bool WipeFilterCallback(const gpt_partition_t& part) {
65     const uint8_t efi_removable_types[][GPT_GUID_LEN] = {
66         GUID_FVM_VALUE,
67         GUID_INSTALL_VALUE,
68         GUID_SYSTEM_VALUE,
69         GUID_BLOB_VALUE,
70         GUID_DATA_VALUE,
71         GUID_ZIRCON_A_VALUE,
72         GUID_ZIRCON_B_VALUE,
73         GUID_ZIRCON_R_VALUE,
74     };
75 
76     if (IsGigabootPartition(part)) {
77         return true;
78     }
79 
80     for (const auto &type : efi_removable_types) {
81         if (memcmp(part.type, type, GPT_GUID_LEN) == 0) {
82             return true;
83         }
84     }
85 
86     return false;
87 }
88 
ReservedHeaderBlocks(size_t blk_size)89 constexpr size_t ReservedHeaderBlocks(size_t blk_size) {
90     constexpr size_t kReservedEntryBlocks = (16 * 1024);
91     return (kReservedEntryBlocks + 2 * blk_size) / blk_size;
92 };
93 
94 
95 // Helper function to auto-deduce type.
96 template <typename T>
WrapUnique(T * ptr)97 fbl::unique_ptr<T> WrapUnique(T* ptr) {
98     return fbl::unique_ptr<T>(ptr);
99 }
100 
101 
OpenPartition(const fbl::unique_fd & devfs_root,const char * path,fbl::Function<bool (const fbl::unique_fd &)> should_filter_file,zx_duration_t timeout,fbl::unique_fd * out_partition)102 zx_status_t OpenPartition(const fbl::unique_fd& devfs_root, const char* path,
103                           fbl::Function<bool(const fbl::unique_fd&)> should_filter_file,
104                           zx_duration_t timeout, fbl::unique_fd* out_partition) {
105     ZX_ASSERT(path != nullptr);
106 
107     struct CallbackInfo {
108         fbl::unique_fd* out_partition;
109         fbl::Function<bool(const fbl::unique_fd&)> should_filter_file;
110     };
111 
112     CallbackInfo info = {
113         .out_partition = out_partition,
114         .should_filter_file = std::move(should_filter_file),
115     };
116 
117     auto cb = [](int dirfd, int event, const char* filename, void* cookie) {
118         if (event != WATCH_EVENT_ADD_FILE) {
119             return ZX_OK;
120         }
121         if ((strcmp(filename, ".") == 0) || strcmp(filename, "..") == 0) {
122             return ZX_OK;
123         }
124         fbl::unique_fd devfd(openat(dirfd, filename, O_RDWR));
125         if (!devfd) {
126             return ZX_OK;
127         }
128         auto info = static_cast<CallbackInfo*>(cookie);
129         if (info->should_filter_file(devfd)) {
130             return ZX_OK;
131         }
132         if (info->out_partition) {
133             *(info->out_partition) = std::move(devfd);
134         }
135         return ZX_ERR_STOP;
136     };
137 
138     fbl::unique_fd dir_fd(openat(devfs_root.get(), path, O_RDWR));
139     if (!dir_fd) {
140         return ZX_ERR_IO;
141     }
142     DIR* dir = fdopendir(dir_fd.release());
143     if (dir == nullptr) {
144         return ZX_ERR_IO;
145     }
146     const auto closer = fbl::MakeAutoCall([&dir]() { closedir(dir); });
147 
148     zx_time_t deadline = zx_deadline_after(timeout);
149     if (fdio_watch_directory(dirfd(dir), cb, deadline, &info) != ZX_ERR_STOP) {
150         return ZX_ERR_NOT_FOUND;
151     }
152     return ZX_OK;
153 }
154 
155 constexpr char kBlockDevPath[] = "class/block/";
156 
OpenBlockPartition(const fbl::unique_fd & devfs_root,const uint8_t * unique_guid,const uint8_t * type_guid,zx_duration_t timeout,fbl::unique_fd * out_fd)157 zx_status_t OpenBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid,
158                                const uint8_t* type_guid, zx_duration_t timeout,
159                                fbl::unique_fd* out_fd) {
160     ZX_ASSERT(unique_guid || type_guid);
161 
162     auto cb = [&](const fbl::unique_fd& fd) {
163         if (TestBlockFilter && TestBlockFilter(fd)) {
164             return true;
165         }
166         uint8_t buf[GUID_LEN];
167         if (type_guid) {
168             if (ioctl_block_get_type_guid(fd.get(), buf, sizeof(buf)) < 0 ||
169                 memcmp(buf, type_guid, GUID_LEN) != 0) {
170                 return true;
171             }
172         }
173         if (unique_guid) {
174             if (ioctl_block_get_partition_guid(fd.get(), buf, sizeof(buf)) < 0 ||
175                 memcmp(buf, unique_guid, GUID_LEN) != 0) {
176                 return true;
177             }
178         }
179         return false;
180     };
181 
182     return OpenPartition(devfs_root, kBlockDevPath, cb, timeout, out_fd);
183 }
184 
185 constexpr char kSkipBlockDevPath[] = "class/skip-block/";
186 
OpenSkipBlockPartition(const fbl::unique_fd & devfs_root,const uint8_t * type_guid,zx_duration_t timeout,fbl::unique_fd * out_fd)187 zx_status_t OpenSkipBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* type_guid,
188                                    zx_duration_t timeout, fbl::unique_fd* out_fd) {
189     ZX_ASSERT(type_guid);
190 
191     auto cb = [&](const fbl::unique_fd& fd) {
192         fzl::FdioCaller caller(fbl::unique_fd(fd.get()));
193 
194         zx_status_t status;
195         fuchsia_hardware_skipblock_PartitionInfo info;
196         fuchsia_hardware_skipblock_SkipBlockGetPartitionInfo(caller.borrow_channel(), &status,
197                                                              &info);
198         caller.release().release();
199         if (status != ZX_OK || memcmp(info.partition_guid, type_guid, GUID_LEN) != 0) {
200             return true;
201         }
202         return false;
203     };
204 
205     return OpenPartition(devfs_root, kSkipBlockDevPath, cb, timeout, out_fd);
206 }
207 
HasSkipBlockDevice(const fbl::unique_fd & devfs_root)208 bool HasSkipBlockDevice(const fbl::unique_fd& devfs_root) {
209     // Our proxy for detected a skip-block device is by checking for the
210     // existence of a device enumerated under the skip-block class.
211     const uint8_t type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
212     return OpenSkipBlockPartition(devfs_root, type, ZX_SEC(1), nullptr) == ZX_OK;
213 }
214 
215 // Attempts to open and overwrite the first block of the underlying
216 // partition. Does not rebind partition drivers.
217 //
218 // At most one of |unique_guid| and |type_guid| may be nullptr.
WipeBlockPartition(const fbl::unique_fd & devfs_root,const uint8_t * unique_guid,const uint8_t * type_guid)219 zx_status_t WipeBlockPartition(const fbl::unique_fd& devfs_root, const uint8_t* unique_guid,
220                                const uint8_t* type_guid) {
221     zx_status_t status = ZX_OK;
222     fbl::unique_fd fd;
223     if ((status = OpenBlockPartition(devfs_root, unique_guid, type_guid, ZX_SEC(3), &fd)) != ZX_OK) {
224         ERROR("Warning: Could not open partition to wipe: %s\n",
225               zx_status_get_string(status));
226         return status;
227     }
228 
229     block_info_t info;
230     ssize_t result = ZX_OK;
231     if ((result = ioctl_block_get_info(fd.get(), &info)) < 0) {
232         status = static_cast<zx_status_t>(result);
233         ERROR("Warning: Could not acquire block info: %s\n",
234               zx_status_get_string(status));
235         return status;
236     }
237 
238     // Overwrite the first block to (hackily) ensure the destroyed partition
239     // doesn't "reappear" in place.
240     char buf[info.block_size];
241     memset(buf, 0, info.block_size);
242 
243     if (pwrite(fd.get(), buf, info.block_size, 0) != info.block_size) {
244         ERROR("Warning: Could not write to block device: %s\n", strerror(errno));
245         return ZX_ERR_IO;
246     }
247 
248     if ((status = FlushBlockDevice(fd)) != ZX_OK) {
249         ERROR("Warning: Failed to synchronize block device: %s\n",
250               zx_status_get_string(status));
251         return status;
252     }
253 
254     return ZX_OK;
255 }
256 
257 } // namespace
258 
Create()259 fbl::unique_ptr<DevicePartitioner> DevicePartitioner::Create() {
260     fbl::unique_fd devfs_root(open("/dev", O_RDWR));
261     fbl::unique_ptr<DevicePartitioner> device_partitioner;
262 #if defined(__x86_64__)
263     if ((CrosDevicePartitioner::Initialize(devfs_root.duplicate(), &device_partitioner) == ZX_OK) ||
264         (EfiDevicePartitioner::Initialize(std::move(devfs_root), &device_partitioner) == ZX_OK)) {
265         return device_partitioner;
266     }
267 #elif defined(__aarch64__)
268     if ((SkipBlockDevicePartitioner::Initialize(devfs_root.duplicate(),
269                                                 &device_partitioner) == ZX_OK) ||
270         (FixedDevicePartitioner::Initialize(std::move(devfs_root), &device_partitioner) == ZX_OK)) {
271         return device_partitioner;
272     }
273 #endif
274     return nullptr;
275 }
276 
277 /*====================================================*
278  *                  GPT Common                        *
279  *====================================================*/
280 
FindTargetGptPath(const fbl::unique_fd & devfs_root,fbl::String * out)281 bool GptDevicePartitioner::FindTargetGptPath(const fbl::unique_fd& devfs_root, fbl::String* out) {
282     fbl::unique_fd d_fd(openat(devfs_root.get(), kBlockDevPath, O_RDWR));
283     if (!d_fd) {
284         ERROR("Cannot inspect block devices\n");
285         return false;
286     }
287     DIR* d = fdopendir(d_fd.release());
288     if (d == nullptr) {
289         ERROR("Cannot inspect block devices\n");
290         return false;
291     }
292     const auto closer = fbl::MakeAutoCall([&]() { closedir(d); });
293 
294     struct dirent* de;
295     while ((de = readdir(d)) != nullptr) {
296         fbl::unique_fd fd(openat(dirfd(d), de->d_name, O_RDWR));
297         if (!fd) {
298             continue;
299         }
300         out->Set(PATH_MAX, '\0');
301         ssize_t r = ioctl_device_get_topo_path(fd.get(), const_cast<char*>(out->data()), PATH_MAX);
302         if (r < 0) {
303             continue;
304         }
305 
306         block_info_t info;
307         if ((r = ioctl_block_get_info(fd.get(), &info) < 0)) {
308             continue;
309         }
310 
311         // TODO(ZX-1344): This is a hack, but practically, will work for our
312         // usage.
313         //
314         // The GPT which will contain an FVM should be the first non-removable
315         // block device that isn't a partition itself.
316         if (!(info.flags & BLOCK_FLAG_REMOVABLE) && strstr(out->c_str(), "part-") == nullptr) {
317             return true;
318         }
319     }
320 
321     ERROR("No candidate GPT found\n");
322     return false;
323 }
324 
InitializeGpt(fbl::unique_fd devfs_root,fbl::unique_ptr<GptDevicePartitioner> * gpt_out)325 zx_status_t GptDevicePartitioner::InitializeGpt(fbl::unique_fd devfs_root,
326                                                 fbl::unique_ptr<GptDevicePartitioner>* gpt_out) {
327     fbl::String gpt_path;
328     if (!FindTargetGptPath(devfs_root, &gpt_path)) {
329         ERROR("Failed to find GPT\n");
330         return ZX_ERR_NOT_FOUND;
331     }
332     fbl::unique_fd fd(open(gpt_path.c_str(), O_RDWR));
333     if (!fd) {
334         ERROR("Failed to open GPT\n");
335         return ZX_ERR_NOT_FOUND;
336     }
337     block_info_t block_info;
338     ssize_t rc = ioctl_block_get_info(fd.get(), &block_info);
339     if (rc < 0) {
340         ERROR("Couldn't get GPT block info\n");
341         return ZX_ERR_NOT_FOUND;
342     }
343 
344     gpt_device_t* gpt;
345     if (gpt_device_init(fd.get(), block_info.block_size, block_info.block_count, &gpt)) {
346         ERROR("Failed to get GPT info\n");
347         return ZX_ERR_BAD_STATE;
348     }
349 
350     auto releaser = fbl::MakeAutoCall([&]() { gpt_device_release(gpt); });
351     if (!gpt->valid) {
352         ERROR("Located GPT is invalid; Attempting to initialize\n");
353         if (gpt_partition_remove_all(gpt)) {
354             ERROR("Failed to create empty GPT\n");
355             return ZX_ERR_BAD_STATE;
356         }
357         if (gpt_device_sync(gpt)) {
358             ERROR("Failed to sync empty GPT\n");
359             return ZX_ERR_BAD_STATE;
360         }
361         // Try to rebind the GPT, in case a prior GPT driver was actually
362         // up and running.
363         if ((rc = ioctl_block_rr_part(fd.get())) != ZX_OK) {
364             ERROR("Failed to re-read GPT\n");
365             return ZX_ERR_BAD_STATE;
366         }
367         // Manually re-bind the GPT driver, since it is almost certainly
368         // too late to be noticed by the block watcher.
369         if ((rc = ioctl_device_bind(fd.get(), kGptDriverName, strlen(kGptDriverName))) != ZX_OK) {
370             ERROR("Failed to bind GPT\n");
371             return ZX_ERR_BAD_STATE;
372         }
373     }
374 
375     releaser.cancel();
376     *gpt_out = WrapUnique(new GptDevicePartitioner(std::move(devfs_root), std::move(fd), gpt,
377                                                    block_info));
378     return ZX_OK;
379 }
380 
381 struct PartitionPosition {
382     size_t start;  // Block, inclusive
383     size_t length; // In Blocks
384 };
385 
FindFirstFit(size_t bytes_requested,size_t * start_out,size_t * length_out) const386 zx_status_t GptDevicePartitioner::FindFirstFit(size_t bytes_requested, size_t* start_out,
387                                                size_t* length_out) const {
388     LOG("Looking for space\n");
389     // Gather GPT-related information.
390     size_t blocks_requested =
391         (bytes_requested + block_info_.block_size - 1) / block_info_.block_size;
392 
393     // Sort all partitions by starting block.
394     // For simplicity, include the 'start' and 'end' reserved spots as
395     // partitions.
396     size_t partition_count = 0;
397     PartitionPosition partitions[PARTITIONS_COUNT + 2];
398     const size_t reserved_blocks = ReservedHeaderBlocks(block_info_.block_size);
399     partitions[partition_count].start = 0;
400     partitions[partition_count++].length = reserved_blocks;
401     partitions[partition_count].start = block_info_.block_count - reserved_blocks;
402     partitions[partition_count++].length = reserved_blocks;
403 
404     for (size_t i = 0; i < PARTITIONS_COUNT; i++) {
405         const gpt_partition_t* p = gpt_->partitions[i];
406         if (!p) {
407             continue;
408         }
409         partitions[partition_count].start = p->first;
410         partitions[partition_count].length = p->last - p->first + 1;
411         LOG("Partition seen with start %zu, end %zu (length %zu)\n", p->first, p->last,
412             partitions[partition_count].length);
413         partition_count++;
414     }
415     LOG("Sorting\n");
416     qsort(partitions, partition_count, sizeof(PartitionPosition),
417           [](const void* p1, const void* p2) {
418               ssize_t s1 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p1)->start);
419               ssize_t s2 = static_cast<ssize_t>(static_cast<const PartitionPosition*>(p2)->start);
420               return static_cast<int>(s1 - s2);
421           });
422 
423     // Look for space between the partitions. Since the reserved spots of the
424     // GPT were included in |partitions|, all available space will be located
425     // "between" partitions.
426     for (size_t i = 0; i < partition_count - 1; i++) {
427         const size_t next = partitions[i].start + partitions[i].length;
428         LOG("Partition[%zu] From Block [%zu, %zu) ... (next partition starts at block %zu)\n",
429             i, partitions[i].start, next, partitions[i + 1].start);
430 
431         if (next > partitions[i + 1].start) {
432             ERROR("Corrupted GPT\n");
433             return ZX_ERR_IO;
434         }
435         const size_t free_blocks = partitions[i + 1].start - next;
436         LOG("    There are %zu free blocks (%zu requested)\n", free_blocks, blocks_requested);
437         if (free_blocks >= blocks_requested) {
438             *start_out = next;
439             *length_out = free_blocks;
440             return ZX_OK;
441         }
442     }
443     ERROR("No GPT space found\n");
444     return ZX_ERR_NO_RESOURCES;
445 }
446 
CreateGptPartition(const char * name,uint8_t * type,uint64_t offset,uint64_t blocks,uint8_t * out_guid)447 zx_status_t GptDevicePartitioner::CreateGptPartition(const char* name, uint8_t* type,
448                                                      uint64_t offset, uint64_t blocks,
449                                                      uint8_t* out_guid) {
450     zx_cprng_draw(out_guid, GPT_GUID_LEN);
451 
452     zx_status_t status;
453     if ((status = gpt_partition_add(gpt_, name, type, out_guid, offset, blocks, 0))) {
454         ERROR("Failed to add partition\n");
455         return ZX_ERR_IO;
456     }
457     if ((status = gpt_device_sync(gpt_))) {
458         ERROR("Failed to sync GPT\n");
459         return ZX_ERR_IO;
460     }
461     if ((status = gpt_partition_clear(gpt_, offset, 1))) {
462         ERROR("Failed to clear first block of new partition\n");
463         return ZX_ERR_IO;
464     }
465     if ((status = static_cast<zx_status_t>(ioctl_block_rr_part(fd_.get()))) < 0) {
466         ERROR("Failed to rebind GPT\n");
467         return status;
468     }
469 
470     return ZX_OK;
471 }
472 
AddPartition(const char * name,uint8_t * type,size_t minimum_size_bytes,size_t optional_reserve_bytes,fbl::unique_fd * out_fd)473 zx_status_t GptDevicePartitioner::AddPartition(
474     const char* name, uint8_t* type, size_t minimum_size_bytes,
475     size_t optional_reserve_bytes, fbl::unique_fd* out_fd) {
476 
477     uint64_t start, length;
478     zx_status_t status;
479     if ((status = FindFirstFit(minimum_size_bytes, &start, &length)) != ZX_OK) {
480         ERROR("Couldn't find fit\n");
481         return status;
482     }
483     LOG("Found space in GPT - OK %zu @ %zu\n", length, start);
484 
485     if (optional_reserve_bytes) {
486         // If we can fulfill the requested size, and we still have space for the
487         // optional reserve section, then we should shorten the amount of blocks
488         // we're asking for.
489         //
490         // This isn't necessary, but it allows growing the GPT later, if necessary.
491         const size_t optional_reserve_blocks = optional_reserve_bytes / block_info_.block_size;
492         if (length - optional_reserve_bytes > (minimum_size_bytes / block_info_.block_size)) {
493             LOG("Space for reserve - OK\n");
494             length -= optional_reserve_blocks;
495         }
496     } else {
497         length = fbl::round_up(minimum_size_bytes, block_info_.block_size) / block_info_.block_size;
498     }
499     LOG("Final space in GPT - OK %zu @ %zu\n", length, start);
500 
501     uint8_t guid[GPT_GUID_LEN];
502     if ((status = CreateGptPartition(name, type, start, length, guid)) != ZX_OK) {
503         return status;
504     }
505     LOG("Added partition, waiting for bind\n");
506 
507     if ((status = OpenBlockPartition(devfs_root_, guid, type, ZX_SEC(5), out_fd)) != ZX_OK) {
508         ERROR("Added partition, waiting for bind - NOT FOUND\n");
509         return status;
510     }
511     LOG("Added partition, waiting for bind - OK\n");
512     return ZX_OK;
513 }
514 
FindPartition(FilterCallback filter,gpt_partition_t ** out,fbl::unique_fd * out_fd)515 zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter, gpt_partition_t** out,
516                                                 fbl::unique_fd* out_fd) {
517     for (size_t i = 0; i < PARTITIONS_COUNT; i++) {
518         gpt_partition_t* p = gpt_->partitions[i];
519         if (!p) {
520             continue;
521         }
522 
523         if (filter(*p)) {
524             LOG("Found partition in GPT, partition %zu\n", i);
525             if (out) {
526                 *out = p;
527             }
528             if (out_fd) {
529                 zx_status_t status;
530                 status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd);
531                 if (status != ZX_OK) {
532                     ERROR("Couldn't open partition\n");
533                     return status;
534                 }
535             }
536             return ZX_OK;
537         }
538     }
539     return ZX_ERR_NOT_FOUND;
540 }
541 
FindPartition(FilterCallback filter,fbl::unique_fd * out_fd) const542 zx_status_t GptDevicePartitioner::FindPartition(FilterCallback filter,
543                                                 fbl::unique_fd* out_fd) const {
544     for (size_t i = 0; i < PARTITIONS_COUNT; i++) {
545         const gpt_partition_t* p = gpt_->partitions[i];
546         if (!p) {
547             continue;
548         }
549 
550         if (filter(*p)) {
551             LOG("Found partition in GPT, partition %zu\n", i);
552             if (out_fd) {
553                 zx_status_t status;
554                 status = OpenBlockPartition(devfs_root_, p->guid, p->type, ZX_SEC(5), out_fd);
555                 if (status != ZX_OK) {
556                     ERROR("Couldn't open partition\n");
557                     return status;
558                 }
559             }
560             return ZX_OK;
561         }
562     }
563     return ZX_ERR_NOT_FOUND;
564 }
565 
WipePartitions(FilterCallback filter)566 zx_status_t GptDevicePartitioner::WipePartitions(FilterCallback filter) {
567     bool modify = false;
568     for (size_t i = 0; i < PARTITIONS_COUNT; i++) {
569         const gpt_partition_t* p = gpt_->partitions[i];
570         if (!p) {
571             continue;
572         }
573         if (!filter(*p)) {
574             continue;
575         }
576 
577         modify = true;
578 
579         // Ignore the return status; wiping is a best-effort approach anyway.
580         WipeBlockPartition(devfs_root_, p->guid, p->type);
581 
582         if (gpt_partition_remove(gpt_, p->guid)) {
583             ERROR("Warning: Could not remove partition\n");
584         } else {
585             // If we successfully clear the partition, then all subsequent
586             // partitions get shifted down. If we just deleted partition 'i',
587             // we now need to look at partition 'i' again, since it's now
588             // occupied by what was in 'i+1'.
589             i--;
590         }
591     }
592     if (modify) {
593         gpt_device_sync(gpt_);
594         LOG("Immediate reboot strongly recommended\n");
595     }
596     ioctl_block_rr_part(fd_.get());
597     return ZX_OK;
598 }
599 
600 /*====================================================*
601  *                 EFI SPECIFIC                       *
602  *====================================================*/
603 
Initialize(fbl::unique_fd devfs_root,fbl::unique_ptr<DevicePartitioner> * partitioner)604 zx_status_t EfiDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
605                                              fbl::unique_ptr<DevicePartitioner>* partitioner) {
606     fbl::unique_ptr<GptDevicePartitioner> gpt;
607     zx_status_t status;
608     if ((status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root), &gpt)) != ZX_OK) {
609         return status;
610     }
611     if (is_cros(gpt->GetGpt())) {
612         ERROR("Use CrOS Device Partitioner.");
613         return ZX_ERR_NOT_SUPPORTED;
614     }
615 
616     LOG("Successfully initialized EFI Device Partitioner\n");
617     *partitioner = WrapUnique(new EfiDevicePartitioner(std::move(gpt)));
618     return ZX_OK;
619 }
620 
AddPartition(Partition partition_type,fbl::unique_fd * out_fd)621 zx_status_t EfiDevicePartitioner::AddPartition(Partition partition_type, fbl::unique_fd* out_fd) {
622     const char* name;
623     uint8_t type[GPT_GUID_LEN];
624     size_t minimum_size_bytes = 0;
625     size_t optional_reserve_bytes = 0;
626 
627     switch (partition_type) {
628     case Partition::kEfi: {
629         const uint8_t efi_type[GPT_GUID_LEN] = GUID_EFI_VALUE;
630         memcpy(type, efi_type, GPT_GUID_LEN);
631         minimum_size_bytes = 20LU * (1 << 20);
632         name = kEfiName;
633         break;
634     }
635     case Partition::kZirconA: {
636         const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
637         memcpy(type, zircon_a_type, GPT_GUID_LEN);
638         minimum_size_bytes = 16LU * (1 << 20);
639         name = kZirconAName;
640         break;
641     }
642     case Partition::kZirconB: {
643         const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE;
644         memcpy(type, zircon_b_type, GPT_GUID_LEN);
645         minimum_size_bytes = 16LU * (1 << 20);
646         name = kZirconBName;
647         break;
648     }
649     case Partition::kZirconR: {
650         const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE;
651         memcpy(type, zircon_r_type, GPT_GUID_LEN);
652         minimum_size_bytes = 24LU * (1 << 20);
653         name = kZirconRName;
654         break;
655     }
656     case Partition::kFuchsiaVolumeManager: {
657         const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
658         memcpy(type, fvm_type, GPT_GUID_LEN);
659         minimum_size_bytes = 8LU * (1 << 30);
660         name = kFvmPartitionName;
661         break;
662     }
663     default:
664         ERROR("EFI partitioner cannot add unknown partition type\n");
665         return ZX_ERR_NOT_SUPPORTED;
666     }
667 
668     return gpt_->AddPartition(name, type, minimum_size_bytes,
669                               optional_reserve_bytes, out_fd);
670 }
671 
FindPartition(Partition partition_type,fbl::unique_fd * out_fd) const672 zx_status_t EfiDevicePartitioner::FindPartition(Partition partition_type,
673                                                 fbl::unique_fd* out_fd) const {
674     switch (partition_type) {
675     case Partition::kEfi: {
676         return gpt_->FindPartition(IsGigabootPartition, out_fd);
677     }
678     case Partition::kZirconA: {
679         const auto filter = [](const gpt_partition_t& part) {
680             const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
681             return KernelFilterCallback(part, guid, kZirconAName);
682         };
683         return gpt_->FindPartition(filter, out_fd);
684     }
685     case Partition::kZirconB: {
686         const auto filter = [](const gpt_partition_t& part) {
687             const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE;
688             return KernelFilterCallback(part, guid, kZirconBName);
689         };
690         return gpt_->FindPartition(filter, out_fd);
691     }
692     case Partition::kZirconR: {
693         const auto filter = [](const gpt_partition_t& part) {
694             const uint8_t guid[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE;
695             return KernelFilterCallback(part, guid, kZirconRName);
696         };
697         return gpt_->FindPartition(filter, out_fd);
698     }
699     case Partition::kFuchsiaVolumeManager:
700         return gpt_->FindPartition(FvmFilterCallback, out_fd);
701 
702     default:
703         ERROR("EFI partitioner cannot find unknown partition type\n");
704         return ZX_ERR_NOT_SUPPORTED;
705     }
706 }
707 
WipePartitions()708 zx_status_t EfiDevicePartitioner::WipePartitions() {
709     return gpt_->WipePartitions(WipeFilterCallback);
710 }
711 
GetBlockSize(const fbl::unique_fd & device_fd,uint32_t * block_size) const712 zx_status_t EfiDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd,
713                                                uint32_t* block_size) const {
714     block_info_t info;
715     zx_status_t status = gpt_->GetBlockInfo(&info);
716     if (status == ZX_OK) {
717         *block_size = info.block_size;
718     }
719     return status;
720 }
721 
722 /*====================================================*
723  *                CROS SPECIFIC                       *
724  *====================================================*/
725 
Initialize(fbl::unique_fd devfs_root,fbl::unique_ptr<DevicePartitioner> * partitioner)726 zx_status_t CrosDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
727                                               fbl::unique_ptr<DevicePartitioner>* partitioner) {
728     fbl::unique_ptr<GptDevicePartitioner> gpt_partitioner;
729     zx_status_t status;
730     if ((status = GptDevicePartitioner::InitializeGpt(std::move(devfs_root),
731                                                       &gpt_partitioner)) != ZX_OK) {
732         return status;
733     }
734 
735     gpt_device_t* gpt = gpt_partitioner->GetGpt();
736     if (!is_cros(gpt)) {
737         return ZX_ERR_NOT_FOUND;
738     }
739 
740     block_info_t info;
741     gpt_partitioner->GetBlockInfo(&info);
742 
743     if (!is_ready_to_pave(gpt, &info, SZ_ZX_PART)) {
744         if ((status = config_cros_for_fuchsia(gpt, &info, SZ_ZX_PART)) != ZX_OK) {
745             ERROR("Failed to configure CrOS for Fuchsia.\n");
746             return status;
747         }
748         gpt_device_sync(gpt);
749         ioctl_block_rr_part(gpt_partitioner->GetFd());
750     }
751 
752     LOG("Successfully initialized CrOS Device Partitioner\n");
753     *partitioner = WrapUnique(new CrosDevicePartitioner(std::move(gpt_partitioner)));
754     return ZX_OK;
755 }
756 
AddPartition(Partition partition_type,fbl::unique_fd * out_fd)757 zx_status_t CrosDevicePartitioner::AddPartition(Partition partition_type,
758                                                 fbl::unique_fd* out_fd) {
759     const char* name;
760     uint8_t type[GPT_GUID_LEN];
761     size_t minimum_size_bytes = 0;
762     size_t optional_reserve_bytes = 0;
763 
764     switch (partition_type) {
765     case Partition::kKernelC: {
766         const uint8_t kernc_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE;
767         memcpy(type, kernc_type, GPT_GUID_LEN);
768         minimum_size_bytes = 64LU * (1 << 20);
769         name = kZirconAName;
770         break;
771     }
772     case Partition::kFuchsiaVolumeManager: {
773         const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
774         memcpy(type, fvm_type, GPT_GUID_LEN);
775         minimum_size_bytes = 8LU * (1 << 30);
776         name = kFvmPartitionName;
777         break;
778     }
779     default:
780         ERROR("Cros partitioner cannot add unknown partition type\n");
781         return ZX_ERR_NOT_SUPPORTED;
782     }
783     return gpt_->AddPartition(name, type, minimum_size_bytes,
784                               optional_reserve_bytes, out_fd);
785 }
786 
FindPartition(Partition partition_type,fbl::unique_fd * out_fd) const787 zx_status_t CrosDevicePartitioner::FindPartition(Partition partition_type,
788                                                  fbl::unique_fd* out_fd) const {
789     switch (partition_type) {
790     case Partition::kKernelC: {
791         const auto filter = [](const gpt_partition_t& part) {
792             const uint8_t guid[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE;
793             return KernelFilterCallback(part, guid, kZirconAName);
794         };
795         return gpt_->FindPartition(filter, out_fd);
796     }
797     case Partition::kFuchsiaVolumeManager:
798         return gpt_->FindPartition(FvmFilterCallback, out_fd);
799 
800     default:
801         ERROR("Cros partitioner cannot find unknown partition type\n");
802         return ZX_ERR_NOT_SUPPORTED;
803     }
804 }
805 
FinalizePartition(Partition partition_type)806 zx_status_t CrosDevicePartitioner::FinalizePartition(Partition partition_type) {
807     // Special partition finalization is only necessary for Zircon partitions.
808     if (partition_type != Partition::kKernelC) {
809         return ZX_OK;
810     }
811 
812     uint8_t top_priority = 0;
813 
814     const uint8_t kern_type[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE;
815     constexpr char kPrefix[] = "ZIRCON-";
816     uint16_t zircon_prefix[strlen(kPrefix)*2];
817     cstring_to_utf16(&zircon_prefix[0], kPrefix, strlen(kPrefix));
818 
819     for(size_t i = 0; i < PARTITIONS_COUNT; ++i) {
820         const gpt_partition_t* part = gpt_->GetGpt()->partitions[i];
821         if (part == NULL) {
822             continue;
823         }
824         if (memcmp(part->type, kern_type, GPT_GUID_LEN)) {
825             continue;
826         }
827         if (memcmp(part->name, zircon_prefix, strlen(kPrefix)*2)) {
828             const uint8_t priority = gpt_cros_attr_get_priority(part->flags);
829             if (priority > top_priority) {
830                 top_priority = priority;
831             }
832         }
833     }
834 
835     const auto filter_zircona = [](const gpt_partition_t& part) {
836         const uint8_t guid[GPT_GUID_LEN] = GUID_CROS_KERNEL_VALUE;
837         return KernelFilterCallback(part, guid, kZirconAName);
838     };
839     zx_status_t status;
840     gpt_partition_t* partition;
841     if ((status = gpt_->FindPartition(filter_zircona, &partition, nullptr)) != ZX_OK) {
842         ERROR("Cannot find %s partition\n", kZirconAName);
843         return status;
844     }
845 
846     // Priority for Zircon A set to higher priority than all other kernels.
847     if (top_priority == UINT8_MAX) {
848         ERROR("Cannot set CrOS partition priority higher than other kernels\n");
849         return ZX_ERR_OUT_OF_RANGE;
850     }
851 
852     // TODO(raggi): when other (B/R) partitions are paved, set their priority
853     // appropriately as well.
854 
855     if (gpt_cros_attr_set_priority(&partition->flags, ++top_priority) != 0) {
856         ERROR("Cannot set CrOS partition priority for ZIRCON-A\n");
857         return ZX_ERR_OUT_OF_RANGE;
858     }
859     // Successful set to 'true' to encourage the bootloader to
860     // use this partition.
861     gpt_cros_attr_set_successful(&partition->flags, true);
862     // Maximize the number of attempts to boot this partition before
863     // we fall back to a different kernel.
864     if (gpt_cros_attr_set_tries(&partition->flags, 15) != 0) {
865         ERROR("Cannot set CrOS partition 'tries' for KERN-C\n");
866         return ZX_ERR_OUT_OF_RANGE;
867     }
868     gpt_device_sync(gpt_->GetGpt());
869     return ZX_OK;
870 }
871 
WipePartitions()872 zx_status_t CrosDevicePartitioner::WipePartitions() {
873     return gpt_->WipePartitions(WipeFilterCallback);
874 }
875 
GetBlockSize(const fbl::unique_fd & device_fd,uint32_t * block_size) const876 zx_status_t CrosDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd,
877                                                uint32_t* block_size) const {
878     block_info_t info;
879     zx_status_t status = gpt_->GetBlockInfo(&info);
880     if (status == ZX_OK) {
881         *block_size = info.block_size;
882     }
883     return status;
884 }
885 
886 /*====================================================*
887  *               FIXED PARTITION MAP                  *
888  *====================================================*/
889 
Initialize(fbl::unique_fd devfs_root,fbl::unique_ptr<DevicePartitioner> * partitioner)890 zx_status_t FixedDevicePartitioner::Initialize(fbl::unique_fd devfs_root,
891                                                fbl::unique_ptr<DevicePartitioner>* partitioner) {
892     if (HasSkipBlockDevice(devfs_root)) {
893         return ZX_ERR_NOT_SUPPORTED;
894     }
895     LOG("Successfully initialized FixedDevicePartitioner Device Partitioner\n");
896     *partitioner = WrapUnique(new FixedDevicePartitioner(std::move(devfs_root)));
897     return ZX_OK;
898 }
899 
FindPartition(Partition partition_type,fbl::unique_fd * out_fd) const900 zx_status_t FixedDevicePartitioner::FindPartition(Partition partition_type,
901                                                   fbl::unique_fd* out_fd) const {
902     uint8_t type[GPT_GUID_LEN];
903 
904     switch (partition_type) {
905     case Partition::kZirconA: {
906         const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
907         memcpy(type, zircon_a_type, GPT_GUID_LEN);
908         break;
909     }
910     case Partition::kZirconB: {
911         const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE;
912         memcpy(type, zircon_b_type, GPT_GUID_LEN);
913         break;
914     }
915     case Partition::kZirconR: {
916         const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE;
917         memcpy(type, zircon_r_type, GPT_GUID_LEN);
918         break;
919     }
920     case Partition::kVbMetaA: {
921         const uint8_t vbmeta_a_type[GPT_GUID_LEN] = GUID_VBMETA_A_VALUE;
922         memcpy(type, vbmeta_a_type, GPT_GUID_LEN);
923         break;
924     }
925     case Partition::kVbMetaB: {
926         const uint8_t vbmeta_b_type[GPT_GUID_LEN] = GUID_VBMETA_B_VALUE;
927         memcpy(type, vbmeta_b_type, GPT_GUID_LEN);
928         break;
929     }
930     case Partition::kFuchsiaVolumeManager: {
931         const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
932         memcpy(type, fvm_type, GPT_GUID_LEN);
933         break;
934     }
935     default:
936         ERROR("partition_type is invalid!\n");
937         return ZX_ERR_NOT_SUPPORTED;
938     }
939 
940     return OpenBlockPartition(devfs_root_, nullptr, type, ZX_SEC(5), out_fd);
941 }
942 
WipePartitions()943 zx_status_t FixedDevicePartitioner::WipePartitions() {
944     const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
945     zx_status_t status;
946     if ((status = WipeBlockPartition(devfs_root_, nullptr, fvm_type)) != ZX_OK) {
947         ERROR("Failed to wipe FVM.\n");
948     } else {
949         LOG("Wiped FVM successfully.\n");
950     }
951     LOG("Immediate reboot strongly recommended\n");
952     return ZX_OK;
953 }
954 
GetBlockSize(const fbl::unique_fd & device_fd,uint32_t * block_size) const955 zx_status_t FixedDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd,
956                                                  uint32_t* block_size) const {
957     ssize_t r;
958     block_info_t block_info;
959     if ((r = ioctl_block_get_info(device_fd.get(), &block_info) < 0)) {
960         return ZX_ERR_IO;
961     }
962     *block_size = block_info.block_size;
963     return ZX_OK;
964 }
965 
966 /*====================================================*
967  *                SKIP BLOCK SPECIFIC                 *
968  *====================================================*/
969 
Initialize(fbl::unique_fd devfs_root,fbl::unique_ptr<DevicePartitioner> * partitioner)970 zx_status_t SkipBlockDevicePartitioner::Initialize(
971     fbl::unique_fd devfs_root, fbl::unique_ptr<DevicePartitioner>* partitioner) {
972 
973     // TODO(surajmalhtora): Use common devfs_root fd for both block and
974     // skip-block devices.
975     fbl::unique_fd block_devfs_root(open("/dev", O_RDWR));
976 
977     if (!HasSkipBlockDevice(devfs_root)) {
978         return ZX_ERR_NOT_SUPPORTED;
979     }
980     LOG("Successfully initialized SkipBlockDevicePartitioner Device Partitioner\n");
981     *partitioner = WrapUnique(new SkipBlockDevicePartitioner(std::move(devfs_root),
982                                                              std::move(block_devfs_root)));
983     return ZX_OK;
984 }
985 
FindPartition(Partition partition_type,fbl::unique_fd * out_fd) const986 zx_status_t SkipBlockDevicePartitioner::FindPartition(Partition partition_type,
987                                                       fbl::unique_fd* out_fd) const {
988     uint8_t type[GPT_GUID_LEN];
989 
990     switch (partition_type) {
991     case Partition::kBootloader: {
992         const uint8_t bootloader_type[GPT_GUID_LEN] = GUID_BOOTLOADER_VALUE;
993         memcpy(type, bootloader_type, GPT_GUID_LEN);
994         break;
995     }
996     case Partition::kZirconA: {
997         const uint8_t zircon_a_type[GPT_GUID_LEN] = GUID_ZIRCON_A_VALUE;
998         memcpy(type, zircon_a_type, GPT_GUID_LEN);
999         break;
1000     }
1001     case Partition::kZirconB: {
1002         const uint8_t zircon_b_type[GPT_GUID_LEN] = GUID_ZIRCON_B_VALUE;
1003         memcpy(type, zircon_b_type, GPT_GUID_LEN);
1004         break;
1005     }
1006     case Partition::kZirconR: {
1007         const uint8_t zircon_r_type[GPT_GUID_LEN] = GUID_ZIRCON_R_VALUE;
1008         memcpy(type, zircon_r_type, GPT_GUID_LEN);
1009         break;
1010     }
1011     case Partition::kVbMetaA: {
1012         const uint8_t vbmeta_a_type[GPT_GUID_LEN] = GUID_VBMETA_A_VALUE;
1013         memcpy(type, vbmeta_a_type, GPT_GUID_LEN);
1014         break;
1015     }
1016     case Partition::kVbMetaB: {
1017         const uint8_t vbmeta_b_type[GPT_GUID_LEN] = GUID_VBMETA_B_VALUE;
1018         memcpy(type, vbmeta_b_type, GPT_GUID_LEN);
1019         break;
1020     }
1021     case Partition::kFuchsiaVolumeManager: {
1022         const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
1023         memcpy(type, fvm_type, GPT_GUID_LEN);
1024         // FVM partition is managed so it should expose a normal block device.
1025         return OpenBlockPartition(block_devfs_root_, nullptr, type, ZX_SEC(5), out_fd);
1026     }
1027     default:
1028         ERROR("partition_type is invalid!\n");
1029         return ZX_ERR_NOT_SUPPORTED;
1030     }
1031 
1032     return OpenSkipBlockPartition(devfs_root_, type, ZX_SEC(5), out_fd);
1033 }
1034 
WipePartitions()1035 zx_status_t SkipBlockDevicePartitioner::WipePartitions() {
1036     const uint8_t fvm_type[GPT_GUID_LEN] = GUID_FVM_VALUE;
1037     zx_status_t status;
1038     fbl::unique_fd block_fd;
1039     status = OpenBlockPartition(block_devfs_root_, nullptr, fvm_type, ZX_SEC(3), &block_fd);
1040     if (status != ZX_OK) {
1041         ERROR("Warning: Could not open partition to wipe: %s\n", zx_status_get_string(status));
1042         return ZX_OK;
1043     }
1044 
1045     char name[PATH_MAX + 1];
1046     ssize_t result;
1047     if ((result = ioctl_device_get_topo_path(block_fd.get(), name, PATH_MAX)) < 0) {
1048         status = static_cast<zx_status_t>(result);
1049         ERROR("Warning: Could not get name for partition: %s\n",
1050               zx_status_get_string(status));
1051         return status;
1052     }
1053 
1054     const char* parent = dirname(name);
1055 
1056     fbl::unique_fd parent_fd(open(parent, O_RDWR));
1057     if (!parent_fd) {
1058         ERROR("Warning: Unable to open block parent device.\n");
1059         return ZX_ERR_IO;
1060     }
1061 
1062     zx::channel svc;
1063     status = fdio_get_service_handle(parent_fd.release(), svc.reset_and_get_address());
1064     if (status != ZX_OK) {
1065         ERROR("Warning: Could not get service handle: %s\n", zx_status_get_string(status));
1066         return status;
1067     }
1068     zx_status_t status2;
1069     status = fuchsia_hardware_block_FtlFormat(svc.get(), &status2);
1070 
1071     return status == ZX_OK ? status2 : status;
1072 }
1073 
GetBlockSize(const fbl::unique_fd & device_fd,uint32_t * block_size) const1074 zx_status_t SkipBlockDevicePartitioner::GetBlockSize(const fbl::unique_fd& device_fd,
1075                                                      uint32_t* block_size) const {
1076     // Just in case we are trying to get info about FVM.
1077     ssize_t r;
1078     block_info_t block_info;
1079     if ((r = ioctl_block_get_info(device_fd.get(), &block_info) >= 0)) {
1080         *block_size = block_info.block_size;
1081         return ZX_OK;
1082     }
1083 
1084     fzl::FdioCaller caller(fbl::unique_fd(device_fd.get()));
1085 
1086     zx_status_t status;
1087     fuchsia_hardware_skipblock_PartitionInfo part_info;
1088     fuchsia_hardware_skipblock_SkipBlockGetPartitionInfo(caller.borrow_channel(), &status,
1089                                                          &part_info);
1090     caller.release().release();
1091     if (status != ZX_OK) {
1092         ERROR("Failed to get partition info with status: %d\n", status);
1093         return status;
1094     }
1095     *block_size = static_cast<uint32_t>(part_info.block_size_bytes);
1096 
1097     return ZX_OK;
1098 }
1099 
1100 } // namespace paver
1101