Initial commit

This commit is contained in:
2025-11-06 10:49:44 +08:00
commit 2d16528ac4
20 changed files with 4653 additions and 0 deletions

182
.gitignore vendored Normal file
View File

@@ -0,0 +1,182 @@
build/
### C++ ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### CLion ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### CLion Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/
# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml
# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/
# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$
# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml
# Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
.idea/**/azureSettings.xml
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

56
CMakeLists.txt Normal file
View File

@@ -0,0 +1,56 @@
cmake_minimum_required(VERSION 3.10)
project(SimpleFS CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# Set include directory
include_directories(include)
# mkfs.simplefs executable
add_executable(mkfs.simplefs
tools/mkfs.cpp
src/disk_io.cpp
src/utils.cpp
)
target_link_libraries(mkfs.simplefs PRIVATE m)
# fsck.simplefs executable
add_executable(fsck.simplefs
tools/fsck.cpp
src/disk_io.cpp
src/utils.cpp
)
target_link_libraries(fsck.simplefs PRIVATE m)
# SimpleFS FUSE daemon
find_package(PkgConfig REQUIRED)
pkg_check_modules(FUSE REQUIRED fuse)
include_directories(${FUSE_INCLUDE_DIRS})
link_directories(${FUSE_LIBRARY_DIRS})
add_executable(simplefs
src/main.cpp
src/fuse_ops.cpp
src/disk_io.cpp
src/metadata.cpp
src/utils.cpp
)
target_link_libraries(simplefs PRIVATE ${FUSE_LIBRARIES})
# Add required FUSE definitions specifically for simplefs target
target_compile_definitions(simplefs PRIVATE _FILE_OFFSET_BITS=64 FUSE_USE_VERSION=29)
# Enable warnings
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -g")
endif()
# Remove global add_definitions for FUSE if they were here, now target-specific
message(STATUS "Project SimpleFS configured (mkfs.simplefs only)")
message(STATUS "CXX Standard: ${CMAKE_CXX_STANDARD}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_VERSION}")
message(STATUS "Source directory: ${CMAKE_SOURCE_DIR}")
message(STATUS "Binary directory: ${CMAKE_BINARY_DIR}")

10
CMakePresets.json Normal file
View File

@@ -0,0 +1,10 @@
{
"version": 4,
"configurePresets": [
{
"name": "default",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build"
}
]
}

10
build.sh Normal file
View File

@@ -0,0 +1,10 @@
#!/bin/bash
cmake --preset default
cd build
ninja
if [ $? -eq 0 ]; then
echo "Build successful!"
else
echo "Build failed!"
exit 1
fi

16
include/disk_io.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "simplefs.h"
#include <cstdint>
#include <vector>
using DeviceFd = int;
// 读取单个块
int read_block(DeviceFd fd, uint32_t block_num, void* buffer);
// 写入单个块
int write_block(DeviceFd fd, uint32_t block_num, const void* buffer);
// 写入零块
int write_zero_blocks(DeviceFd fd, uint32_t start_block_num, uint32_t count);

33
include/fuse_ops.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include <fuse_lowlevel.h>
#include <fuse.h>
#include "simplefs_context.h"
#include "simplefs.h"
// 获取文件系统上下文
SimpleFS_Context* get_fs_context();
// FUSE操作实现
int simplefs_getattr(const char *path, struct stat *stbuf);
int simplefs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi);
int simplefs_mknod(const char *path, mode_t mode, dev_t rdev);
int simplefs_mkdir(const char *path, mode_t mode);
int simplefs_unlink(const char *path);
int simplefs_rmdir(const char *path);
int simplefs_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi);
int simplefs_write(const char *path, const char *buf, size_t size, off_t offset,
struct fuse_file_info *fi);
int simplefs_truncate(const char *path, off_t size);
int simplefs_chmod(const char *path, mode_t mode);
int simplefs_chown(const char *path, uid_t uid, gid_t gid);
int simplefs_utimens(const char *path, const struct timespec tv[2]);
int simplefs_access(const char *path, int mask);
int simplefs_statfs(const char *path, struct statvfs *stbuf);
int simplefs_symlink(const char *target, const char *linkpath);
int simplefs_readlink(const char *path, char *buf, size_t size);
// 初始化FUSE操作结构
void init_fuse_operations(struct fuse_operations *ops);

