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 <errno.h> 6#include <inttypes.h> 7#include <sys/ioctl.h> 8 9#include <fvm/fvm.h> 10 11#include <lib/fit/defer.h> 12 13#include "fvm/container.h" 14 15#if defined(__APPLE__) 16#include <sys/disk.h> 17#define IOCTL_GET_BLOCK_COUNT DKIOCGETBLOCKCOUNT 18#endif 19 20#if defined(__linux__) 21#include <linux/fs.h> 22#define IOCTL_GET_BLOCK_COUNT BLKGETSIZE 23#endif 24 25zx_status_t FvmContainer::Create(const char* path, size_t slice_size, off_t offset, off_t length, 26 fbl::unique_ptr<FvmContainer>* out) { 27 fbl::AllocChecker ac; 28 fbl::unique_ptr<FvmContainer> fvmContainer(new (&ac) FvmContainer(path, slice_size, offset, 29 length)); 30 if (!ac.check()) { 31 return ZX_ERR_NO_MEMORY; 32 } 33 34 zx_status_t status; 35 if ((status = fvmContainer->Init()) != ZX_OK) { 36 return status; 37 } 38 39 *out = fbl::move(fvmContainer); 40 return ZX_OK; 41} 42 43FvmContainer::FvmContainer(const char* path, size_t slice_size, off_t offset, off_t length) 44 : Container(path, slice_size, 0), valid_(false), disk_offset_(offset), disk_size_(length), 45 vpart_hint_(1), pslice_hint_(1) { 46 fd_.reset(open(path, O_RDWR, 0644)); 47 if (!fd_) { 48 if (errno == ENOENT) { 49 fd_.reset(open(path, O_RDWR | O_CREAT | O_EXCL, 0644)); 50 51 if (!fd_) { 52 fprintf(stderr, "Failed to create path %s\n", path); 53 exit(-1); 54 } 55 56 xprintf("Created path %s\n", path); 57 } else { 58 fprintf(stderr, "Failed to open path %s: %s\n", path, strerror(errno)); 59 exit(-1); 60 } 61 } 62 63 struct stat s; 64 if (fstat(fd_.get(), &s) < 0) { 65 fprintf(stderr, "Failed to stat %s\n", path); 66 exit(-1); 67 } 68 69 uint64_t size = s.st_size; 70 71 if (S_ISBLK(s.st_mode)) { 72 uint64_t block_count; 73 if (ioctl(fd_.get(), IOCTL_GET_BLOCK_COUNT, &block_count) >= 0) { 74 size = block_count * 512; 75 } 76 } 77 78 if (size < disk_offset_ + disk_size_) { 79 fprintf(stderr, "Invalid file size %" PRIu64 " for specified offset+length\n", size); 80 exit(-1); 81 } 82 83 // Even if disk size is 0, this will default to at least FVM_BLOCK_SIZE 84 metadata_size_ = fvm::MetadataSize(disk_size_, slice_size_); 85 metadata_.reset(new uint8_t[metadata_size_ * 2]); 86 87 // Clear entire primary copy of metadata 88 memset(metadata_.get(), 0, metadata_size_); 89 90 // If Container already exists, read metadata from disk. 91 if (disk_size_ > 0) { 92 if (lseek(fd_.get(), disk_offset_, SEEK_SET) < 0) { 93 fprintf(stderr, "Seek reset failed\n"); 94 exit(-1); 95 } 96 97 // Read superblock first so we can determine if container has a different slice size. 98 ssize_t rd = read(fd_.get(), metadata_.get(), sizeof(fvm::fvm_t)); 99 if (rd != static_cast<ssize_t>(sizeof(fvm::fvm_t))) { 100 fprintf(stderr, "Superblock read failed: expected %ld, actual %ld\n", 101 sizeof(fvm::fvm_t), rd); 102 exit(-1); 103 } 104 105 // If the image is obviously not an FVM header, bail out early. 106 // Otherwise, we go through the effort of ensuring the header is 107 // valid before using it. 108 if (SuperBlock()->magic != FVM_MAGIC) { 109 return; 110 } 111 112 // Recalculate metadata size. 113 size_t old_slice_size = SuperBlock()->slice_size; 114 size_t old_metadata_size = fvm::MetadataSize(disk_size_, old_slice_size); 115 auto old_metadata = fbl::unique_ptr<uint8_t[]>(new uint8_t[old_metadata_size * 2]); 116 117 if (lseek(fd_.get(), disk_offset_, SEEK_SET) < 0) { 118 fprintf(stderr, "Seek reset failed\n"); 119 exit(-1); 120 } 121 122 // Read remainder of metadata. 123 rd = read(fd_.get(), old_metadata.get(), old_metadata_size * 2); 124 if (rd != static_cast<ssize_t>(old_metadata_size * 2)) { 125 fprintf(stderr, "Metadata read failed: expected %ld, actual %ld\n", 126 old_metadata_size * 2, rd); 127 exit(-1); 128 } 129 130 const void* backup = reinterpret_cast<void*>( 131 reinterpret_cast<uintptr_t>(old_metadata.get()) + old_metadata_size); 132 const void* primary = nullptr; 133 if (fvm_validate_header(old_metadata.get(), backup, old_metadata_size, &primary) == ZX_OK) { 134 if (primary != old_metadata.get()) { 135 fprintf(stderr, "Can only update FVM with valid primary as first copy\n"); 136 exit(-1); 137 } 138 139 valid_ = true; 140 slice_size_ = old_slice_size; 141 metadata_size_ = old_metadata_size; 142 metadata_.reset(old_metadata.release()); 143 } 144 } 145} 146 147FvmContainer::~FvmContainer() = default; 148 149zx_status_t FvmContainer::Init() { 150 // Clear entire primary copy of metadata. 151 memset(metadata_.get(), 0, metadata_size_); 152 153 // Superblock 154 fvm::fvm_t* sb = SuperBlock(); 155 sb->magic = FVM_MAGIC; 156 sb->version = FVM_VERSION; 157 sb->pslice_count = fvm::UsableSlicesCount(disk_size_, slice_size_); 158 sb->slice_size = slice_size_; 159 sb->fvm_partition_size = disk_size_; 160 sb->vpartition_table_size = fvm::kVPartTableLength; 161 sb->allocation_table_size = fvm::AllocTableLength(disk_size_, slice_size_); 162 sb->generation = 0; 163 164 if (sb->pslice_count == 0) { 165 fprintf(stderr, "No space available for slices\n"); 166 return ZX_ERR_NO_SPACE; 167 } 168 169 dirty_ = true; 170 valid_ = true; 171 172 xprintf("fvm_init: Success\n"); 173 xprintf("fvm_init: Slice Count: %" PRIu64 ", size: %" PRIu64 "\n", sb->pslice_count, 174 sb->slice_size); 175 xprintf("fvm_init: Vpart offset: %zu, length: %zu\n", 176 fvm::kVPartTableOffset, fvm::kVPartTableLength); 177 xprintf("fvm_init: Atable offset: %zu, length: %zu\n", 178 fvm::kAllocTableOffset, fvm::AllocTableLength(disk_size_, slice_size_)); 179 xprintf("fvm_init: Backup meta starts at: %zu\n", 180 fvm::BackupStart(disk_size_, slice_size_)); 181 xprintf("fvm_init: Slices start at %zu, there are %zu of them\n", 182 fvm::SlicesStart(disk_size_, slice_size_), 183 fvm::UsableSlicesCount(disk_size_, slice_size_)); 184 return ZX_OK; 185} 186 187zx_status_t FvmContainer::Verify() const { 188 CheckValid(); 189 const void* backup = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(metadata_.get()) + 190 metadata_size_); 191 192 if (fvm_validate_header(metadata_.get(), backup, metadata_size_, nullptr) != ZX_OK) { 193 fprintf(stderr, "Failed to validate header\n"); 194 return ZX_ERR_BAD_STATE; 195 } 196 197 fvm::fvm_t* sb = SuperBlock(); 198 199 xprintf("Total size is %zu\n", disk_size_); 200 xprintf("Metadata size is %zu\n", metadata_size_); 201 xprintf("Slice size is %" PRIu64 "\n", sb->slice_size); 202 xprintf("Slice count is %" PRIu64 "\n", sb->pslice_count); 203 204 off_t start = 0; 205 off_t end = disk_offset_ + metadata_size_ * 2; 206 size_t slice_index = 1; 207 for (size_t vpart_index = 1; vpart_index < FVM_MAX_ENTRIES; ++vpart_index) { 208 fvm::vpart_entry_t* vpart = nullptr; 209 start = end; 210 211 zx_status_t status; 212 if ((status = GetPartition(vpart_index, &vpart)) != ZX_OK) { 213 return status; 214 } 215 216 if (vpart->slices == 0) { 217 break; 218 } 219 220 fbl::Vector<size_t> extent_lengths; 221 size_t last_vslice = 0; 222 223 for (; slice_index <= sb->pslice_count; ++slice_index) { 224 fvm::slice_entry_t* slice = nullptr; 225 if ((status = GetSlice(slice_index, &slice)) != ZX_OK) { 226 return status; 227 } 228 229 if (slice->Vpart() != vpart_index) { 230 break; 231 } 232 233 end += slice_size_; 234 235 if (slice->Vslice() == last_vslice + 1) { 236 extent_lengths[extent_lengths.size() - 1] += slice_size_; 237 } else { 238 extent_lengths.push_back(slice_size_); 239 } 240 241 last_vslice = slice->Vslice(); 242 } 243 244 disk_format_t part; 245 if ((status = Format::Detect(fd_.get(), start, &part)) != ZX_OK) { 246 return status; 247 } 248 249 fbl::unique_fd dupfd(dup(fd_.get())); 250 if (!dupfd) { 251 fprintf(stderr, "Failed to duplicate fd\n"); 252 return ZX_ERR_INTERNAL; 253 } 254 255 if ((status = Format::Check(fbl::move(dupfd), start, end, extent_lengths, part)) != ZX_OK) { 256 fprintf(stderr, "%s fsck returned an error.\n", vpart->name); 257 return status; 258 } 259 260 xprintf("Found valid %s partition\n", vpart->name); 261 } 262 263 return ZX_OK; 264} 265 266zx_status_t FvmContainer::Extend(size_t disk_size) { 267 if (disk_size <= disk_size_) { 268 fprintf(stderr, "Cannot extend to disk size %zu smaller than current size %lu\n", disk_size, 269 disk_size_); 270 return ZX_ERR_INVALID_ARGS; 271 } else if (disk_offset_) { 272 fprintf(stderr, "Cannot extend FVM within another container\n"); 273 return ZX_ERR_BAD_STATE; 274 } 275 276 const char* temp = ".tmp"; 277 278 if (strlen(path_) >= PATH_MAX - strlen(temp) - 1) { 279 fprintf(stderr, "Path name exceeds maximum length\n"); 280 return ZX_ERR_INVALID_ARGS; 281 } 282 283 char path[PATH_MAX]; 284 strncpy(path, path_, PATH_MAX); 285 path[sizeof(path) - 1] = '\0'; 286 287 strncat(path, temp, PATH_MAX - strlen(path) - 1); 288 fbl::unique_fd fd(open(path, O_RDWR | O_CREAT, 0644)); 289 290 if (!fd) { 291 fprintf(stderr, "Unable to open temp file %s\n", path); 292 return ZX_ERR_IO; 293 } 294 295 auto cleanup = fit::defer([path]() { 296 if (unlink(path) < 0) { 297 fprintf(stderr, "Failed to unlink path %s\n", path); 298 } 299 }); 300 301 if (ftruncate(fd.get(), disk_size) != 0) { 302 fprintf(stderr, "Failed to truncate fvm container"); 303 return ZX_ERR_IO; 304 } 305 306 // Since the size and location of both metadata in an FVM is dependent on the size of 307 // the FVM partition, we must relocate any data that already exists within the volume 308 // manager. 309 // 310 // First, we read all old slices from the original device, and write them to their 311 // new locations. 312 // 313 // Then, we update the on-disk metadata to reflect the new size of the disk. 314 // To avoid collision between relocated slices, this is done on a temporary file. 315 uint64_t pslice_count = SuperBlock()->pslice_count; 316 for (uint32_t index = 1; index <= pslice_count; index++) { 317 zx_status_t status; 318 fvm::slice_entry_t* slice = nullptr; 319 if ((status = GetSlice(index, &slice)) != ZX_OK) { 320 fprintf(stderr, "Failed to retrieve slice %u\n", index); 321 return status; 322 } 323 324 if (slice->Vpart() == FVM_SLICE_ENTRY_FREE) { 325 continue; 326 } 327 328 fbl::AllocChecker ac; 329 fbl::Array<uint8_t> data(new (&ac) uint8_t[slice_size_], slice_size_); 330 331 if (!ac.check()) { 332 return ZX_ERR_NO_MEMORY; 333 } 334 335 if (lseek(fd_.get(), fvm::SliceStart(disk_size_, slice_size_, index), SEEK_SET) < 0) { 336 fprintf(stderr, "Cannot seek to slice %u in current FVM\n", index); 337 return ZX_ERR_BAD_STATE; 338 } 339 340 ssize_t r = read(fd_.get(), data.get(), slice_size_); 341 if (r != slice_size_) { 342 fprintf(stderr, "Failed to read data from FVM: %ld\n", r); 343 return ZX_ERR_BAD_STATE; 344 } 345 346 if (lseek(fd.get(), fvm::SliceStart(disk_size, slice_size_, index), SEEK_SET) < 0) { 347 fprintf(stderr, "Cannot seek to slice %u in new FVM\n", index); 348 return ZX_ERR_BAD_STATE; 349 } 350 351 r = write(fd.get(), data.get(), slice_size_); 352 if (r != slice_size_) { 353 fprintf(stderr, "Failed to write data to FVM: %ld\n", r); 354 return ZX_ERR_BAD_STATE; 355 } 356 } 357 358 size_t metadata_size = fvm::MetadataSize(disk_size, slice_size_); 359 zx_status_t status = GrowMetadata(metadata_size); 360 if (status != ZX_OK) { 361 return status; 362 } 363 364 fvm::fvm_t* sb = SuperBlock(); 365 sb->pslice_count = fvm::UsableSlicesCount(disk_size, slice_size_); 366 sb->fvm_partition_size = disk_size; 367 sb->allocation_table_size = fvm::AllocTableLength(disk_size, slice_size_); 368 fvm_update_hash(metadata_.get(), metadata_size_); 369 370 if (lseek(fd.get(), 0, SEEK_SET) < 0) { 371 fprintf(stderr, "Failed to seek\n"); 372 return ZX_ERR_BAD_STATE; 373 } 374 375 if (write(fd.get(), metadata_.get(), metadata_size_) != static_cast<ssize_t>(metadata_size_)) { 376 fprintf(stderr, "Error writing metadata to disk\n"); 377 return ZX_ERR_IO; 378 } 379 380 if (write(fd.get(), metadata_.get(), metadata_size_) != static_cast<ssize_t>(metadata_size_)) { 381 fprintf(stderr, "Error writing metadata to disk\n"); 382 return ZX_ERR_IO; 383 } 384 385 fd_.reset(fd.release()); 386 disk_size_ = disk_size; 387 388 if ((status = Verify()) != ZX_OK) { 389 fprintf(stderr, "Verify failed - cancelling extension\n"); 390 return status; 391 } 392 393 if (rename(path, path_) < 0) { 394 fprintf(stderr, "Failed to copy over temp file\n"); 395 return ZX_ERR_IO; 396 } 397 398 cleanup.cancel(); 399 return ZX_OK; 400} 401 402zx_status_t FvmContainer::Commit() { 403 if (!dirty_) { 404 fprintf(stderr, "Commit: Nothing to write\n"); 405 return ZX_OK; 406 } 407 408 // If the FVM container has just been created, truncate it to an appropriate size 409 if (disk_size_ == 0) { 410 if (partitions_.is_empty()) { 411 fprintf(stderr, "Cannot create new FVM container with 0 partitions\n"); 412 return ZX_ERR_INVALID_ARGS; 413 } 414 415 size_t required_size = 0; 416 for (unsigned i = 0; i < partitions_.size(); i++) { 417 required_size += partitions_[i].slice_count * slice_size_; 418 } 419 420 size_t total_size = required_size; 421 size_t metadata_size = 0; 422 423 while (total_size - (metadata_size * 2) < required_size || metadata_size < metadata_size_) { 424 total_size = required_size + (metadata_size * 2); 425 metadata_size = fvm::MetadataSize(total_size, slice_size_); 426 } 427 428 zx_status_t status; 429 if ((status = GrowMetadata(metadata_size)) != ZX_OK) { 430 return status; 431 } 432 433 if (ftruncate(fd_.get(), total_size) != 0) { 434 fprintf(stderr, "Failed to truncate fvm container"); 435 return ZX_ERR_IO; 436 } 437 438 struct stat s; 439 if (fstat(fd_.get(), &s) < 0) { 440 fprintf(stderr, "Failed to stat container\n"); 441 return ZX_ERR_IO; 442 } 443 444 disk_size_ = s.st_size; 445 446 if (disk_size_ != total_size) { 447 fprintf(stderr, "Truncated to incorrect size\n"); 448 return ZX_ERR_IO; 449 } 450 451 fvm::fvm_t* sb = SuperBlock(); 452 sb->pslice_count = (disk_size_ - metadata_size_ * 2) / slice_size_; 453 sb->fvm_partition_size = disk_size_; 454 sb->allocation_table_size = fvm::AllocTableLength(disk_size_, slice_size_); 455 } 456 457 fvm_update_hash(metadata_.get(), metadata_size_); 458 459 if (lseek(fd_.get(), disk_offset_, SEEK_SET) < 0) { 460 fprintf(stderr, "Error seeking disk\n"); 461 return ZX_ERR_IO; 462 } 463 464 if (write(fd_.get(), metadata_.get(), metadata_size_) != static_cast<ssize_t>(metadata_size_)) { 465 fprintf(stderr, "Error writing metadata to disk\n"); 466 return ZX_ERR_IO; 467 } 468 469 if (write(fd_.get(), metadata_.get(), metadata_size_) != static_cast<ssize_t>(metadata_size_)) { 470 fprintf(stderr, "Error writing metadata to disk\n"); 471 return ZX_ERR_IO; 472 } 473 474 for (unsigned i = 0; i < partitions_.size(); i++) { 475 zx_status_t status; 476 if ((status = WritePartition(i)) != ZX_OK) { 477 return status; 478 } 479 } 480 481 xprintf("Successfully wrote FVM data to disk\n"); 482 return ZX_OK; 483} 484 485size_t FvmContainer::SliceSize() const { 486 CheckValid(); 487 return slice_size_; 488} 489 490zx_status_t FvmContainer::AddPartition(const char* path, const char* type_name) { 491 fbl::unique_ptr<Format> format; 492 zx_status_t status; 493 if ((status = Format::Create(path, type_name, &format)) != ZX_OK) { 494 fprintf(stderr, "Failed to initialize partition\n"); 495 return status; 496 } 497 498 uint8_t guid[FVM_GUID_LEN]; 499 uint8_t type[FVM_GUID_LEN]; 500 char name[FVM_NAME_LEN]; 501 format->Guid(guid); 502 format->Type(type); 503 format->Name(name); 504 uint32_t vpart_index; 505 uint32_t flags = flags_ & format->FlagMask(); 506 if ((status = AllocatePartition(type, guid, name, 1, flags, &vpart_index)) != ZX_OK) { 507 return status; 508 } 509 510 if ((status = format->MakeFvmReady(SliceSize(), vpart_index)) != ZX_OK) { 511 return status; 512 } 513 514 uint32_t slice_count = 0; 515 if ((status = format->GetSliceCount(&slice_count)) != ZX_OK) { 516 return status; 517 } 518 519 // If allocated metadata is too small, grow it to an appropriate size 520 size_t required_size = fvm::kAllocTableOffset + (pslice_hint_ + slice_count) * sizeof(fvm::slice_entry_t); 521 if ((status = GrowMetadata(required_size)) != ZX_OK) { 522 return status; 523 } 524 525 // Allocate all slices for this partition 526 uint32_t pslice_start = 0; 527 uint32_t pslice_total = 0; 528 unsigned extent_index = 0; 529 while (true) { 530 vslice_info_t vslice_info; 531 zx_status_t status; 532 if ((status = format->GetVsliceRange(extent_index, &vslice_info)) != ZX_OK) { 533 if (status == ZX_ERR_OUT_OF_RANGE) { 534 break; 535 } 536 return status; 537 } 538 539 uint32_t vslice = vslice_info.vslice_start / format->BlocksPerSlice(); 540 541 for (unsigned i = 0; i < vslice_info.slice_count; i++) { 542 uint32_t pslice; 543 544 if ((status = AllocateSlice(format->VpartIndex(), vslice + i, &pslice)) != ZX_OK) { 545 return status; 546 } 547 548 if (!pslice_start) { 549 pslice_start = pslice; 550 } 551 552 // On a new FVM container, pslice allocation is expected to be contiguous. 553 if (pslice != pslice_start + pslice_total) { 554 fprintf(stderr, "Unexpected error during slice allocation\n"); 555 return ZX_ERR_INTERNAL; 556 } 557 558 pslice_total++; 559 } 560 561 extent_index++; 562 } 563 564 partition_info_t partition; 565 partition.format = fbl::move(format); 566 partition.vpart_index = vpart_index; 567 partition.pslice_start = pslice_start; 568 partition.slice_count = slice_count; 569 partitions_.push_back(fbl::move(partition)); 570 return ZX_OK; 571} 572 573void FvmContainer::CheckValid() const { 574 if (!valid_) { 575 fprintf(stderr, "Error: FVM is invalid\n"); 576 exit(-1); 577 } 578} 579 580zx_status_t FvmContainer::GrowMetadata(size_t new_size) { 581 if (new_size <= metadata_size_) { 582 return ZX_OK; 583 } 584 585 xprintf("Growing metadata from %zu to %zu\n", metadata_size_, new_size); 586 fbl::AllocChecker ac; 587 fbl::unique_ptr<uint8_t[]> new_metadata(new (&ac) uint8_t[new_size * 2]); 588 if (!ac.check()) { 589 fprintf(stderr, "Unable to acquire resources for new metadata\n"); 590 return ZX_ERR_NO_MEMORY; 591 } 592 593 memcpy(new_metadata.get(), metadata_.get(), metadata_size_); 594 memset(new_metadata.get() + metadata_size_, 0, new_size - metadata_size_); 595 596 metadata_.reset(new_metadata.release()); 597 metadata_size_ = new_size; 598 return ZX_OK; 599} 600 601zx_status_t FvmContainer::AllocatePartition(uint8_t* type, uint8_t* guid, const char* name, 602 uint32_t slices, uint32_t flags, uint32_t* vpart_index) { 603 CheckValid(); 604 for (unsigned index = vpart_hint_; index < FVM_MAX_ENTRIES; index++) { 605 zx_status_t status; 606 fvm::vpart_entry_t* vpart = nullptr; 607 if ((status = GetPartition(index, &vpart)) != ZX_OK) { 608 fprintf(stderr, "Failed to retrieve partition %u\n", index); 609 return status; 610 } 611 612 // Make sure this vpartition has not already been allocated 613 if (vpart->slices == 0) { 614 vpart->init(type, guid, slices, name, flags); 615 vpart_hint_ = index + 1; 616 dirty_ = true; 617 *vpart_index = index; 618 return ZX_OK; 619 } 620 } 621 622 fprintf(stderr, "Unable to find any free partitions\n"); 623 return ZX_ERR_INTERNAL; 624} 625 626zx_status_t FvmContainer::AllocateSlice(uint32_t vpart, uint32_t vslice, uint32_t* pslice) { 627 CheckValid(); 628 fvm::fvm_t* sb = SuperBlock(); 629 630 for (uint32_t index = pslice_hint_; index <= sb->pslice_count; index++) { 631 zx_status_t status; 632 fvm::slice_entry_t* slice = nullptr; 633 if ((status = GetSlice(index, &slice)) != ZX_OK) { 634 fprintf(stderr, "Failed to retrieve slice %u\n", index); 635 return status; 636 } 637 638 if (slice->Vpart() != FVM_SLICE_ENTRY_FREE) { 639 continue; 640 } 641 642 pslice_hint_ = index + 1; 643 644 slice->SetVpart(vpart); 645 slice->SetVslice(vslice); 646 dirty_ = true; 647 *pslice = index; 648 return ZX_OK; 649 } 650 651 fprintf(stderr, "Unable to find any free slices\n"); 652 return ZX_ERR_INTERNAL; 653} 654 655zx_status_t FvmContainer::GetPartition(size_t index, fvm::vpart_entry_t** out) const { 656 CheckValid(); 657 658 if (index < 1 || index > FVM_MAX_ENTRIES) { 659 return ZX_ERR_OUT_OF_RANGE; 660 } 661 662 uintptr_t metadata_start = reinterpret_cast<uintptr_t>(metadata_.get()); 663 uintptr_t offset = static_cast<uintptr_t>(fvm::kVPartTableOffset + 664 index * sizeof(fvm::vpart_entry_t)); 665 *out = reinterpret_cast<fvm::vpart_entry_t*>(metadata_start + offset); 666 return ZX_OK; 667} 668 669zx_status_t FvmContainer::GetSlice(size_t index, fvm::slice_entry_t** out) const { 670 CheckValid(); 671 672 if (index < 1 || index > SuperBlock()->pslice_count) { 673 return ZX_ERR_OUT_OF_RANGE; 674 } 675 676 uintptr_t metadata_start = reinterpret_cast<uintptr_t>(metadata_.get()); 677 uintptr_t offset = static_cast<uintptr_t>(fvm::kAllocTableOffset + 678 index * sizeof(fvm::slice_entry_t)); 679 *out = reinterpret_cast<fvm::slice_entry_t*>(metadata_start + offset); 680 return ZX_OK; 681} 682 683zx_status_t FvmContainer::WritePartition(unsigned part_index) { 684 CheckValid(); 685 if (part_index > partitions_.size()) { 686 fprintf(stderr, "Error: Tried to access partition %u / %zu\n", 687 part_index, partitions_.size()); 688 return ZX_ERR_OUT_OF_RANGE; 689 } 690 691 unsigned extent_index = 0; 692 partition_info_t* partition = &partitions_[part_index]; 693 Format* format = partition->format.get(); 694 uint32_t pslice_start = partition->pslice_start; 695 696 while (true) { 697 zx_status_t status; 698 if ((status = WriteExtent(extent_index++, format, &pslice_start)) != ZX_OK) { 699 if (status != ZX_ERR_OUT_OF_RANGE) { 700 return status; 701 } 702 703 return ZX_OK; 704 } 705 } 706} 707 708zx_status_t FvmContainer::WriteExtent(unsigned extent_index, Format* format, uint32_t* pslice) { 709 vslice_info_t vslice_info{}; 710 zx_status_t status; 711 if ((status = format->GetVsliceRange(extent_index, &vslice_info)) != ZX_OK) { 712 return status; 713 } 714 715 // Write each slice in the given extent 716 uint32_t current_block = 0; 717 for (unsigned i = 0; i < vslice_info.slice_count; i++) { 718 // Write each block in this slice 719 for (uint32_t j = 0; j < format->BlocksPerSlice(); j++) { 720 // If we have gone beyond the blocks written to partition file, write empty block 721 if (current_block >= vslice_info.block_count) { 722 if (!vslice_info.zero_fill) { 723 break; 724 } 725 format->EmptyBlock(); 726 } else { 727 if ((status = format->FillBlock(vslice_info.block_offset + current_block)) != ZX_OK) { 728 fprintf(stderr, "Failed to read block from minfs\n"); 729 return status; 730 } 731 732 current_block++; 733 } 734 735 if ((status = WriteData(*pslice, j, format->BlockSize(), format->Data())) != ZX_OK) { 736 fprintf(stderr, "Failed to write data to FVM\n"); 737 return status; 738 } 739 } 740 (*pslice)++; 741 } 742 743 return ZX_OK; 744} 745 746zx_status_t FvmContainer::WriteData(uint32_t pslice, uint32_t block_offset, size_t block_size, 747 void* data) { 748 CheckValid(); 749 750 if (block_offset * block_size > slice_size_) { 751 fprintf(stderr, "Not enough space in slice\n"); 752 return ZX_ERR_OUT_OF_RANGE; 753 } 754 755 if (lseek(fd_.get(), disk_offset_ + fvm::SliceStart(disk_size_, slice_size_, pslice) + block_offset * block_size, SEEK_SET) < 0) { 756 return ZX_ERR_BAD_STATE; 757 } 758 759 ssize_t r = write(fd_.get(), data, block_size); 760 if (r != block_size) { 761 fprintf(stderr, "Failed to write data to FVM\n"); 762 return ZX_ERR_BAD_STATE; 763 } 764 765 return ZX_OK; 766} 767 768fvm::fvm_t* FvmContainer::SuperBlock() const { 769 return static_cast<fvm::fvm_t*>((void*)metadata_.get()); 770} 771