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 <blobfs/fsck.h>
6#include <inttypes.h>
7
8#ifdef __Fuchsia__
9#include <blobfs/blobfs.h>
10#else
11#include <blobfs/host.h>
12#endif
13
14//TODO(planders): Add more checks for fsck.
15namespace blobfs {
16
17void BlobfsChecker::TraverseInodeBitmap() {
18    for (unsigned n = 0; n < blobfs_->info_.inode_count; n++) {
19        blobfs_inode_t* inode = blobfs_->GetNode(n);
20        if (inode->start_block >= kStartBlockMinimum) {
21            alloc_inodes_++;
22            inode_blocks_ += static_cast<uint32_t>(inode->num_blocks);
23
24            size_t start_block = inode->start_block;
25            size_t end_block = inode->start_block + inode->num_blocks;
26            bool valid = true;
27
28            size_t first_unset = 0;
29            if (!blobfs_->block_map_.Get(start_block, end_block, &first_unset)) {
30                FS_TRACE_ERROR("check: ino %u using blocks [%zu, %zu). "
31                               "Not fully allocated in block bitmap; first unset @%zu\n",
32                               n, start_block, end_block, first_unset);
33                valid = false;
34            }
35
36            if (blobfs_->VerifyBlob(n) != ZX_OK) {
37                FS_TRACE_ERROR("check: detected inode %u with bad state\n", n);
38                valid = false;
39            }
40            if (!valid) {
41                error_blobs_++;
42            }
43        }
44    }
45}
46
47void BlobfsChecker::TraverseBlockBitmap() {
48    for (uint64_t n = 0; n < blobfs_->info_.block_count; n++) {
49        if (blobfs_->block_map_.Get(n, n + 1)) {
50            alloc_blocks_++;
51        }
52    }
53}
54
55zx_status_t BlobfsChecker::CheckAllocatedCounts() const {
56    zx_status_t status = ZX_OK;
57    if (alloc_blocks_ != blobfs_->info_.alloc_block_count) {
58        FS_TRACE_ERROR("check: incorrect allocated block count %" PRIu64
59                       " (should be %u)\n",
60                       blobfs_->info_.alloc_block_count, alloc_blocks_);
61        status = ZX_ERR_BAD_STATE;
62    }
63
64    if (alloc_blocks_ < kStartBlockMinimum) {
65        FS_TRACE_ERROR("check: allocated blocks (%u) are less than minimum%" PRIu64 "\n",
66                       alloc_blocks_, kStartBlockMinimum);
67        status = ZX_ERR_BAD_STATE;
68    }
69
70    if (inode_blocks_ + kStartBlockMinimum != alloc_blocks_) {
71        FS_TRACE_ERROR("check: bitmap allocated blocks (%u) do not match inode allocated blocks "
72                       "(%" PRIu64 ")\n", alloc_blocks_, inode_blocks_ + kStartBlockMinimum);
73        status = ZX_ERR_BAD_STATE;
74    }
75
76    if (alloc_inodes_ != blobfs_->info_.alloc_inode_count) {
77        FS_TRACE_ERROR("check: incorrect allocated inode count %" PRIu64
78                       " (should be %u)\n",
79                       blobfs_->info_.alloc_inode_count, alloc_inodes_);
80        status = ZX_ERR_BAD_STATE;
81    }
82
83    if (error_blobs_) {
84        status = ZX_ERR_BAD_STATE;
85    }
86
87    return status;
88}
89
90BlobfsChecker::BlobfsChecker()
91    : blobfs_(nullptr), alloc_inodes_(0), alloc_blocks_(0), error_blobs_(0), inode_blocks_(0) {};
92
93void BlobfsChecker::Init(fbl::unique_ptr<Blobfs> blob) {
94    blobfs_ = fbl::move(blob);
95}
96
97zx_status_t blobfs_check(fbl::unique_ptr<Blobfs> blob) {
98    zx_status_t status = ZX_OK;
99    BlobfsChecker chk;
100    chk.Init(fbl::move(blob));
101    chk.TraverseInodeBitmap();
102    chk.TraverseBlockBitmap();
103    status |= (status != ZX_OK) ? 0 : chk.CheckAllocatedCounts();
104    return status;
105}
106
107} // namespace blobfs
108