| // Copyright 2017 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 <fcntl.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <digest/digest.h> |
| #include <digest/merkle-tree.h> |
| #include <fs/block-txn.h> |
| #include <fdio/debug.h> |
| #include <fbl/alloc_checker.h> |
| #include <fbl/limits.h> |
| |
| #define MXDEBUG 0 |
| |
| #include "blobstore-private.h" |
| |
| using digest::Digest; |
| using digest::MerkleTree; |
| |
| namespace blobstore { |
| |
| // Number of blocks reserved for the Merkle Tree |
| uint64_t MerkleTreeBlocks(const blobstore_inode_t& blobNode) { |
| uint64_t size_merkle = MerkleTree::GetTreeLength(blobNode.blob_size); |
| return fbl::roundup(size_merkle, kBlobstoreBlockSize) / kBlobstoreBlockSize; |
| } |
| |
| // Sanity check the metadata for the blobstore, given a maximum number of |
| // available blocks. |
| zx_status_t blobstore_check_info(const blobstore_info_t* info, uint64_t max) { |
| if ((info->magic0 != kBlobstoreMagic0) || |
| (info->magic1 != kBlobstoreMagic1)) { |
| fprintf(stderr, "blobstore: bad magic\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (info->version != kBlobstoreVersion) { |
| fprintf(stderr, "blobstore: FS Version: %08x. Driver version: %08x\n", info->version, |
| kBlobstoreVersion); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (info->block_size != kBlobstoreBlockSize) { |
| fprintf(stderr, "blobstore: bsz %u unsupported\n", info->block_size); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if ((info->flags & kBlobstoreFlagFVM) == 0) { |
| if (info->block_count + DataStartBlock(*info) > max) { |
| fprintf(stderr, "blobstore: too large for device\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } else { |
| const size_t blocks_per_slice = info->slice_size / info->block_size; |
| |
| size_t abm_blocks_needed = BlockMapBlocks(*info); |
| size_t abm_blocks_allocated = info->abm_slices * blocks_per_slice; |
| if (abm_blocks_needed > abm_blocks_allocated) { |
| FS_TRACE_ERROR("blobstore: Not enough slices for block bitmap\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } else if (abm_blocks_allocated + BlockMapStartBlock(*info) >= NodeMapStartBlock(*info)) { |
| FS_TRACE_ERROR("blobstore: Block bitmap collides into node map\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| size_t ino_blocks_needed = NodeMapBlocks(*info); |
| size_t ino_blocks_allocated = info->ino_slices * blocks_per_slice; |
| if (ino_blocks_needed > ino_blocks_allocated) { |
| FS_TRACE_ERROR("blobstore: Not enough slices for node map\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } else if (ino_blocks_allocated + NodeMapStartBlock(*info) >= DataStartBlock(*info)) { |
| FS_TRACE_ERROR("blobstore: Node bitmap collides into data blocks\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| size_t dat_blocks_needed = DataBlocks(*info); |
| size_t dat_blocks_allocated = info->dat_slices * blocks_per_slice; |
| if (dat_blocks_needed > dat_blocks_allocated) { |
| FS_TRACE_ERROR("blobstore: Not enough slices for data blocks\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } else if (dat_blocks_allocated + DataStartBlock(*info) > |
| fbl::numeric_limits<uint32_t>::max()) { |
| FS_TRACE_ERROR("blobstore: Data blocks overflow uint32\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| if (info->blob_header_next != 0) { |
| fprintf(stderr, "blobstore: linked blob headers not yet supported\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t blobstore_get_blockcount(int fd, uint64_t* out) { |
| #ifdef __Fuchsia__ |
| block_info_t info; |
| ssize_t r; |
| if ((r = ioctl_block_get_info(fd, &info)) < 0) { |
| return static_cast<zx_status_t>(r); |
| } |
| *out = (info.block_size * info.block_count) / kBlobstoreBlockSize; |
| #else |
| struct stat s; |
| if (fstat(fd, &s) < 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| *out = s.st_size / kBlobstoreBlockSize; |
| #endif |
| return ZX_OK; |
| } |
| |
| zx_status_t readblk(int fd, uint64_t bno, void* data) { |
| off_t off = bno * kBlobstoreBlockSize; |
| if (lseek(fd, off, SEEK_SET) < 0) { |
| fprintf(stderr, "blobstore: cannot seek to block %" PRIu64 "\n", bno); |
| return ZX_ERR_IO; |
| } |
| if (read(fd, data, kBlobstoreBlockSize) != kBlobstoreBlockSize) { |
| fprintf(stderr, "blobstore: cannot read block %" PRIu64 "\n", bno); |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t writeblk(int fd, uint64_t bno, const void* data) { |
| off_t off = bno * kBlobstoreBlockSize; |
| if (lseek(fd, off, SEEK_SET) < 0) { |
| fprintf(stderr, "blobstore: cannot seek to block %" PRIu64 "\n", bno); |
| return ZX_ERR_IO; |
| } |
| if (write(fd, data, kBlobstoreBlockSize) != kBlobstoreBlockSize) { |
| fprintf(stderr, "blobstore: cannot write block %" PRIu64 "\n", bno); |
| return ZX_ERR_IO; |
| } |
| return ZX_OK; |
| } |
| |
| int blobstore_mkfs(int fd, uint64_t block_count) { |
| uint64_t inodes = 32768; |
| |
| blobstore_info_t info; |
| memset(&info, 0x00, sizeof(info)); |
| info.magic0 = kBlobstoreMagic0; |
| info.magic1 = kBlobstoreMagic1; |
| info.version = kBlobstoreVersion; |
| info.flags = kBlobstoreFlagClean; |
| info.block_size = kBlobstoreBlockSize; |
| // Set block_count to max blocks so we can calculate block map blocks |
| info.block_count = block_count; |
| info.inode_count = inodes; |
| info.alloc_block_count = 0; |
| info.alloc_inode_count = 0; |
| info.blob_header_next = 0; // TODO(smklein): Allow chaining |
| // The result of DataStartBlock(info) is based on the current value of info.block_count. As a |
| // result, the block bitmap may have slightly more space allocated than is necessary. |
| info.block_count -= DataStartBlock(info); // Set block_count to number of data blocks |
| |
| |
| #ifdef __Fuchsia__ |
| fvm_info_t fvm_info; |
| |
| if (ioctl_block_fvm_query(fd, &fvm_info) >= 0) { |
| info.slice_size = fvm_info.slice_size; |
| info.flags |= kBlobstoreFlagFVM; |
| |
| if (info.slice_size % kBlobstoreBlockSize) { |
| fprintf(stderr, "blobstore mkfs: Slice size not multiple of blobstore block\n"); |
| return -1; |
| } |
| |
| const size_t kBlocksPerSlice = info.slice_size / kBlobstoreBlockSize; |
| |
| extend_request_t request; |
| request.length = 1; |
| request.offset = kFVMBlockMapStart / kBlocksPerSlice; |
| if (ioctl_block_fvm_extend(fd, &request) < 0) { |
| fprintf(stderr, "blobstore mkfs: Failed to allocate block map\n"); |
| return -1; |
| } |
| request.offset = kFVMNodeMapStart / kBlocksPerSlice; |
| if (ioctl_block_fvm_extend(fd, &request) < 0) { |
| fprintf(stderr, "blobstore mkfs: Failed to allocate node map\n"); |
| return -1; |
| } |
| request.offset = kFVMDataStart / kBlocksPerSlice; |
| if (ioctl_block_fvm_extend(fd, &request) < 0) { |
| fprintf(stderr, "blobstore mkfs: Failed to allocate data blocks\n"); |
| return -1; |
| } |
| |
| info.abm_slices = 1; |
| info.ino_slices = 1; |
| info.dat_slices = 1; |
| info.vslice_count = info.abm_slices + info.ino_slices + info.dat_slices + 1; |
| |
| info.inode_count = static_cast<uint32_t>(info.ino_slices * info.slice_size |
| / kBlobstoreInodeSize); |
| info.block_count = static_cast<uint32_t>(info.dat_slices * info.slice_size |
| / kBlobstoreBlockSize); |
| } else { |
| info.block_count -= DataStartBlock(info); |
| } |
| #endif |
| |
| xprintf("Blobstore Mkfs\n"); |
| xprintf("Disk size : %" PRIu64 "\n", block_count * kBlobstoreBlockSize); |
| xprintf("Block Size : %u\n", kBlobstoreBlockSize); |
| xprintf("Block Count: %" PRIu64 "\n", TotalBlocks(info)); |
| xprintf("Inode Count: %" PRIu64 "\n", inodes); |
| xprintf("FVM-aware: %s\n", (info.flags & kBlobstoreFlagFVM) ? "YES" : "NO"); |
| |
| // Determine the number of blocks necessary for the block map and node map. |
| uint64_t bbm_blocks = BlockMapBlocks(info); |
| uint64_t nbm_blocks = NodeMapBlocks(info); |
| |
| RawBitmap abm; |
| if (abm.Reset(bbm_blocks * kBlobstoreBlockBits)) { |
| fprintf(stderr, "Couldn't allocate blobstore block map\n"); |
| return -1; |
| } else if (abm.Shrink(info.block_count)) { |
| fprintf(stderr, "Couldn't shrink blobstore block map\n"); |
| return -1; |
| } |
| |
| if (info.inode_count * sizeof(blobstore_inode_t) != nbm_blocks * kBlobstoreBlockSize) { |
| fprintf(stderr, "For simplicity, inode table block must be entirely filled\n"); |
| return -1; |
| } |
| |
| // All in-memory structures have been created successfully. Dump everything to disk. |
| char block[kBlobstoreBlockSize]; |
| zx_status_t status; |
| |
| // write the root block to disk |
| memset(block, 0, sizeof(block)); |
| memcpy(block, &info, sizeof(info)); |
| if ((status = writeblk(fd, 0, block)) != ZX_OK) { |
| fprintf(stderr, "Failed to write root block\n"); |
| return status; |
| } |
| |
| // write allocation bitmap to disk |
| for (uint64_t n = 0; n < bbm_blocks; n++) { |
| void* bmdata = get_raw_bitmap_data(abm, n); |
| if ((status = writeblk(fd, BlockMapStartBlock(info) + n, bmdata)) < 0) { |
| fprintf(stderr, "Failed to write blockmap block %" PRIu64 "\n", n); |
| return status; |
| } |
| } |
| |
| // write node map to disk |
| for (uint64_t n = 0; n < nbm_blocks; n++) { |
| memset(block, 0, sizeof(block)); |
| if (writeblk(fd, NodeMapStartBlock(info) + n, block)) { |
| fprintf(stderr, "blobstore: failed writing inode map\n"); |
| return ZX_ERR_IO; |
| } |
| } |
| |
| xprintf("BLOBSTORE: mkfs success\n"); |
| return 0; |
| } |
| |
| } // namespace blobstore |
| |
| #ifndef __Fuchsia__ |
| // This is used by the ioctl wrappers in zircon/device/device.h. It's not |
| // called by host tools, so just satisfy the linker with a stub. |
| ssize_t fdio_ioctl(int fd, int op, const void* in_buf, size_t in_len, void* out_buf, size_t out_len) { |
| return -1; |
| } |
| #endif |