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 
7 #include <fbl/unique_fd.h>
8 
9 #include <utility>
10 
11 #include "fvm-host/container.h"
12 
Create(const char * path,off_t offset,off_t length,uint32_t flags,fbl::unique_ptr<Container> * container)13 zx_status_t Container::Create(const char* path, off_t offset, off_t length,
14                               uint32_t flags, fbl::unique_ptr<Container>* container) {
15     if ((flags & ~fvm::kSparseFlagAllValid) != 0) {
16         fprintf(stderr, "Invalid flags: %08" PRIx32 "\n", flags);
17         return -1;
18     }
19 
20     fbl::unique_fd fd(open(path, O_RDONLY));
21     if (!fd) {
22         fprintf(stderr, "Unable to open path %s\n", path);
23         return -1;
24     }
25 
26     uint8_t data[HEADER_SIZE];
27     if (lseek(fd.get(), offset, SEEK_SET) < 0) {
28         fprintf(stderr, "Error seeking block device\n");
29         return -1;
30     }
31 
32     if (read(fd.get(), data, sizeof(data)) != sizeof(data)) {
33         fprintf(stderr, "Error reading block device\n");
34         return -1;
35     }
36 
37     if (!memcmp(data, fvm_magic, sizeof(fvm_magic))) {
38         fvm::fvm_t* sb = reinterpret_cast<fvm::fvm_t*>(data);
39 
40         // Found fvm container
41         fbl::unique_ptr<Container> fvmContainer(new FvmContainer(path, sb->slice_size,
42                                                                  offset, length));
43         *container = std::move(fvmContainer);
44         return ZX_OK;
45     }
46 
47     fvm::sparse_image_t* image = reinterpret_cast<fvm::sparse_image_t*>(data);
48     if (image->magic == fvm::kSparseFormatMagic) {
49         if (offset > 0) {
50             fprintf(stderr, "Invalid offset for sparse file\n");
51             return ZX_ERR_INVALID_ARGS;
52         }
53 
54         // Found sparse container
55         fbl::unique_ptr<Container> sparseContainer(new SparseContainer(path, image->slice_size,
56                                                                        flags));
57         *container = std::move(sparseContainer);
58         return ZX_OK;
59     }
60 
61     fprintf(stderr, "File format not supported\n");
62     return ZX_ERR_NOT_SUPPORTED;
63 }
64 
Container(const char * path,size_t slice_size,uint32_t flags)65 Container::Container(const char* path, size_t slice_size, uint32_t flags)
66     : slice_size_(slice_size), flags_(flags) {
67     path_.AppendPrintf("%s", path);
68 }
69 
~Container()70 Container::~Container() {}
71 
CalculateDiskSizeForSlices(size_t slice_count) const72 uint64_t Container::CalculateDiskSizeForSlices(size_t slice_count) const {
73     uint64_t data_size = slice_count * slice_size_;
74     uint64_t total_size = 0;
75     size_t metadata_size = 0;
76 
77     // This loop will necessarily break at some point. The re-calculation of total_size and
78     // metadata_size will cause both of these values to increase, except on the last iteration
79     // where metadata_size will not change, causing the loop condition to become false.
80     // metadata_size *must* stop increasing at some point, since the data_size is always a fixed
81     // value, and the metadata by itself will not grow fast enough to necessitate its continued
82     // growth at the same or higher rate.
83     // As an example, with the current metadata size calculation and a slice size of 8192 bytes,
84     // around ~8mb of disk space will require metadata growth of only ~8kb. Even if the
85     // metadata_size grows very quickly at first, this amount will diminish very quickly until it
86     // reaches 0.
87     do {
88         total_size = data_size + (metadata_size * 2);
89         metadata_size = fvm::MetadataSize(total_size, slice_size_);
90     } while (total_size - (metadata_size * 2) < data_size);
91 
92     return total_size;
93 }
94