48
include/metadata.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include "simplefs.h"
#include "simplefs_context.h"
#include <cstdint>
#include <vector>
#include <string>
// inode管理
uint32_t alloc_inode(SimpleFS_Context& context, mode_t mode);
void free_inode(SimpleFS_Context& context, uint32_t inode_num, mode_t mode_of_freed_inode);
// 块管理
uint32_t alloc_block(SimpleFS_Context& context, uint32_t preferred_group_for_inode = static_cast<uint32_t>(-1));
void free_block(SimpleFS_Context& context, uint32_t block_num);
// inode读写
int write_inode_to_disk(SimpleFS_Context& context, uint32_t inode_num, const SimpleFS_Inode* inode_data);
int read_inode_from_disk(SimpleFS_Context& context, uint32_t inode_num, SimpleFS_Inode* inode_struct);
// 目录操作
int add_dir_entry(SimpleFS_Context& context, SimpleFS_Inode* parent_inode, uint32_t parent_inode_num,
const std::string& entry_name, uint32_t child_inode_num, uint8_t file_type);
int remove_dir_entry(SimpleFS_Context& context, SimpleFS_Inode* parent_inode, uint32_t parent_inode_num, const std::string& entry_name_to_remove);
// 路径解析
void parse_path(const std::string& path, std::string& dirname, std::string& basename);
// 位图操作
void set_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
void clear_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
bool is_bitmap_bit_set(const std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
// 元数据同步
void sync_fs_metadata(SimpleFS_Context& context);
// 权限检查
struct fuse_context;
int check_access(const struct fuse_context* caller_context, const SimpleFS_Inode* inode, int requested_perm);
// 块释放
void free_all_inode_blocks(SimpleFS_Context& context, SimpleFS_Inode* inode);
// 块映射
uint32_t get_or_alloc_dir_block(SimpleFS_Context& context, SimpleFS_Inode* dir_inode, uint32_t dir_inode_num, uint32_t logical_block_idx);
uint32_t map_logical_to_physical_block(SimpleFS_Context& context, const SimpleFS_Inode* inode, uint32_t logical_block_idx);
void release_logical_block_range(SimpleFS_Context& context, SimpleFS_Inode* inode, uint32_t start_lbn, uint32_t end_lbn);

101
include/simplefs.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include <cstdint>
#include <sys/types.h>
#pragma pack(push, 1)
// 文件系统常量定义
constexpr uint16_t SIMPLEFS_MAGIC = 0x5350;
constexpr uint32_t SIMPLEFS_BLOCK_SIZE = 4096;
constexpr uint32_t SIMPLEFS_ROOT_INODE_NUM = 2;
constexpr uint32_t SIMPLEFS_INODE_SIZE = 128;
constexpr uint32_t SIMPLEFS_NUM_DIRECT_BLOCKS = 12;
constexpr uint32_t SIMPLEFS_NUM_INDIRECT_BLOCKS = 1;
constexpr uint32_t SIMPLEFS_NUM_D_INDIRECT_BLOCKS = 1;
constexpr uint32_t SIMPLEFS_NUM_T_INDIRECT_BLOCKS = 1;
constexpr uint32_t SIMPLEFS_INODE_BLOCK_PTRS = SIMPLEFS_NUM_DIRECT_BLOCKS + \
SIMPLEFS_NUM_INDIRECT_BLOCKS + \
SIMPLEFS_NUM_D_INDIRECT_BLOCKS + \
SIMPLEFS_NUM_T_INDIRECT_BLOCKS;
constexpr uint32_t SIMPLEFS_MAX_FILENAME_LEN = 255;
// 文件类型常量
#ifndef S_IFMT
#define S_IFMT 0xF000 // 文件类型掩码
#define S_IFSOCK 0xC000 // 套接字
#define S_IFLNK 0xA000 // 符号链接
#define S_IFREG 0x8000 // 普通文件
#define S_IFBLK 0x6000 // 块设备
#define S_IFDIR 0x4000 // 目录
#define S_IFCHR 0x2000 // 字符设备
#define S_IFIFO 0x1000 // 管道
#endif
// 超级块结构
struct SimpleFS_SuperBlock {
uint16_t s_magic; // 魔数
uint32_t s_inodes_count; // 总inode数
uint32_t s_blocks_count; // 总块数
uint32_t s_free_blocks_count; // 空闲块数
uint32_t s_free_inodes_count; // 空闲inode数
uint32_t s_first_data_block; // 首个数据块
uint32_t s_log_block_size; // 块大小的对数值
uint32_t s_blocks_per_group; // 每组块数
uint32_t s_inodes_per_group; // 每组inode数
uint32_t s_mtime; // 最后挂载时间
uint32_t s_wtime; // 最后写入时间
uint16_t s_mnt_count; // 挂载次数
uint16_t s_max_mnt_count; // 最大挂载次数
uint16_t s_state; // 文件系统状态
uint16_t s_errors; // 错误处理方式
uint32_t s_first_ino; // 首个非保留inode号
uint16_t s_inode_size; // inode大小
uint16_t s_block_group_nr; // 块组号
uint32_t s_root_inode; // 根inode号
uint8_t s_padding[962]; // 填充到1024字节
};
static_assert(sizeof(SimpleFS_SuperBlock) == 1024, "超级块大小必须为1024字节");
// 块组描述符结构
struct SimpleFS_GroupDesc {
uint32_t bg_block_bitmap; // 块位图块号
uint32_t bg_inode_bitmap; // inode位图块号
uint32_t bg_inode_table; // inode表起始块号
uint16_t bg_free_blocks_count; // 空闲块数
uint16_t bg_free_inodes_count; // 空闲inode数
uint16_t bg_used_dirs_count; // 已使用目录数
uint8_t bg_padding[14]; // 填充到32字节
};
static_assert(sizeof(SimpleFS_GroupDesc) == 32, "块组描述符大小必须为32字节");
// inode结构
struct SimpleFS_Inode {
uint16_t i_mode; // 文件模式
uint16_t i_uid; // 用户ID
uint32_t i_size; // 文件大小
uint32_t i_atime; // 访问时间
uint32_t i_ctime; // 创建时间
uint32_t i_mtime; // 修改时间
uint32_t i_dtime; // 删除时间
uint16_t i_gid; // 组ID
uint16_t i_links_count; // 硬链接数
uint32_t i_blocks; // 块数
uint32_t i_flags; // 标志
uint32_t i_block[SIMPLEFS_INODE_BLOCK_PTRS]; // 块指针数组
uint8_t i_padding[32]; // 填充到128字节
};
static_assert(sizeof(SimpleFS_Inode) == 128, "inode大小必须为128字节");
// 目录项结构
struct SimpleFS_DirEntry {
uint32_t inode; // inode号
uint16_t rec_len; // 记录长度
uint8_t name_len; // 文件名长度
uint8_t file_type; // 文件类型
char name[SIMPLEFS_MAX_FILENAME_LEN + 1]; // 文件名
};
#pragma pack(pop)

View File

@@ -0,0 +1,13 @@
#pragma once
#include "simplefs.h"
#include "disk_io.h"
#include <vector>
#include <string>
// 文件系统全局上下文
struct SimpleFS_Context {
DeviceFd device_fd;
SimpleFS_SuperBlock sb;
std::vector<SimpleFS_GroupDesc> gdt;
};

27
include/utils.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <vector>
#include <string>
#include <cstdint>
#include "simplefs.h"
// 位图操作
void set_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
void clear_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
bool is_bitmap_bit_set(const std::vector<uint8_t>& bitmap_data, uint32_t bit_index);
// 路径解析
void parse_path(const std::string& path, std::string& dirname, std::string& basename);
// 目录项长度计算
inline uint16_t calculate_dir_entry_len(uint8_t name_len) {
uint16_t len = 8 + name_len;
return (len + 3) & ~3U;
}
// 设备类型检查
bool is_block_device(int fd);
// 块组备份检查
bool is_backup_group(uint32_t group_index);

52
src/disk_io.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "disk_io.h"
#include "simplefs.h"
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <vector>
// 读取磁盘块
int read_block(DeviceFd fd, uint32_t block_num, void* buffer) {
off_t offset = static_cast<off_t>(block_num) * SIMPLEFS_BLOCK_SIZE;
ssize_t bytes_read = pread(fd, buffer, SIMPLEFS_BLOCK_SIZE, offset);
if (bytes_read == -1) {
perror("磁盘读取失败");
return -1;
}
if (bytes_read < SIMPLEFS_BLOCK_SIZE) {
return -1;
}
return 0;
}
// 写入磁盘块
int write_block(DeviceFd fd, uint32_t block_num, const void* buffer) {
off_t offset = static_cast<off_t>(block_num) * SIMPLEFS_BLOCK_SIZE;
ssize_t bytes_written = pwrite(fd, buffer, SIMPLEFS_BLOCK_SIZE, offset);
if (bytes_written == -1) {
perror("磁盘写入失败");
return -1;
}
if (bytes_written < SIMPLEFS_BLOCK_SIZE) {
return -1;
}
return 0;
}
// 批量写入零块
int write_zero_blocks(DeviceFd fd, uint32_t start_block_num, uint32_t count) {
if (count == 0) return 0;
std::vector<uint8_t> zero_buffer(SIMPLEFS_BLOCK_SIZE, 0);
for (uint32_t i = 0; i < count; ++i) {
if (write_block(fd, start_block_num + i, zero_buffer.data()) != 0) {
return -1;
}
}
return 0;
}

1618
src/fuse_ops.cpp Normal file

File diff suppressed because it is too large Load Diff

113
src/main.cpp Normal file
View File

@@ -0,0 +1,113 @@
#include <fuse.h>
#include "fuse_ops.h" // init_fuse_operations和SimpleFS_Context
#include "disk_io.h" // read_block等
#include "simplefs.h" // 结构体
#include "utils.h" // is_block_device
#include <iostream>
#include <vector>
#include <string>
#include <cstring> // memset, strerror
#include <cerrno> // errno
#include <fcntl.h> // open
#include <unistd.h> // close
#include <cmath> // ceil
static SimpleFS_Context fs_context; // 全局文件系统上下文
int main(int argc, char *argv[]) {
if (argc < 3) {
std::cerr << "用法: " << argv[0] << " <设备文件> <挂载点> [FUSE选项...]" << std::endl;
return 1;
}
// 设备镜像路径是argv[1]
// 挂载点是argv[2]
// FUSE选项从argv[2]开始,如果我们移位的话,或者手动处理
// 保持简单FUSE选项在挂载点之后传递
std::string device_path = argv[1];
fs_context.device_fd = open(device_path.c_str(), O_RDWR);
if (fs_context.device_fd < 0) {
perror("无法打开设备文件");
return 1;
}
// 读取超级块
std::vector<uint8_t> sb_buffer(SIMPLEFS_BLOCK_SIZE);
if (read_block(fs_context.device_fd, 1, sb_buffer.data()) != 0) { // 超级块在块1
std::cerr << "无法读取超级块" << std::endl;
close(fs_context.device_fd);
return 1;
}
std::memcpy(&fs_context.sb, sb_buffer.data(), sizeof(SimpleFS_SuperBlock));
// 验证超级块魔数
if (fs_context.sb.s_magic != SIMPLEFS_MAGIC) {
std::cerr << "魔数不匹配不是有效的SimpleFS文件系统" << std::endl;
close(fs_context.device_fd);
return 1;
}
std::cout << "SimpleFS已加载 - 块总数: " << fs_context.sb.s_blocks_count
<< ", 空闲块: " << fs_context.sb.s_free_blocks_count << std::endl;
// 读取组描述符表(GDT)
uint32_t num_block_groups = static_cast<uint32_t>(std::ceil(static_cast<double>(fs_context.sb.s_blocks_count) / fs_context.sb.s_blocks_per_group));
if (num_block_groups == 0 && fs_context.sb.s_blocks_count > 0) num_block_groups = 1;
uint32_t gdt_size_bytes = num_block_groups * sizeof(SimpleFS_GroupDesc);
uint32_t gdt_blocks_count = static_cast<uint32_t>(std::ceil(static_cast<double>(gdt_size_bytes) / SIMPLEFS_BLOCK_SIZE));
fs_context.gdt.resize(num_block_groups);
std::vector<uint8_t> gdt_buffer_raw(gdt_blocks_count * SIMPLEFS_BLOCK_SIZE);
uint32_t gdt_start_block = 1 + 1; // 超级块在块1GDT从块2开始
for (uint32_t i = 0; i < gdt_blocks_count; ++i) {
if (read_block(fs_context.device_fd, gdt_start_block + i, gdt_buffer_raw.data() + (i * SIMPLEFS_BLOCK_SIZE)) != 0) {
std::cerr << "无法读取组描述符表" << std::endl;
close(fs_context.device_fd);
return 1;
}
}
std::memcpy(fs_context.gdt.data(), gdt_buffer_raw.data(), gdt_size_bytes);
// 准备FUSE参数
bool is_blk_dev = is_block_device(fs_context.device_fd);
bool allow_other_found = false;
for (int i = 0; i < argc; ++i) {
if (std::string(argv[i]) == "-o" && (i + 1 < argc) && std::string(argv[i+1]).find("allow_other") != std::string::npos) {
allow_other_found = true;
break;
}
if (std::string(argv[i]).find("-oallow_other") != std::string::npos) {
allow_other_found = true;
break;
}
}
std::vector<char*> fuse_argv_vec;
fuse_argv_vec.push_back(argv[0]); // 程序名
// 添加原始参数,除了设备路径
for (int i = 2; i < argc; ++i) {
fuse_argv_vec.push_back(argv[i]);
}
if (!allow_other_found) {
fuse_argv_vec.push_back(const_cast<char*>("-o"));
fuse_argv_vec.push_back(const_cast<char*>("allow_other"));
}
struct fuse_operations simplefs_ops;
init_fuse_operations(&simplefs_ops);
// 传递上下文给FUSE
int ret = fuse_main(fuse_argv_vec.size(), fuse_argv_vec.data(), &simplefs_ops, &fs_context);
close(fs_context.device_fd);
return ret;
}

1022
src/metadata.cpp Normal file

File diff suppressed because it is too large Load Diff

100
src/utils.cpp Normal file
View File

@@ -0,0 +1,100 @@
#include "utils.h"
#include <iostream>
#include <cstring>
void set_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index) {
uint32_t byte_index = bit_index / 8;
uint8_t bit_offset = bit_index % 8;
if (bit_index < bitmap_data.size() * 8) {
bitmap_data[byte_index] |= (1 << bit_offset);
}
}
void clear_bitmap_bit(std::vector<uint8_t>& bitmap_data, uint32_t bit_index) {
uint32_t byte_index = bit_index / 8;
uint8_t bit_offset = bit_index % 8;
if (bit_index < bitmap_data.size() * 8) {
bitmap_data[byte_index] &= ~(1 << bit_offset);
}
}
bool is_bitmap_bit_set(const std::vector<uint8_t>& bitmap_data, uint32_t bit_index) {
uint32_t byte_index = bit_index / 8;
uint8_t bit_offset = bit_index % 8;
if (bit_index < bitmap_data.size() * 8) {
return (bitmap_data[byte_index] & (1 << bit_offset)) != 0;
}
return true;
}
void parse_path(const std::string& path, std::string& dirname, std::string& basename) {
if (path.empty()) {
dirname = ".";
basename = "";
return;
}
// 规范化路径
std::string p_normalized;
p_normalized.reserve(path.length());
if (path[0] == '/') {
p_normalized += '/';
}
for (size_t i = 0; i < path.length(); ++i) {
if (path[i] == '/') {
if (!p_normalized.empty() && p_normalized.back() != '/') {
p_normalized += '/';
}
} else {
p_normalized += path[i];
}
}
// 移除尾部斜杠,除非是根路径
if (p_normalized.length() > 1 && p_normalized.back() == '/') {
p_normalized.pop_back();
}
if (p_normalized.empty() && path[0] == '/') {
p_normalized = "/";
}
if (p_normalized == "/") {
dirname = "/";
basename = "/";
return;
}
size_t last_slash = p_normalized.find_last_of('/');
if (last_slash == std::string::npos) {
dirname = ".";
basename = p_normalized;
} else {
basename = p_normalized.substr(last_slash + 1);
if (last_slash == 0) {
dirname = "/";
} else {
dirname = p_normalized.substr(0, last_slash);
}
}
}
#include <sys/stat.h>
bool is_block_device(int fd) {
struct stat st;
if (fstat(fd, &st) == -1) {
return false;
}
return S_ISBLK(st.st_mode);
}
bool is_backup_group(uint32_t group_index) {
if (group_index == 0 || group_index == 1) return true;
uint32_t n = group_index;
while (n % 3 == 0) n /= 3;
while (n % 5 == 0) n /= 5;
while (n % 7 == 0) n /= 7;
return n == 1;
}

