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 <fcntl.h>
7 #include <libgen.h>
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <string.h>
11 
12 #include <block-client/cpp/client.h>
13 #include <crypto/bytes.h>
14 #include <fbl/algorithm.h>
15 #include <fbl/array.h>
16 #include <fbl/auto_call.h>
17 #include <fbl/unique_fd.h>
18 #include <fbl/vector.h>
19 #include <fs-management/fvm.h>
20 #include <fs-management/mount.h>
21 #include <fs-management/ramdisk.h>
22 #include <fuchsia/hardware/skipblock/c/fidl.h>
23 #include <fvm/fvm-sparse.h>
24 #include <fvm/sparse-reader.h>
25 #include <lib/cksum.h>
26 #include <lib/fzl/fdio.h>
27 #include <lib/fzl/resizeable-vmo-mapper.h>
28 #include <lib/fzl/vmo-mapper.h>
29 #include <lib/zx/fifo.h>
30 #include <lib/zx/vmo.h>
31 #include <zircon/boot/image.h>
32 #include <zircon/device/block.h>
33 #include <zircon/device/device.h>
34 #include <zircon/status.h>
35 #include <zircon/syscalls.h>
36 #include <zxcrypt/volume.h>
37 
38 #include <utility>
39 
40 #include "fvm/fvm-sparse.h"
41 #include "fvm/fvm.h"
42 #include "pave-lib.h"
43 #include "pave-logging.h"
44 #include "pave-utils.h"
45 
46 #define ZXCRYPT_DRIVER_LIB "/boot/driver/zxcrypt.so"
47 
48 namespace paver {
49 namespace {
50 
PartitionType(const Command command)51 static Partition PartitionType(const Command command) {
52     switch (command) {
53     case Command::kInstallBootloader:
54         return Partition::kBootloader;
55     case Command::kInstallEfi:
56         return Partition::kEfi;
57     case Command::kInstallKernc:
58         return Partition::kKernelC;
59     case Command::kInstallZirconA:
60         return Partition::kZirconA;
61     case Command::kInstallZirconB:
62         return Partition::kZirconB;
63     case Command::kInstallZirconR:
64         return Partition::kZirconR;
65     case Command::kInstallVbMetaA:
66         return Partition::kVbMetaA;
67     case Command::kInstallVbMetaB:
68         return Partition::kVbMetaB;
69     case Command::kInstallFvm:
70         return Partition::kFuchsiaVolumeManager;
71     default:
72         return Partition::kUnknown;
73     }
74 }
75 
76 // The number of additional slices a partition will need to become
77 // zxcrypt'd.
78 //
79 // TODO(aarongreen): Replace this with a value supplied by ulib/zxcrypt.
80 constexpr size_t kZxcryptExtraSlices = 1;
81 
82 // Confirm that the file descriptor to the underlying partition exists within an
83 // FVM, not, for example, a GPT or MBR.
84 //
85 // |out| is true if |fd| is a VPartition, else false.
FvmIsVirtualPartition(const fbl::unique_fd & fd,bool * out)86 zx_status_t FvmIsVirtualPartition(const fbl::unique_fd& fd, bool* out) {
87     char path[PATH_MAX];
88     const ssize_t r = ioctl_device_get_topo_path(fd.get(), path, sizeof(path));
89     if (r < 0) {
90         return ZX_ERR_IO;
91     }
92 
93     *out = strstr(path, "fvm") != nullptr;
94     return ZX_OK;
95 }
96 
97 // Describes the state of a partition actively being written
98 // out to disk.
99 struct PartitionInfo {
PartitionInfopaver::__anon42e4de980111::PartitionInfo100     PartitionInfo()
101         : pd(nullptr) {}
102 
103     fvm::partition_descriptor_t* pd;
104     fbl::unique_fd new_part;
105 };
106 
GetExtent(fvm::partition_descriptor_t * pd,size_t extent)107 inline fvm::extent_descriptor_t* GetExtent(fvm::partition_descriptor_t* pd, size_t extent) {
108     return reinterpret_cast<fvm::extent_descriptor_t*>(
109         reinterpret_cast<uintptr_t>(pd) + sizeof(fvm::partition_descriptor_t) +
110         extent * sizeof(fvm::extent_descriptor_t));
111 }
112 
113 // Registers a FIFO
RegisterFastBlockIo(const fbl::unique_fd & fd,const zx::vmo & vmo,vmoid_t * vmoid_out,block_client::Client * client_out)114 zx_status_t RegisterFastBlockIo(const fbl::unique_fd& fd, const zx::vmo& vmo,
115                                 vmoid_t* vmoid_out, block_client::Client* client_out) {
116     zx::fifo fifo;
117     if (ioctl_block_get_fifos(fd.get(), fifo.reset_and_get_address()) < 0) {
118         ERROR("Couldn't attach fifo to partition\n");
119         return ZX_ERR_IO;
120     }
121     zx::vmo dup;
122     if (vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup) != ZX_OK) {
123         ERROR("Couldn't duplicate buffer vmo\n");
124         return ZX_ERR_IO;
125     }
126     zx_handle_t h = dup.release();
127     if (ioctl_block_attach_vmo(fd.get(), &h, vmoid_out) < 0) {
128         ERROR("Couldn't attach VMO\n");
129         return ZX_ERR_IO;
130     }
131     return block_client::Client::Create(std::move(fifo), client_out);
132 }
133 
134 // Stream an FVM partition to disk.
StreamFvmPartition(fvm::SparseReader * reader,PartitionInfo * part,const fzl::VmoMapper & mapper,const block_client::Client & client,size_t block_size,block_fifo_request_t * request)135 zx_status_t StreamFvmPartition(fvm::SparseReader* reader, PartitionInfo* part,
136                                const fzl::VmoMapper& mapper, const block_client::Client& client,
137                                size_t block_size, block_fifo_request_t* request) {
138     size_t slice_size = reader->Image()->slice_size;
139     const size_t vmo_cap = mapper.size();
140     for (size_t e = 0; e < part->pd->extent_count; e++) {
141         LOG("Writing extent %zu... \n", e);
142         fvm::extent_descriptor_t* ext = GetExtent(part->pd, e);
143         size_t offset = ext->slice_start * slice_size;
144         size_t bytes_left = ext->extent_length;
145 
146         // Write real data
147         while (bytes_left > 0) {
148             size_t vmo_sz = 0;
149             size_t actual;
150             zx_status_t status = reader->ReadData(
151                 &reinterpret_cast<uint8_t*>(mapper.start())[vmo_sz],
152                 fbl::min(bytes_left, vmo_cap - vmo_sz), &actual);
153             vmo_sz += actual;
154             bytes_left -= actual;
155 
156             if (vmo_sz == 0) {
157                 ERROR("Read nothing from src_fd; %zu bytes left\n", bytes_left);
158                 return ZX_ERR_IO;
159             } else if (vmo_sz % block_size != 0) {
160                 ERROR("Cannot write non-block size multiple: %zu\n", vmo_sz);
161                 return ZX_ERR_IO;
162             } else if (status != ZX_OK) {
163                 ERROR("Error reading partition data\n");
164                 return status;
165             }
166 
167             uint64_t length = vmo_sz / block_size;
168             if (length > UINT32_MAX) {
169                 ERROR("Error writing partition: Too large\n");
170                 return ZX_ERR_OUT_OF_RANGE;
171             }
172             request->length = static_cast<uint32_t>(length);
173             request->vmo_offset = 0;
174             request->dev_offset = offset / block_size;
175 
176             ssize_t r;
177             if ((r = client.Transaction(request, 1)) != ZX_OK) {
178                 ERROR("Error writing partition data\n");
179                 return static_cast<zx_status_t>(r);
180             }
181 
182             offset += vmo_sz;
183         }
184 
185         // Write trailing zeroes (which are implied, but were omitted from
186         // transfer).
187         bytes_left = (ext->slice_count * slice_size) - ext->extent_length;
188         if (bytes_left > 0) {
189             LOG("%zu bytes written, %zu zeroes left\n", ext->extent_length, bytes_left);
190             memset(mapper.start(), 0, vmo_cap);
191         }
192         while (bytes_left > 0) {
193             uint64_t length = fbl::min(bytes_left, vmo_cap) / block_size;
194             if (length > UINT32_MAX) {
195                 ERROR("Error writing trailing zeroes: Too large\n");
196                 return ZX_ERR_OUT_OF_RANGE;
197             }
198             request->length = static_cast<uint32_t>(length);
199             request->vmo_offset = 0;
200             request->dev_offset = offset / block_size;
201 
202             zx_status_t status;
203             if ((status = client.Transaction(request, 1)) != ZX_OK) {
204                 ERROR("Error writing trailing zeroes\n");
205                 return status;
206             }
207 
208             offset += request->length * block_size;
209             bytes_left -= request->length * block_size;
210         }
211     }
212     return ZX_OK;
213 }
214 
215 // Stream a raw (non-FVM) partition to a vmo.
StreamPayloadToVmo(fzl::ResizeableVmoMapper & mapper,const fbl::unique_fd & src_fd,uint32_t block_size_bytes,size_t * payload_size)216 zx_status_t StreamPayloadToVmo(fzl::ResizeableVmoMapper& mapper, const fbl::unique_fd& src_fd,
217                                uint32_t block_size_bytes, size_t* payload_size) {
218     zx_status_t status;
219     ssize_t r;
220     size_t vmo_offset = 0;
221 
222     while ((r = read(src_fd.get(), &reinterpret_cast<uint8_t*>(mapper.start())[vmo_offset],
223                      mapper.size() - vmo_offset)) > 0) {
224         vmo_offset += r;
225         if (mapper.size() - vmo_offset == 0) {
226             // The buffer is full, let's grow the VMO.
227             if ((status = mapper.Grow(mapper.size() << 1)) != ZX_OK) {
228                 ERROR("Failed to grow VMO\n");
229                 return status;
230             }
231         }
232     }
233 
234     if (r < 0) {
235         ERROR("Error reading partition data\n");
236         return static_cast<zx_status_t>(r);
237     }
238 
239     if (vmo_offset % block_size_bytes) {
240         // We have a partial block to write.
241         const size_t rounded_length = fbl::round_up(vmo_offset, block_size_bytes);
242         memset(&reinterpret_cast<uint8_t*>(mapper.start())[vmo_offset], 0,
243                rounded_length - vmo_offset);
244         vmo_offset = rounded_length;
245     }
246     *payload_size = vmo_offset;
247     return ZX_OK;
248 }
249 
250 // Writes a raw (non-FVM) partition to a block device from a VMO.
WriteVmoToBlock(const zx::vmo & vmo,size_t vmo_size,const fbl::unique_fd & partition_fd,uint32_t block_size_bytes)251 zx_status_t WriteVmoToBlock(const zx::vmo& vmo, size_t vmo_size,
252                             const fbl::unique_fd& partition_fd, uint32_t block_size_bytes) {
253     ZX_ASSERT(vmo_size % block_size_bytes == 0);
254 
255     vmoid_t vmoid;
256     block_client::Client client;
257     zx_status_t status = RegisterFastBlockIo(partition_fd, vmo, &vmoid, &client);
258     if (status != ZX_OK) {
259         ERROR("Cannot register fast block I/O\n");
260         return status;
261     }
262 
263     block_fifo_request_t request;
264     request.group = 0;
265     request.vmoid = vmoid;
266     request.opcode = BLOCKIO_WRITE;
267 
268     uint64_t length = vmo_size / block_size_bytes;
269     if (length > UINT32_MAX) {
270         ERROR("Error writing partition data: Too large\n");
271         return ZX_ERR_OUT_OF_RANGE;
272     }
273     request.length = static_cast<uint32_t>(length);
274     request.vmo_offset = 0;
275     request.dev_offset = 0;
276 
277     if ((status = client.Transaction(&request, 1)) != ZX_OK) {
278         ERROR("Error writing partition data: %s\n", zx_status_get_string(status));
279         return status;
280     }
281     return ZX_OK;
282 }
283 
284 // Writes a raw (non-FVM) partition to a skip-block device from a VMO.
WriteVmoToSkipBlock(const zx::vmo & vmo,size_t vmo_size,const fzl::FdioCaller & caller,uint32_t block_size_bytes)285 zx_status_t WriteVmoToSkipBlock(const zx::vmo& vmo, size_t vmo_size,
286                                 const fzl::FdioCaller& caller, uint32_t block_size_bytes) {
287     ZX_ASSERT(vmo_size % block_size_bytes == 0);
288 
289     zx::vmo dup;
290     zx_status_t status;
291     if ((status = vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup)) != ZX_OK) {
292         ERROR("Couldn't duplicate buffer vmo\n");
293         return status;
294     }
295 
296     fuchsia_hardware_skipblock_ReadWriteOperation operation = {
297         .vmo = dup.release(),
298         .vmo_offset = 0,
299         .block = 0,
300         .block_count = static_cast<uint32_t>(vmo_size / block_size_bytes),
301     };
302     bool bad_block_grown;
303 
304     fuchsia_hardware_skipblock_SkipBlockWrite(caller.borrow_channel(), &operation, &status,
305                                               &bad_block_grown);
306     if (status != ZX_OK) {
307         ERROR("Error writing partition data: %s\n", zx_status_get_string(status));
308         return status;
309     }
310     return ZX_OK;
311 }
312 
313 // Checks first few bytes of buffer to ensure it is a ZBI.
314 // Also validates architecture in kernel header matches the target.
ValidateKernelZbi(const uint8_t * buffer,size_t size,Arch arch)315 bool ValidateKernelZbi(const uint8_t* buffer, size_t size, Arch arch) {
316     const auto payload = reinterpret_cast<const zircon_kernel_t*>(buffer);
317     const uint32_t expected_kernel = (arch == Arch::X64) ? ZBI_TYPE_KERNEL_X64
318                                                          : ZBI_TYPE_KERNEL_ARM64;
319 
320     const auto crc_valid = [](const zbi_header_t* hdr) {
321         const uint32_t crc = crc32(0, reinterpret_cast<const uint8_t*>(hdr + 1),
322                                    hdr->length);
323         return hdr->crc32 == crc;
324     };
325 
326     return size >= sizeof(zircon_kernel_t) &&
327            // Container header
328            payload->hdr_file.type == ZBI_TYPE_CONTAINER &&
329            payload->hdr_file.extra == ZBI_CONTAINER_MAGIC &&
330            (payload->hdr_file.length - offsetof(zircon_kernel_t, hdr_kernel)) <= size &&
331            payload->hdr_file.magic == ZBI_ITEM_MAGIC &&
332            payload->hdr_file.flags == ZBI_FLAG_VERSION &&
333            payload->hdr_file.crc32 == ZBI_ITEM_NO_CRC32 &&
334            // Kernel header
335            payload->hdr_kernel.type == expected_kernel &&
336            (payload->hdr_kernel.length - offsetof(zircon_kernel_t, data_kernel)) <= size &&
337            payload->hdr_kernel.magic == ZBI_ITEM_MAGIC &&
338            (payload->hdr_kernel.flags & ZBI_FLAG_VERSION) == ZBI_FLAG_VERSION &&
339            ((payload->hdr_kernel.flags & ZBI_FLAG_CRC32)
340                 ? crc_valid(&payload->hdr_kernel)
341                 : payload->hdr_kernel.crc32 == ZBI_ITEM_NO_CRC32);
342 }
343 
344 // Parses a partition and validates that it matches the expected format.
ValidateKernelPayload(const fzl::ResizeableVmoMapper & mapper,size_t vmo_size,Partition partition_type,Arch arch)345 zx_status_t ValidateKernelPayload(const fzl::ResizeableVmoMapper& mapper, size_t vmo_size,
346                                   Partition partition_type, Arch arch) {
347     // TODO(surajmalhotra): Re-enable this as soon as we have a good way to
348     // determine whether the payload is signed or not. (Might require bootserver
349     // changes).
350     if (false) {
351         const auto* buffer = reinterpret_cast<uint8_t*>(mapper.start());
352         switch (partition_type) {
353         case Partition::kZirconA:
354         case Partition::kZirconB:
355         case Partition::kZirconR:
356             if (!ValidateKernelZbi(buffer, vmo_size, arch)) {
357                 ERROR("Invalid ZBI payload!");
358                 return ZX_ERR_BAD_STATE;
359             }
360             break;
361 
362         default:
363             // TODO(surajmalhotra): Validate non-zbi payloads as well.
364             LOG("Skipping validation as payload is not a ZBI\n");
365             break;
366         }
367     }
368 
369     return ZX_OK;
370 }
371 
372 // Attempt to bind an FVM driver to a partition fd.
TryBindToFvmDriver(const fbl::unique_fd & partition_fd,zx::duration timeout)373 fbl::unique_fd TryBindToFvmDriver(const fbl::unique_fd& partition_fd,
374                                   zx::duration timeout) {
375     char path[PATH_MAX];
376     ssize_t r = ioctl_device_get_topo_path(partition_fd.get(), path, sizeof(path));
377     if (r < 0) {
378         ERROR("Failed to get topological path\n");
379         return fbl::unique_fd();
380     }
381 
382     constexpr char kFvmDriverLib[] = "/boot/driver/fvm.so";
383     r = ioctl_device_bind(partition_fd.get(), kFvmDriverLib, sizeof(kFvmDriverLib));
384     if (r < 0) {
385         ERROR("Could not bind fvm driver\n");
386         return fbl::unique_fd();
387     }
388 
389     char fvm_path[PATH_MAX];
390     snprintf(fvm_path, sizeof(fvm_path), "%s/fvm", path);
391     if (wait_for_device(fvm_path, timeout.get()) != ZX_OK) {
392         ERROR("Error waiting for fvm driver to bind\n");
393         return fbl::unique_fd();
394     }
395     return fbl::unique_fd(open(fvm_path, O_RDWR));
396 }
397 
398 // Options for locating an FVM within a partition.
399 enum class BindOption {
400     // Bind to the FVM, if it exists already.
401     TryBind,
402     // Reformat the partition, regardless of if it already exists as an FVM.
403     Reformat,
404 };
405 
406 // Formats the FVM within the provided partition if it is not already formatted.
407 //
408 // On success, returns a file descriptor to an FVM.
409 // On failure, returns -1
FvmPartitionFormat(fbl::unique_fd partition_fd,size_t slice_size,BindOption option)410 fbl::unique_fd FvmPartitionFormat(fbl::unique_fd partition_fd, size_t slice_size,
411                                   BindOption option) {
412     // Although the format (based on the magic in the FVM superblock)
413     // indicates this is (or at least was) an FVM image, it may be invalid.
414     //
415     // Attempt to bind the FVM driver to this partition, but fall-back to
416     // reinitializing the FVM image so the rest of the paving
417     // process can continue successfully.
418     fbl::unique_fd fvm_fd;
419     if (option == BindOption::TryBind) {
420         disk_format_t df = detect_disk_format(partition_fd.get());
421         if (df == DISK_FORMAT_FVM) {
422             fvm_fd = TryBindToFvmDriver(partition_fd, zx::sec(3));
423             if (fvm_fd) {
424                 LOG("Found already formatted FVM.\n");
425                 fvm_info_t info;
426                 ssize_t r = ioctl_block_fvm_query(fvm_fd.get(), &info);
427                 if (r >= 0) {
428                     if (info.slice_size == slice_size) {
429                         return fvm_fd;
430                     } else {
431                         ERROR("Mismatched slice size. Reinitializing FVM.\n");
432                     }
433                 } else {
434                     ERROR("Could not query FVM for info. Reinitializing FVM.\n");
435                 }
436             } else {
437                 ERROR("Saw DISK_FORMAT_FVM, but could not bind driver. Reinitializing FVM.\n");
438             }
439         }
440     }
441 
442     LOG("Initializing partition as FVM\n");
443     zx_status_t status = fvm_init(partition_fd.get(), slice_size);
444     if (status != ZX_OK) {
445         ERROR("Failed to initialize fvm: %s\n", zx_status_get_string(status));
446         return fbl::unique_fd();
447     }
448 
449     ssize_t r = ioctl_block_rr_part(partition_fd.get());
450     if (r < 0) {
451         ERROR("Could not rebind partition: %s\n",
452               zx_status_get_string(static_cast<zx_status_t>(r)));
453         return fbl::unique_fd();
454     }
455 
456     return TryBindToFvmDriver(partition_fd, zx::sec(3));
457 }
458 
459 // Formats a block device as a zxcrypt volume.
460 //
461 // On success, returns a file descriptor to an FVM.
462 // On failure, returns -1
ZxcryptCreate(PartitionInfo * part)463 zx_status_t ZxcryptCreate(PartitionInfo* part) {
464     zx_status_t status;
465 
466     char path[PATH_MAX];
467     ssize_t r;
468     if ((r = ioctl_device_get_topo_path(part->new_part.get(), path, sizeof(path))) < 0) {
469         status = static_cast<zx_status_t>(r);
470         ERROR("Failed to get topological path\n");
471         return status;
472     }
473     // TODO(security): ZX-1130. We need to bind with channel in order to pass a key here.
474     // TODO(security): ZX-1864. The created volume must marked as needing key rotation.
475     crypto::Secret key;
476     uint8_t* tmp;
477     if ((status = key.Allocate(zxcrypt::kZx1130KeyLen, &tmp)) != ZX_OK) {
478         return status;
479     }
480     memset(tmp, 0, key.len());
481 
482     fbl::unique_ptr<zxcrypt::Volume> volume;
483     if ((status = zxcrypt::Volume::Create(std::move(part->new_part), key, &volume)) != ZX_OK ||
484         (status = volume->Open(zx::sec(3), &part->new_part)) != ZX_OK) {
485         ERROR("Could not create zxcrypt volume\n");
486         return status;
487     }
488 
489     fvm::extent_descriptor_t* ext = GetExtent(part->pd, 0);
490     size_t reserved = volume->reserved_slices();
491 
492     // |Create| guarantees at least |reserved| + 1 slices are allocated.  If the first extent had a
493     // single slice, we're done.
494     size_t allocated = fbl::max(reserved + 1, ext->slice_count);
495     size_t needed = reserved + ext->slice_count;
496     if (allocated >= needed) {
497         return ZX_OK;
498     }
499 
500     // Otherwise, extend by the number of slices we stole for metadata
501     extend_request_t req;
502     req.offset = allocated - reserved;
503     req.length = needed - allocated;
504 
505     if ((r = ioctl_block_fvm_extend(part->new_part.get(), &req)) < 0) {
506         status = static_cast<zx_status_t>(r);
507         ERROR("Failed to extend zxcrypt volume: %s\n", zx_status_get_string(status));
508         return status;
509     }
510 
511     return ZX_OK;
512 }
513 
514 // Returns |ZX_OK| if |partition_fd| is a child of |fvm_fd|.
FvmPartitionIsChild(const fbl::unique_fd & fvm_fd,const fbl::unique_fd & partition_fd)515 zx_status_t FvmPartitionIsChild(const fbl::unique_fd& fvm_fd, const fbl::unique_fd& partition_fd) {
516     char fvm_path[PATH_MAX];
517     char part_path[PATH_MAX];
518     ssize_t r;
519     if ((r = ioctl_device_get_topo_path(fvm_fd.get(), fvm_path, sizeof(fvm_path))) < 0) {
520         ERROR("Couldn't get topological path of FVM\n");
521         return static_cast<zx_status_t>(r);
522     } else if ((r = ioctl_device_get_topo_path(partition_fd.get(), part_path,
523                                                sizeof(part_path))) < 0) {
524         ERROR("Couldn't get topological path of partition\n");
525         return static_cast<zx_status_t>(r);
526     }
527     if (strncmp(fvm_path, part_path, strlen(fvm_path))) {
528         ERROR("Partition does not exist within FVM\n");
529         return ZX_ERR_BAD_STATE;
530     }
531     return ZX_OK;
532 }
533 
534 // Warn users about issues in a way that is intended to stand out from
535 // typical error logs. These errors typically require user intervention,
536 // or may result in data loss.
Warn(const char * problem,const char * action)537 void Warn(const char* problem, const char* action) {
538     ERROR("-----------------------------------------------------\n");
539     ERROR("\n");
540     ERROR("%s:\n", problem);
541     ERROR("%s\n", action);
542     ERROR("\n");
543     ERROR("-----------------------------------------------------\n");
544 }
545 
RecommendWipe(const char * problem)546 void RecommendWipe(const char* problem) {
547     Warn(problem, "Please run 'install-disk-image wipe' to wipe your partitions");
548 }
549 
550 // Deletes all partitions within the FVM with a type GUID matching |type_guid|
551 // until there are none left.
WipeAllFvmPartitionsWithGUID(const fbl::unique_fd & fvm_fd,const uint8_t type_guid[])552 zx_status_t WipeAllFvmPartitionsWithGUID(const fbl::unique_fd& fvm_fd, const uint8_t type_guid[]) {
553     fbl::unique_fd old_part;
554     while ((old_part.reset(open_partition(nullptr, type_guid, ZX_MSEC(500), nullptr))), old_part) {
555         bool is_vpartition;
556         if (FvmIsVirtualPartition(old_part, &is_vpartition) != ZX_OK) {
557             ERROR("Couldn't confirm old vpartition type\n");
558             return ZX_ERR_IO;
559         }
560         if (FvmPartitionIsChild(fvm_fd, old_part) != ZX_OK) {
561             RecommendWipe("Streaming a partition type which also exists outside the target FVM");
562             return ZX_ERR_BAD_STATE;
563         }
564         if (!is_vpartition) {
565             RecommendWipe("Streaming a partition type which also exists in a GPT");
566             return ZX_ERR_BAD_STATE;
567         }
568 
569         // We're paving a partition that already exists within the FVM: let's
570         // destroy it before we pave anew.
571         ssize_t r = ioctl_block_fvm_destroy_partition(old_part.get());
572         if (r < 0) {
573             ERROR("Couldn't destroy partition: %ld\n", r);
574             return static_cast<zx_status_t>(r);
575         }
576     }
577 
578     return ZX_OK;
579 }
580 
581 // Calculate the amount of space necessary for the incoming partitions,
582 // validating the header along the way. Additionally, deletes any old partitions
583 // which match the type GUID of the provided partition.
584 //
585 // Parses the information from the |reader| into |parts|.
PreProcessPartitions(const fbl::unique_fd & fvm_fd,const fbl::unique_ptr<fvm::SparseReader> & reader,const fbl::Array<PartitionInfo> & parts,size_t * out_requested_slices)586 zx_status_t PreProcessPartitions(const fbl::unique_fd& fvm_fd,
587                                  const fbl::unique_ptr<fvm::SparseReader>& reader,
588                                  const fbl::Array<PartitionInfo>& parts,
589                                  size_t* out_requested_slices) {
590     fvm::partition_descriptor_t* part = reader->Partitions();
591     fvm::sparse_image_t* hdr = reader->Image();
592 
593     // Validate the header and determine the necessary slice requirements for
594     // all partitions and all offsets.
595     size_t requested_slices = 0;
596     for (size_t p = 0; p < hdr->partition_count; p++) {
597         parts[p].pd = part;
598         if (parts[p].pd->magic != fvm::kPartitionDescriptorMagic) {
599             ERROR("Bad partition magic\n");
600             return ZX_ERR_IO;
601         }
602 
603         zx_status_t status = WipeAllFvmPartitionsWithGUID(fvm_fd, parts[p].pd->type);
604         if (status != ZX_OK) {
605             ERROR("Failure wiping old partitions matching this GUID\n");
606             return status;
607         }
608 
609         fvm::extent_descriptor_t* ext = GetExtent(parts[p].pd, 0);
610         if (ext->magic != fvm::kExtentDescriptorMagic) {
611             ERROR("Bad extent magic\n");
612             return ZX_ERR_IO;
613         }
614         if (ext->slice_start != 0) {
615             ERROR("First slice must start at zero\n");
616             return ZX_ERR_IO;
617         }
618         if (ext->slice_count == 0) {
619             ERROR("Extents must have > 0 slices\n");
620             return ZX_ERR_IO;
621         }
622         if (ext->extent_length > ext->slice_count * hdr->slice_size) {
623             ERROR("Extent length must fit within allocated slice count\n");
624             return ZX_ERR_IO;
625         }
626 
627         // Filter drivers may require additional space.
628         if ((parts[p].pd->flags & fvm::kSparseFlagZxcrypt) != 0) {
629             requested_slices += kZxcryptExtraSlices;
630         }
631 
632         for (size_t e = 1; e < parts[p].pd->extent_count; e++) {
633             ext = GetExtent(parts[p].pd, e);
634             if (ext->magic != fvm::kExtentDescriptorMagic) {
635                 ERROR("Bad extent magic\n");
636                 return ZX_ERR_IO;
637             } else if (ext->slice_count == 0) {
638                 ERROR("Extents must have > 0 slices\n");
639                 return ZX_ERR_IO;
640             } else if (ext->extent_length > ext->slice_count * hdr->slice_size) {
641                 ERROR("Extent must fit within allocated slice count\n");
642                 return ZX_ERR_IO;
643             }
644 
645             requested_slices += ext->slice_count;
646         }
647         part = reinterpret_cast<fvm::partition_descriptor*>(
648             reinterpret_cast<uintptr_t>(ext) + sizeof(fvm::extent_descriptor_t));
649     }
650 
651     *out_requested_slices = requested_slices;
652     return ZX_OK;
653 }
654 
655 // Allocates the space requested by the partitions by creating new
656 // partitions and filling them with extents. This guarantees that
657 // streaming the data to the device will not run into "no space" issues
658 // later.
AllocatePartitions(const fbl::unique_fd & fvm_fd,const fbl::Array<PartitionInfo> & parts)659 zx_status_t AllocatePartitions(const fbl::unique_fd& fvm_fd,
660                                const fbl::Array<PartitionInfo>& parts) {
661     for (size_t p = 0; p < parts.size(); p++) {
662         fvm::extent_descriptor_t* ext = GetExtent(parts[p].pd, 0);
663         alloc_req_t alloc;
664         // Allocate this partition as inactive so it gets deleted on the next
665         // reboot if this stream fails.
666         alloc.flags = fvm::kVPartFlagInactive;
667         alloc.slice_count = ext->slice_count;
668         memcpy(&alloc.type, parts[p].pd->type, sizeof(alloc.type));
669         zx_cprng_draw(alloc.guid, GPT_GUID_LEN);
670         memcpy(&alloc.name, parts[p].pd->name, sizeof(alloc.name));
671         LOG("Allocating partition %s consisting of %zu slices\n", alloc.name, alloc.slice_count);
672         parts[p].new_part.reset(fvm_allocate_partition(fvm_fd.get(), &alloc));
673         if (!parts[p].new_part) {
674             ERROR("Couldn't allocate partition\n");
675             return ZX_ERR_NO_SPACE;
676         }
677 
678         // Add filter drivers.
679         if ((parts[p].pd->flags & fvm::kSparseFlagZxcrypt) != 0) {
680             LOG("Creating zxcrypt volume\n");
681             zx_status_t status = ZxcryptCreate(&parts[p]);
682             if (status != ZX_OK) {
683                 return status;
684             }
685         }
686 
687         // The 0th index extent is allocated alongside the partition, so we
688         // begin indexing from the 1st extent here.
689         for (size_t e = 1; e < parts[p].pd->extent_count; e++) {
690             ext = GetExtent(parts[p].pd, e);
691             extend_request_t request;
692             request.offset = ext->slice_start;
693             request.length = ext->slice_count;
694             ssize_t result = ioctl_block_fvm_extend(parts[p].new_part.get(), &request);
695             if (result < 0) {
696                 ERROR("Failed to extend partition: %s\n",
697                       zx_status_get_string(static_cast<zx_status_t>(result)));
698                 return ZX_ERR_NO_SPACE;
699             }
700         }
701     }
702 
703     return ZX_OK;
704 }
705 
706 // Given an fd representing a "sparse FVM format", fill the FVM with the
707 // provided partitions described by |src_fd|.
708 //
709 // Decides to overwrite or create new partitions based on the type
710 // GUID, not the instance GUID.
FvmStreamPartitions(fbl::unique_fd partition_fd,fbl::unique_fd src_fd)711 zx_status_t FvmStreamPartitions(fbl::unique_fd partition_fd, fbl::unique_fd src_fd) {
712     fbl::unique_ptr<fvm::SparseReader> reader;
713     zx_status_t status;
714     if ((status = fvm::SparseReader::Create(std::move(src_fd), &reader)) != ZX_OK) {
715         return status;
716     }
717 
718     LOG("Header Validated - OK\n");
719     // Duplicate the partition fd; we may need it later if we reformat the FVM.
720     fbl::unique_fd partition_fd2(dup(partition_fd.get()));
721     if (!partition_fd2) {
722         ERROR("Coudln't dup partition fd\n");
723         return ZX_ERR_IO;
724     }
725 
726     fvm::sparse_image_t* hdr = reader->Image();
727     // Acquire an fd to the FVM, either by finding one that already
728     // exists, or formatting a new one.
729     fbl::unique_fd fvm_fd(FvmPartitionFormat(std::move(partition_fd2), hdr->slice_size,
730                                              BindOption::TryBind));
731     if (!fvm_fd) {
732         ERROR("Couldn't find FVM partition\n");
733         return ZX_ERR_IO;
734     }
735 
736     fbl::Array<PartitionInfo> parts(new PartitionInfo[hdr->partition_count],
737                                     hdr->partition_count);
738 
739     // Parse the incoming image and calculate its size.
740     //
741     // Additionally, delete the old versions of any new partitions.
742     size_t requested_slices = 0;
743     if ((status = PreProcessPartitions(fvm_fd, reader, parts, &requested_slices)) != ZX_OK) {
744         ERROR("Failed to validate partitions: %s\n", zx_status_get_string(status));
745         return status;
746     }
747 
748     // Contend with issues from an image that may be too large for this device.
749     fvm_info_t info;
750     ssize_t result = ioctl_block_fvm_query(fvm_fd.get(), &info);
751     if (result < 0) {
752         zx_status_t status = static_cast<zx_status_t>(result);
753         ERROR("Failed to acquire FVM info: %s\n", zx_status_get_string(status));
754         return status;
755     }
756     size_t free_slices = info.pslice_total_count - info.pslice_allocated_count;
757     if (info.pslice_total_count < requested_slices) {
758         char buf[256];
759         snprintf(buf, sizeof(buf), "Image size (%zu) > Storage size (%zu)",
760                  requested_slices * hdr->slice_size,
761                  info.pslice_total_count * hdr->slice_size);
762         Warn(buf, "Image is too large to be paved to device");
763         return ZX_ERR_NO_SPACE;
764     }
765     if (free_slices < requested_slices) {
766         Warn("Not enough space to non-destructively pave",
767              "Automatically reinitializing FVM; Expect data loss");
768         fvm_fd = FvmPartitionFormat(std::move(partition_fd), hdr->slice_size,
769                                     BindOption::Reformat);
770         if (!fvm_fd) {
771             ERROR("Couldn't reformat FVM partition.\n");
772             return ZX_ERR_IO;
773         }
774         LOG("FVM Reformatted successfully.\n");
775     }
776 
777     LOG("Partitions pre-validated successfully: Enough space exists to pave.\n");
778 
779     // Actually allocate the storage for the incoming image.
780     if ((status = AllocatePartitions(fvm_fd, parts)) != ZX_OK) {
781         ERROR("Failed to allocate partitions: %s\n", zx_status_get_string(status));
782         return status;
783     }
784 
785     LOG("Partition space pre-allocated successfully.\n");
786 
787     constexpr size_t vmo_size = 1 << 20;
788 
789     fzl::VmoMapper mapping;
790     zx::vmo vmo;
791     if ((status = mapping.CreateAndMap(vmo_size, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
792                                        nullptr, &vmo)) != ZX_OK) {
793         ERROR("Failed to create stream VMO\n");
794         return ZX_ERR_NO_MEMORY;
795     }
796 
797     // Now that all partitions are preallocated, begin streaming data to them.
798     for (size_t p = 0; p < parts.size(); p++) {
799         vmoid_t vmoid;
800         block_client::Client client;
801         zx_status_t status = RegisterFastBlockIo(parts[p].new_part, vmo, &vmoid, &client);
802         if (status != ZX_OK) {
803             ERROR("Failed to register fast block IO\n");
804             return status;
805         }
806 
807         block_info_t binfo;
808         if ((ioctl_block_get_info(parts[p].new_part.get(), &binfo)) < 0) {
809             ERROR("Couldn't get partition block info\n");
810             return ZX_ERR_IO;
811         }
812         size_t block_size = binfo.block_size;
813 
814         block_fifo_request_t request;
815         request.group = 0;
816         request.vmoid = vmoid;
817         request.opcode = BLOCKIO_WRITE;
818 
819         LOG("Streaming partition %zu\n", p);
820         status = StreamFvmPartition(reader.get(), &parts[p], mapping, client, block_size,
821                                     &request);
822         LOG("Done streaming partition %zu\n", p);
823         if (status != ZX_OK) {
824             ERROR("Failed to stream partition\n");
825             return status;
826         }
827         if ((status = FlushClient(client)) != ZX_OK) {
828             ERROR("Failed to flush client\n");
829             return status;
830         }
831         LOG("Done flushing partition %zu\n", p);
832     }
833 
834     for (size_t p = 0; p < parts.size(); p++) {
835         // Upgrade the old partition (currently active) to the new partition (currently
836         // inactive) so the new partition persists.
837         upgrade_req_t upgrade;
838         memset(&upgrade, 0, sizeof(upgrade));
839         if (ioctl_block_get_partition_guid(parts[p].new_part.get(), &upgrade.new_guid,
840                                            GUID_LEN) < 0) {
841             ERROR("Failed to get unique GUID of new partition\n");
842             return ZX_ERR_BAD_STATE;
843         }
844 
845         if (ioctl_block_fvm_upgrade(fvm_fd.get(), &upgrade) < 0) {
846             ERROR("Failed to upgrade partition\n");
847             return ZX_ERR_IO;
848         }
849     }
850 
851     return ZX_OK;
852 }
853 
854 } // namespace
855 
PartitionPave(fbl::unique_ptr<DevicePartitioner> partitioner,fbl::unique_fd payload_fd,Partition partition_type,Arch arch)856 zx_status_t PartitionPave(fbl::unique_ptr<DevicePartitioner> partitioner,
857                           fbl::unique_fd payload_fd, Partition partition_type, Arch arch) {
858     LOG("Paving partition.\n");
859 
860     zx_status_t status;
861     fbl::unique_fd partition_fd;
862     if ((status = partitioner->FindPartition(partition_type, &partition_fd)) != ZX_OK) {
863         if (status != ZX_ERR_NOT_FOUND) {
864             ERROR("Failure looking for partition: %s\n", zx_status_get_string(status));
865             return status;
866         }
867         if ((status = partitioner->AddPartition(partition_type, &partition_fd)) != ZX_OK) {
868             ERROR("Failure creating partition: %s\n", zx_status_get_string(status));
869             return status;
870         }
871     } else {
872         LOG("Partition already exists\n");
873     }
874 
875     if (partition_type == Partition::kFuchsiaVolumeManager) {
876         if (partitioner->UseSkipBlockInterface()) {
877             LOG("Attempting to format FTL...\n");
878             status = partitioner->WipePartitions();
879             if (status != ZX_OK) {
880                 ERROR("Failed to format FTL: %s\n", zx_status_get_string(status));
881             } else {
882                 LOG("Formatted successfully!\n");
883             }
884         }
885         LOG("Streaming partitions...\n");
886         if ((status = FvmStreamPartitions(std::move(partition_fd), std::move(payload_fd))) != ZX_OK) {
887             ERROR("Failed to stream partitions: %s\n", zx_status_get_string(status));
888             return status;
889         }
890         LOG("Completed successfully\n");
891         return ZX_OK;
892     }
893 
894     uint32_t block_size_bytes;
895     if ((status = partitioner->GetBlockSize(partition_fd, &block_size_bytes)) != ZX_OK) {
896         ERROR("Couldn't get partition block size\n");
897         return status;
898     }
899 
900     const size_t vmo_sz = fbl::round_up(1LU << 20, block_size_bytes);
901     fzl::ResizeableVmoMapper mapper;
902     if ((status = mapper.CreateAndMap(vmo_sz, "partition-pave")) != ZX_OK) {
903         ERROR("Failed to create stream VMO\n");
904         return status;
905     }
906     // The streamed partition size may not line up with the mapped vmo size.
907     size_t payload_size = 0;
908     if ((status = StreamPayloadToVmo(mapper, payload_fd, block_size_bytes,
909                                      &payload_size)) != ZX_OK) {
910         ERROR("Failed to stream partition to VMO\n");
911         return status;
912     }
913     if ((status = ValidateKernelPayload(mapper, payload_size, partition_type, arch)) != ZX_OK) {
914         ERROR("Failed to validate partition\n");
915         return status;
916     }
917     if (partitioner->UseSkipBlockInterface()) {
918         fzl::FdioCaller caller(std::move(partition_fd));
919         status = WriteVmoToSkipBlock(mapper.vmo(), payload_size, caller, block_size_bytes);
920         partition_fd = caller.release();
921     } else {
922         status = WriteVmoToBlock(mapper.vmo(), payload_size, partition_fd, block_size_bytes);
923     }
924     if (status != ZX_OK) {
925         ERROR("Failed to write partition to block\n");
926         return status;
927     }
928 
929     if ((status = partitioner->FinalizePartition(partition_type)) != ZX_OK) {
930         ERROR("Failed to finalize partition\n");
931         return status;
932     }
933 
934     LOG("Completed successfully\n");
935     return ZX_OK;
936 }
937 
Drain(fbl::unique_fd fd)938 void Drain(fbl::unique_fd fd) {
939     char buf[8192];
940     while (read(fd.get(), &buf, sizeof(buf)) > 0)
941         continue;
942 }
943 
RealMain(Flags flags)944 zx_status_t RealMain(Flags flags) {
945     auto device_partitioner = DevicePartitioner::Create();
946     if (!device_partitioner) {
947         ERROR("Unable to initialize a partitioner.");
948         return ZX_ERR_BAD_STATE;
949     }
950     const bool is_cros_device = device_partitioner->IsCros();
951 
952     switch (flags.cmd) {
953     case Command::kWipe:
954         return device_partitioner->WipePartitions();
955     case Command::kInstallFvm:
956     case Command::kInstallVbMetaA:
957     case Command::kInstallVbMetaB:
958        break;
959     case Command::kInstallBootloader:
960         if (flags.arch == Arch::X64 && !flags.force) {
961             LOG("SKIPPING BOOTLOADER install on x64 device, pass --force if desired.\n");
962             Drain(std::move(flags.payload_fd));
963             return ZX_OK;
964         }
965         break;
966     case Command::kInstallEfi:
967         if ((is_cros_device || flags.arch == Arch::ARM64) && !flags.force) {
968             LOG("SKIPPING EFI install on ARM64/CROS device, pass --force if desired.\n");
969             Drain(std::move(flags.payload_fd));
970             return ZX_OK;
971         }
972         break;
973     case Command::kInstallKernc:
974         if (!is_cros_device && !flags.force) {
975             LOG("SKIPPING KERNC install on non-CROS device, pass --force if desired.\n");
976             Drain(std::move(flags.payload_fd));
977             return ZX_OK;
978         }
979         break;
980     case Command::kInstallZirconA:
981     case Command::kInstallZirconB:
982     case Command::kInstallZirconR:
983         if (is_cros_device && !flags.force) {
984             LOG("SKIPPING Zircon-{A/B/R} install on CROS device, pass --force if desired.\n");
985             Drain(std::move(flags.payload_fd));
986             return ZX_OK;
987         }
988         break;
989     case Command::kInstallDataFile:
990         return DataFilePave(std::move(device_partitioner), std::move(flags.payload_fd), flags.path);
991 
992     default:
993         ERROR("Unsupported command.");
994         return ZX_ERR_NOT_SUPPORTED;
995     }
996     return PartitionPave(std::move(device_partitioner), std::move(flags.payload_fd),
997                          PartitionType(flags.cmd), flags.arch);
998 }
999 
DataFilePave(fbl::unique_ptr<DevicePartitioner> partitioner,fbl::unique_fd payload_fd,char * data_path)1000 zx_status_t DataFilePave(fbl::unique_ptr<DevicePartitioner> partitioner,
1001                          fbl::unique_fd payload_fd, char* data_path) {
1002 
1003     const char* mount_path = "/volume/data";
1004     const uint8_t data_guid[] = GUID_DATA_VALUE;
1005     char minfs_path[PATH_MAX] = {0};
1006     char path[PATH_MAX] = {0};
1007     zx_status_t status = ZX_OK;
1008 
1009     fbl::unique_fd part_fd(open_partition(nullptr, data_guid, ZX_SEC(1), path));
1010     if (!part_fd) {
1011         ERROR("DATA partition not found in FVM\n");
1012         Drain(std::move(payload_fd));
1013         return ZX_ERR_NOT_FOUND;
1014     }
1015 
1016     switch (detect_disk_format(part_fd.get())) {
1017     case DISK_FORMAT_MINFS:
1018         // If the disk we found is actually minfs, we can just use the block
1019         // device path we were given by open_partition.
1020         strncpy(minfs_path, path, PATH_MAX);
1021         break;
1022 
1023     case DISK_FORMAT_ZXCRYPT:
1024         // Compute the topological path of the FVM block driver, and then tack
1025         // the zxcrypt-device string onto the end. This should be improved.
1026         ioctl_device_get_topo_path(part_fd.get(), path, sizeof(path));
1027         snprintf(minfs_path, sizeof(minfs_path), "%s/zxcrypt/block", path);
1028 
1029         // TODO(security): ZX-1130. We need to bind with channel in order to
1030         // pass a key here. Where does the key come from? We need to determine
1031         // if this is unattended.
1032         ioctl_device_bind(part_fd.get(), ZXCRYPT_DRIVER_LIB, strlen(ZXCRYPT_DRIVER_LIB));
1033 
1034         if ((status = wait_for_device(minfs_path, ZX_SEC(5))) != ZX_OK) {
1035             ERROR("zxcrypt bind error: %s\n", zx_status_get_string(status));
1036             return status;
1037         }
1038 
1039         break;
1040 
1041     default:
1042         ERROR("unsupported disk format at %s\n", path);
1043         return ZX_ERR_NOT_SUPPORTED;
1044     }
1045 
1046     mount_options_t opts(default_mount_options);
1047     opts.create_mountpoint = true;
1048     if ((status = mount(open(minfs_path, O_RDWR), mount_path, DISK_FORMAT_MINFS,
1049                         &opts, launch_logs_async)) != ZX_OK) {
1050         ERROR("mount error: %s\n", zx_status_get_string(status));
1051         Drain(std::move(payload_fd));
1052         return status;
1053     }
1054 
1055     // mkdir any intermediate directories between mount_path and basename(data_path)
1056     snprintf(path, sizeof(path), "%s/%s", mount_path, data_path);
1057     size_t cur = strlen(mount_path);
1058     size_t max = strlen(path) - strlen(basename(path));
1059     // note: the call to basename above modifies path, so it needs reconstruction.
1060     snprintf(path, sizeof(path), "%s/%s", mount_path, data_path);
1061     while (cur < max) {
1062         ++cur;
1063         if (path[cur] == '/') {
1064             path[cur] = 0;
1065             // errors ignored, let the open() handle that later.
1066             mkdir(path, 0700);
1067             path[cur] = '/';
1068         }
1069     }
1070 
1071     // We append here, because the primary use case here is to send SSH keys
1072     // which can be appended, but we may want to revisit this choice for other
1073     // files in the future.
1074     {
1075         char buf[8192];
1076         ssize_t n;
1077         fbl::unique_fd kfd(open(path, O_CREAT | O_WRONLY | O_APPEND, 0600));
1078         if (!kfd) {
1079             umount(mount_path);
1080             ERROR("open %s error: %s\n", data_path, strerror(errno));
1081             Drain(std::move(payload_fd));
1082             return ZX_ERR_IO;
1083         }
1084         while ((n = read(payload_fd.get(), &buf, sizeof(buf))) > 0) {
1085             if (write(kfd.get(), &buf, n) != n) {
1086                 umount(mount_path);
1087                 ERROR("write %s error: %s\n", data_path, strerror(errno));
1088                 Drain(std::move(payload_fd));
1089                 return ZX_ERR_IO;
1090             }
1091         }
1092         fsync(kfd.get());
1093     }
1094 
1095     if ((status = umount(mount_path)) != ZX_OK) {
1096         ERROR("unmount %s failed: %s\n", mount_path,
1097             zx_status_get_string(status));
1098         return status;
1099     }
1100 
1101     LOG("Wrote %s\n", data_path);
1102     return ZX_OK;
1103 }
1104 
1105 } //  namespace paver
1106