1 // Copyright 2017 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 <inttypes.h>
6 #include <utility>
7
8 #include "fvm-host/container.h"
9
10 constexpr size_t kLz4HeaderSize = 15;
11
12 static LZ4F_preferences_t lz4_prefs = {
13 .frameInfo = {
14 .blockSizeID = LZ4F_max64KB,
15 .blockMode = LZ4F_blockIndependent,
16 },
17 .compressionLevel = 0,
18 };
19
Setup(size_t max_len)20 zx_status_t CompressionContext::Setup(size_t max_len) {
21 LZ4F_errorCode_t errc = LZ4F_createCompressionContext(&cctx_, LZ4F_VERSION);
22 if (LZ4F_isError(errc)) {
23 fprintf(stderr, "Could not create compression context: %s\n", LZ4F_getErrorName(errc));
24 return ZX_ERR_INTERNAL;
25 }
26
27 Reset(kLz4HeaderSize + LZ4F_compressBound(max_len, &lz4_prefs));
28
29 size_t r = LZ4F_compressBegin(cctx_, GetBuffer(), GetRemaining(), &lz4_prefs);
30 if (LZ4F_isError(r)) {
31 fprintf(stderr, "Could not begin compression: %s\n", LZ4F_getErrorName(r));
32 return ZX_ERR_INTERNAL;
33 }
34
35 IncreaseOffset(r);
36 return ZX_OK;
37 }
38
Compress(const void * data,size_t length)39 zx_status_t CompressionContext::Compress(const void* data, size_t length) {
40 size_t r = LZ4F_compressUpdate(cctx_, GetBuffer(), GetRemaining(), data, length, NULL);
41 if (LZ4F_isError(r)) {
42 fprintf(stderr, "Could not compress data: %s\n", LZ4F_getErrorName(r));
43 return ZX_ERR_INTERNAL;
44 }
45
46 IncreaseOffset(r);
47 return ZX_OK;
48 }
49
Finish()50 zx_status_t CompressionContext::Finish() {
51 size_t r = LZ4F_compressEnd(cctx_, GetBuffer(), GetRemaining(), NULL);
52 if (LZ4F_isError(r)) {
53 fprintf(stderr, "Could not finish compression: %s\n", LZ4F_getErrorName(r));
54 return ZX_ERR_INTERNAL;
55 }
56
57 IncreaseOffset(r);
58 LZ4F_errorCode_t errc = LZ4F_freeCompressionContext(cctx_);
59 if (LZ4F_isError(errc)) {
60 fprintf(stderr, "Could not free compression context: %s\n", LZ4F_getErrorName(errc));
61 return ZX_ERR_INTERNAL;
62 }
63
64 return ZX_OK;
65 }
66
Create(const char * path,size_t slice_size,uint32_t flags,fbl::unique_ptr<SparseContainer> * out)67 zx_status_t SparseContainer::Create(const char* path, size_t slice_size, uint32_t flags,
68 fbl::unique_ptr<SparseContainer>* out) {
69 fbl::unique_ptr<SparseContainer> sparseContainer(new SparseContainer(path, slice_size,
70 flags));
71 zx_status_t status;
72 if ((status = sparseContainer->Init()) != ZX_OK) {
73 return status;
74 }
75
76 *out = std::move(sparseContainer);
77 return ZX_OK;
78 }
79
SparseContainer(const char * path,uint64_t slice_size,uint32_t flags)80 SparseContainer::SparseContainer(const char* path, uint64_t slice_size, uint32_t flags)
81 : Container(path, slice_size, flags), valid_(false), disk_size_(0),
82 extent_size_(0) {
83 fd_.reset(open(path, O_CREAT | O_RDWR, 0666));
84
85 if (!fd_) {
86 fprintf(stderr, "Failed to open sparse data path\n");
87 return;
88 }
89
90 struct stat s;
91 if (fstat(fd_.get(), &s) < 0) {
92 fprintf(stderr, "Failed to stat %s\n", path);
93 return;
94 }
95
96 if (s.st_size > 0) {
97 disk_size_ = s.st_size;
98
99 fbl::unique_fd dup_fd(dup(fd_.get()));
100 if (fvm::SparseReader::CreateSilent(std::move(dup_fd), &reader_) != ZX_OK) {
101 fprintf(stderr, "SparseContainer: Failed to read metadata from sparse file\n");
102 return;
103 }
104
105 memcpy(&image_, reader_->Image(), sizeof(fvm::sparse_image_t));
106 slice_size_ = image_.slice_size;
107 extent_size_ = disk_size_ - image_.header_length;
108
109 uintptr_t partition_ptr = reinterpret_cast<uintptr_t>(reader_->Partitions());
110
111 for (unsigned i = 0; i < image_.partition_count; i++) {
112 SparsePartitionInfo partition;
113 memcpy(&partition.descriptor, reinterpret_cast<void*>(partition_ptr),
114 sizeof(fvm::partition_descriptor_t));
115 partitions_.push_back(std::move(partition));
116 partition_ptr += sizeof(fvm::partition_descriptor_t);
117
118 for (size_t j = 0; j < partitions_[i].descriptor.extent_count; j++) {
119 fvm::extent_descriptor_t extent;
120 memcpy(&extent, reinterpret_cast<void*>(partition_ptr),
121 sizeof(fvm::extent_descriptor_t));
122 partitions_[i].extents.push_back(extent);
123 partition_ptr += sizeof(fvm::extent_descriptor_t);
124 }
125 }
126
127 valid_ = true;
128 xprintf("Successfully read from existing sparse data container.\n");
129 }
130 }
131
132 SparseContainer::~SparseContainer() = default;
133
Init()134 zx_status_t SparseContainer::Init() {
135 if (slice_size_ == 0) {
136 fprintf(stderr, "Cannot initialize sparse container with no slice size\n");
137 return ZX_ERR_BAD_STATE;
138 }
139
140 image_.magic = fvm::kSparseFormatMagic;
141 image_.version = fvm::kSparseFormatVersion;
142 image_.slice_size = slice_size_;
143 image_.partition_count = 0;
144 image_.header_length = sizeof(fvm::sparse_image_t);
145 image_.flags = flags_;
146 partitions_.reset();
147 dirty_ = true;
148 valid_ = true;
149 extent_size_ = 0;
150 xprintf("Initialized new sparse data container.\n");
151 return ZX_OK;
152 }
153
Verify() const154 zx_status_t SparseContainer::Verify() const {
155 CheckValid();
156
157 if (image_.flags & fvm::kSparseFlagLz4) {
158 // Decompression must occur before verification, since all contents must be available for
159 // fsck.
160 fprintf(stderr, "SparseContainer: Found compressed container; contents cannot be"
161 " verified\n");
162 return ZX_ERR_INVALID_ARGS;
163 }
164
165 if (image_.magic != fvm::kSparseFormatMagic) {
166 fprintf(stderr, "SparseContainer: Bad magic\n");
167 return ZX_ERR_IO;
168 }
169
170 xprintf("Slice size is %" PRIu64 "\n", image_.slice_size);
171 xprintf("Found %" PRIu64 " partitions\n", image_.partition_count);
172
173 off_t start = 0;
174 off_t end = image_.header_length;
175 for (unsigned i = 0; i < image_.partition_count; i++) {
176 fbl::Vector<size_t> extent_lengths;
177 start = end;
178 xprintf("Found partition %u with %u extents\n", i,
179 partitions_[i].descriptor.extent_count);
180
181 for (unsigned j = 0; j < partitions_[i].descriptor.extent_count; j++) {
182 extent_lengths.push_back(partitions_[i].extents[j].extent_length);
183 end += partitions_[i].extents[j].extent_length;
184 }
185
186 zx_status_t status;
187 disk_format_t part;
188 if ((status = Format::Detect(fd_.get(), start, &part)) != ZX_OK) {
189 return status;
190 }
191
192 fbl::unique_fd dupfd(dup(fd_.get()));
193 if (!dupfd) {
194 fprintf(stderr, "Failed to duplicate fd\n");
195 return ZX_ERR_INTERNAL;
196 }
197
198 if ((status = Format::Check(std::move(dupfd), start, end, extent_lengths, part)) != ZX_OK) {
199 const char* name = reinterpret_cast<const char*>(partitions_[i].descriptor.name);
200 fprintf(stderr, "%s fsck returned an error.\n", name);
201 return status;
202 }
203 }
204
205 if (end != disk_size_) {
206 fprintf(stderr, "Header + extent sizes (%" PRIu64 ") do not match sparse file size "
207 "(%zu)\n", end, disk_size_);
208 return ZX_ERR_IO_DATA_INTEGRITY;
209 }
210
211 return ZX_OK;
212 }
213
CheckDiskSize(uint64_t target_disk_size) const214 zx_status_t SparseContainer::CheckDiskSize(uint64_t target_disk_size) const {
215 CheckValid();
216
217 size_t usable_slices = fvm::UsableSlicesCount(target_disk_size, image_.slice_size);
218 size_t required_slices = SliceCount();
219
220 if (usable_slices < required_slices) {
221 return ZX_ERR_OUT_OF_RANGE;
222 }
223
224 uint64_t required_disk_size = fvm::SlicesStart(target_disk_size, image_.slice_size)
225 + (required_slices * image_.slice_size);
226 if (target_disk_size < required_disk_size) {
227 return ZX_ERR_OUT_OF_RANGE;
228 }
229
230 return ZX_OK;
231 }
232
CalculateDiskSize() const233 uint64_t SparseContainer::CalculateDiskSize() const {
234 CheckValid();
235 return CalculateDiskSizeForSlices(SliceCount());
236 }
237
Commit()238 zx_status_t SparseContainer::Commit() {
239 if (!dirty_ || image_.partition_count == 0) {
240 fprintf(stderr, "Commit: Nothing to write.\n");
241 return ZX_OK;
242 }
243
244 // Reset file length to 0
245 if (ftruncate(fd_.get(), 0) != 0) {
246 fprintf(stderr, "Failed to truncate fvm container");
247 return ZX_ERR_IO;
248 }
249
250 // Recalculate and verify header length
251 uint64_t header_length = 0;
252
253 if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
254 fprintf(stderr, "Seek reset failed\n");
255 return ZX_ERR_IO;
256 }
257
258 header_length += sizeof(fvm::sparse_image_t);
259 if (write(fd_.get(), &image_, sizeof(fvm::sparse_image_t)) != sizeof(fvm::sparse_image_t)) {
260 fprintf(stderr, "Write sparse image header failed\n");
261 return ZX_ERR_IO;
262 }
263
264 for (unsigned i = 0; i < image_.partition_count; i++) {
265 fvm::partition_descriptor_t partition = partitions_[i].descriptor;
266
267 header_length += sizeof(fvm::partition_descriptor_t);
268 if (write(fd_.get(), &partition, sizeof(fvm::partition_descriptor_t))
269 != sizeof(fvm::partition_descriptor_t)) {
270 fprintf(stderr, "Write partition failed\n");
271 return ZX_ERR_IO;
272 }
273
274 for (unsigned j = 0; j < partition.extent_count; j++) {
275 fvm::extent_descriptor_t extent = partitions_[i].extents[j];
276 header_length += sizeof(fvm::extent_descriptor_t);
277 if (write(fd_.get(), &extent, sizeof(fvm::extent_descriptor_t))
278 != sizeof(fvm::extent_descriptor_t)) {
279 fprintf(stderr, "Write extent failed\n");
280 return ZX_ERR_IO;
281 }
282 }
283 }
284
285 if (header_length != image_.header_length) {
286 fprintf(stderr, "Header length does not match!\n");
287 return ZX_ERR_INTERNAL;
288 }
289
290 zx_status_t status;
291 if ((status = PrepareWrite(extent_size_)) != ZX_OK) {
292 return status;
293 }
294
295 // Write each partition out to sparse file
296 for (unsigned i = 0; i < image_.partition_count; i++) {
297 fvm::partition_descriptor_t partition = partitions_[i].descriptor;
298 Format* format = partitions_[i].format.get();
299
300 vslice_info_t vslice_info;
301 // Write out each extent in the partition
302 for (unsigned j = 0; j < partition.extent_count; j++) {
303 if (format->GetVsliceRange(j, &vslice_info) != ZX_OK) {
304 fprintf(stderr, "Unable to access partition extent\n");
305 return ZX_ERR_OUT_OF_RANGE;
306 }
307
308 // Write out each block in the extent
309 for (unsigned k = 0; k < vslice_info.block_count; k++) {
310 if (format->FillBlock(vslice_info.block_offset + k) != ZX_OK) {
311 fprintf(stderr, "Failed to read block\n");
312 return ZX_ERR_IO;
313 }
314
315 if (WriteData(format->Data(), format->BlockSize()) != ZX_OK) {
316 fprintf(stderr, "Failed to write data to sparse file\n");
317 return ZX_ERR_IO;
318 }
319 }
320 }
321 }
322
323 if ((status = CompleteWrite()) != ZX_OK) {
324 return status;
325 }
326
327 struct stat s;
328 if (fstat(fd_.get(), &s) < 0) {
329 fprintf(stderr, "Failed to stat container\n");
330 return ZX_ERR_IO;
331 }
332
333 disk_size_ = s.st_size;
334 xprintf("Successfully wrote sparse data to disk.\n");
335 return ZX_OK;
336 }
337
Pave(const char * path,size_t disk_offset,size_t disk_size)338 zx_status_t SparseContainer::Pave(const char* path, size_t disk_offset, size_t disk_size) {
339 if (disk_size == 0) {
340 // If target disk does not already exist, create it.
341 if (disk_offset > 0) {
342 fprintf(stderr, "Cannot specify offset without length\n");
343 return ZX_ERR_INVALID_ARGS;
344 }
345
346 fbl::unique_fd fd(open(path, O_CREAT | O_EXCL | O_WRONLY, 0644));
347
348 if (!fd) {
349 return ZX_ERR_IO;
350 }
351
352 disk_size = CalculateDiskSize();
353
354 if (ftruncate(fd.get(), disk_size) < 0) {
355 return ZX_ERR_IO;
356 }
357 }
358
359 fbl::unique_ptr<SparsePaver> paver;
360 zx_status_t status = SparsePaver::Create(path, slice_size_, disk_offset, disk_size, &paver);
361 if (status != ZX_OK) {
362 return status;
363 }
364
365 for (uint32_t i = 0; i < image_.partition_count; i++) {
366 if ((partitions_[i].descriptor.flags & fvm::kSparseFlagZxcrypt) != 0) {
367 //TODO(planders): Remove this error when we can create zxcrypt'd FVMs on host.
368 printf("SparseContainer::Pave: zxcrypt not yet implemented for host-side FVM\n");
369 return ZX_ERR_NOT_SUPPORTED;
370 }
371
372 if ((status = paver->AddPartition(&partitions_[i], reader_.get())) != ZX_OK) {
373 return status;
374 }
375 }
376
377 return paver->Commit();
378 }
379
SliceSize() const380 size_t SparseContainer::SliceSize() const {
381 return image_.slice_size;
382 }
383
SliceCount() const384 size_t SparseContainer::SliceCount() const {
385 CheckValid();
386 size_t slices = 0;
387
388 for (unsigned i = 0; i < image_.partition_count; i++) {
389 if ((partitions_[i].descriptor.flags & fvm::kSparseFlagZxcrypt) != 0) {
390 slices += kZxcryptExtraSlices;
391 }
392
393 for (unsigned j = 0; j < partitions_[i].descriptor.extent_count; j++) {
394 slices += partitions_[i].extents[j].slice_count;
395 }
396 }
397
398 return slices;
399 }
400
AddPartition(const char * path,const char * type_name)401 zx_status_t SparseContainer::AddPartition(const char* path, const char* type_name) {
402 fbl::unique_ptr<Format> format;
403 zx_status_t status;
404
405 if ((status = Format::Create(path, type_name, &format)) != ZX_OK) {
406 fprintf(stderr, "Failed to initialize partition\n");
407 return status;
408 }
409
410 if ((status = AllocatePartition(std::move(format))) != ZX_OK) {
411 fprintf(stderr, "Sparse partition allocation failed\n");
412 return status;
413 }
414
415 return ZX_OK;
416 }
417
Decompress(const char * path)418 zx_status_t SparseContainer::Decompress(const char* path) {
419 if ((flags_ & fvm::kSparseFlagLz4) == 0) {
420 fprintf(stderr, "Cannot decompress un-compressed sparse file\n");
421 return ZX_ERR_NOT_SUPPORTED;
422 }
423
424 fbl::unique_fd fd;
425
426 fd.reset(open(path, O_WRONLY | O_CREAT | O_EXCL, 0644));
427 if (!fd) {
428 fprintf(stderr, "could not open %s: %s\n", path, strerror(errno));
429 return ZX_ERR_IO;
430 }
431
432 return reader_->WriteDecompressed(std::move(fd));
433 }
434
AllocatePartition(fbl::unique_ptr<Format> format)435 zx_status_t SparseContainer::AllocatePartition(fbl::unique_ptr<Format> format) {
436 SparsePartitionInfo partition;
437 format->GetPartitionInfo(&partition.descriptor);
438 partition.descriptor.magic = fvm::kPartitionDescriptorMagic;
439 partition.descriptor.extent_count = 0;
440 image_.header_length += sizeof(fvm::partition_descriptor_t);
441 uint32_t part_index = image_.partition_count;
442
443 zx_status_t status;
444 if ((status = format->MakeFvmReady(SliceSize(), part_index)) != ZX_OK) {
445 return status;
446 }
447
448 partitions_.push_back(std::move(partition));
449
450 if (++image_.partition_count != partitions_.size()) {
451 fprintf(stderr, "Unexpected number of partitions\n");
452 return ZX_ERR_INTERNAL;
453 }
454
455 vslice_info_t vslice_info;
456 unsigned i = 0;
457 while ((status = format->GetVsliceRange(i++, &vslice_info)) == ZX_OK) {
458 if ((status = AllocateExtent(part_index,
459 vslice_info.vslice_start / format->BlocksPerSlice(),
460 vslice_info.slice_count,
461 vslice_info.block_count * format->BlockSize())) != ZX_OK) {
462 return status;
463 }
464 }
465
466 // This is expected if we have read all the other slices.
467 if (status != ZX_ERR_OUT_OF_RANGE) {
468 return status;
469 }
470
471 partitions_[part_index].format = std::move(format);
472 return ZX_OK;
473 }
474
AllocateExtent(uint32_t part_index,uint64_t slice_start,uint64_t slice_count,uint64_t extent_length)475 zx_status_t SparseContainer::AllocateExtent(uint32_t part_index, uint64_t slice_start,
476 uint64_t slice_count, uint64_t extent_length) {
477 if (part_index >= image_.partition_count) {
478 fprintf(stderr, "Partition is not yet allocated\n");
479 return ZX_ERR_OUT_OF_RANGE;
480 }
481
482 SparsePartitionInfo* partition = &partitions_[part_index];
483 fvm::extent_descriptor_t extent;
484 extent.magic = fvm::kExtentDescriptorMagic;
485 extent.slice_start = slice_start;
486 extent.slice_count = slice_count;
487 extent.extent_length = extent_length;
488 partition->extents.push_back(extent);
489
490 if (partition->extents.size() != ++partition->descriptor.extent_count) {
491 fprintf(stderr, "Unexpected number of extents\n");
492 return ZX_ERR_INTERNAL;
493 }
494
495 image_.header_length += sizeof(fvm::extent_descriptor_t);
496 extent_size_ += extent_length;
497 dirty_ = true;
498 return ZX_OK;
499 }
500
PrepareWrite(size_t max_len)501 zx_status_t SparseContainer::PrepareWrite(size_t max_len) {
502 if ((flags_ & fvm::kSparseFlagLz4) == 0) {
503 return ZX_OK;
504 }
505
506 return compression_.Setup(max_len);
507 }
508
WriteData(const void * data,size_t length)509 zx_status_t SparseContainer::WriteData(const void* data, size_t length) {
510 if ((flags_ & fvm::kSparseFlagLz4) != 0) {
511 return compression_.Compress(data, length);
512 } else if (write(fd_.get(), data, length) != length) {
513 return ZX_ERR_IO;
514 }
515
516 return ZX_OK;
517 }
518
CompleteWrite()519 zx_status_t SparseContainer::CompleteWrite() {
520 if ((flags_ & fvm::kSparseFlagLz4) == 0) {
521 return ZX_OK;
522 }
523
524 zx_status_t status = compression_.Finish();
525
526 if (status != ZX_OK) {
527 return status;
528 }
529
530 if (write(fd_.get(), compression_.GetData(), compression_.GetLength())
531 != compression_.GetLength()) {
532 return ZX_ERR_IO;
533 }
534
535 return ZX_OK;
536 }
537
CheckValid() const538 void SparseContainer::CheckValid() const {
539 if (!valid_) {
540 fprintf(stderr, "Error: Sparse container is invalid\n");
541 exit(-1);
542 }
543 }