598
stress_test.py Normal file
View File

@@ -0,0 +1,598 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import subprocess
import time
import hashlib
import pwd
import grp
import threading
import random
import string
import shutil
import sys
from collections import defaultdict
# --- 配置部分 ---
# 项目根目录 (假设此脚本位于项目根目录下)
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
# 构建目录
BUILD_DIR = os.path.join(PROJECT_DIR, "build")
# SimpleFS 可执行文件路径
SIMPLEFS_EXEC = os.path.join(BUILD_DIR, "simplefs")
# mkfs.simplefs 可执行文件路径
MKFS_EXEC = os.path.join(BUILD_DIR, "mkfs.simplefs")
# 测试环境配置
TEST_DIR = os.path.join(PROJECT_DIR, "simplefs_test_environment") # 测试环境的主目录
MOUNT_POINT = os.path.join(TEST_DIR, "mountpoint") # 文件系统挂载点
DISK_IMAGE = os.path.join(TEST_DIR, "simplefs.img") # 磁盘镜像文件
DISK_SIZE_MB = 256 # 磁盘镜像大小 (MB)
# 大文件测试配置
LARGE_FILE_SIZE_MB = 64 # 大文件大小 (MB)
LARGE_FILE_PATH = os.path.join(MOUNT_POINT, "large_file.dat")
# 大量文件测试配置
MANY_FILES_COUNT = 1024 # 创建的文件数量
MANY_FILES_DIR = os.path.join(MOUNT_POINT, "many_files_test")
# 权限测试配置
TEST_USER_NAME = "testuser"
TEST_GROUP_NAME = "testgroup"
# --- 日志和颜色 ---
class Colors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
def log_info(message):
print(f"{Colors.OKBLUE}[INFO] {message}{Colors.ENDC}")
def log_success(message):
print(f"{Colors.OKGREEN}[SUCCESS] {message}{Colors.ENDC}")
def log_warning(message):
print(f"{Colors.WARNING}[WARNING] {message}{Colors.ENDC}")
def log_error(message):
print(f"{Colors.FAIL}[ERROR] {message}{Colors.ENDC}")
sys.exit(1) # 发生错误时直接退出
def log_header(message):
print(f"\n{Colors.HEADER}{Colors.BOLD}===== {message} ====={Colors.ENDC}")
# --- 辅助函数 ---
def run_command(command, cwd=None, check=True, as_user=None):
"""执行一个 shell 命令"""
log_info(f"执行命令: {' '.join(command)}")
if as_user:
command = ['sudo', '-u', as_user] + command
try:
process = subprocess.run(
command,
cwd=cwd,
check=check,
capture_output=True,
text=True,
timeout=600 # 10分钟超时
)
if process.stdout:
print(process.stdout)
if process.stderr:
print(process.stderr)
return process
except subprocess.CalledProcessError as e:
log_error(f"命令执行失败,返回码: {e.returncode}")
log_error(f"Stdout: {e.stdout}")
log_error(f"Stderr: {e.stderr}")
raise
except subprocess.TimeoutExpired as e:
log_error(f"命令执行超时: {' '.join(command)}")
raise
def get_file_hash(filepath):
"""计算文件的 SHA256 哈希值"""
sha256 = hashlib.sha256()
with open(filepath, 'rb') as f:
for chunk in iter(lambda: f.read(4096), b''):
sha256.update(chunk)
return sha256.hexdigest()
def create_random_file(filepath, size_bytes):
"""创建一个指定大小的随机内容文件"""
log_info(f"正在创建大小为 {size_bytes / 1024 / 1024:.2f} MB 的随机文件: {filepath}")
with open(filepath, 'wb') as f:
f.write(os.urandom(size_bytes))
log_success(f"文件创建成功: {filepath}")
# --- 环境准备和清理 ---
def setup_environment():
"""准备测试环境"""
log_header("准备测试环境")
if os.geteuid() != 0:
log_error("此脚本需要 root 权限来执行挂载和用户切换操作。请使用 sudo 运行。")
# 清理旧环境
if os.path.exists(MOUNT_POINT):
run_command(['fusermount', '-u', MOUNT_POINT], check=False)
shutil.rmtree(TEST_DIR, ignore_errors=True)
# 创建新目录
os.makedirs(MOUNT_POINT, exist_ok=True)
log_success(f"测试目录 '{TEST_DIR}' 和挂载点 '{MOUNT_POINT}' 已创建。")
def compile_project():
"""编译 SimpleFS 项目"""
log_header("编译项目")
if not os.path.exists(BUILD_DIR):
os.makedirs(BUILD_DIR)
# 检查CMakeLists.txt是否存在
if not os.path.exists(os.path.join(PROJECT_DIR, "CMakeLists.txt")):
log_error("项目根目录下未找到 CMakeLists.txt请确认脚本位置是否正确。")
run_command(['cmake', '..'], cwd=BUILD_DIR)
run_command(['make', '-j'], cwd=BUILD_DIR) # 使用多核编译
if not os.path.exists(SIMPLEFS_EXEC) or not os.path.exists(MKFS_EXEC):
log_error("编译失败,未找到可执行文件。")
log_success("项目编译成功。")
def create_and_format_disk():
"""创建并格式化磁盘镜像"""
log_header("创建和格式化磁盘镜像")
# 创建一个稀疏文件作为磁盘镜像
run_command(['truncate', '-s', f'{DISK_SIZE_MB}M', DISK_IMAGE])
log_success(f"创建了 {DISK_SIZE_MB}MB 的磁盘镜像: {DISK_IMAGE}")
# 格式化磁盘
run_command([MKFS_EXEC, DISK_IMAGE])
log_success("磁盘镜像格式化成功。")
def mount_fs():
"""挂载文件系统"""
log_header("挂载文件系统")
# 使用 -f 在前台运行,便于调试,但这里我们需要后台运行
command = [SIMPLEFS_EXEC, DISK_IMAGE, MOUNT_POINT, '-o', 'allow_other']
# 在后台启动 simplefs 进程
fs_process = subprocess.Popen(command)
time.sleep(2) # 等待 FUSE 挂载完成
# 检查挂载是否成功
if not os.path.ismount(MOUNT_POINT):
fs_process.kill()
log_error("文件系统挂载失败!")
log_success(f"文件系统已成功挂载到 {MOUNT_POINT}")
return fs_process
def unmount_fs(fs_process):
"""卸载文件系统并终止进程"""
log_header("卸载文件系统")
run_command(['fusermount', '-u', MOUNT_POINT])
# 尝试正常终止进程
fs_process.terminate()
try:
fs_process.wait(timeout=5)
log_success("SimpleFS 进程已正常终止。")
except subprocess.TimeoutExpired:
log_warning("SimpleFS 进程无法正常终止,强制杀死。")
fs_process.kill()
if os.path.ismount(MOUNT_POINT):
log_warning("卸载失败,挂载点仍然存在。")
else:
log_success("文件系统已成功卸载。")
def cleanup_environment():
"""清理测试环境"""
log_header("清理测试环境")
shutil.rmtree(TEST_DIR, ignore_errors=True)
cleanup_test_user()
log_success("测试环境已清理。")
def setup_test_user():
"""创建用于权限测试的用户和组"""
log_header("设置测试用户和组")
try:
grp.getgrnam(TEST_GROUP_NAME)
log_info(f"'{TEST_GROUP_NAME}' 已存在。")
except KeyError:
run_command(['groupadd', TEST_GROUP_NAME])
log_success(f"'{TEST_GROUP_NAME}' 已创建。")
try:
pwd.getpwnam(TEST_USER_NAME)
log_info(f"用户 '{TEST_USER_NAME}' 已存在。")
except KeyError:
run_command(['useradd', '-m', '-g', TEST_GROUP_NAME, '-s', '/bin/bash', TEST_USER_NAME])
log_success(f"用户 '{TEST_USER_NAME}' 已创建。")
def cleanup_test_user():
"""删除测试用户和组"""
log_info("清理测试用户和组...")
run_command(['userdel', '-r', TEST_USER_NAME], check=False)
run_command(['groupdel', TEST_GROUP_NAME], check=False)
log_success("测试用户和组已清理。")
# --- 测试用例 ---
def test_large_file_io():
"""
测试大文件的读写和完整性。
1. 在本地创建一个大文件。
2. 计算本地文件的哈希值。
3. 使用 dd 命令将文件复制到挂载点。
4. 从挂载点将文件复制回来。
5. 比较复制回来的文件和原始文件的哈希值。
6. 直接读取挂载点上的文件并验证其哈希值。
7. 删除文件。
"""
log_header("开始大文件读写和完整性测试")
local_source_file = os.path.join(TEST_DIR, "large_file_source.dat")
local_dest_file = os.path.join(TEST_DIR, "large_file_dest.dat")
file_size_bytes = LARGE_FILE_SIZE_MB * 1024 * 1024
# 1. 创建本地源文件
create_random_file(local_source_file, file_size_bytes)
# 2. 计算源文件哈希
source_hash = get_file_hash(local_source_file)
log_info(f"源文件哈希: {source_hash}")
# 3. 写入文件到 simplefs
log_info("使用 dd 命令将大文件写入 simplefs...")
run_command([
'dd', f'if={local_source_file}', f'of={LARGE_FILE_PATH}', 'bs=4M', 'oflag=direct'
])
log_success("大文件写入成功。")
time.sleep(1) # 等待数据落盘
# 4. 检查文件大小
stat_info = os.stat(LARGE_FILE_PATH)
if stat_info.st_size != file_size_bytes:
log_error(f"文件大小不匹配!期望: {file_size_bytes}, 实际: {stat_info.st_size}")
log_success("文件大小验证成功。")
# 5. 直接验证挂载点上文件的哈希
log_info("直接验证挂载点上文件的哈希...")
fs_file_hash = get_file_hash(LARGE_FILE_PATH)
log_info(f"SimpleFS 中的文件哈希: {fs_file_hash}")
if fs_file_hash != source_hash:
log_error("文件内容完整性检查失败!哈希值不匹配。")
log_success("文件内容完整性(直接读取)验证成功!")
# 6. 从 simplefs 读回文件
log_info("使用 dd 命令从 simplefs 读回大文件...")
run_command([
'dd', f'if={LARGE_FILE_PATH}', f'of={local_dest_file}', 'bs=4M', 'iflag=direct'
])
# 7. 验证读回文件的哈希
dest_hash = get_file_hash(local_dest_file)
log_info(f"读回文件哈希: {dest_hash}")
if dest_hash != source_hash:
log_error("文件内容完整性检查失败!读回的文件与源文件不匹配。")
log_success("文件内容完整性(读回)验证成功!")
# 8. 删除文件
os.remove(LARGE_FILE_PATH)
if os.path.exists(LARGE_FILE_PATH):
log_error("删除大文件失败!")
log_success("大文件删除成功。")
# 清理本地临时文件
os.remove(local_source_file)
os.remove(local_dest_file)
def test_many_files_io():
"""
测试大量小文件的创建、读写和删除。
1. 创建一个目录。
2. 在目录中创建大量小文件,并写入少量随机数据。
3. 记录每个文件的路径和其内容的哈希。
4. 随机抽查一些文件,验证其内容是否正确。
5. 验证目录列表是否能正确显示所有文件。
6. 依次删除所有文件。
7. 删除目录。
"""
log_header("开始大量小文件读写测试")
os.makedirs(MANY_FILES_DIR, exist_ok=True)
file_hashes = {}
# 1. 创建大量文件
log_info(f"正在创建 {MANY_FILES_COUNT} 个小文件...")
for i in range(MANY_FILES_COUNT):
filename = f"small_file_{i}.txt"
filepath = os.path.join(MANY_FILES_DIR, filename)
content = ''.join(random.choices(string.ascii_letters + string.digits, k=128)).encode('utf-8')
with open(filepath, 'wb') as f:
f.write(content)
file_hashes[filepath] = hashlib.sha256(content).hexdigest()
log_success(f"{MANY_FILES_COUNT} 个小文件创建完成。")
# 2. 验证文件数量
listed_files = os.listdir(MANY_FILES_DIR)
if len(listed_files) != MANY_FILES_COUNT:
log_error(f"文件数量不匹配!期望: {MANY_FILES_COUNT}, 实际: {len(listed_files)}")
log_success("文件数量验证成功。")
# 3. 随机抽样验证
log_info("随机抽样 50 个文件进行内容验证...")
sample_paths = random.sample(list(file_hashes.keys()), 50)
for path in sample_paths:
with open(path, 'rb') as f:
content = f.read()
read_hash = hashlib.sha256(content).hexdigest()
if read_hash != file_hashes[path]:
log_error(f"文件 {path} 内容验证失败!")
log_success("随机抽样文件内容验证成功。")
# 4. 删除所有文件
log_info(f"正在删除 {MANY_FILES_COUNT} 个小文件...")
for filepath in file_hashes.keys():
os.remove(filepath)
log_success("所有小文件删除成功。")
# 5. 验证目录是否为空
if len(os.listdir(MANY_FILES_DIR)) != 0:
log_error("删除文件后,目录不为空!")
log_success("目录已清空验证成功。")
# 6. 删除目录
os.rmdir(MANY_FILES_DIR)
if os.path.exists(MANY_FILES_DIR):
log_error("删除目录失败!")
log_success("测试目录删除成功。")
def test_permission_system():
"""
测试文件系统的权限控制。
1. 创建一个测试文件,所有者为 root。
2. 切换到 testuser尝试读取该文件应该失败
3. 切换到 testuser尝试写入该文件应该失败
4. 使用 root 修改文件权限为 0644允许其他人读取。
5. 切换到 testuser尝试读取该文件应该成功
6. 切换到 testuser尝试写入该文件应该失败
7. 使用 root 修改文件权限为 0666允许其他人写入。
8. 切换到 testuser尝试写入该文件应该成功
9. 使用 chown 修改文件所有者为 testuser。
10. 切换到 testuser修改文件权限为 0600。
11. 切换回 root尝试读取文件应该成功因为 root 有超级权限)。
"""
log_header("开始权限系统测试")
setup_test_user()
test_file = os.path.join(MOUNT_POINT, "permission_test.txt")
test_content = "permission test content"
# 1. root 创建文件
with open(test_file, 'w') as f:
f.write(test_content)
run_command(['chown', f'root:root', test_file])
run_command(['chmod', '0600', test_file])
log_success("root 创建了文件 permission_test.txt (权限 0600)")
# 2. testuser 尝试读取 (失败)
log_info("测试: testuser 尝试读取 root 的 0600 文件 (应失败)")
try:
run_command(['cat', test_file], as_user=TEST_USER_NAME, check=True)
log_error("权限测试失败testuser 不应能读取此文件!")
except subprocess.CalledProcessError:
log_success("测试通过testuser 无法读取文件。")
# 3. testuser 尝试写入 (失败)
log_info("测试: testuser 尝试写入 root 的 0600 文件 (应失败)")
try:
run_command(['sh', '-c', f'echo "more" >> {test_file}'], as_user=TEST_USER_NAME, check=True)
log_error("权限测试失败testuser 不应能写入此文件!")
except subprocess.CalledProcessError:
log_success("测试通过testuser 无法写入文件。")
# 4. root 修改权限为 0644
log_info("root 修改权限为 0644 (允许 others 读取)")
run_command(['chmod', '0644', test_file])
# 5. testuser 尝试读取 (成功)
log_info("测试: testuser 尝试读取 root 的 0644 文件 (应成功)")
run_command(['cat', test_file], as_user=TEST_USER_NAME)
log_success("测试通过testuser 可以读取文件。")
# 6. testuser 尝试写入 (失败)
log_info("测试: testuser 尝试写入 root 的 0644 文件 (应失败)")
try:
run_command(['sh', '-c', f'echo "more" >> {test_file}'], as_user=TEST_USER_NAME, check=True)
log_error("权限测试失败testuser 不应能写入此文件!")
except subprocess.CalledProcessError:
log_success("测试通过testuser 无法写入文件。")
# 7. root 修改所有者为 testuser
log_info("root 使用 chown 将文件所有者改为 testuser")
run_command(['chown', f'{TEST_USER_NAME}:{TEST_GROUP_NAME}', test_file])
# 8. testuser 尝试写入 (成功)
log_info("测试: testuser 作为所有者尝试写入文件 (应成功)")
run_command(['sh', '-c', f'echo " more content" >> {test_file}'], as_user=TEST_USER_NAME)
log_success("测试通过testuser 作为所有者可以写入文件。")
# 9. testuser 修改权限为 0600
log_info("testuser 修改权限为 0600")
run_command(['chmod', '0600', test_file], as_user=TEST_USER_NAME)
# 10. root 尝试读取 (成功)
log_info("测试: root 尝试读取 testuser 的 0600 文件 (应成功)")
run_command(['cat', test_file]) # root 默认有权限
log_success("测试通过root 可以读取任何文件。")
# 清理
os.remove(test_file)
log_success("权限测试文件已清理。")
def test_links():
"""
测试硬链接和符号链接。
1. 创建一个源文件。
2. 创建一个硬链接到该文件。
3. 验证硬链接和源文件有相同的 inode。
4. 验证修改源文件后,硬链接内容也改变。
5. 删除源文件,验证硬链接依然可以访问。
6. 删除硬链接。
7. 创建一个新的源文件。
8. 创建一个符号链接到该文件。
9. 验证读取符号链接会返回源文件的内容。
10. 删除源文件,验证访问符号链接会失败(悬空链接)。
11. 重新创建源文件,验证符号链接恢复正常。
12. 删除符号链接和源文件。
"""
log_header("开始链接功能测试")
# --- 硬链接测试 ---
log_info("--- 硬链接测试 ---")
source_file = os.path.join(MOUNT_POINT, "hardlink_source.txt")
hard_link = os.path.join(MOUNT_POINT, "hardlink_link.txt")
# 1. 创建源文件
with open(source_file, "w") as f:
f.write("initial content")
log_success(f"创建源文件: {source_file}")
# 2. 创建硬链接
os.link(source_file, hard_link)
log_success(f"创建硬链接: {hard_link}")
# 3. 验证 inode
source_stat = os.stat(source_file)
link_stat = os.stat(hard_link)
if source_stat.st_ino != link_stat.st_ino:
log_error(f"硬链接 Inode 不匹配! 源: {source_stat.st_ino}, 链接: {link_stat.st_ino}")
log_success("Inode 验证成功。")
if source_stat.st_nlink != 2:
log_error(f"链接数不为 2实际为: {source_stat.st_nlink}")
log_success("链接数验证成功 (nlink=2)。")
# 4. 验证内容同步
with open(source_file, "a") as f:
f.write(" appended")
with open(hard_link, "r") as f:
content = f.read()
if content != "initial content appended":
log_error("硬链接内容未同步!")
log_success("内容同步验证成功。")
# 5. 删除源文件后访问硬链接
os.remove(source_file)
log_info("源文件已删除。")
with open(hard_link, "r") as f:
content = f.read()
if content != "initial content appended":
log_error("删除源文件后,无法通过硬链接访问正确内容!")
link_stat_after_rm = os.stat(hard_link)
if link_stat_after_rm.st_nlink != 1:
log_error(f"删除源文件后,链接数不为 1实际为: {link_stat_after_rm.st_nlink}")
log_success("删除源文件后,硬链接依然可用,且 nlink=1。")
# 6. 删除硬链接
os.remove(hard_link)
log_success("硬链接已删除。")
# --- 符号链接测试 ---
log_info("--- 符号链接测试 ---")
source_file_sym = os.path.join(MOUNT_POINT, "symlink_source.txt")
sym_link = os.path.join(MOUNT_POINT, "symlink_link.txt")
# 7. 创建源文件
with open(source_file_sym, "w") as f:
f.write("symlink test")
log_success(f"创建源文件: {source_file_sym}")
# 8. 创建符号链接
os.symlink(source_file_sym, sym_link)
log_success(f"创建符号链接: {sym_link}")
if not os.path.islink(sym_link):
log_error("创建的不是符号链接!")
log_success("符号链接类型验证成功。")
# 9. 验证内容
with open(sym_link, "r") as f:
content = f.read()
if content != "symlink test":
log_error("通过符号链接读取的内容不正确!")
log_success("通过符号链接读取内容验证成功。")
# 10. 删除源文件后访问 (悬空链接)
os.remove(source_file_sym)
log_info("源文件已删除。")
try:
with open(sym_link, "r") as f:
f.read()
log_error("访问悬空链接时没有报错!")
except FileNotFoundError:
log_success("访问悬空链接时正确地抛出 FileNotFoundError。")
# 11. 重新创建源文件
with open(source_file_sym, "w") as f:
f.write("symlink reborn")
log_info("源文件已重新创建。")
with open(sym_link, "r") as f:
content = f.read()
if content != "symlink reborn":
log_error("重新创建源文件后,符号链接内容不正确!")
log_success("重新创建源文件后,符号链接恢复正常。")
# 12. 清理
os.remove(sym_link)
os.remove(source_file_sym)
log_success("符号链接测试清理完毕。")
# --- 主函数 ---
def main():
"""主执行函数"""
fs_process = None
try:
setup_environment()
compile_project()
create_and_format_disk()
fs_process = mount_fs()
# --- 按顺序执行所有测试 ---
test_large_file_io()
test_many_files_io()
test_permission_system()
test_links()
log_header("所有测试已成功完成!")
except Exception as e:
log_error(f"测试过程中发生未捕获的异常: {e}")
import traceback
traceback.print_exc()
finally:
if fs_process:
unmount_fs(fs_process)
cleanup_environment()
log_info("脚本执行结束。")
if __name__ == '__main__':
main()

