root/src/tools/bfs_shell/command_checkfs.cpp
/*
 * Copyright 2008-2017, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include "fssh_stdio.h"
#include "syscalls.h"

#include "bfs.h"
#include "bfs_control.h"


namespace FSShell {


fssh_status_t
command_checkfs(int argc, const char* const* argv)
{
        if (argc == 2 && !strcmp(argv[1], "--help")) {
                fssh_dprintf("Usage: %s [-c]\n"
                        "  -c  Check only; don't perform any changes\n", argv[0]);
                return B_OK;
        }

        bool checkOnly = false;
        if (argc == 2 && !strcmp(argv[1], "-c"))
                checkOnly = true;

        int rootDir = _kern_open_dir(-1, "/myfs");
        if (rootDir < 0)
                return rootDir;

        struct check_control result;
        memset(&result, 0, sizeof(result));
        result.magic = BFS_IOCTL_CHECK_MAGIC;
        result.flags = 0;
        if (!checkOnly) {
                result.flags |= BFS_FIX_BITMAP_ERRORS | BFS_REMOVE_WRONG_TYPES
                        | BFS_REMOVE_INVALID | BFS_FIX_NAME_MISMATCHES | BFS_FIX_BPLUSTREES;
        }

        // start checking
        fssh_status_t status = _kern_ioctl(rootDir, BFS_IOCTL_START_CHECKING,
                &result, sizeof(result));
        if (status != B_OK)
            return status;

        uint64 attributeDirectories = 0, attributes = 0;
        uint64 files = 0, directories = 0, indices = 0;
        uint64 counter = 0;
        uint32 previousPass = result.pass;

        // check all files and report errors
        while (_kern_ioctl(rootDir, BFS_IOCTL_CHECK_NEXT_NODE, &result,
                        sizeof(result)) == B_OK) {
                if (++counter % 50 == 0) {
                        fssh_dprintf("%9" FSSH_B_PRIu64 " nodes processed\x1b[1A\n",
                                counter);
                }

                if (result.pass == BFS_CHECK_PASS_BITMAP) {
                        if (result.errors) {
                                fssh_dprintf("%s (inode = %" FSSH_B_PRIdINO ")", result.name,
                                        result.inode);
                                if ((result.errors & BFS_MISSING_BLOCKS) != 0)
                                        fssh_dprintf(", some blocks weren't allocated");
                                if ((result.errors & BFS_BLOCKS_ALREADY_SET) != 0)
                                        fssh_dprintf(", has blocks already set");
                                if ((result.errors & BFS_INVALID_BLOCK_RUN) != 0)
                                        fssh_dprintf(", has invalid block run(s)");
                                if ((result.errors & BFS_COULD_NOT_OPEN) != 0)
                                        fssh_dprintf(", could not be opened");
                                if ((result.errors & BFS_WRONG_TYPE) != 0)
                                        fssh_dprintf(", has wrong type");
                                if ((result.errors & BFS_NAMES_DONT_MATCH) != 0)
                                        fssh_dprintf(", names don't match");
                                if ((result.errors & BFS_INVALID_BPLUSTREE) != 0)
                                        fssh_dprintf(", invalid b+tree");
                                fssh_dprintf("\n");
                        }

                        if ((result.mode & (S_INDEX_DIR | 0777)) == S_INDEX_DIR)
                                indices++;
                        else if (result.mode & S_ATTR_DIR)
                                attributeDirectories++;
                        else if (result.mode & S_ATTR)
                                attributes++;
                        else if (S_ISDIR(result.mode))
                                directories++;
                        else
                                files++;
                } else if (result.pass == BFS_CHECK_PASS_INDEX) {
                        if (previousPass != result.pass) {
                                fssh_dprintf("Recreating broken index b+trees...\n");
                                previousPass = result.pass;
                                counter = 0;
                        }
                }
        }

        // stop checking
        if (_kern_ioctl(rootDir, BFS_IOCTL_STOP_CHECKING, &result, sizeof(result))
                        != B_OK) {
                _kern_close(rootDir);
                return errno;
        }

        _kern_close(rootDir);

        fssh_dprintf("        %" FSSH_B_PRIu64 " nodes checked,\n\t%" FSSH_B_PRIu64
                " blocks not allocated,\n\t%" FSSH_B_PRIu64 " blocks already set,\n\t%"
                B_PRIu64 " blocks could be freed\n\n", counter, result.stats.missing,
                result.stats.already_set, result.stats.freed);
        fssh_dprintf("\tfiles\t\t%" FSSH_B_PRIu64 "\n\tdirectories\t%"
                FSSH_B_PRIu64 "\n"
                "\tattributes\t%" FSSH_B_PRIu64 "\n\tattr. dirs\t%" FSSH_B_PRIu64 "\n"
                "\tindices\t\t%" FSSH_B_PRIu64 "\n", files, directories, attributes,
                attributeDirectories, indices);

        fssh_dprintf("\n\tdirect block runs\t\t%" FSSH_B_PRIu64 " (%" FSSH_B_PRIu64
                ")\n", result.stats.direct_block_runs,
                result.stats.blocks_in_direct * result.stats.block_size);
        fssh_dprintf("\tindirect block runs\t\t%" FSSH_B_PRIu64 " (in %"
                FSSH_B_PRIu64 " array blocks, %" FSSH_B_PRIu64 ")\n",
                result.stats.indirect_block_runs, result.stats.indirect_array_blocks,
                result.stats.blocks_in_indirect * result.stats.block_size);
        fssh_dprintf("\tdouble indirect block runs\t%" FSSH_B_PRIu64 " (in %"
                FSSH_B_PRIu64 " array blocks, %" FSSH_B_PRIu64 ")\n",
                result.stats.double_indirect_block_runs,
                result.stats.double_indirect_array_blocks,
                result.stats.blocks_in_double_indirect * result.stats.block_size);

        if (result.status == B_ENTRY_NOT_FOUND)
                result.status = B_OK;

        return result.status;
}


}       // namespace FSShell