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