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