summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--CMakeLists.txt17
-rw-r--r--README.md31
-rw-r--r--build/.keep0
-rw-r--r--cmake/modules/CompilerSetup.cmake115
-rw-r--r--cmake/modules/ConanPackages.cmake20
-rw-r--r--conanfile.txt5
-rw-r--r--include/fs/detail/superblock.hpp82
-rw-r--r--include/fs/extfs.hpp41
-rw-r--r--src/CMakeLists.txt8
-rw-r--r--src/extsh.cpp19
-rw-r--r--src/fs/CMakeLists.txt10
-rw-r--r--src/fs/extfs.cpp35
-rwxr-xr-xtools/mkvdisk.sh18
14 files changed, 408 insertions, 0 deletions
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
--- /dev/null
+++ b/build/.keep
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 </dev/null 2>&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 <array>
+#include <cstdint>
+
+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<std::size_t Size>
+ using chr_arr = std::array<char, Size>;
+
+ template<std::size_t Size>
+ using u08_arr = std::array<u08, Size>;
+
+ template<std::size_t Size>
+ using u32_arr = std::array<u32, Size>;
+
+
+ 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 <fstream>
+#include <string>
+
+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 <iostream>
+#include <string>
+
+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 <fstream>
+#include <string>
+
+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<char *>(&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