From 31bb7fd29bca88f86860bdc8aa7f09c3e8e3f111 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 23 Dec 2016 22:19:13 +0100 Subject: Initial commit --- .gitignore | 7 +++ CMakeLists.txt | 17 ++++++ README.md | 31 ++++++++++ build/.keep | 0 cmake/modules/CompilerSetup.cmake | 115 ++++++++++++++++++++++++++++++++++++++ cmake/modules/ConanPackages.cmake | 20 +++++++ conanfile.txt | 5 ++ include/fs/detail/superblock.hpp | 82 +++++++++++++++++++++++++++ include/fs/extfs.hpp | 41 ++++++++++++++ src/CMakeLists.txt | 8 +++ src/extsh.cpp | 19 +++++++ src/fs/CMakeLists.txt | 10 ++++ src/fs/extfs.cpp | 35 ++++++++++++ tools/mkvdisk.sh | 18 ++++++ 14 files changed, 408 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 build/.keep create mode 100644 cmake/modules/CompilerSetup.cmake create mode 100644 cmake/modules/ConanPackages.cmake create mode 100644 conanfile.txt create mode 100644 include/fs/detail/superblock.hpp create mode 100644 include/fs/extfs.hpp create mode 100644 src/CMakeLists.txt create mode 100644 src/extsh.cpp create mode 100644 src/fs/CMakeLists.txt create mode 100644 src/fs/extfs.cpp create mode 100755 tools/mkvdisk.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..959bd59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build +*.img + +.ycm_extra_conf.py +*.py[cod] + +.color_coded diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..11db086 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.2) +project("extfs" CXX) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") +set(CXX_VERSION "c++1z") + +include("CompilerSetup") +include("ConanPackages") + +option(EXTFS_BUILD_STATIC "Build extfs as a static library" ON) + +include_directories("include") +add_subdirectory("src") diff --git a/README.md b/README.md new file mode 100644 index 0000000..e5b6442 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +#extfs +**NOTE:** this library is in very early development + +**extfs** is a simple implementation of the ext family (2/3/4) of file systems. + +#Disclaimer +I take no responsibility for any damage done to your main file system or other +data. + +#Requirements + - [base] Probably some flavor of Linux (tested on **Arch**) + - [build] A C++17 capable compiler (e.g modern **GCC** or **Clang**) + - [build] **CMake** (tested on 3.6, should work down to 3.2) + - [build] Some build tool supported by cmake (**GNU Autotools**, **Ninja**, etc.) + - [build] **Conan** + - [testing] **e2fsprogs** + +#Building +```bash +$ git clone https://github.com/fmorgner/extfs.git +$ cd extfs/build +$ cmake .. +$ cmake --build . -- -j$(nproc) +``` + +##Build options +| Option | Default | Description | +| ------------------------ | ------- | ---------------------------------- | +| EXTFS_BUILD_STATIC | On | Build libextfs as a static library | +| EXTFS_ENABLE_RTTI | On | Enable runtime type information | +| EXTFS_ENABLE_EXCEPTIONS | On | Enable C++ exceptions | diff --git a/build/.keep b/build/.keep new file mode 100644 index 0000000..e69de29 diff --git a/cmake/modules/CompilerSetup.cmake b/cmake/modules/CompilerSetup.cmake new file mode 100644 index 0000000..82d0b2d --- /dev/null +++ b/cmake/modules/CompilerSetup.cmake @@ -0,0 +1,115 @@ +if(NOT CXX_VERSION) + set(CXX_VERSION "c++14") +endif() + +# Acquire compiler information +execute_process( + COMMAND + bash -c "${CMAKE_CXX_COMPILER} -E -xc++ -std=${CXX_VERSION} - -v &1" + OUTPUT_VARIABLE + COMPILER_SETTINGS + ) + +# Extract system headers +string( + REGEX + REPLACE + "(.*#include <\.\.\.> search starts here:)\n(.*)\n(End of search list\..*)" "\\2" + LINES + ${COMPILER_SETTINGS} + ) + +# Prepare the list of system headrs +string(STRIP ${LINES} LINES) +string(REPLACE " " "" LINES ${LINES}) +string(REPLACE "\n" ";" LINES ${LINES}) + +# Trick CMake to explicitely add /usr/include to the system headers +list(APPEND LINES "/usr/bin/../include") + +# Add system header locations to the system header search-path +include_directories(SYSTEM ${LINES}) + +# Prepare some generic C++ compiler flags +set(CMAKE_CXX_FLAGS "-std=${CXX_VERSION} -Wall -Wextra -pedantic -Werror") + +# Make pedantic warning fatal with GCC +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic-errors") +endif() + +# Create upper-case copy of the project name for variable prefixes +string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER) + +# Enable support for different sanitizers when building with clang +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + option(${PROJECT_NAME_UPPER}_ENABLE_ASAN "Enable ASan (address sanitization)" OFF) + option(${PROJECT_NAME_UPPER}_ENABLE_UBSAN "Enable UBSan (undefined-behavior sanitization)" OFF) + + if(${PROJECT_NAME_UPPER}_ENABLE_ASAN OR ${PROJECT_NAME_UPPER}_ENABLE_UBSAN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer") + set(SANITIZERS "") + + if(${PROJECT_NAME_UPPER}_ENABLE_ASAN) + list(APPEND SANITIZERS "address") + endif() + + if(${PROJECT_NAME_UPPER}_ENABLE_UBSAN) + list(APPEND SANITIZERS "undefined") + endif() + + string(REPLACE ";" "," SANITIZERS "${SANITIZERS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${SANITIZERS}") + endif() +endif() + +# Allow disabling RTTI +option(${PROJECT_NAME_UPPER}_ENABLE_RTTI "Enable runtime type information" ON) +if(${PROJECT_NAME_UPPER}_ENABLE_RTTI) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frtti") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + +# Allow disable Exceptions +option(${PROJECT_NAME_UPPER}_ENABLE_EXCEPTIONS "Enable C++ exceptions" ON) +if(${PROJECT_NAME_UPPER}_ENABLE_EXCEPTIONS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions") +endif() + +# Export general C++ compiler flags +set( + CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native" + CACHE STRING "Flags used by the compiler during all builds." + FORCE + ) + +# Export additional C++ compiler flags for debug builds +set( + CMAKE_CXX_FLAGS_DEBUG "-g3" + CACHE STRING "Flags used by the compiler during debug builds." + FORCE + ) + +# Export additional C++ compiler flags for release builds +set( + CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" + CACHE STRING "Flags used by the compiler during release builds." + FORCE + ) + +# Export additional C++ compiler flags for release builds with debug information +set( + CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g -DNDEBUG" + CACHE STRING "Flags used by the compiler during release builds with debug info." + FORCE + ) + +# Export additional C++ compiler flags for minimum size release builds +set( + CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG" + CACHE STRING "Flags used by the compiler during release builds for minimum size." + FORCE + ) diff --git a/cmake/modules/ConanPackages.cmake b/cmake/modules/ConanPackages.cmake new file mode 100644 index 0000000..9fdcc68 --- /dev/null +++ b/cmake/modules/ConanPackages.cmake @@ -0,0 +1,20 @@ +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/conanfile.txt") + message(STATUS "Current conanfile.txt directory: ${CMAKE_CURRENT_SOURCE_DIR}") + if(NOT EXISTS "${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") + message(STATUS "Collecting conan packages") + execute_process( + COMMAND + conan install --build=missing --generator cmake ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_QUIET + ) + endif() +else() + message(FATAL_ERROR "No conanfile found at '${CMAKE_CURRENT_SOURCE_DIR}/conanfile.txt'") +endif() + +include("${CMAKE_BINARY_DIR}/conanbuildinfo.cmake") + +set(CONAN_SYSTEM_INCLUDES ON) +conan_check_compiler() +conan_flags_setup() +conan_set_find_paths() diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000..a103f33 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,5 @@ +[requires] +CUTE/2.0.0@fmorgner/stable + +[generators] +cmake diff --git a/include/fs/detail/superblock.hpp b/include/fs/detail/superblock.hpp new file mode 100644 index 0000000..5b7a4ca --- /dev/null +++ b/include/fs/detail/superblock.hpp @@ -0,0 +1,82 @@ +#ifndef EXTFS_SUPERBLOCK_HPP +#define EXTFS_SUPERBLOCK_HPP + +#include +#include + +namespace fs::detail + { + + using u32 = std::uint32_t; + using s32 = std::int32_t; + using u16 = std::uint16_t; + using s16 = std::uint16_t; + using u08 = std::uint8_t; + + template + using chr_arr = std::array; + + template + using u08_arr = std::array; + + template + using u32_arr = std::array; + + + struct extfs_superblock + { + u32 const inodes_count{}; + u32 const blocks_count{}; + u32 const reserved_blocks_count{}; + u32 const free_blocks_count{}; + u32 const free_inodes_count{}; + u32 const first_data_block_id{}; + u32 const logical_block_size{}; + s32 const logical_fragment_size{}; + u32 const blocks_per_group{}; + u32 const fragments_per_group{}; + u32 const inodes_per_group{}; + u32 const last_mount_timestamp{}; + u32 const last_write_timestamp{}; + u16 const mount_count{}; + u16 const maximum_mount_count{}; + u16 const magic_number{}; + s16 const state{}; + s16 const error_behaviour{}; + s16 const revision_level_minor{}; + u32 const last_check_timestamp{}; + u32 const check_interval{}; + u32 const creator_operating_system_id{}; + u32 const revision_level{}; + u16 const super_user_id{}; + u16 const super_user_group_id{}; + u32 const first_inode_id{}; + u16 const inode_size{}; + u16 const superblock_group_id{}; + u32 const compatible_features{}; + u32 const incompatible_features{}; + u32 const read_only_compatible_features{}; + u08_arr<16> const uuid{}; + chr_arr<16> const volume_name{}; + chr_arr<64> const last_mount_point{}; + u32 const compression_algorithm{}; + u08 const file_preallocated_blocks_count{}; + u08 const directory_preallocated_blocks_count{}; + u16 const _padding{}; + u08_arr<16> const journal_superblock_uuid{}; + u32 const journal_inode_id{}; + u32 const journal_device_number{}; + u32 const last_orphan_inode_id{}; + u32_arr<4> const hash_seed{}; + u08 const hash_version{}; + u08_arr<3> const _reserved0{}; + u32 const default_mount_options{}; + u32 const first_meta_block_group_id{}; + u08_arr<760> const _reserved1{}; + }; + + static_assert(sizeof(extfs_superblock) == 1024, "An ext* super block must have an exact size of 1024 byte!"); + + } + +#endif diff --git a/include/fs/extfs.hpp b/include/fs/extfs.hpp new file mode 100644 index 0000000..0075572 --- /dev/null +++ b/include/fs/extfs.hpp @@ -0,0 +1,41 @@ +#ifndef EXTFS_EXTFS_HPP +#define EXTFS_EXTFS_HPP + +#include "fs/detail/superblock.hpp" + +#include +#include + +namespace fs + { + + struct extfs + { + enum struct mode : char + { + read_only, + writeable + }; + + /** + * Open the filesystem at a given path + * + * @param path The path to a device/file containing an ext* file system. + * @param openMode Whether to open the file system in read_only or writeable mode. + * @since 1.0 + */ + explicit extfs(std::string const & path, mode const openMode = mode::read_only); + + /** + * Check if the filesystem is valid + */ + operator bool() const; + + private: + std::fstream m_stream{}; + detail::extfs_superblock m_primarySuperblock{}; + }; + + } + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..e09c4a2 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(fs) + +add_executable(extsh + extsh.cpp + ) +target_link_libraries(extsh + extfs + ) diff --git a/src/extsh.cpp b/src/extsh.cpp new file mode 100644 index 0000000..477f6c8 --- /dev/null +++ b/src/extsh.cpp @@ -0,0 +1,19 @@ +#include "fs/extfs.hpp" + +#include +#include + +int main(int argc, char const * argv[]) + { + auto const & path = [&]{ return std::string{argc > 1 ? argv[1] : "vdisk.img"}; }(); + auto const & disk = fs::extfs{path}; + + if(disk) + { + std::clog << "[EXT2] Successfully opened ext*fs at: '" << path << "'\n"; + } + else + { + std::clog << "[EXT2] Failed to open ext*fs at: '" << path << "'\n"; + } + } diff --git a/src/fs/CMakeLists.txt b/src/fs/CMakeLists.txt new file mode 100644 index 0000000..3ca33b8 --- /dev/null +++ b/src/fs/CMakeLists.txt @@ -0,0 +1,10 @@ +if(EXTFS_BUILD_STATIC) + set(LIBRARY_TYPE "STATIC") +else() + set(LIBRARY_TYPE "SHARED") +endif() + +add_library(extfs + ${LIBRARY_TYPE} + extfs.cpp + ) diff --git a/src/fs/extfs.cpp b/src/fs/extfs.cpp new file mode 100644 index 0000000..19c294b --- /dev/null +++ b/src/fs/extfs.cpp @@ -0,0 +1,35 @@ +#include "fs/detail/superblock.hpp" +#include "fs/extfs.hpp" + +#include +#include + +namespace + { + auto constexpr kPrimarySuperblockLocation = 1024; + auto constexpr kExtfsMagic = 0xef53; + + auto read_superblock(std::fstream & stream, fs::detail::extfs_superblock & superblock) + { + auto const originalPosition = stream.tellg(); + stream.seekg(kPrimarySuperblockLocation); + stream.read(reinterpret_cast(&superblock), sizeof(fs::detail::extfs_superblock)); + stream.seekg(originalPosition); + } + } + +namespace fs + { + + extfs::extfs(std::string const & path, extfs::mode const openMode) : + m_stream{path, std::ios::binary | (openMode == mode::read_only ? std::ios::in : std::ios::in | std::ios::out)} + { + read_superblock(m_stream, m_primarySuperblock); + } + + extfs::operator bool() const + { + return m_stream && m_primarySuperblock.magic_number == kExtfsMagic; + } + + } diff --git a/tools/mkvdisk.sh b/tools/mkvdisk.sh new file mode 100755 index 0000000..bc31f95 --- /dev/null +++ b/tools/mkvdisk.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# This script creates a 16Mb disk in a file + +echo -n "Creating disk image..." +if dd if=/dev/zero of=vdisk.img bs=1M count=16 &>/dev/null; then + echo " done" + echo -n "Creating ext2 file system..." + if mkfs.ext2 -L vdisk vdisk.img &>/dev/null; then + echo " done" + else + echo " failed" + exit 2 + fi +else + echo " failed" + exit 1 +fi -- cgit v1.2.3