77
tools/fsck.cpp Normal file
View File

@@ -0,0 +1,77 @@
#include "simplefs.h"
#include "disk_io.h"
#include "utils.h"
#include <iostream>
#include <vector>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <cmath>
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "用法: " << argv[0] << " <设备文件>" << std::endl;
return 1;
}
const char* path = argv[1];
int fd = open(path, O_RDONLY);
if (fd < 0) {
perror("打开设备文件失败");
return 1;
}
std::vector<uint8_t> buf(SIMPLEFS_BLOCK_SIZE);
if (read_block(fd, 1, buf.data()) != 0) {
std::cerr << "读取超级块失败" << std::endl;
close(fd);
return 1;
}
SimpleFS_SuperBlock sb;
std::memcpy(&sb, buf.data(), sizeof(sb));
if (sb.s_magic != SIMPLEFS_MAGIC) {
std::cerr << "魔数不匹配不是SimpleFS镜像" << std::endl;
close(fd);
return 1;
}
uint32_t num_groups = static_cast<uint32_t>(std::ceil((double)sb.s_blocks_count / sb.s_blocks_per_group));
uint32_t gdt_size = num_groups * sizeof(SimpleFS_GroupDesc);
uint32_t gdt_blocks = static_cast<uint32_t>(std::ceil((double)gdt_size / SIMPLEFS_BLOCK_SIZE));
std::vector<uint8_t> gdt_raw(gdt_blocks * SIMPLEFS_BLOCK_SIZE);
for (uint32_t i=0;i<gdt_blocks;++i){
if (read_block(fd, 2 + i, gdt_raw.data()+i*SIMPLEFS_BLOCK_SIZE)!=0){
std::cerr<<"读取组描述符表失败"<<std::endl;
close(fd);
return 1;
}
}
std::vector<SimpleFS_GroupDesc> gdt(num_groups);
std::memcpy(gdt.data(), gdt_raw.data(), gdt_size);
uint64_t calc_free_blocks=0, calc_free_inodes=0;
for(uint32_t grp=0; grp<num_groups; ++grp){
const auto& gd=gdt[grp];
std::vector<uint8_t> bb(SIMPLEFS_BLOCK_SIZE), ib(SIMPLEFS_BLOCK_SIZE);
read_block(fd, gd.bg_block_bitmap, bb.data());
read_block(fd, gd.bg_inode_bitmap, ib.data());
uint32_t freeb=0, freei=0;
for(uint32_t b=0;b<sb.s_blocks_per_group && (grp*sb.s_blocks_per_group+b)<sb.s_blocks_count;++b)
if(!is_bitmap_bit_set(bb,b)) freeb++;
for(uint32_t i=0;i<sb.s_inodes_per_group;++i)
if(!is_bitmap_bit_set(ib,i)) freei++;
if(freeb!=gd.bg_free_blocks_count)
std::cout<<""<<grp<<" 块计数不匹配: 位图="<<freeb<<" 描述符="<<gd.bg_free_blocks_count<<std::endl;
if(freei!=gd.bg_free_inodes_count)
std::cout<<""<<grp<<" inode计数不匹配: 位图="<<freei<<" 描述符="<<gd.bg_free_inodes_count<<std::endl;
calc_free_blocks+=freeb;
calc_free_inodes+=freei;
}
if(calc_free_blocks!=sb.s_free_blocks_count)
std::cout<<"超级块空闲块计数不匹配: "<<calc_free_blocks<<" vs "<<sb.s_free_blocks_count<<std::endl;
if(calc_free_inodes!=sb.s_free_inodes_count)
std::cout<<"超级块空闲inode计数不匹配: "<<calc_free_inodes<<" vs "<<sb.s_free_inodes_count<<std::endl;
std::cout<<"fsck检查完成"<<std::endl;
close(fd);
return 0;
}

