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 }