| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <errno.h> |
| #include <lib/cksum.h> |
| #include <lib/zbitl/fd.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <memory> |
| #include <new> |
| |
| namespace zbitl { |
| namespace { |
| |
| constexpr size_t kBufferSize = 8192; |
| |
| using error_type = StorageTraits<fbl::unique_fd>::error_type; |
| |
| } // namespace |
| |
| fitx::result<error_type, uint32_t> StorageTraits<fbl::unique_fd>::Capacity( |
| const fbl::unique_fd& fd) { |
| struct stat st; |
| if (fstat(fd.get(), &st) < 0) { |
| return fitx::error{errno}; |
| } |
| auto size = std::min(static_cast<off_t>(std::numeric_limits<uint32_t>::max()), st.st_size); |
| return fitx::ok(static_cast<uint32_t>(size)); |
| } |
| |
| fitx::result<error_type> StorageTraits<fbl::unique_fd>::EnsureCapacity(fbl::unique_fd& fd, |
| uint32_t capacity_bytes) { |
| auto current = Capacity(fd); |
| if (current.is_error()) { |
| return current.take_error(); |
| } else if (current.value() >= capacity_bytes) { |
| return fitx::ok(); // Current capacity is sufficient. |
| } |
| // Write a single byte to reserve enough space for the new capacity. |
| if (ssize_t n = pwrite(fd.get(), "", 1, capacity_bytes - 1); n < 0) { |
| return fitx::error{errno}; |
| } |
| return fitx::ok(); |
| } |
| |
| fitx::result<error_type, zbi_header_t> StorageTraits<fbl::unique_fd>::Header( |
| const fbl::unique_fd& fd, uint32_t offset) { |
| zbi_header_t header; |
| ssize_t n = pread(fd.get(), &header, sizeof(header), offset); |
| if (n < 0) { |
| return fitx::error{errno}; |
| } |
| if (static_cast<size_t>(n) < sizeof(header)) { |
| return fitx::error{ESPIPE}; |
| } |
| return fitx::ok(header); |
| } |
| |
| fitx::result<error_type> StorageTraits<fbl::unique_fd>::Read(const fbl::unique_fd& fd, off_t offset, |
| void* buffer, uint32_t length) { |
| auto p = static_cast<std::byte*>(buffer); |
| while (length > 0) { |
| ssize_t n = pread(fd.get(), p, length, offset); |
| if (n < 0) { |
| return fitx::error{errno}; |
| } |
| if (n == 0) { |
| return fitx::error{ESPIPE}; |
| } |
| ZX_DEBUG_ASSERT(static_cast<size_t>(n) <= length); |
| length -= static_cast<uint32_t>(n); |
| p += n; |
| offset += n; |
| } |
| return fitx::ok(); |
| } |
| |
| fitx::result<error_type> StorageTraits<fbl::unique_fd>::DoRead(const fbl::unique_fd& fd, |
| off_t offset, uint32_t length, |
| bool (*cb)(void*, ByteView), |
| void* arg) { |
| if (length == 0) { |
| cb(arg, {}); |
| return fitx::ok(); |
| } |
| |
| // This always copies, when mmap'ing might be better for large sizes. But |
| // address space is cheap, so users concerned with large sizes can just mmap |
| // the whole ZBI in and use View<std::span> instead. |
| auto size = [&]() { return std::min(static_cast<size_t>(length), kBufferSize); }; |
| std::unique_ptr<std::byte[]> buf{new std::byte[size()]}; |
| |
| while (length > 0) { |
| ssize_t n = pread(fd.get(), buf.get(), size(), offset); |
| if (n < 0) { |
| return fitx::error{errno}; |
| } |
| if (n == 0) { |
| return fitx::error{ESPIPE}; |
| } |
| ZX_ASSERT(static_cast<size_t>(n) <= kBufferSize); |
| if (!cb(arg, {buf.get(), static_cast<size_t>(n)})) { |
| break; |
| } |
| offset += n; |
| length -= static_cast<uint32_t>(n); |
| } |
| |
| return fitx::ok(); |
| } |
| |
| fitx::result<error_type> StorageTraits<fbl::unique_fd>::Write(const fbl::unique_fd& fd, |
| uint32_t offset, ByteView data) { |
| while (!data.empty()) { |
| ssize_t n = pwrite(fd.get(), data.data(), data.size(), offset); |
| if (n < 0) { |
| return fitx::error{errno}; |
| } |
| ZX_ASSERT(static_cast<size_t>(n) <= data.size()); |
| offset += static_cast<uint32_t>(n); |
| data.remove_prefix(static_cast<size_t>(n)); |
| } |
| return fitx::ok(); |
| } |
| |
| } // namespace zbitl |