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 "fvm/container.h"
8
9static LZ4F_preferences_t lz4_prefs = {
10    .frameInfo = {
11        .blockSizeID = LZ4F_max64KB,
12        .blockMode = LZ4F_blockIndependent,
13    },
14    .compressionLevel = 0,
15};
16
17zx_status_t CompressionContext::Setup(size_t max_len) {
18    LZ4F_errorCode_t errc = LZ4F_createCompressionContext(&cctx_, LZ4F_VERSION);
19    if (LZ4F_isError(errc)) {
20        fprintf(stderr, "Could not create compression context: %s\n", LZ4F_getErrorName(errc));
21        return ZX_ERR_INTERNAL;
22    }
23
24    Reset(LZ4F_compressBound(max_len, &lz4_prefs));
25
26    size_t r = LZ4F_compressBegin(cctx_, GetBuffer(), GetRemaining(), &lz4_prefs);
27    if (LZ4F_isError(r)) {
28        fprintf(stderr, "Could not begin compression: %s\n", LZ4F_getErrorName(r));
29        return ZX_ERR_INTERNAL;
30    }
31
32    IncreaseOffset(r);
33    return ZX_OK;
34}
35
36zx_status_t CompressionContext::Compress(const void* data, size_t length) {
37    size_t r = LZ4F_compressUpdate(cctx_, GetBuffer(), GetRemaining(), data, length, NULL);
38    if (LZ4F_isError(r)) {
39        fprintf(stderr, "Could not compress data: %s\n", LZ4F_getErrorName(r));
40        return ZX_ERR_INTERNAL;
41    }
42
43    IncreaseOffset(r);
44    return ZX_OK;
45}
46
47zx_status_t CompressionContext::Finish() {
48    size_t r = LZ4F_compressEnd(cctx_, GetBuffer(), GetRemaining(), NULL);
49    if (LZ4F_isError(r)) {
50        fprintf(stderr, "Could not finish compression: %s\n", LZ4F_getErrorName(r));
51        return ZX_ERR_INTERNAL;
52    }
53
54    IncreaseOffset(r);
55    LZ4F_errorCode_t errc = LZ4F_freeCompressionContext(cctx_);
56    if (LZ4F_isError(errc)) {
57        fprintf(stderr, "Could not free compression context: %s\n", LZ4F_getErrorName(errc));
58        return ZX_ERR_INTERNAL;
59    }
60
61    return ZX_OK;
62}
63
64zx_status_t SparseContainer::Create(const char* path, size_t slice_size, uint32_t flags,
65                                    fbl::unique_ptr<SparseContainer>* out) {
66    fbl::AllocChecker ac;
67    fbl::unique_ptr<SparseContainer> sparseContainer(new (&ac) SparseContainer(path, slice_size,
68                                                                               flags));
69    if (!ac.check()) {
70        return ZX_ERR_NO_MEMORY;
71    }
72
73    zx_status_t status;
74    if ((status = sparseContainer->Init()) != ZX_OK) {
75        return status;
76    }
77
78    *out = fbl::move(sparseContainer);
79    return ZX_OK;
80}
81
82SparseContainer::SparseContainer(const char* path, uint64_t slice_size, uint32_t flags)
83    : Container(path, slice_size, flags), valid_(false), disk_size_(0),
84      extent_size_(0) {
85    fd_.reset(open(path, O_CREAT | O_RDWR, 0666));
86
87    if (!fd_) {
88        fprintf(stderr, "Failed to open sparse data path\n");
89        return;
90    }
91
92    struct stat s;
93    if (fstat(fd_.get(), &s) < 0) {
94        fprintf(stderr, "Failed to stat %s\n", path);
95        return;
96    }
97
98    if (s.st_size > 0) {
99        disk_size_ = s.st_size;
100
101        if (read(fd_.get(), &image_, sizeof(fvm::sparse_image_t)) != sizeof(fvm::sparse_image_t)) {
102            fprintf(stderr, "SparseContainer: Failed to read the sparse header\n");
103            return;
104        }
105
106        if (image_.flags & fvm::kSparseFlagLz4) {
107            return;
108        }
109
110        extent_size_ = disk_size_ - image_.header_length;
111
112        for (unsigned i = 0; i < image_.partition_count; i++) {
113            partition_info_t partition;
114            partitions_.push_back(fbl::move(partition));
115            if (read(fd_.get(), &partitions_[i].descriptor, sizeof(fvm::partition_descriptor_t)) !=
116                    sizeof(fvm::partition_descriptor_t)) {
117                fprintf(stderr, "SparseContainer: Failed to read partition %u\n", i);
118                return;
119            }
120
121            for (unsigned j = 0; j < partitions_[i].descriptor.extent_count; j++) {
122                fvm::extent_descriptor_t extent;
123                partitions_[i].extents.push_back(extent);
124                if (read(fd_.get(), &partitions_[i].extents[j], sizeof(fvm::extent_descriptor_t)) !=
125                        sizeof(fvm::extent_descriptor_t)) {
126                    fprintf(stderr, "SparseContainer: Failed to read extent\n");
127                    return;
128                }
129            }
130        }
131
132        valid_ = true;
133        xprintf("Successfully read from existing sparse data container.\n");
134    }
135}
136
137SparseContainer::~SparseContainer() = default;
138
139zx_status_t SparseContainer::Init() {
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
154zx_status_t SparseContainer::Verify() const {
155    if (!valid_) {
156        fprintf(stderr, "SparseContainer: Found invalid container\n");
157        return ZX_ERR_INTERNAL;
158    }
159    if (image_.magic != fvm::kSparseFormatMagic) {
160        fprintf(stderr, "SparseContainer: Bad magic\n");
161        return ZX_ERR_IO;
162    }
163
164    xprintf("Slice size is %" PRIu64 "\n", image_.slice_size);
165    xprintf("Found %" PRIu64 " partitions\n", image_.partition_count);
166
167    off_t start = 0;
168    off_t end = image_.header_length;
169    for (unsigned i = 0; i < image_.partition_count; i++) {
170        fbl::Vector<size_t> extent_lengths;
171        start = end;
172        xprintf("Found partition %u with %u extents\n", i,
173               partitions_[i].descriptor.extent_count);
174
175        for (unsigned j = 0; j < partitions_[i].descriptor.extent_count; j++) {
176            extent_lengths.push_back(partitions_[i].extents[j].extent_length);
177            end += partitions_[i].extents[j].extent_length;
178        }
179
180        zx_status_t status;
181        disk_format_t part;
182        if ((status = Format::Detect(fd_.get(), start, &part)) != ZX_OK) {
183            return status;
184        }
185
186        fbl::unique_fd dupfd(dup(fd_.get()));
187        if (!dupfd) {
188            fprintf(stderr, "Failed to duplicate fd\n");
189            return ZX_ERR_INTERNAL;
190        }
191
192        if ((status = Format::Check(fbl::move(dupfd), start, end, extent_lengths, part)) != ZX_OK) {
193            const char* name = reinterpret_cast<const char*>(partitions_[i].descriptor.name);
194            fprintf(stderr, "%s fsck returned an error.\n", name);
195            return status;
196        }
197    }
198
199    if (end != disk_size_) {
200        fprintf(stderr, "Header + extent sizes (%" PRIu64 ") do not match sparse file size "
201                "(%zu)\n", end, disk_size_);
202        return ZX_ERR_IO_DATA_INTEGRITY;
203    }
204
205    return ZX_OK;
206}
207
208zx_status_t SparseContainer::Commit() {
209    if (!dirty_ || image_.partition_count == 0) {
210        fprintf(stderr, "Commit: Nothing to write.\n");
211        return ZX_OK;
212    }
213
214    // Reset file length to 0
215    if (ftruncate(fd_.get(), 0) != 0) {
216        fprintf(stderr, "Failed to truncate fvm container");
217        return ZX_ERR_IO;
218    }
219
220    // Recalculate and verify header length
221    uint64_t header_length = 0;
222
223    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
224        fprintf(stderr, "Seek reset failed\n");
225        return ZX_ERR_IO;
226    }
227
228    header_length += sizeof(fvm::sparse_image_t);
229    if (write(fd_.get(), &image_, sizeof(fvm::sparse_image_t)) != sizeof(fvm::sparse_image_t)) {
230        fprintf(stderr, "Write sparse image header failed\n");
231        return ZX_ERR_IO;
232    }
233
234    for (unsigned i = 0; i < image_.partition_count; i++) {
235        fvm::partition_descriptor_t partition = partitions_[i].descriptor;
236
237        header_length += sizeof(fvm::partition_descriptor_t);
238        if (write(fd_.get(), &partition, sizeof(fvm::partition_descriptor_t))
239            != sizeof(fvm::partition_descriptor_t)) {
240            fprintf(stderr, "Write partition failed\n");
241            return ZX_ERR_IO;
242        }
243
244        for (unsigned j = 0; j < partition.extent_count; j++) {
245            fvm::extent_descriptor_t extent = partitions_[i].extents[j];
246            header_length += sizeof(fvm::extent_descriptor_t);
247            if (write(fd_.get(), &extent, sizeof(fvm::extent_descriptor_t))
248                != sizeof(fvm::extent_descriptor_t)) {
249                fprintf(stderr, "Write extent failed\n");
250                return ZX_ERR_IO;
251            }
252        }
253    }
254
255    if (header_length != image_.header_length) {
256        fprintf(stderr, "Header length does not match!\n");
257        return ZX_ERR_INTERNAL;
258    }
259
260    zx_status_t status;
261    if ((status = PrepareWrite(extent_size_)) != ZX_OK) {
262        return status;
263    }
264
265    // Write each partition out to sparse file
266    for (unsigned i = 0; i < image_.partition_count; i++) {
267        fvm::partition_descriptor_t partition = partitions_[i].descriptor;
268        Format* format = partitions_[i].format.get();
269
270        vslice_info_t vslice_info;
271        // Write out each extent in the partition
272        for (unsigned j = 0; j < partition.extent_count; j++) {
273            if (format->GetVsliceRange(j, &vslice_info) != ZX_OK) {
274                fprintf(stderr, "Unable to access partition extent\n");
275                return ZX_ERR_OUT_OF_RANGE;
276            }
277
278            // Write out each block in the extent
279            for (unsigned k = 0; k < vslice_info.block_count; k++) {
280                if (format->FillBlock(vslice_info.block_offset + k) != ZX_OK) {
281                    fprintf(stderr, "Failed to read block\n");
282                    return ZX_ERR_IO;
283                }
284
285                if (WriteData(format->Data(), format->BlockSize()) != ZX_OK) {
286                    fprintf(stderr, "Failed to write data to sparse file\n");
287                    return ZX_ERR_IO;
288                }
289            }
290        }
291    }
292
293    if ((status = CompleteWrite()) != ZX_OK) {
294        return status;
295    }
296
297    struct stat s;
298    if (fstat(fd_.get(), &s) < 0) {
299        fprintf(stderr, "Failed to stat container\n");
300        return ZX_ERR_IO;
301    }
302
303    disk_size_ = s.st_size;
304    xprintf("Successfully wrote sparse data to disk.\n");
305    return ZX_OK;
306}
307
308size_t SparseContainer::SliceSize() const {
309    return image_.slice_size;
310}
311
312zx_status_t SparseContainer::AddPartition(const char* path, const char* type_name) {
313    fbl::unique_ptr<Format> format;
314    zx_status_t status;
315
316    if ((status = Format::Create(path, type_name, &format)) != ZX_OK) {
317        fprintf(stderr, "Failed to initialize partition\n");
318        return status;
319    }
320
321    if ((status = AllocatePartition(fbl::move(format))) != ZX_OK) {
322        fprintf(stderr, "Sparse partition allocation failed\n");
323        return status;
324    }
325
326    return ZX_OK;
327}
328
329zx_status_t SparseContainer::AllocatePartition(fbl::unique_ptr<Format> format) {
330    partition_info_t partition;
331    partition.descriptor.magic = fvm::kPartitionDescriptorMagic;
332    format->Type(partition.descriptor.type);
333    format->Name(reinterpret_cast<char*>(partition.descriptor.name));
334    partition.descriptor.extent_count = 0;
335    partition.descriptor.flags = flags_ & format->FlagMask();
336    image_.header_length += sizeof(fvm::partition_descriptor_t);
337    uint32_t part_index = image_.partition_count;
338
339    zx_status_t status;
340    if ((status = format->MakeFvmReady(SliceSize(), part_index)) != ZX_OK) {
341        return status;
342    }
343
344    partitions_.push_back(fbl::move(partition));
345
346    if (++image_.partition_count != partitions_.size()) {
347        fprintf(stderr, "Unexpected number of partitions\n");
348        return ZX_ERR_INTERNAL;
349    }
350
351    vslice_info_t vslice_info;
352    unsigned i = 0;
353    while ((status = format->GetVsliceRange(i++, &vslice_info)) == ZX_OK) {
354        if ((status = AllocateExtent(part_index,
355                                     vslice_info.vslice_start / format->BlocksPerSlice(),
356                                     vslice_info.slice_count,
357                                     vslice_info.block_count * format->BlockSize())) != ZX_OK) {
358            return status;
359        }
360    }
361
362    // This is expected if we have read all the other slices.
363    if (status != ZX_ERR_OUT_OF_RANGE) {
364        return status;
365    }
366
367    partitions_[part_index].format = fbl::move(format);
368    return ZX_OK;
369}
370
371zx_status_t SparseContainer::AllocateExtent(uint32_t part_index, uint64_t slice_start,
372                                            uint64_t slice_count, uint64_t extent_length) {
373    if (part_index >= image_.partition_count) {
374        fprintf(stderr, "Partition is not yet allocated\n");
375        return ZX_ERR_OUT_OF_RANGE;
376    }
377
378    partition_info_t* partition = &partitions_[part_index];
379    fvm::extent_descriptor_t extent;
380    extent.magic = fvm::kExtentDescriptorMagic;
381    extent.slice_start = slice_start;
382    extent.slice_count = slice_count;
383    extent.extent_length = extent_length;
384    partition->extents.push_back(extent);
385
386    if (partition->extents.size() != ++partition->descriptor.extent_count) {
387        fprintf(stderr, "Unexpected number of extents\n");
388        return ZX_ERR_INTERNAL;
389    }
390
391    image_.header_length += sizeof(fvm::extent_descriptor_t);
392    extent_size_ += extent_length;
393    dirty_ = true;
394    return ZX_OK;
395}
396
397zx_status_t SparseContainer::PrepareWrite(size_t max_len) {
398    if ((flags_ & fvm::kSparseFlagLz4) == 0) {
399        return ZX_OK;
400    }
401
402    return compression_.Setup(max_len);
403}
404
405zx_status_t SparseContainer::WriteData(const void* data, size_t length) {
406    if ((flags_ & fvm::kSparseFlagLz4) != 0) {
407        return compression_.Compress(data, length);
408    } else if (write(fd_.get(), data, length) != length) {
409        return ZX_ERR_IO;
410    }
411
412    return ZX_OK;
413}
414
415zx_status_t SparseContainer::CompleteWrite() {
416    if ((flags_ & fvm::kSparseFlagLz4) == 0) {
417        return ZX_OK;
418    }
419
420    zx_status_t status = compression_.Finish();
421
422    if (status != ZX_OK) {
423        return status;
424    }
425
426    if (write(fd_.get(), compression_.GetData(), compression_.GetLength())
427        != compression_.GetLength()) {
428        return ZX_ERR_IO;
429    }
430
431    return ZX_OK;
432}
433