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 "fvm-host/fvm-info.h"
6 #include "fvm-host/sparse-paver.h"
7 
Create(const char * path,size_t slice_size,size_t disk_offset,size_t disk_size,fbl::unique_ptr<SparsePaver> * out)8 zx_status_t SparsePaver::Create(const char* path, size_t slice_size, size_t disk_offset,
9                                 size_t disk_size, fbl::unique_ptr<SparsePaver>* out) {
10     fbl::unique_ptr<SparsePaver> paver(new SparsePaver(disk_offset, disk_size));
11 
12     zx_status_t status = paver->Init(path, slice_size);
13 
14     if (status != ZX_OK) {
15         return status;
16     }
17 
18     *out = std::move(paver);
19     return status;
20 }
21 
AddPartition(const SparsePartitionInfo * partition,fvm::SparseReader * reader)22 zx_status_t SparsePaver::AddPartition(const SparsePartitionInfo* partition,
23                                       fvm::SparseReader* reader) {
24     info_.CheckValid();
25 
26     // Assign random guid.
27     uint8_t guid[FVM_GUID_LEN];
28     static unsigned int seed = time(0);
29     for (size_t i = 0; i < FVM_GUID_LEN; i++) {
30         guid[i] = rand_r(&seed);
31     }
32 
33     uint32_t vpart_index;
34     const fvm::partition_descriptor_t* descriptor = &partition->descriptor;
35     zx_status_t status = info_.AllocatePartition(descriptor, guid, &vpart_index);
36     if (status != ZX_OK) {
37         return status;
38     }
39 
40     // Allocate all slices for this partition.
41     for (uint32_t i = 0; i < descriptor->extent_count; i++) {
42         status = AddExtent(vpart_index, &partition->extents[i], reader);
43         if (status != ZX_OK) {
44             return status;
45         }
46     }
47 
48     return ZX_OK;
49 }
50 
Commit()51 zx_status_t SparsePaver::Commit() {
52     info_.CheckValid();
53 
54     if (disk_ptr_ > disk_offset_ + disk_size_) {
55         fprintf(stderr, "FVM metadata size exceeds disk size\n");
56         return ZX_ERR_INTERNAL;
57     }
58 
59     zx_status_t status = info_.Write(fd_, disk_offset_, disk_size_);
60 
61     if (status != ZX_OK) {
62         return status;
63     }
64 
65     // Move pointer to the end of the designated partition size to prevent any further edits.
66     disk_ptr_ = disk_offset_ + disk_size_ + 1;
67     return ZX_OK;
68 }
69 
Init(const char * path,size_t slice_size)70 zx_status_t SparsePaver::Init(const char* path, size_t slice_size) {
71     fd_.reset(open(path, O_WRONLY, 0644));
72     if (!fd_) {
73         return ZX_ERR_IO;
74     }
75 
76     zx_status_t status = info_.Reset(disk_size_, slice_size);
77     if (status != ZX_OK) {
78         return status;
79     }
80 
81     disk_ptr_ = disk_offset_ + info_.MetadataSize() * 2;
82     if (disk_ptr_ >= disk_offset_ + disk_size_) {
83         fprintf(stderr, "FVM metadata size exceeds disk size\n");
84         return ZX_ERR_INTERNAL;
85     }
86 
87     if (lseek(fd_.get(), disk_ptr_, SEEK_SET) != disk_ptr_) {
88         return ZX_ERR_IO;
89     }
90 
91     data_.reset(new uint8_t[info_.SliceSize()]);
92     return ZX_OK;
93 }
94 
AddExtent(uint32_t vpart_index,fvm::extent_descriptor_t * extent,fvm::SparseReader * reader)95 zx_status_t SparsePaver::AddExtent(uint32_t vpart_index, fvm::extent_descriptor_t* extent,
96                                    fvm::SparseReader* reader) {
97     uint32_t pslice_start = 0;
98     uint32_t pslice_total = 0;
99 
100     size_t bytes_left = extent->extent_length;
101     uint32_t vslice = extent->slice_start;
102 
103     for (unsigned i = 0; i < extent->slice_count; i++) {
104         uint32_t pslice;
105 
106         zx_status_t status = info_.AllocateSlice(vpart_index, vslice + i, &pslice);
107         if (status != ZX_OK) {
108             return status;
109         }
110 
111         if (!pslice_start) {
112             pslice_start = pslice;
113         }
114 
115         // On a new FVM container, pslice allocation is expected to be contiguous.
116         if (pslice != pslice_start + pslice_total) {
117             fprintf(stderr,
118                     "fvm: pslice allocation unexpectedly non-contiguous (internal error)\n");
119             return ZX_ERR_INTERNAL;
120         }
121 
122         if ((status = WriteSlice(&bytes_left, reader)) != ZX_OK) {
123             return status;
124         }
125 
126         pslice_total++;
127     }
128 
129     return ZX_OK;
130 }
131 
WriteSlice(size_t * bytes_left,fvm::SparseReader * reader)132 zx_status_t SparsePaver::WriteSlice(size_t* bytes_left, fvm::SparseReader* reader) {
133     info_.CheckValid();
134     const size_t slice_size = info_.SliceSize();
135 
136     if (disk_ptr_ + slice_size > disk_offset_ + disk_size_) {
137         fprintf(stderr, "Partition data exceeds the provided disk size\n");
138         return ZX_ERR_INVALID_ARGS;
139     }
140 
141     size_t read_length = fbl::min(slice_size, *bytes_left);
142 
143     if (read_length > 0) {
144         size_t bytes_read = 0;
145         zx_status_t status = reader->ReadData(data_.get(), read_length, &bytes_read);
146         if (status != ZX_OK) {
147             return status;
148         }
149 
150         if (bytes_read < read_length) {
151             fprintf(stderr, "Slice data is less than expected\n");
152             return ZX_ERR_IO;
153         }
154 
155         if (read_length < slice_size) {
156             memset(&data_[read_length], 0, slice_size - read_length);
157         }
158 
159         *bytes_left -= bytes_read;
160     } else {
161         memset(data_.get(), 0, slice_size);
162     }
163 
164     ssize_t result = write(fd_.get(), data_.get(), slice_size);
165     if (result != slice_size) {
166         return ZX_ERR_IO;
167     }
168 
169     disk_ptr_ += slice_size;
170     return ZX_OK;
171 }
172