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