Initial commit
This commit is contained in:
182
.gitignore
vendored
Normal file
182
.gitignore
vendored
Normal 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
|
||||||
BIN
2330110900+张顾皓+2330110878+刘蕊菁_操作系统课程设计答辩记录.docx
Normal file
BIN
2330110900+张顾皓+2330110878+刘蕊菁_操作系统课程设计答辩记录.docx
Normal file
Binary file not shown.
56
CMakeLists.txt
Normal file
56
CMakeLists.txt
Normal 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
10
CMakePresets.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"configurePresets": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"generator": "Ninja",
|
||||||
|
"binaryDir": "${sourceDir}/build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
10
build.sh
Normal file
10
build.sh
Normal 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
16
include/disk_io.h
Normal 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
33
include/fuse_ops.h
Normal 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
48
include/metadata.h
Normal 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
101
include/simplefs.h
Normal 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)
|
||||||
13
include/simplefs_context.h
Normal file
13
include/simplefs_context.h
Normal 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
27
include/utils.h
Normal 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
52
src/disk_io.cpp
Normal 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
1618
src/fuse_ops.cpp
Normal file
File diff suppressed because it is too large
Load Diff
113
src/main.cpp
Normal file
113
src/main.cpp
Normal 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; // 超级块在块1,GDT从块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
1022
src/metadata.cpp
Normal file
File diff suppressed because it is too large
Load Diff
100
src/utils.cpp
Normal file
100
src/utils.cpp
Normal 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
598
stress_test.py
Normal 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
77
tools/fsck.cpp
Normal 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
577
tools/mkfs.cpp
Normal 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];
|
||||||
|
// 根inode(SIMPLEFS_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
BIN
课程设计报告.docx
Normal file
Binary file not shown.
Reference in New Issue
Block a user