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 <fcntl.h> 6#include <inttypes.h> 7#include <stdarg.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/stat.h> 12#include <unistd.h> 13 14#include <digest/digest.h> 15#include <digest/merkle-tree.h> 16#include <fbl/alloc_checker.h> 17#include <fbl/limits.h> 18#include <lib/fdio/debug.h> 19#include <fs/block-txn.h> 20#include <fs/trace.h> 21 22#ifdef __Fuchsia__ 23#include <fs/fvm.h> 24#endif 25 26#define ZXDEBUG 0 27 28#include <blobfs/common.h> 29 30using digest::Digest; 31using digest::MerkleTree; 32 33namespace blobfs { 34 35// Number of blocks reserved for the Merkle Tree 36uint64_t MerkleTreeBlocks(const blobfs_inode_t& blobNode) { 37 uint64_t size_merkle = MerkleTree::GetTreeLength(blobNode.blob_size); 38 return fbl::round_up(size_merkle, kBlobfsBlockSize) / kBlobfsBlockSize; 39} 40 41// Sanity check the metadata for the blobfs, given a maximum number of 42// available blocks. 43zx_status_t blobfs_check_info(const blobfs_info_t* info, uint64_t max) { 44 if ((info->magic0 != kBlobfsMagic0) || 45 (info->magic1 != kBlobfsMagic1)) { 46 fprintf(stderr, "blobfs: bad magic\n"); 47 return ZX_ERR_INVALID_ARGS; 48 } 49 if (info->version != kBlobfsVersion) { 50 fprintf(stderr, "blobfs: FS Version: %08x. Driver version: %08x\n", info->version, 51 kBlobfsVersion); 52 return ZX_ERR_INVALID_ARGS; 53 } 54 if (info->block_size != kBlobfsBlockSize) { 55 fprintf(stderr, "blobfs: bsz %u unsupported\n", info->block_size); 56 return ZX_ERR_INVALID_ARGS; 57 } 58 if ((info->flags & kBlobFlagFVM) == 0) { 59 if (info->block_count + DataStartBlock(*info) > max) { 60 fprintf(stderr, "blobfs: too large for device\n"); 61 return ZX_ERR_INVALID_ARGS; 62 } 63 } else { 64 const size_t blocks_per_slice = info->slice_size / info->block_size; 65 66 size_t abm_blocks_needed = BlockMapBlocks(*info); 67 size_t abm_blocks_allocated = info->abm_slices * blocks_per_slice; 68 if (abm_blocks_needed > abm_blocks_allocated) { 69 FS_TRACE_ERROR("blobfs: Not enough slices for block bitmap\n"); 70 return ZX_ERR_INVALID_ARGS; 71 } else if (abm_blocks_allocated + BlockMapStartBlock(*info) >= NodeMapStartBlock(*info)) { 72 FS_TRACE_ERROR("blobfs: Block bitmap collides into node map\n"); 73 return ZX_ERR_INVALID_ARGS; 74 } 75 76 size_t ino_blocks_needed = NodeMapBlocks(*info); 77 size_t ino_blocks_allocated = info->ino_slices * blocks_per_slice; 78 if (ino_blocks_needed > ino_blocks_allocated) { 79 FS_TRACE_ERROR("blobfs: Not enough slices for node map\n"); 80 return ZX_ERR_INVALID_ARGS; 81 } else if (ino_blocks_allocated + NodeMapStartBlock(*info) >= DataStartBlock(*info)) { 82 FS_TRACE_ERROR("blobfs: Node bitmap collides into data blocks\n"); 83 return ZX_ERR_INVALID_ARGS; 84 } 85 86 size_t dat_blocks_needed = DataBlocks(*info); 87 size_t dat_blocks_allocated = info->dat_slices * blocks_per_slice; 88 if (dat_blocks_needed < kStartBlockMinimum) { 89 FS_TRACE_ERROR("blobfs: Partition too small; no space left for data blocks\n"); 90 return ZX_ERR_INVALID_ARGS; 91 } else if (dat_blocks_needed > dat_blocks_allocated) { 92 FS_TRACE_ERROR("blobfs: Not enough slices for data blocks\n"); 93 return ZX_ERR_INVALID_ARGS; 94 } else if (dat_blocks_allocated + DataStartBlock(*info) > 95 fbl::numeric_limits<uint32_t>::max()) { 96 FS_TRACE_ERROR("blobfs: Data blocks overflow uint32\n"); 97 return ZX_ERR_INVALID_ARGS; 98 } 99 } 100 if (info->blob_header_next != 0) { 101 fprintf(stderr, "blobfs: linked blob headers not yet supported\n"); 102 return ZX_ERR_INVALID_ARGS; 103 } 104 return ZX_OK; 105} 106 107zx_status_t blobfs_get_blockcount(int fd, uint64_t* out) { 108#ifdef __Fuchsia__ 109 block_info_t info; 110 ssize_t r; 111 if ((r = ioctl_block_get_info(fd, &info)) < 0) { 112 return static_cast<zx_status_t>(r); 113 } 114 *out = (info.block_size * info.block_count) / kBlobfsBlockSize; 115#else 116 struct stat s; 117 if (fstat(fd, &s) < 0) { 118 return ZX_ERR_BAD_STATE; 119 } 120 *out = s.st_size / kBlobfsBlockSize; 121#endif 122 return ZX_OK; 123} 124 125zx_status_t readblk(int fd, uint64_t bno, void* data) { 126 off_t off = bno * kBlobfsBlockSize; 127 if (lseek(fd, off, SEEK_SET) < 0) { 128 fprintf(stderr, "blobfs: cannot seek to block %" PRIu64 "\n", bno); 129 return ZX_ERR_IO; 130 } 131 if (read(fd, data, kBlobfsBlockSize) != kBlobfsBlockSize) { 132 fprintf(stderr, "blobfs: cannot read block %" PRIu64 "\n", bno); 133 return ZX_ERR_IO; 134 } 135 return ZX_OK; 136} 137 138zx_status_t writeblk(int fd, uint64_t bno, const void* data) { 139 off_t off = bno * kBlobfsBlockSize; 140 if (lseek(fd, off, SEEK_SET) < 0) { 141 fprintf(stderr, "blobfs: cannot seek to block %" PRIu64 "\n", bno); 142 return ZX_ERR_IO; 143 } 144 if (write(fd, data, kBlobfsBlockSize) != kBlobfsBlockSize) { 145 fprintf(stderr, "blobfs: cannot write block %" PRIu64 "\n", bno); 146 return ZX_ERR_IO; 147 } 148 return ZX_OK; 149} 150 151int blobfs_mkfs(int fd, uint64_t block_count) { 152 uint64_t inodes = kBlobfsDefaultInodeCount; 153 154 blobfs_info_t info; 155 memset(&info, 0x00, sizeof(info)); 156 info.magic0 = kBlobfsMagic0; 157 info.magic1 = kBlobfsMagic1; 158 info.version = kBlobfsVersion; 159 info.flags = kBlobFlagClean; 160 info.block_size = kBlobfsBlockSize; 161 // Set block_count to max blocks so we can calculate block map blocks 162 info.block_count = block_count; 163 info.inode_count = inodes; 164 info.alloc_block_count = 0; 165 info.alloc_inode_count = 0; 166 info.blob_header_next = 0; // TODO(smklein): Allow chaining 167 // The result of DataStartBlock(info) is based on the current value of info.block_count. As a 168 // result, the block bitmap may have slightly more space allocated than is necessary. 169 info.block_count -= DataStartBlock(info); // Set block_count to number of data blocks 170 171#ifdef __Fuchsia__ 172 fvm_info_t fvm_info; 173 174 if (ioctl_block_fvm_query(fd, &fvm_info) >= 0) { 175 info.slice_size = fvm_info.slice_size; 176 info.flags |= kBlobFlagFVM; 177 178 if (info.slice_size % kBlobfsBlockSize) { 179 fprintf(stderr, "blobfs mkfs: Slice size not multiple of blobfs block\n"); 180 return -1; 181 } 182 183 if (fs::fvm_reset_volume_slices(fd) != ZX_OK) { 184 fprintf(stderr, "blobfs mkfs: Failed to reset slices\n"); 185 return -1; 186 } 187 188 const size_t kBlocksPerSlice = info.slice_size / kBlobfsBlockSize; 189 190 extend_request_t request; 191 request.length = 1; 192 request.offset = kFVMBlockMapStart / kBlocksPerSlice; 193 if (ioctl_block_fvm_extend(fd, &request) < 0) { 194 fprintf(stderr, "blobfs mkfs: Failed to allocate block map\n"); 195 return -1; 196 } 197 request.offset = kFVMNodeMapStart / kBlocksPerSlice; 198 if (ioctl_block_fvm_extend(fd, &request) < 0) { 199 fprintf(stderr, "blobfs mkfs: Failed to allocate node map\n"); 200 return -1; 201 } 202 request.offset = kFVMDataStart / kBlocksPerSlice; 203 request.length = fbl::round_up(kMinimumDataBlocks, kBlocksPerSlice) / kBlocksPerSlice; 204 if (ioctl_block_fvm_extend(fd, &request) < 0) { 205 fprintf(stderr, "blobfs mkfs: Failed to allocate data blocks\n"); 206 return -1; 207 } 208 209 info.abm_slices = 1; 210 info.ino_slices = 1; 211 info.dat_slices = static_cast<uint32_t>(request.length); 212 213 info.vslice_count = info.abm_slices + info.ino_slices + info.dat_slices + 1; 214 215 info.inode_count = static_cast<uint32_t>(info.ino_slices * info.slice_size / kBlobfsInodeSize); 216 info.block_count = static_cast<uint32_t>(info.dat_slices * info.slice_size / kBlobfsBlockSize); 217 } 218#endif 219 220 xprintf("Blobfs Mkfs\n"); 221 xprintf("Disk size : %" PRIu64 "\n", block_count * kBlobfsBlockSize); 222 xprintf("Block Size : %u\n", kBlobfsBlockSize); 223 xprintf("Block Count: %" PRIu64 "\n", TotalBlocks(info)); 224 xprintf("Inode Count: %" PRIu64 "\n", inodes); 225 xprintf("FVM-aware: %s\n", (info.flags & kBlobFlagFVM) ? "YES" : "NO"); 226 227 if (info.block_count < kMinimumDataBlocks) { 228 fprintf(stderr, "blobfs mkfs: Not enough space for minimum blobfs size\n"); 229 return -1; 230 } 231 232 // Determine the number of blocks necessary for the block map and node map. 233 uint64_t bbm_blocks = BlockMapBlocks(info); 234 uint64_t nbm_blocks = NodeMapBlocks(info); 235 236 RawBitmap abm; 237 if (abm.Reset(bbm_blocks * kBlobfsBlockBits)) { 238 fprintf(stderr, "Couldn't allocate blobfs block map\n"); 239 return -1; 240 } else if (abm.Shrink(info.block_count)) { 241 fprintf(stderr, "Couldn't shrink blobfs block map\n"); 242 return -1; 243 } 244 245 // Reserve first 2 data blocks 246 abm.Set(0, kStartBlockMinimum); 247 info.alloc_block_count += kStartBlockMinimum; 248 249 if (info.inode_count * sizeof(blobfs_inode_t) != nbm_blocks * kBlobfsBlockSize) { 250 fprintf(stderr, "For simplicity, inode table block must be entirely filled\n"); 251 return -1; 252 } 253 254 // All in-memory structures have been created successfully. Dump everything to disk. 255 char block[kBlobfsBlockSize]; 256 zx_status_t status; 257 258 // write the root block to disk 259 memset(block, 0, sizeof(block)); 260 memcpy(block, &info, sizeof(info)); 261 if ((status = writeblk(fd, 0, block)) != ZX_OK) { 262 fprintf(stderr, "Failed to write root block\n"); 263 return status; 264 } 265 266 // write allocation bitmap to disk 267 for (uint64_t n = 0; n < bbm_blocks; n++) { 268 void* bmdata = get_raw_bitmap_data(abm, n); 269 if ((status = writeblk(fd, BlockMapStartBlock(info) + n, bmdata)) < 0) { 270 fprintf(stderr, "Failed to write blockmap block %" PRIu64 "\n", n); 271 return status; 272 } 273 } 274 275 // write node map to disk 276 for (uint64_t n = 0; n < nbm_blocks; n++) { 277 memset(block, 0, sizeof(block)); 278 if (writeblk(fd, NodeMapStartBlock(info) + n, block)) { 279 fprintf(stderr, "blobfs: failed writing inode map\n"); 280 return ZX_ERR_IO; 281 } 282 } 283 284 xprintf("BLOBFS: mkfs success\n"); 285 return 0; 286} 287 288} // namespace blobfs 289