577
tools/mkfs.cpp Normal file
View File

@@ -0,0 +1,577 @@
#include "simplefs.h"
#include "disk_io.h"
#include "utils.h" // 位图操作工具函数
// 如果位图工具在utils.h中mkfs.cpp就不再直接需要metadata.h了
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cstring> // 字符串操作
#include <sys/stat.h> // stat, fstat
#include <fcntl.h> // open
#include <unistd.h> // close, ftruncate
#include <cmath> // ceil
#include <numeric> // std::fill
#include <algorithm> // std::fill
#include <sys/ioctl.h> // ioctl
#include <linux/fs.h> // BLKGETSIZE64
// 默认参数(可通过选项覆盖)
const uint32_t DEFAULT_BLOCKS_PER_GROUP = SIMPLEFS_BLOCK_SIZE * 8; // 位图中每位对应一个块
const uint32_t DEFAULT_INODES_PER_GROUP = 1024; // 选定的默认值
// 静态位图辅助函数已移至metadata.cpp
void print_usage(const char* prog_name) {
std::cerr << "用法: " << prog_name << " <设备文件> [块数量]" << std::endl;
std::cerr << " <设备文件>: 磁盘镜像文件或块设备路径" << std::endl;
std::cerr << " [块数量]: 可选,新镜像文件的总块数" << std::endl;
}
// 获取文件大小的函数
off_t get_file_size(int fd) {
struct stat st;
if (fstat(fd, &st) == -1) {
perror("获取文件大小失败");
return -1;
}
return st.st_size;
}
int main(int argc, char* argv[]) {
if (argc < 2 || argc > 3) {
print_usage(argv[0]);
return 1;
}
std::string device_path = argv[1];
uint64_t total_blocks_on_device = 0;
uint64_t device_size_bytes = 0;
bool create_new_image = false;
DeviceFd fd = open(device_path.c_str(), O_RDWR);
if (fd == -1) {
if (errno == ENOENT && argc == 3) { // 文件不存在,且提供了大小
std::cout << "正在创建新镜像文件: " << device_path << std::endl;
fd = open(device_path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0666);
if (fd == -1) {
perror("创建镜像文件失败");
return 1;
}
create_new_image = true;
try {
total_blocks_on_device = std::stoull(argv[2]);
if (total_blocks_on_device == 0) {
std::cerr << "块数量必须为正数" << std::endl;
close(fd);
unlink(device_path.c_str()); // 清理已创建的文件
return 1;
}
device_size_bytes = total_blocks_on_device * SIMPLEFS_BLOCK_SIZE;
if (ftruncate(fd, device_size_bytes) == -1) {
perror("设置镜像大小失败");
close(fd);
unlink(device_path.c_str()); // 清理
return 1;
}
std::cout << "已创建新镜像文件 " << device_path << " (大小: "
<< total_blocks_on_device << " 块, " << device_size_bytes << " 字节)" << std::endl;
} catch (const std::exception& e) {
std::cerr << "块数量参数无效: " << e.what() << std::endl;
close(fd);
unlink(device_path.c_str()); // 清理
return 1;
}
} else if (errno == ENOENT) {
std::cerr << "错误: 镜像文件 '" << device_path << "' 未找到,请提供块数量来创建" << std::endl;
perror("打开设备/镜像失败");
return 1;
}
else {
perror("打开设备/镜像失败");
return 1;
}
} else {
bool is_blk_dev = is_block_device(fd);
if (is_blk_dev) {
std::cout << "检测到块设备" << std::endl;
uint64_t size_from_ioctl = 0;
if (ioctl(fd, BLKGETSIZE64, &size_from_ioctl) == 0) {
std::cout << "通过ioctl获取块设备大小: " << size_from_ioctl << " 字节" << std::endl;
if (size_from_ioctl % SIMPLEFS_BLOCK_SIZE != 0) {
std::cerr << "警告: 块设备大小不是文件系统块大小的整数倍" << std::endl;
}
device_size_bytes = size_from_ioctl;
total_blocks_on_device = device_size_bytes / SIMPLEFS_BLOCK_SIZE;
if (argc == 3) {
std::cout << "注意: 忽略[num_blocks]参数,已自动检测设备大小" << std::endl;
}
} else {
perror("ioctl获取设备大小失败");
std::cerr << "无法自动检测块设备大小" << std::endl;
if (argc != 3) {
std::cerr << "错误: 请手动提供[num_blocks]参数" << std::endl;
close(fd);
return 1;
}
try {
total_blocks_on_device = std::stoull(argv[2]);
if (total_blocks_on_device == 0) {
std::cerr << "块数量必须为正数" << std::endl;
close(fd);
return 1;
}
device_size_bytes = total_blocks_on_device * SIMPLEFS_BLOCK_SIZE;
} catch (const std::exception& e) {
std::cerr << "块数量参数无效: " << e.what() << std::endl;
close(fd);
return 1;
}
}
std::cout << "格式化块设备 " << device_path << ",块数: " << total_blocks_on_device
<< " (" << device_size_bytes << " 字节)" << std::endl;
} else {
// 这是常规文件,获取其大小
off_t existing_size = get_file_size(fd);
if (existing_size == -1) {
close(fd);
return 1;
}
if (existing_size == 0) {
if (argc == 3) { // 这是空文件但用户要用指定大小格式化
std::cout << "现有文件为空,按新镜像创建处理" << std::endl;
create_new_image = true;
try {
total_blocks_on_device = std::stoull(argv[2]);
if (total_blocks_on_device == 0) {
std::cerr << "块数量必须为正数" << std::endl;
close(fd);
return 1;
}
device_size_bytes = total_blocks_on_device * SIMPLEFS_BLOCK_SIZE;
if (ftruncate(fd, device_size_bytes) == -1) {
perror("设置镜像大小失败");
close(fd);
return 1;
}
std::cout << "设置镜像 " << device_path << " 大小为 " << total_blocks_on_device
<< " 块 (" << device_size_bytes << " 字节)" << std::endl;
} catch (const std::exception& e) {
std::cerr << "块数量参数无效: " << e.what() << std::endl;
close(fd);
return 1;
}
} else {
std::cerr << "错误: 镜像文件为空" << std::endl;
std::cerr << "如需格式化请提供num_blocks参数" << std::endl;
close(fd);
return 1;
}
} else {
if (existing_size % SIMPLEFS_BLOCK_SIZE != 0) {
std::cerr << "警告: 设备/镜像大小 (" << existing_size
<< " 字节) 不是块大小 (" << SIMPLEFS_BLOCK_SIZE << " 字节) 的整数倍" << std::endl;
}
device_size_bytes = existing_size;
total_blocks_on_device = device_size_bytes / SIMPLEFS_BLOCK_SIZE;
std::cout << "Opened existing image " << device_path << ". Total blocks: " << total_blocks_on_device
<< " (" << device_size_bytes << " bytes)." << std::endl;
if (argc == 3 && !create_new_image) {
std::cout << "Note: [num_blocks] argument is ignored when using an existing, non-empty image file." << std::endl;
}
}
}
}
if (total_blocks_on_device < 64) {
std::cerr << "错误: 设备/镜像太小至少需要64个块" << std::endl;
close(fd);
if (create_new_image) unlink(device_path.c_str());
return 1;
}
std::cout << "正在格式化 " << device_path << " 为 SimpleFS..." << std::endl;
uint32_t inodes_per_block = SIMPLEFS_BLOCK_SIZE / SIMPLEFS_INODE_SIZE;
uint32_t sb_blocks_per_group = DEFAULT_BLOCKS_PER_GROUP;
uint32_t sb_inodes_per_group = DEFAULT_INODES_PER_GROUP;
if (sb_inodes_per_group > SIMPLEFS_BLOCK_SIZE * 8) {
sb_inodes_per_group = SIMPLEFS_BLOCK_SIZE * 8;
std::cout << "警告: 请求的每组inode数超过inode位图最大容量调整为 "
<< sb_inodes_per_group << std::endl;
}
uint32_t num_block_groups = static_cast<uint32_t>(std::ceil(static_cast<double>(total_blocks_on_device) / sb_blocks_per_group));
if (num_block_groups == 0) num_block_groups = 1;
uint64_t total_inodes_fs = static_cast<uint64_t>(num_block_groups) * sb_inodes_per_group;
if (total_inodes_fs > UINT32_MAX) {
total_inodes_fs = UINT32_MAX;
}
std::cout << "文件系统布局:" << std::endl;
std::cout << " 总块数: " << total_blocks_on_device << std::endl;
std::cout << " 每组inode数: " << sb_inodes_per_group << std::endl;
std::cout << " 块组数: " << num_block_groups << std::endl;
std::cout << " 总inode数: " << total_inodes_fs << std::endl;
SimpleFS_SuperBlock sb;
std::memset(&sb, 0, sizeof(SimpleFS_SuperBlock));
sb.s_magic = SIMPLEFS_MAGIC;
sb.s_blocks_count = total_blocks_on_device;
sb.s_inodes_count = static_cast<uint32_t>(total_inodes_fs);
sb.s_log_block_size = static_cast<uint32_t>(std::log2(SIMPLEFS_BLOCK_SIZE)) - 10;
sb.s_blocks_per_group = sb_blocks_per_group;
sb.s_inodes_per_group = sb_inodes_per_group;
sb.s_inode_size = SIMPLEFS_INODE_SIZE;
sb.s_root_inode = SIMPLEFS_ROOT_INODE_NUM;
sb.s_first_ino = 11;
sb.s_state = 1;
sb.s_errors = 1;
sb.s_max_mnt_count = 20;
sb.s_mnt_count = 0;
sb.s_wtime = time(nullptr);
sb.s_block_group_nr = 0;
uint32_t gdt_size_bytes = num_block_groups * sizeof(SimpleFS_GroupDesc);
uint32_t gdt_blocks = static_cast<uint32_t>(std::ceil(static_cast<double>(gdt_size_bytes) / SIMPLEFS_BLOCK_SIZE));
std::cout << " GDT size: " << gdt_size_bytes << " bytes, requiring " << gdt_blocks << " blocks." << std::endl;
uint32_t superblock_location_block = 1;
uint32_t gdt_start_block = superblock_location_block + 1;
std::vector<SimpleFS_GroupDesc> gdt(num_block_groups);
std::memset(gdt.data(), 0, gdt_size_bytes);
uint32_t running_total_free_blocks = total_blocks_on_device;
uint32_t running_total_free_inodes = sb.s_inodes_count;
if (superblock_location_block < total_blocks_on_device) running_total_free_blocks--; else { std::cerr << "No space for SB!" << std::endl; return 1; }
for(uint32_t i = 0; i < gdt_blocks; ++i) {
if ((gdt_start_block + i) < total_blocks_on_device) running_total_free_blocks--; else { std::cerr << "No space for GDT!" << std::endl; return 1; }
}
uint32_t current_group_meta_start_block = gdt_start_block + gdt_blocks;
for (uint32_t i = 0; i < num_block_groups; ++i) {
SimpleFS_GroupDesc& current_gd = gdt[i];
uint32_t group_abs_start_block_for_data = i * sb.s_blocks_per_group;
uint32_t inode_table_size_blocks = static_cast<uint32_t>(std::ceil(static_cast<double>(sb.s_inodes_per_group) * SIMPLEFS_INODE_SIZE / SIMPLEFS_BLOCK_SIZE));
bool backup_here = is_backup_group(i);
if (i == 0) {
current_gd.bg_block_bitmap = current_group_meta_start_block;
current_gd.bg_inode_bitmap = current_gd.bg_block_bitmap + 1;
current_gd.bg_inode_table = current_gd.bg_inode_bitmap + 1;
sb.s_first_data_block = current_gd.bg_inode_table + inode_table_size_blocks;
} else if (backup_here) {
current_gd.bg_block_bitmap = group_abs_start_block_for_data + 1 + gdt_blocks;
current_gd.bg_inode_bitmap = current_gd.bg_block_bitmap + 1;
current_gd.bg_inode_table = current_gd.bg_inode_bitmap + 1;
} else {
current_gd.bg_block_bitmap = group_abs_start_block_for_data;
current_gd.bg_inode_bitmap = current_gd.bg_block_bitmap + 1;
current_gd.bg_inode_table = current_gd.bg_inode_bitmap + 1;
}
uint32_t last_meta_block_for_group = current_gd.bg_inode_table + inode_table_size_blocks -1;
if (last_meta_block_for_group >= total_blocks_on_device || current_gd.bg_block_bitmap >= total_blocks_on_device) {
std::cerr << "错误: 组 " << i << " 的元数据超出设备限制" << std::endl; close(fd); if(create_new_image) unlink(device_path.c_str()); return 1;
}
running_total_free_blocks--; // 块位图
running_total_free_blocks--; // inode位图
running_total_free_blocks -= inode_table_size_blocks; // inode表
// 注意主组组0的超级块和GDT已在前面减去这里不重复减去
if (backup_here && i != 0) { // 只有备份组且不是组0才减去
running_total_free_blocks--; // 备份超级块
running_total_free_blocks -= gdt_blocks; // 备份GDT
}
uint32_t blocks_in_this_group_range = (i == num_block_groups - 1) ?
(total_blocks_on_device - group_abs_start_block_for_data) :
sb.s_blocks_per_group;
current_gd.bg_free_blocks_count = blocks_in_this_group_range;
current_gd.bg_free_blocks_count -= 1; // 块位图
current_gd.bg_free_blocks_count -= 1; // inode位图
current_gd.bg_free_blocks_count -= inode_table_size_blocks; // inode表
if (i == 0) {
current_gd.bg_free_blocks_count--; // 超级块
current_gd.bg_free_blocks_count -= gdt_blocks; // GDT
} else if (backup_here) {
current_gd.bg_free_blocks_count--; // 备份超级块
current_gd.bg_free_blocks_count -= gdt_blocks; // 备份GDT
}
// 主要SB/GDT的额外空闲块调整已在前面完成
current_gd.bg_free_inodes_count = sb.s_inodes_per_group;
current_gd.bg_used_dirs_count = 0;
}
sb.s_free_blocks_count = running_total_free_blocks;
sb.s_free_inodes_count = running_total_free_inodes;
if (gdt_start_block + gdt_blocks > gdt[0].bg_block_bitmap) {
std::cerr << "严重错误: GDT与组0块位图重叠设备太小或计算错误" << std::endl;
close(fd); if(create_new_image) unlink(device_path.c_str()); return 1;
}
std::cout << "超级块(根目录前的最终估计值):" << std::endl;
std::cout << " 空闲块数: " << sb.s_free_blocks_count << std::endl;
std::cout << " 空闲inode数: " << sb.s_free_inodes_count << std::endl;
std::cout << " 首个数据块(全局): " << sb.s_first_data_block << std::endl;
std::vector<uint8_t> fs_block_buffer(SIMPLEFS_BLOCK_SIZE, 0);
std::memcpy(fs_block_buffer.data(), &sb, sizeof(sb));
if (write_block(fd, superblock_location_block, fs_block_buffer.data()) != 0) {
std::cerr << "超级块写入失败" << std::endl; close(fd); if (create_new_image) unlink(device_path.c_str()); return 1;
}
std::cout << "超级块已写入块 " << superblock_location_block << std::endl;
uint8_t* gdt_write_ptr = reinterpret_cast<uint8_t*>(gdt.data());
for (uint32_t i = 0; i < gdt_blocks; ++i) {
std::fill(fs_block_buffer.begin(), fs_block_buffer.end(), 0);
uint32_t bytes_to_copy_this_block = (gdt_size_bytes - (i * SIMPLEFS_BLOCK_SIZE) < SIMPLEFS_BLOCK_SIZE) ?
(gdt_size_bytes % SIMPLEFS_BLOCK_SIZE == 0 && gdt_size_bytes > 0 ? SIMPLEFS_BLOCK_SIZE : gdt_size_bytes % SIMPLEFS_BLOCK_SIZE)
: SIMPLEFS_BLOCK_SIZE;
if (gdt_size_bytes == 0 && i==0) bytes_to_copy_this_block = 0;
std::memcpy(fs_block_buffer.data(), gdt_write_ptr + (i * SIMPLEFS_BLOCK_SIZE), bytes_to_copy_this_block);
if (write_block(fd, gdt_start_block + i, fs_block_buffer.data()) != 0) {
std::cerr << "GDT块 " << (gdt_start_block + i) << " 写入失败" << std::endl; close(fd); if (create_new_image) unlink(device_path.c_str()); return 1;
}
}
std::cout << "GDT已写入块 " << gdt_start_block << "" << (gdt_start_block + gdt_blocks - 1) << std::endl;
std::vector<uint8_t> group_block_bitmap_buffer(SIMPLEFS_BLOCK_SIZE, 0);
std::vector<uint8_t> group_inode_bitmap_buffer(SIMPLEFS_BLOCK_SIZE, 0);
for (uint32_t i = 0; i < num_block_groups; ++i) {
SimpleFS_GroupDesc& current_gd_ref = gdt[i];
uint32_t group_abs_data_start_block = i * sb.s_blocks_per_group; // 这是该组位图中位0的块号
uint32_t inode_table_size_blocks = static_cast<uint32_t>(std::ceil(static_cast<double>(sb.s_inodes_per_group) * SIMPLEFS_INODE_SIZE / SIMPLEFS_BLOCK_SIZE));
std::cout << "Processing Group " << i << ":" << std::endl;
std::cout << " BB@" << current_gd_ref.bg_block_bitmap << ", IB@" << current_gd_ref.bg_inode_bitmap << ", IT@" << current_gd_ref.bg_inode_table << " (" << inode_table_size_blocks << " blocks)" << std::endl;
std::fill(group_block_bitmap_buffer.begin(), group_block_bitmap_buffer.end(), 0);
std::fill(group_inode_bitmap_buffer.begin(), group_inode_bitmap_buffer.end(), 0);
// 在其块位图中将组自己的元数据块标记为已使用
// 组位图中的位索引相对于该组管理的块起始位置
set_bitmap_bit(group_block_bitmap_buffer, current_gd_ref.bg_block_bitmap - group_abs_data_start_block);
set_bitmap_bit(group_block_bitmap_buffer, current_gd_ref.bg_inode_bitmap - group_abs_data_start_block);
for (uint32_t j = 0; j < inode_table_size_blocks; ++j) {
set_bitmap_bit(group_block_bitmap_buffer, (current_gd_ref.bg_inode_table + j) - group_abs_data_start_block);
}
bool backup_here = is_backup_group(i);
if (backup_here) {
uint32_t sb_blk = (i == 0) ? superblock_location_block : group_abs_data_start_block;
set_bitmap_bit(group_block_bitmap_buffer, sb_blk - group_abs_data_start_block);
for (uint32_t gdt_idx = 0; gdt_idx < gdt_blocks; ++gdt_idx) {
uint32_t abs_gdt_block = (i == 0) ? (gdt_start_block + gdt_idx)
: (group_abs_data_start_block + 1 + gdt_idx);
set_bitmap_bit(group_block_bitmap_buffer, abs_gdt_block - group_abs_data_start_block);
}
}
if (i == 0) {
if (!is_bitmap_bit_set(group_block_bitmap_buffer, 0)) {
set_bitmap_bit(group_block_bitmap_buffer, 0);
bool block0_was_sb = (superblock_location_block == 0);
bool block0_was_gdt = false;
for(uint32_t gdt_idx=0; gdt_idx < gdt_blocks; ++gdt_idx) if((gdt_start_block + gdt_idx) == 0) block0_was_gdt = true;
bool block0_was_group0_bb = (current_gd_ref.bg_block_bitmap == 0);
bool block0_was_group0_ib = (current_gd_ref.bg_inode_bitmap == 0);
bool block0_was_group0_it = (current_gd_ref.bg_inode_table == 0);
if (!block0_was_sb && !block0_was_gdt && !block0_was_group0_bb && !block0_was_group0_ib && !block0_was_group0_it) {
if (current_gd_ref.bg_free_blocks_count > 0) current_gd_ref.bg_free_blocks_count--;
if (sb.s_free_blocks_count > 0) sb.s_free_blocks_count--;
}
}
set_bitmap_bit(group_inode_bitmap_buffer, 0);
set_bitmap_bit(group_inode_bitmap_buffer, 1);
if (current_gd_ref.bg_free_inodes_count >= 2) current_gd_ref.bg_free_inodes_count -= 2; else current_gd_ref.bg_free_inodes_count = 0;
if (sb.s_free_inodes_count >= 2) sb.s_free_inodes_count -= 2; else sb.s_free_inodes_count = 0;
}
if (write_block(fd, current_gd_ref.bg_block_bitmap, group_block_bitmap_buffer.data()) != 0) { /* error */ return 1; }
std::cout << " Written Block Bitmap. Group free blocks: " << current_gd_ref.bg_free_blocks_count << std::endl;
if (write_block(fd, current_gd_ref.bg_inode_bitmap, group_inode_bitmap_buffer.data()) != 0) { /* error */ return 1; }
std::cout << " Written Inode Bitmap. Group free inodes: " << current_gd_ref.bg_free_inodes_count << std::endl;
if (write_zero_blocks(fd, current_gd_ref.bg_inode_table, inode_table_size_blocks) != 0) { /* error */ return 1; }
std::cout << " Zeroed Inode Table." << std::endl;
}
std::cout << "Re-writing Superblock and GDT (final pre-root dir)..." << std::endl;
std::memcpy(fs_block_buffer.data(), &sb, sizeof(sb));
if (write_block(fd, superblock_location_block, fs_block_buffer.data()) != 0) { /* error */ return 1;}
gdt_write_ptr = reinterpret_cast<uint8_t*>(gdt.data());
for (uint32_t i = 0; i < gdt_blocks; ++i) {
std::fill(fs_block_buffer.begin(), fs_block_buffer.end(), 0);
uint32_t bytes_to_copy_this_block = (gdt_size_bytes - (i * SIMPLEFS_BLOCK_SIZE) < SIMPLEFS_BLOCK_SIZE) ?
(gdt_size_bytes % SIMPLEFS_BLOCK_SIZE == 0 && gdt_size_bytes > 0 ? SIMPLEFS_BLOCK_SIZE : gdt_size_bytes % SIMPLEFS_BLOCK_SIZE)
: SIMPLEFS_BLOCK_SIZE;
if (gdt_size_bytes == 0 && i==0) bytes_to_copy_this_block = 0;
std::memcpy(fs_block_buffer.data(), gdt_write_ptr + (i * SIMPLEFS_BLOCK_SIZE), bytes_to_copy_this_block);
if (write_block(fd, gdt_start_block + i, fs_block_buffer.data()) != 0) { /* error */ return 1; }
}
// 将超级块和GDT的备份副本写入指定组
for (uint32_t grp = 1; grp < num_block_groups; ++grp) {
if (!is_backup_group(grp)) continue;
uint32_t grp_start = grp * sb.s_blocks_per_group;
std::memcpy(fs_block_buffer.data(), &sb, sizeof(sb));
if (write_block(fd, grp_start, fs_block_buffer.data()) != 0) { /* error */ return 1; }
for (uint32_t i = 0; i < gdt_blocks; ++i) {
std::fill(fs_block_buffer.begin(), fs_block_buffer.end(), 0);
uint32_t bytes_to_copy_this_block = (gdt_size_bytes - (i * SIMPLEFS_BLOCK_SIZE) < SIMPLEFS_BLOCK_SIZE) ?
(gdt_size_bytes % SIMPLEFS_BLOCK_SIZE == 0 && gdt_size_bytes > 0 ? SIMPLEFS_BLOCK_SIZE : gdt_size_bytes % SIMPLEFS_BLOCK_SIZE)
: SIMPLEFS_BLOCK_SIZE;
if (gdt_size_bytes == 0 && i==0) bytes_to_copy_this_block = 0;
std::memcpy(fs_block_buffer.data(), gdt_write_ptr + (i * SIMPLEFS_BLOCK_SIZE), bytes_to_copy_this_block);
if (write_block(fd, grp_start + 1 + i, fs_block_buffer.data()) != 0) { /* error */ return 1; }
}
}
std::cout << "Creating root directory..." << std::endl;
SimpleFS_GroupDesc& group0_gd = gdt[0];
// 根inodeSIMPLEFS_ROOT_INODE_NUM通常为2和inode 1位0
// 已在inode位图中标记以及超级块/组描述符
// 在组0的初始设置循环中已减少了空闲inode计数
// 因此此处不需要再次调用set_bitmap_bit或减少计数
// 只需为组0增加used_dirs_count
group0_gd.bg_used_dirs_count++;
std::cout << " Root inode " << SIMPLEFS_ROOT_INODE_NUM << " allocation accounted for (marked in bitmap and counts updated earlier)." << std::endl;
std::cout << " Incremented used_dirs_count for group 0." << std::endl;
// 如果后续决策依赖于最新状态重新读取inode位图是好习惯
// 但此处不对根inode号本身做分配决策只分配其数据块故继续进行
// 如有疑问,可重新读取:
// if (read_block(fd, group0_gd.bg_inode_bitmap, group_inode_bitmap_buffer.data()) != 0) {
// std::cerr << "根目录数据分配前重新读取组0 inode位图失败" << std::endl; return 1;
// }
uint32_t root_dir_data_block_num = 0;
if (read_block(fd, group0_gd.bg_block_bitmap, group_block_bitmap_buffer.data()) != 0) {
std::cerr << "组0块位图读取失败无法分配根目录数据块" << std::endl; return 1;
}
uint32_t group0_abs_start_block = 0 * sb.s_blocks_per_group;
uint32_t search_start_offset_in_group0 = 0;
if (sb.s_first_data_block >= group0_abs_start_block && sb.s_first_data_block < (group0_abs_start_block + sb.s_blocks_per_group) ) {
search_start_offset_in_group0 = sb.s_first_data_block - group0_abs_start_block;
} else if (sb.s_first_data_block < group0_abs_start_block) {
search_start_offset_in_group0 = (gdt[0].bg_inode_table + static_cast<uint32_t>(std::ceil(static_cast<double>(sb.s_inodes_per_group) * SIMPLEFS_INODE_SIZE / SIMPLEFS_BLOCK_SIZE))) - group0_abs_start_block;
}
for (uint32_t block_offset_in_group = search_start_offset_in_group0;
block_offset_in_group < sb.s_blocks_per_group; ++block_offset_in_group) {
if (!is_bitmap_bit_set(group_block_bitmap_buffer, block_offset_in_group)) {
root_dir_data_block_num = group0_abs_start_block + block_offset_in_group;
set_bitmap_bit(group_block_bitmap_buffer, block_offset_in_group);
group0_gd.bg_free_blocks_count--;
sb.s_free_blocks_count--;
break;
}
}
if (root_dir_data_block_num == 0) {
std::cerr << "错误: 组0中找不到根目录的空闲数据块" << std::endl; return 1;
}
if (write_block(fd, group0_gd.bg_block_bitmap, group_block_bitmap_buffer.data()) != 0) {
std::cerr << "组0块位图更新失败" << std::endl; return 1;
}
std::cout << " 已为根目录分配数据块 " << root_dir_data_block_num << std::endl;
std::vector<uint8_t> root_dir_data_buffer(SIMPLEFS_BLOCK_SIZE, 0);
uint16_t current_offset = 0;
SimpleFS_DirEntry dot_entry;
dot_entry.inode = SIMPLEFS_ROOT_INODE_NUM;
dot_entry.name_len = 1;
dot_entry.file_type = S_IFDIR >> 12;
std::strncpy(dot_entry.name, ".", 1);
dot_entry.rec_len = calculate_dir_entry_len(dot_entry.name_len);
std::memcpy(root_dir_data_buffer.data() + current_offset, &dot_entry, (size_t)8 + dot_entry.name_len);
current_offset += dot_entry.rec_len;
SimpleFS_DirEntry dotdot_entry;
dotdot_entry.inode = SIMPLEFS_ROOT_INODE_NUM;
dotdot_entry.name_len = 2;
dotdot_entry.file_type = S_IFDIR >> 12;
std::strncpy(dotdot_entry.name, "..", 2);
dotdot_entry.rec_len = SIMPLEFS_BLOCK_SIZE - current_offset;
std::memcpy(root_dir_data_buffer.data() + current_offset, &dotdot_entry, (size_t)8 + dotdot_entry.name_len);
if (write_block(fd, root_dir_data_block_num, root_dir_data_buffer.data()) != 0) {
std::cerr << "根目录数据块写入失败" << std::endl; return 1;
}
std::cout << " 已向根目录数据块写入'.'和'..'项" << std::endl;
SimpleFS_Inode root_inode;
std::memset(&root_inode, 0, sizeof(SimpleFS_Inode));
root_inode.i_mode = S_IFDIR | 0777; // 从0755改为0777便于测试
root_inode.i_uid = 0;
root_inode.i_gid = 0;
root_inode.i_size = SIMPLEFS_BLOCK_SIZE;
root_inode.i_links_count = 2;
root_inode.i_blocks = SIMPLEFS_BLOCK_SIZE / 512;
root_inode.i_atime = root_inode.i_ctime = root_inode.i_mtime = time(nullptr);
root_inode.i_block[0] = root_dir_data_block_num;
// 基于1的inode编号的修正计算
uint32_t root_inode_idx_in_group = SIMPLEFS_ROOT_INODE_NUM - 1;
uint32_t root_inode_block_in_table = group0_gd.bg_inode_table + (root_inode_idx_in_group / inodes_per_block);
uint32_t root_inode_offset_in_block = (root_inode_idx_in_group % inodes_per_block) * SIMPLEFS_INODE_SIZE;
std::vector<uint8_t> inode_table_block_buffer(SIMPLEFS_BLOCK_SIZE);
if (read_block(fd, root_inode_block_in_table, inode_table_block_buffer.data()) != 0) {
std::cerr << "根inode的inode表块读取失败" << std::endl; return 1;
}
std::memcpy(inode_table_block_buffer.data() + root_inode_offset_in_block, &root_inode, sizeof(SimpleFS_Inode));
if (write_block(fd, root_inode_block_in_table, inode_table_block_buffer.data()) != 0) {
std::cerr << "根inode写入inode表失败" << std::endl; return 1;
}
std::cout << " Initialized and written root inode (inode " << SIMPLEFS_ROOT_INODE_NUM << ")." << std::endl;
std::cout << "Finalizing Superblock and GDT..." << std::endl;
std::memcpy(fs_block_buffer.data(), &sb, sizeof(sb));
if (write_block(fd, superblock_location_block, fs_block_buffer.data()) != 0) { /* error */ return 1;}
gdt_write_ptr = reinterpret_cast<uint8_t*>(gdt.data());
for (uint32_t i = 0; i < gdt_blocks; ++i) {
std::fill(fs_block_buffer.begin(), fs_block_buffer.end(), 0);
uint32_t bytes_to_copy_this_block = (gdt_size_bytes - (i * SIMPLEFS_BLOCK_SIZE) < SIMPLEFS_BLOCK_SIZE) ?
(gdt_size_bytes % SIMPLEFS_BLOCK_SIZE == 0 && gdt_size_bytes > 0 ? SIMPLEFS_BLOCK_SIZE : gdt_size_bytes % SIMPLEFS_BLOCK_SIZE)
: SIMPLEFS_BLOCK_SIZE;
if (gdt_size_bytes == 0 && i==0) bytes_to_copy_this_block = 0;
std::memcpy(fs_block_buffer.data(), gdt_write_ptr + (i * SIMPLEFS_BLOCK_SIZE), bytes_to_copy_this_block);
if (write_block(fd, gdt_start_block + i, fs_block_buffer.data()) != 0) { /* error */ return 1; }
}
std::cout << "超级块和GDT已完成" << std::endl;
std::cout << "文件系统格式化成功" << std::endl;
if (close(fd) == -1) {
perror("关闭设备/镜像失败");
}
std::cout << "SimpleFS格式化工具完成" << std::endl;
return 0;
}

BIN
课程设计报告.docx Normal file

Binary file not shown.