├── .github └── workflows │ ├── build-aarch64.yml │ ├── build-amd64.yml │ ├── build-mingw-amd64.yml │ ├── build-msvc-aarch64.yml │ ├── build-msvc-amd64.yml │ ├── build-msvc-arm.yml │ ├── build-msvc-x86.yml │ └── build-x86.yml ├── .gitmodules ├── CMakeLists.txt ├── LICENCE ├── README.md ├── aarch64.cmake ├── amd64.cmake ├── clang-aarch64.cmake ├── clang-amd64.cmake ├── clang-arm.cmake ├── clang-x86.cmake ├── mingw-amd64.cmake ├── mingw-x86.cmake ├── msvc-aarch64.cmake ├── msvc-amd64.cmake ├── msvc-armv7.cmake ├── msvc-x86.cmake └── src ├── btrfs.c ├── btrfs.h ├── crc32c.c ├── lzo.c ├── misc.c ├── misc.h ├── quibbleproto.h └── zstd-shim.h /.github/workflows/build-aarch64.yml: -------------------------------------------------------------------------------- 1 | name: build aarch64 2 | on: [push] 3 | jobs: 4 | aarch64: 5 | runs-on: ubuntu-rolling 6 | steps: 7 | - run: apt-get update 8 | - run: apt-get install -y git cmake nodejs gcc-aarch64-linux-gnu 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_C_COMPILER=/usr/bin/aarch64-linux-gnu-gcc \ 16 | -DCMAKE_EXE_LINKER_FLAGS=-static \ 17 | -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=ARM64 \ 18 | -S ${SHORT_SHA} -B debug-work && \ 19 | cmake --build debug-work --parallel `nproc` && \ 20 | cp debug-work/btrfs.efi install/debug/ 21 | - run: | 22 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 23 | -DCMAKE_C_COMPILER=/usr/bin/aarch64-linux-gnu-gcc \ 24 | -DCMAKE_EXE_LINKER_FLAGS=-static \ 25 | -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=ARM64 \ 26 | -S ${SHORT_SHA} -B release-work && \ 27 | cmake --build release-work --parallel `nproc` && \ 28 | cp release-work/btrfs.efi install/ 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: ${{ github.sha }} 32 | overwrite: true 33 | path: | 34 | install 35 | -------------------------------------------------------------------------------- /.github/workflows/build-amd64.yml: -------------------------------------------------------------------------------- 1 | name: build amd64 2 | on: [push] 3 | jobs: 4 | amd64: 5 | runs-on: ubuntu-rolling 6 | steps: 7 | - run: apt-get update 8 | - run: apt-get install -y git cmake nodejs gcc-x86-64-linux-gnu 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_C_COMPILER=/usr/bin/x86_64-linux-gnu-gcc \ 16 | -DCMAKE_EXE_LINKER_FLAGS=-static \ 17 | -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=AMD64 \ 18 | -S ${SHORT_SHA} -B debug-work && \ 19 | cmake --build debug-work --parallel `nproc` && \ 20 | cp debug-work/btrfs.efi install/debug/ 21 | - run: | 22 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 23 | -DCMAKE_C_COMPILER=/usr/bin/x86_64-linux-gnu-gcc \ 24 | -DCMAKE_EXE_LINKER_FLAGS=-static \ 25 | -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=AMD64 \ 26 | -S ${SHORT_SHA} -B release-work && \ 27 | cmake --build release-work --parallel `nproc` && \ 28 | cp release-work/btrfs.efi install/ 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: ${{ github.sha }} 32 | overwrite: true 33 | path: | 34 | install 35 | -------------------------------------------------------------------------------- /.github/workflows/build-mingw-amd64.yml: -------------------------------------------------------------------------------- 1 | name: build mingw amd64 2 | on: [push] 3 | jobs: 4 | amd64: 5 | runs-on: ubuntu-rolling 6 | steps: 7 | - run: apt-get update 8 | - run: apt-get install -y git cmake nodejs gcc-mingw-w64-x86-64 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_TOOLCHAIN_FILE=mingw-amd64.cmake \ 16 | -S ${SHORT_SHA} -B debug-work && \ 17 | cmake --build debug-work --parallel `nproc` && \ 18 | cp debug-work/btrfs.efi install/debug/ 19 | - run: | 20 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 21 | -DCMAKE_TOOLCHAIN_FILE=mingw-amd64.cmake \ 22 | -S ${SHORT_SHA} -B release-work && \ 23 | cmake --build release-work --parallel `nproc` && \ 24 | cp release-work/btrfs.efi install/ 25 | - uses: actions/upload-artifact@v3 26 | with: 27 | name: ${{ github.sha }} 28 | overwrite: true 29 | path: | 30 | install 31 | -------------------------------------------------------------------------------- /.github/workflows/build-msvc-aarch64.yml: -------------------------------------------------------------------------------- 1 | name: build msvc aarch64 2 | on: [push] 3 | env: 4 | PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/eselect/wine/bin 5 | jobs: 6 | aarch64: 7 | runs-on: msvc-wine 8 | steps: 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_TOOLCHAIN_FILE=msvc-aarch64.cmake \ 16 | -DCMAKE_SYSTEM_PROCESSOR=ARM64 \ 17 | -S ${SHORT_SHA} -B debug-work && \ 18 | cmake --build debug-work --parallel `nproc` && \ 19 | cp debug-work/btrfs.efi install/debug/ && \ 20 | cp debug-work/btrfs.pdb install/debug/ 21 | - run: | 22 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 23 | -DCMAKE_TOOLCHAIN_FILE=msvc-aarch64.cmake \ 24 | -DCMAKE_SYSTEM_PROCESSOR=ARM64 \ 25 | -S ${SHORT_SHA} -B release-work && \ 26 | cmake --build release-work --parallel `nproc` && \ 27 | cp release-work/btrfs.efi install/ && \ 28 | cp release-work/btrfs.pdb install/ 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: ${{ github.sha }} 32 | overwrite: true 33 | path: | 34 | install 35 | -------------------------------------------------------------------------------- /.github/workflows/build-msvc-amd64.yml: -------------------------------------------------------------------------------- 1 | name: build msvc amd64 2 | on: [push] 3 | env: 4 | PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/eselect/wine/bin 5 | jobs: 6 | amd64: 7 | runs-on: msvc-wine 8 | steps: 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_TOOLCHAIN_FILE=msvc-amd64.cmake \ 16 | -DCMAKE_SYSTEM_PROCESSOR=AMD64 \ 17 | -S ${SHORT_SHA} -B debug-work && \ 18 | cmake --build debug-work --parallel `nproc` && \ 19 | cp debug-work/btrfs.efi install/debug/ && \ 20 | cp debug-work/btrfs.pdb install/debug/ 21 | - run: | 22 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 23 | -DCMAKE_TOOLCHAIN_FILE=msvc-amd64.cmake \ 24 | -DCMAKE_SYSTEM_PROCESSOR=AMD64 \ 25 | -S ${SHORT_SHA} -B release-work && \ 26 | cmake --build release-work --parallel `nproc` && \ 27 | cp release-work/btrfs.efi install/ && \ 28 | cp release-work/btrfs.pdb install/ 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: ${{ github.sha }} 32 | overwrite: true 33 | path: | 34 | install 35 | -------------------------------------------------------------------------------- /.github/workflows/build-msvc-arm.yml: -------------------------------------------------------------------------------- 1 | # FIXME - this doesn't work. MSVC brings in __rt_udiv, which links to 2 | # kernel32!RaiseException through __helper_divide_by_0. 3 | # Need to write dummy __fphelper_raise_exception? 4 | 5 | name: build msvc arm 6 | on: [push] 7 | env: 8 | PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/eselect/wine/bin 9 | jobs: 10 | arm: 11 | runs-on: msvc-wine 12 | steps: 13 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 14 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 15 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 16 | - run: mkdir -p install/debug 17 | - run: | 18 | cmake -DCMAKE_BUILD_TYPE=Debug \ 19 | -DCMAKE_TOOLCHAIN_FILE=msvc-armv7.cmake \ 20 | -DCMAKE_SYSTEM_PROCESSOR=ARM \ 21 | -S ${SHORT_SHA} -B debug-work && \ 22 | cmake --build debug-work --parallel `nproc` && \ 23 | cp debug-work/btrfs.efi install/debug/ && \ 24 | cp debug-work/btrfs.pdb install/debug/ 25 | - run: | 26 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 27 | -DCMAKE_TOOLCHAIN_FILE=msvc-armv7.cmake \ 28 | -DCMAKE_SYSTEM_PROCESSOR=ARM \ 29 | -S ${SHORT_SHA} -B release-work && \ 30 | cmake --build release-work --parallel `nproc` && \ 31 | cp release-work/btrfs.efi install/ && \ 32 | cp release-work/btrfs.pdb install/ 33 | - uses: actions/upload-artifact@v3 34 | with: 35 | name: ${{ github.sha }} 36 | overwrite: true 37 | path: | 38 | install 39 | -------------------------------------------------------------------------------- /.github/workflows/build-msvc-x86.yml: -------------------------------------------------------------------------------- 1 | name: build msvc x86 2 | on: [push] 3 | env: 4 | PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/etc/eselect/wine/bin 5 | jobs: 6 | x86: 7 | runs-on: msvc-wine 8 | steps: 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_TOOLCHAIN_FILE=msvc-x86.cmake \ 16 | -DCMAKE_SYSTEM_PROCESSOR=X86 \ 17 | -S ${SHORT_SHA} -B debug-work && \ 18 | cmake --build debug-work --parallel `nproc` && \ 19 | cp debug-work/btrfs.efi install/debug/ && \ 20 | cp debug-work/btrfs.pdb install/debug/ 21 | - run: | 22 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 23 | -DCMAKE_TOOLCHAIN_FILE=msvc-x86.cmake \ 24 | -DCMAKE_SYSTEM_PROCESSOR=X86 \ 25 | -S ${SHORT_SHA} -B release-work && \ 26 | cmake --build release-work --parallel `nproc` && \ 27 | cp release-work/btrfs.efi install/ && \ 28 | cp release-work/btrfs.pdb install/ 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: ${{ github.sha }} 32 | overwrite: true 33 | path: | 34 | install 35 | -------------------------------------------------------------------------------- /.github/workflows/build-x86.yml: -------------------------------------------------------------------------------- 1 | name: build x86 2 | on: [push] 3 | jobs: 4 | x86: 5 | runs-on: ubuntu-rolling 6 | steps: 7 | - run: apt-get update 8 | - run: apt-get install -y git cmake nodejs gcc-mingw-w64-i686 9 | - run: echo "SHORT_SHA=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV 10 | - run: git clone --recurse-submodules ${{ github.server_url }}/${{ github.repository }} ${SHORT_SHA} 11 | - run: cd ${SHORT_SHA} && git checkout ${{ github.sha }} 12 | - run: mkdir -p install/debug 13 | - run: | 14 | cmake -DCMAKE_BUILD_TYPE=Debug \ 15 | -DCMAKE_TOOLCHAIN_FILE=mingw-x86.cmake \ 16 | -S ${SHORT_SHA} -B debug-work && \ 17 | cmake --build debug-work --parallel `nproc` && \ 18 | cp debug-work/btrfs.efi install/debug/ 19 | - run: | 20 | cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo \ 21 | -DCMAKE_TOOLCHAIN_FILE=mingw-x86.cmake \ 22 | -S ${SHORT_SHA} -B release-work && \ 23 | cmake --build release-work --parallel `nproc` && \ 24 | cp release-work/btrfs.efi install/ 25 | - uses: actions/upload-artifact@v3 26 | with: 27 | name: ${{ github.sha }} 28 | overwrite: true 29 | path: | 30 | install 31 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "gnu-efi"] 2 | path = gnu-efi 3 | url = https://git.code.sf.net/p/gnu-efi/code 4 | [submodule "zstd"] 5 | path = src/zstd 6 | url = https://github.com/facebook/zstd 7 | [submodule "src/zlib"] 8 | path = src/zlib 9 | url = https://github.com/madler/zlib 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | project(btrfs-efi LANGUAGES C VERSION 20230328) 4 | 5 | if(MSVC) 6 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:efi_main") 7 | add_compile_options("/GS-") 8 | string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 9 | string(REGEX REPLACE "/RTC(su|[1su])" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") 10 | 11 | # work around bug in Visual Studio 12 | if (MSVC_CXX_ARCHITECTURE_ID STREQUAL "X86") 13 | set(CMAKE_SYSTEM_PROCESSOR "X86") 14 | endif() 15 | else() 16 | add_compile_options(-fno-stack-check -fno-stack-protector -ffreestanding -Werror-implicit-function-declaration) 17 | endif() 18 | 19 | include_directories(${CMAKE_SOURCE_DIR}/gnu-efi/inc) 20 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") 21 | include_directories(${CMAKE_SOURCE_DIR}/gnu-efi/inc/x86_64) 22 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "X86") 23 | include_directories(${CMAKE_SOURCE_DIR}/gnu-efi/inc/ia32) 24 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 25 | include_directories(${CMAKE_SOURCE_DIR}/gnu-efi/inc/aarch64) 26 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm") 27 | include_directories(${CMAKE_SOURCE_DIR}/gnu-efi/inc/arm) 28 | endif() 29 | 30 | if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") 31 | add_compile_options(-mno-stack-arg-probe) 32 | elseif(NOT MSVC) 33 | add_compile_options(-fpic -fpie -fshort-wchar -fno-ident) 34 | add_compile_options(-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0) 35 | 36 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 37 | add_compile_options(-mcmodel=tiny) 38 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") 39 | add_compile_options(-mno-sse) 40 | endif() 41 | endif() 42 | 43 | # ----------------------------------- 44 | 45 | set(ZSTD_SRC_FILES src/zstd/lib/common/entropy_common.c 46 | src/zstd/lib/common/error_private.c 47 | src/zstd/lib/common/fse_decompress.c 48 | src/zstd/lib/decompress/huf_decompress.c 49 | src/zstd/lib/common/zstd_common.c 50 | src/zstd/lib/decompress/zstd_ddict.c 51 | src/zstd/lib/decompress/zstd_decompress.c 52 | src/zstd/lib/decompress/zstd_decompress_block.c 53 | src/zstd/lib/common/xxhash.c) 54 | 55 | add_library(zstd STATIC ${ZSTD_SRC_FILES}) 56 | target_compile_definitions(zstd PRIVATE -DZSTD_DEPS_MALLOC -DXXH_NO_STDLIB -DZSTD_HAVE_WEAK_SYMBOLS=0 -DZSTD_DISABLE_ASM) 57 | 58 | if(NOT MSVC) 59 | target_compile_options(zstd PRIVATE -ffunction-sections -include ${CMAKE_SOURCE_DIR}/src/zstd-shim.h) 60 | else() 61 | target_compile_options(zstd PRIVATE /Gy /FI ${CMAKE_SOURCE_DIR}/src/zstd-shim.h) 62 | endif() 63 | 64 | # ----------------------------------- 65 | 66 | set(ZLIB_SRC_FILES src/zlib/adler32.c 67 | src/zlib/deflate.c 68 | src/zlib/inffast.c 69 | src/zlib/inflate.c 70 | src/zlib/inftrees.c 71 | src/zlib/trees.c 72 | src/zlib/zutil.c) 73 | 74 | add_library(zlib STATIC ${ZLIB_SRC_FILES}) 75 | target_compile_definitions(zlib PRIVATE -DNO_GZIP -DZ_SOLO) 76 | 77 | if(NOT MSVC) 78 | target_compile_options(zlib PRIVATE -ffunction-sections) 79 | else() 80 | target_compile_options(zlib PRIVATE /Gy) 81 | endif() 82 | 83 | # ----------------------------------- 84 | 85 | set(SRC_FILES src/btrfs.c 86 | src/crc32c.c 87 | src/misc.c 88 | src/lzo.c) 89 | 90 | add_executable(btrfs ${SRC_FILES}) 91 | target_link_libraries(btrfs zstd zlib) 92 | 93 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 94 | set_target_properties(btrfs PROPERTIES SUFFIX ".efi") 95 | else() 96 | target_compile_definitions(btrfs PRIVATE NEED_DUMMY_RELOC) 97 | target_compile_definitions(btrfs PRIVATE GNU_EFI_USE_MS_ABI) 98 | 99 | if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") 100 | target_link_options(btrfs PRIVATE -T ${CMAKE_SOURCE_DIR}/gnu-efi/gnuefi/elf_x86_64_efi.lds) 101 | add_custom_command(TARGET btrfs 102 | POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O efi-bsdrv-x86_64 -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rodata -j .rel -j .rela -j ".rel.*" -j ".rela.*" -j ".rel*" -j ".rela*" -j .reloc --strip-unneeded btrfs btrfs.efi 103 | BYPRODUCTS btrfs.efi 104 | ) 105 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") 106 | target_link_options(btrfs PRIVATE -T ${CMAKE_SOURCE_DIR}/gnu-efi/gnuefi/elf_aarch64_efi.lds) 107 | add_custom_command(TARGET btrfs 108 | POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O efi-bsdrv-aarch64 -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rodata -j .rel -j .rela -j ".rel.*" -j ".rela.*" -j ".rel*" -j ".rela*" -j .reloc --strip-unneeded btrfs btrfs.efi 109 | BYPRODUCTS btrfs.efi 110 | ) 111 | elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "X86") 112 | message(FATAL_ERROR "Please use mingw to compile for x86") 113 | endif() 114 | endif() 115 | 116 | if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") 117 | target_link_options(btrfs PRIVATE "-nostartfiles") 118 | 119 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 120 | target_link_options(btrfs PRIVATE "-Wl,--subsystem,11") 121 | endif() 122 | 123 | if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "X86") 124 | target_link_options(btrfs PRIVATE "-e_efi_main") 125 | else() 126 | target_link_options(btrfs PRIVATE "-eefi_main") 127 | endif() 128 | elseif(MSVC) 129 | target_link_options(btrfs PRIVATE "/SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER") 130 | target_compile_options(btrfs PRIVATE "/Oi-") 131 | endif() 132 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | btrfs-efi 2 | --------- 3 | 4 | btrfs-efi is a Btrfs filesystem driver for EFI. It is intended for use with the free 5 | Windows bootloader [Quibble](https://github.com/maharmstone/quibble), but you 6 | should be able to use it for anything EFI-related. 7 | 8 | Changelog 9 | --------- 10 | 11 | * 20230328 12 | * Initial release, split off from the Quibble codebase 13 | 14 | Licences and Thanks 15 | ------------------- 16 | 17 | btrfs-efi contains portions of the following software: 18 | 19 | ### Zlib 20 | 21 | Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler 22 | 23 | This software is provided 'as-is', without any express or implied 24 | warranty. In no event will the authors be held liable for any damages 25 | arising from the use of this software. 26 | 27 | Permission is granted to anyone to use this software for any purpose, 28 | including commercial applications, and to alter it and redistribute it 29 | freely, subject to the following restrictions: 30 | 31 | 1. The origin of this software must not be misrepresented; you must not 32 | claim that you wrote the original software. If you use this software 33 | in a product, an acknowledgment in the product documentation would be 34 | appreciated but is not required. 35 | 2. Altered source versions must be plainly marked as such, and must not be 36 | misrepresented as being the original software. 37 | 3. This notice may not be removed or altered from any source distribution. 38 | 39 | ### LZO 40 | 41 | btrfs-efi contains portions of an early version of lzo, which is copyright 1996 42 | Markus Oberhumer. Modern versions are licensed under the GPL, but this was 43 | licensed under the LGPL, so I believe it is okay to use. 44 | 45 | ### Zstd 46 | 47 | Copyright (c) 2016-present, Facebook, Inc. All rights reserved. 48 | 49 | Redistribution and use in source and binary forms, with or without modification, 50 | are permitted provided that the following conditions are met: 51 | 52 | * Redistributions of source code must retain the above copyright notice, this 53 | list of conditions and the following disclaimer. 54 | 55 | * Redistributions in binary form must reproduce the above copyright notice, 56 | this list of conditions and the following disclaimer in the documentation 57 | and/or other materials provided with the distribution. 58 | 59 | * Neither the name Facebook nor the names of its contributors may be used to 60 | endorse or promote products derived from this software without specific 61 | prior written permission. 62 | 63 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 64 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 65 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 66 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 67 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 68 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 69 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 70 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 71 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 72 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 73 | 74 | -------------------------------------------------------------------------------- /aarch64.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Linux) 2 | 3 | SET(CMAKE_C_COMPILER aarch64-unknown-linux-gnu-gcc) 4 | SET(CMAKE_CXX_COMPILER aarch64-unknown-linux-gnu-g++) 5 | SET(CMAKE_SYSTEM_PROCESSOR "ARM64") 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "-static") 8 | 9 | SET(CMAKE_FIND_ROOT_PATH /usr/aarch64-unknown-linux-gnu) 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /amd64.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Linux) 2 | 3 | SET(CMAKE_C_COMPILER x86_64-pc-linux-gnu-gcc) 4 | SET(CMAKE_CXX_COMPILER x86_64-pc-linux-gnu-g++) 5 | SET(CMAKE_SYSTEM_PROCESSOR "AMD64") 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "-static") 8 | 9 | SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-pc-linux-gnu) 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | -------------------------------------------------------------------------------- /clang-aarch64.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER clang) 4 | SET(CMAKE_SYSTEM_PROCESSOR ARM64) 5 | 6 | SET(CLANG_TARGET_TRIPLE aarch64-w64-mingw32) 7 | SET(CMAKE_C_COMPILER_TARGET aarch64-w64-mingw32) 8 | 9 | set(CMAKE_EXE_LINKER_FLAGS "-static") 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /clang-amd64.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER clang) 4 | SET(CMAKE_SYSTEM_PROCESSOR "AMD64") 5 | 6 | SET(CLANG_TARGET_TRIPLE x86_64-w64-mingw32) 7 | SET(CMAKE_C_COMPILER_TARGET x86_64-w64-mingw32) 8 | 9 | set(CMAKE_EXE_LINKER_FLAGS "-static") 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /clang-arm.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER clang) 4 | SET(CMAKE_SYSTEM_PROCESSOR ARM) 5 | 6 | SET(CLANG_TARGET_TRIPLE armv7l-w64-mingw32) 7 | SET(CMAKE_C_COMPILER_TARGET armv7l-w64-mingw32) 8 | 9 | set(CMAKE_EXE_LINKER_FLAGS "-static") 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /clang-x86.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER clang) 4 | SET(CMAKE_SYSTEM_PROCESSOR X86) 5 | 6 | SET(CLANG_TARGET_TRIPLE i686-w64-mingw32) 7 | SET(CMAKE_C_COMPILER_TARGET i686-w64-mingw32) 8 | 9 | set(CMAKE_EXE_LINKER_FLAGS "-static") 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /mingw-amd64.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 4 | SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 5 | SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 6 | SET(CMAKE_SYSTEM_PROCESSOR "AMD64") 7 | 8 | set(CMAKE_EXE_LINKER_FLAGS "-static") 9 | 10 | SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) 11 | 12 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 14 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 15 | 16 | -------------------------------------------------------------------------------- /mingw-x86.cmake: -------------------------------------------------------------------------------- 1 | SET(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 4 | SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 5 | SET(CMAKE_SYSTEM_PROCESSOR X86) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "-static") 8 | 9 | SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) 10 | 11 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 12 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 13 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 14 | 15 | -------------------------------------------------------------------------------- /msvc-aarch64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER /opt/msvc/bin/arm64/cl) 4 | SET(CMAKE_CXX_COMPILER /opt/msvc/bin/arm64/cl) 5 | SET(CMAKE_RC_COMPILER /opt/msvc/bin/arm64/rc) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") 8 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO") 9 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFEST:NO") 10 | -------------------------------------------------------------------------------- /msvc-amd64.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER /opt/msvc/bin/x64/cl) 4 | SET(CMAKE_CXX_COMPILER /opt/msvc/bin/x64/cl) 5 | SET(CMAKE_RC_COMPILER /opt/msvc/bin/x64/rc) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") 8 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO") 9 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFEST:NO") 10 | -------------------------------------------------------------------------------- /msvc-armv7.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER /opt/msvc/bin/arm/cl) 4 | SET(CMAKE_CXX_COMPILER /opt/msvc/bin/arm/cl) 5 | SET(CMAKE_RC_COMPILER /opt/msvc/bin/arm/rc) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") 8 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO") 9 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFEST:NO") 10 | -------------------------------------------------------------------------------- /msvc-x86.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Windows) 2 | 3 | SET(CMAKE_C_COMPILER /opt/msvc/bin/x86/cl) 4 | SET(CMAKE_CXX_COMPILER /opt/msvc/bin/x86/cl) 5 | SET(CMAKE_RC_COMPILER /opt/msvc/bin/x86/rc) 6 | 7 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO") 8 | set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /MANIFEST:NO") 9 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /MANIFEST:NO") 10 | 11 | set(CMAKE_PREFIX_PATH "/home/hellas/wine/vcpkg/installed/x64-windows") 12 | -------------------------------------------------------------------------------- /src/btrfs.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "misc.h" 25 | #include "quibbleproto.h" 26 | #include "btrfs.h" 27 | 28 | #include "zlib/zlib.h" 29 | 30 | #define ZSTD_STATIC_LINKING_ONLY 31 | 32 | #include "zstd/lib/zstd.h" 33 | 34 | #define __S_IFDIR 0040000 35 | 36 | static EFI_SYSTEM_TABLE* systable; 37 | static EFI_BOOT_SERVICES* bs; 38 | static EFI_QUIBBLE_INFO_PROTOCOL* info_proto = NULL; 39 | 40 | static EFI_DRIVER_BINDING_PROTOCOL drvbind; 41 | 42 | typedef struct { 43 | uint64_t address; 44 | LIST_ENTRY list_entry; 45 | CHUNK_ITEM chunk_item; 46 | } chunk; 47 | 48 | typedef struct { 49 | LIST_ENTRY list_entry; 50 | uint64_t id; 51 | ROOT_ITEM root_item; 52 | void* top_tree; 53 | } root; 54 | 55 | typedef struct { 56 | EFI_SIMPLE_FILE_SYSTEM_PROTOCOL proto; 57 | EFI_QUIBBLE_PROTOCOL quibble_proto; 58 | EFI_OPEN_SUBVOL_PROTOCOL open_subvol_proto; 59 | superblock* sb; 60 | EFI_HANDLE controller; 61 | EFI_BLOCK_IO_PROTOCOL* block; 62 | EFI_DISK_IO_PROTOCOL* disk_io; 63 | bool chunks_loaded; 64 | LIST_ENTRY chunks; 65 | LIST_ENTRY roots; 66 | root* root_root; 67 | root* chunk_root; 68 | LIST_ENTRY list_entry; 69 | root* fsroot; 70 | } volume; 71 | 72 | typedef struct { 73 | void* data; 74 | KEY* key; 75 | void* item; 76 | uint16_t itemlen; 77 | uint16_t* positions; 78 | } traverse_ptr; 79 | 80 | typedef struct { 81 | EFI_FILE_PROTOCOL proto; 82 | root* r; 83 | uint64_t inode; 84 | volume* vol; 85 | bool inode_loaded; 86 | INODE_ITEM inode_item; 87 | uint64_t position; 88 | LIST_ENTRY* dir_position; 89 | WCHAR* name; 90 | LIST_ENTRY extents; 91 | LIST_ENTRY children; 92 | bool children_found; 93 | } inode; 94 | 95 | typedef struct { 96 | LIST_ENTRY list_entry; 97 | uint64_t size; 98 | uint64_t blocks; 99 | BTRFS_TIME atime; 100 | BTRFS_TIME mtime; 101 | BTRFS_TIME otime; 102 | DIR_ITEM dir_item; 103 | } inode_child; 104 | 105 | typedef struct { 106 | LIST_ENTRY list_entry; 107 | uint64_t offset; 108 | uint16_t size; 109 | EXTENT_DATA extent_data; 110 | } extent; 111 | 112 | typedef struct { 113 | LIST_ENTRY list_entry; 114 | char name[1]; 115 | } path_segment; 116 | 117 | static void* zstd_malloc(void* opaque, size_t size); 118 | static void zstd_free(void* opaque, void* address); 119 | 120 | static ZSTD_customMem zstd_mem; 121 | 122 | #define UNUSED(x) (void)(x) 123 | #define sector_align(n, a) ((n)&((a)-1)?(((n)+(a))&~((a)-1)):(n)) 124 | 125 | #define COMPAT_FLAGS (BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF | BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL | \ 126 | BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS | BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO | \ 127 | BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD | BTRFS_INCOMPAT_FLAGS_BIG_METADATA | \ 128 | BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF | BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA | \ 129 | BTRFS_INCOMPAT_FLAGS_NO_HOLES | BTRFS_INCOMPAT_FLAGS_METADATA_UUID) 130 | // FIXME - RAID56 131 | // FIXME - RAID1C34 132 | 133 | __inline static void populate_file_handle(EFI_FILE_PROTOCOL* h); 134 | static EFI_STATUS load_inode(inode* ino); 135 | 136 | EFI_STATUS lzo_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, uint32_t inpageoff); 137 | 138 | // crc32c.c 139 | uint32_t calc_crc32c(uint32_t seed, uint8_t* msg, unsigned int msglen); 140 | 141 | static LIST_ENTRY volumes; 142 | 143 | void do_print(const char* s) { 144 | if (info_proto) 145 | info_proto->Print(s); 146 | } 147 | 148 | void do_print_error(const char* func, EFI_STATUS Status) { 149 | char s[255], *p; 150 | 151 | p = stpcpy(s, func); 152 | p = stpcpy(p, " returned "); 153 | p = stpcpy(p, error_string(Status)); 154 | p = stpcpy(p, "\n"); 155 | 156 | do_print(s); 157 | } 158 | 159 | static EFI_STATUS EFIAPI drv_supported(EFI_DRIVER_BINDING_PROTOCOL* This, EFI_HANDLE ControllerHandle, 160 | EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath) { 161 | EFI_STATUS Status; 162 | EFI_DISK_IO_PROTOCOL* disk_io; 163 | EFI_GUID guid_disk = EFI_DISK_IO_PROTOCOL_GUID; 164 | EFI_GUID guid_block = EFI_BLOCK_IO_PROTOCOL_GUID; 165 | 166 | UNUSED(RemainingDevicePath); 167 | 168 | Status = bs->OpenProtocol(ControllerHandle, &guid_disk, (void**)&disk_io, This->DriverBindingHandle, 169 | ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); 170 | 171 | if (EFI_ERROR(Status)) 172 | return Status; 173 | 174 | bs->CloseProtocol(ControllerHandle, &guid_disk, This->DriverBindingHandle, ControllerHandle); 175 | 176 | return bs->OpenProtocol(ControllerHandle, &guid_block, NULL, This->DriverBindingHandle, 177 | ControllerHandle, EFI_OPEN_PROTOCOL_TEST_PROTOCOL); 178 | } 179 | 180 | static EFI_STATUS bootstrap_roots(volume* vol) { 181 | EFI_STATUS Status; 182 | root* r; 183 | 184 | InitializeListHead(&vol->roots); 185 | 186 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(root), (void**)&r); 187 | if (EFI_ERROR(Status)) { 188 | do_print_error("AllocatePool", Status); 189 | return Status; 190 | } 191 | 192 | memset(r, 0, sizeof(root)); 193 | 194 | r->id = BTRFS_ROOT_ROOT; 195 | r->root_item.block_number = vol->sb->root_tree_addr; 196 | r->root_item.root_level = vol->sb->root_level; 197 | 198 | vol->root_root = r; 199 | 200 | InsertTailList(&vol->roots, &r->list_entry); 201 | 202 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(root), (void**)&r); 203 | if (EFI_ERROR(Status)) { 204 | do_print_error("AllocatePool", Status); 205 | return Status; 206 | } 207 | 208 | memset(r, 0, sizeof(root)); 209 | 210 | r->id = BTRFS_ROOT_CHUNK; 211 | r->root_item.block_number = vol->sb->chunk_tree_addr; 212 | r->root_item.root_level = vol->sb->chunk_root_level; 213 | 214 | vol->chunk_root = r; 215 | 216 | InsertTailList(&vol->roots, &r->list_entry); 217 | 218 | return EFI_SUCCESS; 219 | } 220 | 221 | static EFI_STATUS read_data(volume* vol, uint64_t address, uint32_t size, void* data) { 222 | EFI_STATUS Status; 223 | LIST_ENTRY* le; 224 | chunk* c = NULL; 225 | CHUNK_ITEM_STRIPE* stripes; 226 | 227 | le = vol->chunks.Flink; 228 | while (le != &vol->chunks) { 229 | chunk* c2 = _CR(le, chunk, list_entry); 230 | 231 | if (address >= c2->address && address < c2->address + c2->chunk_item.size) { 232 | c = c2; 233 | break; 234 | } else if (c2->address > address) 235 | break; 236 | 237 | le = le->Flink; 238 | } 239 | 240 | if (!c) { 241 | char s[100], *p; 242 | 243 | p = stpcpy(s, "Could not find chunk for address "); 244 | p = hex_to_str(p, address); 245 | p = stpcpy(p, ".\n"); 246 | 247 | do_print(s); 248 | 249 | return EFI_INVALID_PARAMETER; 250 | } 251 | 252 | // FIXME - support RAID 253 | 254 | if (c->chunk_item.type & BLOCK_FLAG_RAID0) { 255 | do_print("FIXME - support RAID0.\n"); 256 | return EFI_INVALID_PARAMETER; 257 | } else if (c->chunk_item.type & BLOCK_FLAG_RAID10) { 258 | do_print("FIXME - support RAID10.\n"); 259 | return EFI_INVALID_PARAMETER; 260 | } else if (c->chunk_item.type & BLOCK_FLAG_RAID5) { 261 | do_print("FIXME - support RAID5.\n"); 262 | return EFI_INVALID_PARAMETER; 263 | } else if (c->chunk_item.type & BLOCK_FLAG_RAID6) { 264 | do_print("FIXME - support RAID6.\n"); 265 | return EFI_INVALID_PARAMETER; 266 | } 267 | 268 | stripes = (CHUNK_ITEM_STRIPE*)((uint8_t*)&c->chunk_item + sizeof(CHUNK_ITEM)); 269 | 270 | for (unsigned int i = 0; i < c->chunk_item.num_stripes; i++) { 271 | // FIXME - support other devices 272 | // FIXME - use other stripe if csum error 273 | 274 | if (stripes[i].dev_id == vol->sb->dev_item.dev_id) { 275 | Status = vol->block->ReadBlocks(vol->block, vol->block->Media->MediaId, 276 | (stripes[i].offset + address - c->address) / vol->block->Media->BlockSize, 277 | size, data); 278 | if (EFI_ERROR(Status)) { 279 | do_print_error("ReadBlocks", Status); 280 | continue; 281 | } 282 | 283 | return EFI_SUCCESS; 284 | } 285 | } 286 | 287 | return EFI_VOLUME_CORRUPTED; 288 | } 289 | 290 | static int keycmp(const KEY* key1, const KEY* key2) { 291 | if (key1->obj_id < key2->obj_id) 292 | return -1; 293 | 294 | if (key1->obj_id > key2->obj_id) 295 | return 1; 296 | 297 | if (key1->obj_type < key2->obj_type) 298 | return -1; 299 | 300 | if (key1->obj_type > key2->obj_type) 301 | return 1; 302 | 303 | if (key1->offset < key2->offset) 304 | return -1; 305 | 306 | if (key1->offset > key2->offset) 307 | return 1; 308 | 309 | return 0; 310 | } 311 | 312 | static EFI_STATUS find_item(volume* vol, root* r, traverse_ptr* tp, const KEY* searchkey) { 313 | EFI_STATUS Status; 314 | tree_header* tree; 315 | uint64_t addr; 316 | 317 | Status = bs->AllocatePool(EfiBootServicesData, (r->root_item.root_level + 1) * vol->sb->leaf_size, (void**)&tp->data); 318 | if (EFI_ERROR(Status)) { 319 | do_print_error("AllocatePool", Status); 320 | return Status; 321 | } 322 | 323 | Status = bs->AllocatePool(EfiBootServicesData, (r->root_item.root_level + 1) * sizeof(uint16_t), (void**)&tp->positions); 324 | if (EFI_ERROR(Status)) { 325 | do_print_error("AllocatePool", Status); 326 | bs->FreePool(tp->data); 327 | return Status; 328 | } 329 | 330 | addr = r->root_item.block_number; 331 | 332 | if (!r->top_tree) { 333 | Status = bs->AllocatePool(EfiBootServicesData, vol->sb->leaf_size, &r->top_tree); 334 | if (EFI_ERROR(Status)) { 335 | do_print_error("AllocatePool", Status); 336 | return Status; 337 | } 338 | 339 | Status = read_data(vol, r->root_item.block_number, vol->sb->leaf_size, r->top_tree); 340 | if (EFI_ERROR(Status)) { 341 | do_print_error("read_data", Status); 342 | bs->FreePool(r->top_tree); 343 | r->top_tree = NULL; 344 | return Status; 345 | } 346 | } 347 | 348 | memcpy(tp->data, r->top_tree, vol->sb->leaf_size); 349 | 350 | for (unsigned int i = 0; i < (unsigned int)(r->root_item.root_level + 1); i++) { 351 | if (i != 0) { 352 | Status = read_data(vol, addr, vol->sb->leaf_size, (uint8_t*)tp->data + (i * vol->sb->leaf_size)); 353 | if (EFI_ERROR(Status)) { 354 | do_print_error("read_data", Status); 355 | return Status; 356 | } 357 | } 358 | 359 | tree = (tree_header*)((uint8_t*)tp->data + (i * vol->sb->leaf_size)); 360 | 361 | // FIXME - check csum 362 | 363 | if (tree->level != r->root_item.root_level - i) { 364 | char s[100], *p; 365 | 366 | p = stpcpy(s, "Tree level was "); 367 | p = dec_to_str(p, tree->level); 368 | p = stpcpy(p, ", expected"); 369 | p = dec_to_str(p, r->root_item.root_level - i); 370 | p = stpcpy(p, ".\n"); 371 | 372 | do_print(s); 373 | 374 | return EFI_VOLUME_CORRUPTED; 375 | } 376 | 377 | if (tree->level != 0) { 378 | internal_node* nodes = (internal_node*)((uint8_t*)tree + sizeof(tree_header)); 379 | 380 | for (unsigned int j = 0; j < tree->num_items; j++) { 381 | int cmp = keycmp(searchkey, &nodes[j].key); 382 | 383 | if (cmp == 0 || (cmp != -1 && j == tree->num_items - 1) || (cmp == -1 && j == 0)) { 384 | tp->positions[i] = j; 385 | addr = nodes[j].address; 386 | break; 387 | } 388 | 389 | if (cmp == -1) { 390 | tp->positions[i] = j - 1; 391 | addr = nodes[j - 1].address; 392 | break; 393 | } 394 | } 395 | } else { 396 | leaf_node* nodes = (leaf_node*)((uint8_t*)tree + sizeof(tree_header)); 397 | 398 | for (unsigned int j = 0; j < tree->num_items; j++) { 399 | int cmp = keycmp(searchkey, &nodes[j].key); 400 | 401 | if (cmp == 0 || (cmp == -1 && j == 0)) { 402 | tp->key = &nodes[j].key; 403 | tp->item = (uint8_t*)nodes + nodes[j].offset; 404 | tp->itemlen = nodes[j].size; 405 | tp->positions[i] = j; 406 | return EFI_SUCCESS; 407 | } 408 | 409 | if (cmp == -1) { 410 | tp->key = &nodes[j - 1].key; 411 | tp->item = (uint8_t*)nodes + nodes[j - 1].offset; 412 | tp->itemlen = nodes[j - 1].size; 413 | tp->positions[i] = j - 1; 414 | return EFI_SUCCESS; 415 | } 416 | } 417 | 418 | tp->key = &nodes[tree->num_items - 1].key; 419 | tp->item = (uint8_t*)nodes + nodes[tree->num_items - 1].offset; 420 | tp->itemlen = nodes[tree->num_items - 1].size; 421 | tp->positions[i] = tree->num_items - 1; 422 | 423 | return EFI_SUCCESS; 424 | } 425 | } 426 | 427 | return EFI_NOT_FOUND; 428 | } 429 | 430 | static EFI_STATUS next_item(volume* vol, traverse_ptr* tp) { 431 | EFI_STATUS Status; 432 | uint8_t level = ((tree_header*)tp->data)->level; 433 | 434 | tp->positions[level]++; 435 | 436 | for (int i = level; i >= 0; i--) { 437 | tree_header* tree = (tree_header*)((uint8_t*)tp->data + (i * vol->sb->leaf_size)); 438 | 439 | if (tp->positions[i] == tree->num_items) { 440 | if (i == 0) 441 | return EFI_NOT_FOUND; 442 | 443 | tp->positions[i-1]++; 444 | } else { 445 | leaf_node* nodes; 446 | 447 | for (unsigned int j = i + 1; j <= level; j++) { 448 | internal_node* int_nodes = (internal_node*)((uint8_t*)tp->data + ((j - 1) * vol->sb->leaf_size) + sizeof(tree_header)); 449 | uint64_t addr = int_nodes[tp->positions[j - 1]].address; 450 | 451 | Status = read_data(vol, addr, vol->sb->leaf_size, (uint8_t*)tp->data + (j * vol->sb->leaf_size)); 452 | if (EFI_ERROR(Status)) { 453 | do_print_error("read_data", Status); 454 | return Status; 455 | } 456 | 457 | // FIXME - check crc32 458 | 459 | tp->positions[j] = 0; 460 | } 461 | 462 | nodes = (leaf_node*)((uint8_t*)tp->data + (level * vol->sb->leaf_size) + sizeof(tree_header)); 463 | 464 | tp->key = &nodes[tp->positions[level]].key; 465 | tp->item = (uint8_t*)nodes + nodes[tp->positions[level]].offset; 466 | tp->itemlen = nodes[tp->positions[level]].size; 467 | 468 | return EFI_SUCCESS; 469 | } 470 | } 471 | 472 | return EFI_SUCCESS; 473 | } 474 | 475 | static void free_traverse_ptr(traverse_ptr* tp) { 476 | bs->FreePool(tp->data); 477 | } 478 | 479 | static EFI_STATUS load_roots(volume* vol) { 480 | EFI_STATUS Status; 481 | traverse_ptr tp; 482 | KEY searchkey; 483 | 484 | searchkey.obj_id = 0; 485 | searchkey.obj_type = 0; 486 | searchkey.offset = 0; 487 | 488 | Status = find_item(vol, vol->root_root, &tp, &searchkey); 489 | if (EFI_ERROR(Status)) { 490 | do_print_error("find_item", Status); 491 | return Status; 492 | } 493 | 494 | do { 495 | if (tp.key->obj_type == TYPE_ROOT_ITEM && tp.itemlen >= sizeof(ROOT_ITEM)) { 496 | root* r; 497 | 498 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(root), (void**)&r); 499 | if (EFI_ERROR(Status)) { 500 | do_print_error("AllocatePool", Status); 501 | return Status; 502 | } 503 | 504 | memset(r, 0, sizeof(root)); 505 | 506 | r->id = tp.key->obj_id; 507 | memcpy(&r->root_item, tp.item, sizeof(ROOT_ITEM)); 508 | 509 | if (r->id > _CR(vol->roots.Blink, root, list_entry)->id) { 510 | InsertTailList(&vol->roots, &r->list_entry); 511 | } else { 512 | LIST_ENTRY* le = vol->roots.Flink; 513 | bool inserted = false; 514 | 515 | while (le != &vol->roots) { 516 | root* r2 = _CR(le, root, list_entry); 517 | 518 | if (r2->id > r->id) { 519 | InsertHeadList(r2->list_entry.Blink, &r->list_entry); 520 | inserted = true; 521 | break; 522 | } 523 | 524 | le = le->Flink; 525 | } 526 | 527 | if (!inserted) 528 | InsertTailList(&vol->roots, &r->list_entry); 529 | } 530 | } 531 | 532 | Status = next_item(vol, &tp); 533 | if (Status == EFI_NOT_FOUND) 534 | break; 535 | else if (EFI_ERROR(Status)) { 536 | do_print_error("next_item", Status); 537 | break; 538 | } 539 | } while (true); 540 | 541 | free_traverse_ptr(&tp); 542 | 543 | return EFI_SUCCESS; 544 | } 545 | 546 | static EFI_STATUS find_default_subvol(volume* vol, uint64_t* subvol) { 547 | EFI_STATUS Status; 548 | KEY searchkey; 549 | traverse_ptr tp; 550 | DIR_ITEM* di; 551 | 552 | static const char fn[] = "default"; 553 | static uint32_t crc32 = 0x8dbfc2d2; 554 | 555 | // get default subvol 556 | 557 | searchkey.obj_id = vol->sb->root_dir_objectid; 558 | searchkey.obj_type = TYPE_DIR_ITEM; 559 | searchkey.offset = crc32; 560 | 561 | Status = find_item(vol, vol->root_root, &tp, &searchkey); 562 | if (EFI_ERROR(Status)) { 563 | do_print_error("find_item", Status); 564 | return Status; 565 | } 566 | 567 | if (keycmp(tp.key, &searchkey)) { 568 | char s[100], *p; 569 | 570 | p = stpcpy(s, "Could not find ("); 571 | p = hex_to_str(p, searchkey.obj_id); 572 | p = stpcpy(p, ","); 573 | p = hex_to_str(p, searchkey.obj_type); 574 | p = stpcpy(p, ","); 575 | p = hex_to_str(p, searchkey.offset); 576 | p = stpcpy(p, ") in root tree.\n"); 577 | 578 | do_print(s); 579 | 580 | Status = EFI_NOT_FOUND; 581 | goto end; 582 | } 583 | 584 | if (tp.itemlen < sizeof(DIR_ITEM)) { 585 | char s[100], *p; 586 | 587 | p = stpcpy(s, "("); 588 | p = hex_to_str(p, searchkey.obj_id); 589 | p = stpcpy(p, ","); 590 | p = hex_to_str(p, searchkey.obj_type); 591 | p = stpcpy(p, ","); 592 | p = hex_to_str(p, searchkey.offset); 593 | p = stpcpy(p, ") was "); 594 | p = dec_to_str(p, tp.itemlen); 595 | p = stpcpy(p, " bytes, expected at least "); 596 | p = dec_to_str(p, sizeof(DIR_ITEM)); 597 | p = stpcpy(p, ".\n"); 598 | 599 | do_print(s); 600 | 601 | Status = EFI_NOT_FOUND; 602 | goto end; 603 | } 604 | 605 | di = (DIR_ITEM*)tp.item; 606 | 607 | if (tp.itemlen < offsetof(DIR_ITEM, name[0]) + di->n) { 608 | char s[100], *p; 609 | 610 | p = stpcpy(s, "("); 611 | p = hex_to_str(p, searchkey.obj_id); 612 | p = stpcpy(p, ","); 613 | p = hex_to_str(p, searchkey.obj_type); 614 | p = stpcpy(p, ","); 615 | p = hex_to_str(p, searchkey.offset); 616 | p = stpcpy(p, ") was "); 617 | p = dec_to_str(p, tp.itemlen); 618 | p = stpcpy(p, " bytes, expected "); 619 | p = dec_to_str(p, offsetof(DIR_ITEM, name[0]) + di->n); 620 | p = stpcpy(p, ".\n"); 621 | 622 | do_print(s); 623 | 624 | Status = EFI_NOT_FOUND; 625 | goto end; 626 | } 627 | 628 | if (di->n != sizeof(fn) - 1 || memcmp(di->name, fn, di->n)) { 629 | do_print("root DIR_ITEM had same CRC32, but was not \"default\"\n"); 630 | 631 | Status = EFI_NOT_FOUND; 632 | goto end; 633 | } 634 | 635 | if (di->key.obj_type != TYPE_ROOT_ITEM) { 636 | char s[100], *p; 637 | 638 | p = stpcpy(s, "default root has key ("); 639 | p = hex_to_str(p, di->key.obj_id); 640 | p = stpcpy(p, ","); 641 | p = hex_to_str(p, di->key.obj_type); 642 | p = stpcpy(p, ","); 643 | p = hex_to_str(p, di->key.offset); 644 | p = stpcpy(p, "), expected subvolume\r\n"); 645 | 646 | do_print(s); 647 | 648 | Status = EFI_NOT_FOUND; 649 | goto end; 650 | } 651 | 652 | *subvol = di->key.obj_id; 653 | 654 | Status = EFI_SUCCESS; 655 | 656 | end: 657 | free_traverse_ptr(&tp); 658 | 659 | return Status; 660 | } 661 | 662 | static EFI_STATUS load_chunks(volume* vol) { 663 | EFI_STATUS Status; 664 | uint8_t* data; 665 | uint32_t n = vol->sb->n; 666 | KEY searchkey; 667 | traverse_ptr tp; 668 | LIST_ENTRY chunks2; 669 | LIST_ENTRY* le; 670 | uint64_t subvol_no = BTRFS_ROOT_FSTREE; 671 | 672 | InitializeListHead(&vol->chunks); 673 | 674 | // load bootstrapped chunks 675 | 676 | data = (uint8_t*)vol->sb->sys_chunk_array; 677 | 678 | while (n >= sizeof(KEY) + sizeof(CHUNK_ITEM) + sizeof(CHUNK_ITEM_STRIPE)) { 679 | KEY* key = (KEY*)data; 680 | CHUNK_ITEM* ci; 681 | chunk* c; 682 | 683 | if (key->obj_type != TYPE_CHUNK_ITEM) 684 | break; 685 | 686 | n -= sizeof(KEY); 687 | data += sizeof(KEY); 688 | 689 | ci = (CHUNK_ITEM*)data; 690 | 691 | if (n < sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE))) 692 | break; 693 | 694 | Status = bs->AllocatePool(EfiBootServicesData, 695 | offsetof(chunk, chunk_item) + sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE)), 696 | (void**)&c); 697 | if (EFI_ERROR(Status)) { 698 | do_print_error("AllocatePool", Status); 699 | return Status; 700 | } 701 | 702 | c->address = key->offset; 703 | memcpy(&c->chunk_item, data, sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE))); 704 | InsertTailList(&vol->chunks, &c->list_entry); 705 | 706 | data += sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE)); 707 | n -= sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE)); 708 | } 709 | 710 | Status = bootstrap_roots(vol); 711 | if (EFI_ERROR(Status)) { 712 | do_print_error("bootstrap_roots", Status); 713 | return Status; 714 | } 715 | 716 | InitializeListHead(&chunks2); 717 | 718 | searchkey.obj_id = 0; 719 | searchkey.obj_type = 0; 720 | searchkey.offset = 0; 721 | 722 | Status = find_item(vol, vol->chunk_root, &tp, &searchkey); 723 | if (EFI_ERROR(Status)) { 724 | do_print_error("find_item", Status); 725 | return Status; 726 | } 727 | 728 | do { 729 | if (tp.key->obj_type == TYPE_CHUNK_ITEM && tp.itemlen >= sizeof(CHUNK_ITEM)) { 730 | chunk* c; 731 | CHUNK_ITEM* ci; 732 | 733 | ci = (CHUNK_ITEM*)tp.item; 734 | 735 | if (tp.itemlen >= sizeof(CHUNK_ITEM) + (ci->num_stripes * sizeof(CHUNK_ITEM_STRIPE))) { 736 | Status = bs->AllocatePool(EfiBootServicesData, offsetof(chunk, chunk_item) + tp.itemlen, (void**)&c); 737 | if (EFI_ERROR(Status)) { 738 | do_print_error("AllocatePool", Status); 739 | return Status; 740 | } 741 | 742 | c->address = tp.key->offset; 743 | memcpy(&c->chunk_item, tp.item, tp.itemlen); 744 | InsertTailList(&chunks2, &c->list_entry); 745 | } 746 | } 747 | 748 | Status = next_item(vol, &tp); 749 | if (Status == EFI_NOT_FOUND) 750 | break; 751 | else if (EFI_ERROR(Status)) { 752 | do_print_error("next_item", Status); 753 | break; 754 | } 755 | } while (true); 756 | 757 | free_traverse_ptr(&tp); 758 | 759 | // replace chunks 760 | while (!IsListEmpty(&vol->chunks)) { 761 | chunk* c = _CR(vol->chunks.Flink, chunk, list_entry); 762 | 763 | RemoveEntryList(&c->list_entry); 764 | bs->FreePool(c); 765 | } 766 | 767 | vol->chunks.Flink = chunks2.Flink; 768 | vol->chunks.Flink->Blink = &vol->chunks; 769 | vol->chunks.Blink = chunks2.Blink; 770 | vol->chunks.Blink->Flink = &vol->chunks; 771 | 772 | Status = load_roots(vol); 773 | if (EFI_ERROR(Status)) { 774 | do_print_error("load_roots", Status); 775 | return Status; 776 | } 777 | 778 | if (vol->sb->incompat_flags & BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL) { 779 | Status = find_default_subvol(vol, &subvol_no); 780 | if (EFI_ERROR(Status)) 781 | return Status; 782 | } 783 | 784 | le = vol->roots.Flink; 785 | while (le != &vol->roots) { 786 | root* r2 = _CR(le, root, list_entry); 787 | 788 | if (r2->id == subvol_no) { 789 | vol->fsroot = r2; 790 | break; 791 | } 792 | 793 | le = le->Flink; 794 | } 795 | 796 | vol->chunks_loaded = true; 797 | 798 | return EFI_SUCCESS; 799 | } 800 | 801 | static EFI_STATUS find_file_in_dir(volume* vol, root* r, uint64_t inode_num, WCHAR* name, unsigned int name_len, 802 | root** out_r, uint64_t* out_inode) { 803 | EFI_STATUS Status; 804 | unsigned int fnlen; 805 | char* fn; 806 | uint32_t hash; 807 | KEY searchkey; 808 | traverse_ptr tp; 809 | DIR_ITEM* di; 810 | unsigned int len; 811 | 812 | // convert name from UTF-16 to UTF-8 813 | 814 | Status = utf16_to_utf8(NULL, 0, &fnlen, name, name_len * sizeof(WCHAR)); 815 | if (EFI_ERROR(Status)) { 816 | do_print_error("utf16_to_utf8", Status); 817 | return Status; 818 | } 819 | 820 | Status = bs->AllocatePool(EfiBootServicesData, fnlen, (void**)&fn); 821 | if (EFI_ERROR(Status)) { 822 | do_print_error("AllocatePool", Status); 823 | return Status; 824 | } 825 | 826 | Status = utf16_to_utf8(fn, fnlen, &fnlen, name, name_len * sizeof(WCHAR)); 827 | if (EFI_ERROR(Status)) { 828 | do_print_error("utf16_to_utf8", Status); 829 | bs->FreePool(fn); 830 | return Status; 831 | } 832 | 833 | // get CRC32 hash of name 834 | 835 | hash = calc_crc32c(0xfffffffe, (uint8_t*)fn, fnlen); 836 | 837 | // lookup DIR_ITEM of hash 838 | 839 | searchkey.obj_id = inode_num; 840 | searchkey.obj_type = TYPE_DIR_ITEM; 841 | searchkey.offset = hash; 842 | 843 | Status = find_item(vol, r, &tp, &searchkey); 844 | if (Status == EFI_NOT_FOUND) { 845 | bs->FreePool(fn); 846 | return Status; 847 | } else if (EFI_ERROR(Status)) { 848 | do_print_error("find_item", Status); 849 | bs->FreePool(fn); 850 | return Status; 851 | } 852 | 853 | if (keycmp(tp.key, &searchkey)) { 854 | bs->FreePool(fn); 855 | free_traverse_ptr(&tp); 856 | return EFI_NOT_FOUND; 857 | } 858 | 859 | di = (DIR_ITEM*)tp.item; 860 | len = tp.itemlen; 861 | 862 | while (len >= sizeof(DIR_ITEM) && len >= offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 863 | if (di->n == fnlen && !memcmp(fn, di->name, fnlen)) { 864 | if (di->key.obj_type == TYPE_ROOT_ITEM) { 865 | LIST_ENTRY* le = vol->roots.Flink; 866 | 867 | *out_r = NULL; 868 | *out_inode = SUBVOL_ROOT_INODE; 869 | 870 | while (le != &vol->roots) { 871 | root* r = _CR(le, root, list_entry); 872 | 873 | if (r->id == di->key.obj_id) 874 | *out_r = r; 875 | else if (r->id > di->key.obj_id) 876 | break; 877 | 878 | le = le->Flink; 879 | } 880 | 881 | if (!*out_r) { 882 | char s[100], *p; 883 | 884 | p = stpcpy(s, "Could not find subvol "); 885 | p = hex_to_str(p, di->key.obj_id); 886 | p = stpcpy(p, ".\n"); 887 | 888 | do_print(s); 889 | 890 | bs->FreePool(fn); 891 | free_traverse_ptr(&tp); 892 | 893 | return EFI_NOT_FOUND; 894 | } 895 | } else { 896 | *out_r = r; 897 | *out_inode = di->key.obj_id; 898 | } 899 | 900 | bs->FreePool(fn); 901 | free_traverse_ptr(&tp); 902 | return EFI_SUCCESS; 903 | } 904 | 905 | len -= offsetof(DIR_ITEM, name[0]) + di->m + di->n; 906 | di = (DIR_ITEM*)((uint8_t*)di + offsetof(DIR_ITEM, name[0]) + di->m + di->n); 907 | } 908 | 909 | bs->FreePool(fn); 910 | 911 | free_traverse_ptr(&tp); 912 | 913 | return EFI_NOT_FOUND; 914 | } 915 | 916 | static EFI_STATUS find_file_in_dir_cached(volume* vol, inode* ino, WCHAR* name, unsigned int name_len, 917 | root** out_r, uint64_t* out_inode) { 918 | EFI_STATUS Status; 919 | unsigned int fnlen; 920 | char* fn; 921 | LIST_ENTRY* le; 922 | 923 | // convert name from UTF-16 to UTF-8 924 | 925 | Status = utf16_to_utf8(NULL, 0, &fnlen, name, name_len * sizeof(WCHAR)); 926 | if (EFI_ERROR(Status)) { 927 | do_print_error("utf16_to_utf8", Status); 928 | return Status; 929 | } 930 | 931 | Status = bs->AllocatePool(EfiBootServicesData, fnlen, (void**)&fn); 932 | if (EFI_ERROR(Status)) { 933 | do_print_error("AllocatePool", Status); 934 | return Status; 935 | } 936 | 937 | Status = utf16_to_utf8(fn, fnlen, &fnlen, name, name_len * sizeof(WCHAR)); 938 | if (EFI_ERROR(Status)) { 939 | do_print_error("utf16_to_utf8", Status); 940 | bs->FreePool(fn); 941 | return Status; 942 | } 943 | 944 | le = ino->children.Flink; 945 | 946 | while (le != &ino->children) { 947 | DIR_ITEM* di = &(_CR(le, inode_child, list_entry)->dir_item); 948 | 949 | if (di->n == fnlen && !memcmp(fn, di->name, fnlen)) { 950 | if (di->key.obj_type == TYPE_ROOT_ITEM) { 951 | LIST_ENTRY* le = vol->roots.Flink; 952 | 953 | *out_r = NULL; 954 | *out_inode = SUBVOL_ROOT_INODE; 955 | 956 | while (le != &vol->roots) { 957 | root* r = _CR(le, root, list_entry); 958 | 959 | if (r->id == di->key.obj_id) 960 | *out_r = r; 961 | else if (r->id > di->key.obj_id) 962 | break; 963 | 964 | le = le->Flink; 965 | } 966 | 967 | if (!*out_r) { 968 | char s[100], *p; 969 | 970 | p = stpcpy(s, "Could not find subvol "); 971 | p = hex_to_str(p, di->key.obj_id); 972 | p = stpcpy(p, ".\n"); 973 | 974 | do_print(s); 975 | 976 | bs->FreePool(fn); 977 | 978 | return EFI_NOT_FOUND; 979 | } 980 | } else { 981 | *out_r = ino->r; 982 | *out_inode = di->key.obj_id; 983 | } 984 | 985 | bs->FreePool(fn); 986 | 987 | return EFI_SUCCESS; 988 | } 989 | 990 | le = le->Flink; 991 | } 992 | 993 | bs->FreePool(fn); 994 | 995 | return EFI_NOT_FOUND; 996 | } 997 | 998 | static void normalize_path(WCHAR* path) { 999 | size_t len = wcslen(path); 1000 | 1001 | for (unsigned int i = 1; i < len; i++) { 1002 | if (path[i] == '\\' && path[i-1] == '\\') { 1003 | // remove empty directory name 1004 | memcpy(&path[i], &path[i+1], (len - i) * sizeof(WCHAR)); 1005 | len--; 1006 | i--; 1007 | continue; 1008 | } else if (path[i] == '.' && path[i-1] == '\\' && (path[i+1] == 0 || path[i+1] == '\\')) { 1009 | // remove . 1010 | if (path[i+1] == '\\') { 1011 | memcpy(&path[i], &path[i+2], (len - i - 1) * sizeof(WCHAR)); 1012 | len -= 2; 1013 | i--; 1014 | continue; 1015 | } else if (path[i+1] == 0) { 1016 | path[i] = 0; 1017 | return; 1018 | } 1019 | } else if (i >= 3 && path[i] == '.' && path[i-1] == '.' && path[i-2] == '\\' && (path[i+1] == 0 || path[i+1] == '\\')) { 1020 | unsigned int bs = 0; 1021 | 1022 | // remove .. 1023 | 1024 | for (int j = i - 3; j >= 0; j--) { 1025 | if (path[j] == '\\') { 1026 | bs = j; 1027 | break; 1028 | } 1029 | } 1030 | 1031 | if (path[i+1] == '\\') { 1032 | memcpy(&path[bs + 1], &path[i + 2], (len - i - 1) * sizeof(WCHAR)); 1033 | len -= i - bs + 1; 1034 | i = bs; 1035 | continue; 1036 | } else { 1037 | path[bs] = 0; 1038 | return; 1039 | } 1040 | } 1041 | } 1042 | } 1043 | 1044 | static void load_child_info(inode* ino, inode_child* ic) { 1045 | EFI_STATUS Status; 1046 | traverse_ptr tp; 1047 | KEY searchkey; 1048 | INODE_ITEM* ii; 1049 | root* search_root; 1050 | 1051 | if (ic->dir_item.key.obj_type == TYPE_ROOT_ITEM) { 1052 | LIST_ENTRY* le; 1053 | 1054 | search_root = NULL; 1055 | 1056 | le = ino->vol->roots.Flink; 1057 | while (le != &ino->vol->roots) { 1058 | root* r = _CR(le, root, list_entry); 1059 | 1060 | if (r->id == ic->dir_item.key.obj_id) 1061 | search_root = r; 1062 | else if (r->id > ic->dir_item.key.obj_id) 1063 | break; 1064 | 1065 | le = le->Flink; 1066 | } 1067 | 1068 | if (!search_root) 1069 | return; 1070 | 1071 | searchkey.obj_id = SUBVOL_ROOT_INODE; 1072 | } else { 1073 | search_root = ino->r; 1074 | searchkey.obj_id = ic->dir_item.key.obj_id; 1075 | } 1076 | 1077 | searchkey.obj_type = TYPE_INODE_ITEM; 1078 | searchkey.offset = 0xffffffffffffffff; 1079 | 1080 | Status = find_item(ino->vol, search_root, &tp, &searchkey); 1081 | if (EFI_ERROR(Status)) 1082 | return; 1083 | 1084 | if (tp.key->obj_id != searchkey.obj_id || tp.key->obj_type != searchkey.obj_type) 1085 | goto end; 1086 | 1087 | if (tp.itemlen < sizeof(INODE_ITEM)) 1088 | goto end; 1089 | 1090 | ii = tp.item; 1091 | 1092 | ic->size = ii->st_size; 1093 | ic->blocks = ii->st_blocks; 1094 | ic->atime = ii->st_atime; 1095 | ic->mtime = ii->st_mtime; 1096 | ic->otime = ii->otime; 1097 | 1098 | end: 1099 | free_traverse_ptr(&tp); 1100 | } 1101 | 1102 | static EFI_STATUS find_children(inode* ino) { 1103 | EFI_STATUS Status; 1104 | KEY searchkey; 1105 | traverse_ptr tp; 1106 | LIST_ENTRY* le; 1107 | 1108 | searchkey.obj_id = ino->inode; 1109 | searchkey.obj_type = TYPE_DIR_INDEX; 1110 | searchkey.offset = ino->position; 1111 | 1112 | Status = find_item(ino->vol, ino->r, &tp, &searchkey); 1113 | if (EFI_ERROR(Status)) { 1114 | do_print_error("find_item", Status); 1115 | return Status; 1116 | } 1117 | 1118 | while (tp.key->obj_id < ino->inode || (tp.key->obj_id == ino->inode && tp.key->obj_type < TYPE_DIR_INDEX)) { 1119 | Status = next_item(ino->vol, &tp); 1120 | 1121 | if (Status == EFI_NOT_FOUND) { // no children 1122 | ino->children_found = true; 1123 | free_traverse_ptr(&tp); 1124 | return EFI_SUCCESS; 1125 | } else if (EFI_ERROR(Status)) { 1126 | do_print_error("next_item", Status); 1127 | free_traverse_ptr(&tp); 1128 | return Status; 1129 | } 1130 | } 1131 | 1132 | while (tp.key->obj_id == ino->inode && tp.key->obj_type == TYPE_DIR_INDEX) { 1133 | DIR_ITEM* di = (DIR_ITEM*)tp.item; 1134 | 1135 | if (tp.itemlen < sizeof(DIR_ITEM)) { 1136 | char s[100], *p; 1137 | 1138 | p = stpcpy(s, "DIR_ITEM length was "); 1139 | p = dec_to_str(p, tp.itemlen); 1140 | p = stpcpy(p, " bytes, expected at least "); 1141 | p = dec_to_str(p, sizeof(DIR_ITEM)); 1142 | p = stpcpy(p, ".\n"); 1143 | 1144 | do_print(s); 1145 | } else if (tp.itemlen < offsetof(DIR_ITEM, name[0]) + di->m + di->n) { 1146 | char s[100], *p; 1147 | 1148 | p = stpcpy(s, "DIR_ITEM length was "); 1149 | p = dec_to_str(p, tp.itemlen); 1150 | p = stpcpy(p, " bytes, expected "); 1151 | p = dec_to_str(p, offsetof(DIR_ITEM, name[0]) + di->m + di->n); 1152 | p = stpcpy(p, ".\n"); 1153 | 1154 | do_print(s); 1155 | } else { 1156 | inode_child* ic; 1157 | 1158 | Status = bs->AllocatePool(EfiBootServicesData, offsetof(inode_child, dir_item) + tp.itemlen, (void**)&ic); 1159 | if (EFI_ERROR(Status)) { 1160 | do_print_error("AllocatePool", Status); 1161 | free_traverse_ptr(&tp); 1162 | return Status; 1163 | } 1164 | 1165 | ic->size = 0; 1166 | ic->blocks = 0; 1167 | ic->atime.seconds = ic->atime.nanoseconds = 0; 1168 | ic->mtime.seconds = ic->mtime.nanoseconds = 0; 1169 | ic->otime.seconds = ic->otime.nanoseconds = 0; 1170 | 1171 | memcpy(&ic->dir_item, tp.item, tp.itemlen); 1172 | InsertTailList(&ino->children, &ic->list_entry); 1173 | } 1174 | 1175 | Status = next_item(ino->vol, &tp); 1176 | 1177 | if (Status == EFI_NOT_FOUND) 1178 | break; 1179 | else if (EFI_ERROR(Status)) { 1180 | do_print_error("next_item", Status); 1181 | free_traverse_ptr(&tp); 1182 | return Status; 1183 | } 1184 | } 1185 | 1186 | free_traverse_ptr(&tp); 1187 | 1188 | le = ino->children.Flink; 1189 | while (le != &ino->children) { 1190 | inode_child* ic = _CR(le, inode_child, list_entry); 1191 | 1192 | load_child_info(ino, ic); 1193 | 1194 | le = le->Flink; 1195 | } 1196 | 1197 | ino->children_found = true; 1198 | ino->dir_position = ino->children.Flink; 1199 | 1200 | return EFI_SUCCESS; 1201 | } 1202 | 1203 | static EFI_STATUS EFIAPI file_open(struct _EFI_FILE_HANDLE* File, struct _EFI_FILE_HANDLE** NewHandle, CHAR16* FileName, 1204 | UINT64 OpenMode, UINT64 Attributes) { 1205 | EFI_STATUS Status; 1206 | inode* ino = _CR(File, inode, proto); 1207 | WCHAR* fn = FileName; 1208 | root* r; 1209 | uint64_t inode_num; 1210 | inode* ino2; 1211 | WCHAR* path; 1212 | size_t pathlen; 1213 | size_t ino_name_len = ino->name ? wcslen(ino->name) : 0; 1214 | 1215 | UNUSED(Attributes); 1216 | 1217 | if (OpenMode & EFI_FILE_MODE_CREATE) 1218 | return EFI_UNSUPPORTED; 1219 | 1220 | if (fn[0] == '\\') { 1221 | pathlen = wcslen(fn); 1222 | 1223 | Status = bs->AllocatePool(EfiBootServicesData, (pathlen + 1) * sizeof(WCHAR), (void**)&path); 1224 | if (EFI_ERROR(Status)) { 1225 | do_print_error("AllocatePool", Status); 1226 | return Status; 1227 | } 1228 | 1229 | memcpy(path, fn, pathlen * sizeof(WCHAR)); 1230 | path[pathlen] = 0; 1231 | } else { 1232 | WCHAR* p; 1233 | 1234 | pathlen = wcslen(fn) + 1 + ino_name_len; 1235 | 1236 | Status = bs->AllocatePool(EfiBootServicesData, (pathlen + 1) * sizeof(WCHAR), (void**)&path); 1237 | if (EFI_ERROR(Status)) { 1238 | do_print_error("AllocatePool", Status); 1239 | return Status; 1240 | } 1241 | 1242 | if (ino->name) { 1243 | memcpy(path, ino->name, ino_name_len * sizeof(WCHAR)); 1244 | p = &path[ino_name_len]; 1245 | 1246 | *p = '\\'; 1247 | p++; 1248 | } else { 1249 | path[0] = '\\'; 1250 | p = &path[1]; 1251 | } 1252 | 1253 | memcpy(p, fn, wcslen(fn) * sizeof(WCHAR)); 1254 | p += wcslen(fn); 1255 | 1256 | *p = 0; 1257 | } 1258 | 1259 | normalize_path(path); 1260 | 1261 | if (path[0] != 0 && path[1] != 0 && path[wcslen(path) - 1] == '\\') 1262 | path[wcslen(path) - 1] = 0; 1263 | 1264 | if (path[0] == 0) { 1265 | path[0] = '\\'; 1266 | path[1] = 0; 1267 | } 1268 | 1269 | pathlen = wcslen(path); 1270 | 1271 | if (ino->name && pathlen > ino_name_len && !memcmp(ino->name, path, ino_name_len * sizeof(WCHAR)) && path[ino_name_len] == '\\') { 1272 | r = ino->r; 1273 | inode_num = ino->inode; 1274 | fn = &path[ino_name_len + 1]; 1275 | } else { 1276 | r = ino->vol->fsroot; 1277 | inode_num = SUBVOL_ROOT_INODE; 1278 | fn = &path[1]; 1279 | } 1280 | 1281 | // FIXME - follow symlinks? 1282 | 1283 | while (true) { 1284 | unsigned int backslash; 1285 | 1286 | if (fn[0] == 0) 1287 | break; 1288 | 1289 | { 1290 | unsigned int i = 0; 1291 | 1292 | while (fn[i] != '\\' && fn[i] != 0) { 1293 | i++; 1294 | } 1295 | 1296 | backslash = i; 1297 | } 1298 | 1299 | if (backslash == 0) { 1300 | fn++; 1301 | continue; 1302 | } else if (backslash == 1 && fn[0] == '.') { 1303 | if (fn[1] == 0) 1304 | break; 1305 | 1306 | fn += 2; 1307 | continue; 1308 | } else if (backslash == 2 && fn[0] == '.' && fn[1] == '.') { 1309 | // shouldn't happen - removed by normalize_path 1310 | return EFI_INVALID_PARAMETER; 1311 | } else { 1312 | if (r == ino->r && inode_num == ino->inode) { 1313 | if (!ino->children_found) { 1314 | Status = find_children(ino); 1315 | if (EFI_ERROR(Status)) { 1316 | do_print_error("find_children", Status); 1317 | bs->FreePool(path); 1318 | return Status; 1319 | } 1320 | } 1321 | 1322 | Status = find_file_in_dir_cached(ino->vol, ino, fn, backslash, &r, &inode_num); 1323 | if (Status == EFI_NOT_FOUND) { 1324 | bs->FreePool(path); 1325 | return Status; 1326 | } else if (EFI_ERROR(Status)) { 1327 | do_print_error("find_file_in_dir_cached", Status); 1328 | bs->FreePool(path); 1329 | return Status; 1330 | } 1331 | } else { 1332 | Status = find_file_in_dir(ino->vol, r, inode_num, fn, backslash, &r, &inode_num); 1333 | if (Status == EFI_NOT_FOUND) { 1334 | bs->FreePool(path); 1335 | return Status; 1336 | } else if (EFI_ERROR(Status)) { 1337 | do_print_error("find_file_in_dir", Status); 1338 | bs->FreePool(path); 1339 | return Status; 1340 | } 1341 | } 1342 | 1343 | fn += backslash; 1344 | 1345 | if (fn[0] == '\\') 1346 | fn++; 1347 | } 1348 | } 1349 | 1350 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(inode), (void**)&ino2); 1351 | if (EFI_ERROR(Status)) { 1352 | do_print_error("AllocatePool", Status); 1353 | bs->FreePool(path); 1354 | return Status; 1355 | } 1356 | 1357 | memset(ino2, 0, sizeof(inode)); 1358 | 1359 | populate_file_handle(&ino2->proto); 1360 | 1361 | InitializeListHead(&ino2->children); 1362 | 1363 | ino2->r = r; 1364 | ino2->inode = inode_num; 1365 | ino2->vol = ino->vol; 1366 | ino2->name = path; 1367 | 1368 | *NewHandle = &ino2->proto; 1369 | 1370 | return EFI_SUCCESS; 1371 | } 1372 | 1373 | static EFI_STATUS EFIAPI file_close(struct _EFI_FILE_HANDLE* File) { 1374 | inode* ino = _CR(File, inode, proto); 1375 | 1376 | while (!IsListEmpty(&ino->children)) { 1377 | inode_child* ic = _CR(ino->children.Flink, inode_child, list_entry); 1378 | 1379 | RemoveEntryList(&ic->list_entry); 1380 | bs->FreePool(ic); 1381 | } 1382 | 1383 | if (ino->name) 1384 | bs->FreePool(ino->name); 1385 | 1386 | if (ino->inode_loaded) { 1387 | while (!IsListEmpty(&ino->extents)) { 1388 | extent* ext = _CR(ino->extents.Flink, extent, list_entry); 1389 | 1390 | RemoveEntryList(&ext->list_entry); 1391 | bs->FreePool(ext); 1392 | } 1393 | } 1394 | 1395 | bs->FreePool(ino); 1396 | 1397 | return EFI_SUCCESS; 1398 | } 1399 | 1400 | static EFI_STATUS EFIAPI file_delete(struct _EFI_FILE_HANDLE* File) { 1401 | UNUSED(File); 1402 | 1403 | return EFI_UNSUPPORTED; 1404 | } 1405 | 1406 | static void btrfs_time_to_efi_time(const BTRFS_TIME* b, EFI_TIME* e) { 1407 | signed long long j, e2, f, g, h; 1408 | uint64_t days = b->seconds / 86400; 1409 | uint64_t secs = b->seconds % 86400; 1410 | 1411 | j = days + 2440588; 1412 | 1413 | f = (4 * j) + 274277; 1414 | f /= 146097; 1415 | f *= 3; 1416 | f /= 4; 1417 | f += j; 1418 | f += 1363; 1419 | 1420 | e2 = (4 * f) + 3; 1421 | g = (e2 % 1461) / 4; 1422 | h = (5 * g) + 2; 1423 | 1424 | e->Day = (uint8_t)(((h % 153) / 5) + 1); 1425 | e->Month = (uint8_t)(((h / 153) + 2) % 12 + 1); 1426 | e->Year = (uint16_t)((e2 / 1461) - 4716 + ((14 - e->Month) / 12)); 1427 | e->Hour = secs / 3600; 1428 | e->Minute = (secs % 3600) / 60; 1429 | e->Second = secs % 60; 1430 | e->Nanosecond = b->nanoseconds; 1431 | e->TimeZone = 0; 1432 | e->Daylight = 0; 1433 | } 1434 | 1435 | static EFI_STATUS read_dir(inode* ino, UINTN* bufsize, void* buf) { 1436 | EFI_STATUS Status; 1437 | inode_child* ic; 1438 | DIR_ITEM* di; 1439 | unsigned int fnlen; 1440 | EFI_FILE_INFO* info; 1441 | 1442 | if (!ino->children_found) { 1443 | Status = find_children(ino); 1444 | if (EFI_ERROR(Status)) { 1445 | do_print_error("find_children", Status); 1446 | return Status; 1447 | } 1448 | } 1449 | 1450 | // no more entries 1451 | if (ino->dir_position == &ino->children) { 1452 | *bufsize = 0; 1453 | return EFI_SUCCESS; 1454 | } 1455 | 1456 | ic = _CR(ino->dir_position, inode_child, list_entry); 1457 | di = &ic->dir_item; 1458 | 1459 | Status = utf8_to_utf16(NULL, 0, &fnlen, di->name, di->n); 1460 | if (EFI_ERROR(Status)) { 1461 | do_print_error("utf8_to_utf16", Status); 1462 | return Status; 1463 | } 1464 | 1465 | if (*bufsize < offsetof(EFI_FILE_INFO, FileName[0]) + fnlen) { 1466 | *bufsize = offsetof(EFI_FILE_INFO, FileName[0]) + fnlen; 1467 | return EFI_BUFFER_TOO_SMALL; 1468 | } 1469 | 1470 | *bufsize = offsetof(EFI_FILE_INFO, FileName[0]) + fnlen; 1471 | info = (EFI_FILE_INFO*)buf; 1472 | 1473 | info->Size = offsetof(EFI_FILE_INFO, FileName[0]) + fnlen; 1474 | info->FileSize = ic->size; 1475 | info->PhysicalSize = ic->blocks; 1476 | btrfs_time_to_efi_time(&ic->otime, &info->CreateTime); 1477 | btrfs_time_to_efi_time(&ic->atime, &info->LastAccessTime); 1478 | btrfs_time_to_efi_time(&ic->mtime, &info->ModificationTime); 1479 | info->Attribute = di->type == BTRFS_TYPE_DIRECTORY ? EFI_FILE_DIRECTORY : 0; 1480 | 1481 | Status = utf8_to_utf16(info->FileName, fnlen, &fnlen, di->name, di->n); 1482 | if (EFI_ERROR(Status)) { 1483 | do_print_error("utf8_to_utf16", Status); 1484 | return Status; 1485 | } 1486 | 1487 | info->FileName[fnlen / sizeof(WCHAR)] = 0; 1488 | 1489 | ino->position++; 1490 | ino->dir_position = ino->dir_position->Flink; 1491 | 1492 | return EFI_SUCCESS; 1493 | } 1494 | 1495 | static void* zlib_alloc(void* opaque, unsigned int items, unsigned int size) { 1496 | EFI_STATUS Status; 1497 | void* r; 1498 | 1499 | UNUSED(opaque); 1500 | 1501 | Status = bs->AllocatePool(EfiBootServicesData, items * size, &r); 1502 | if (EFI_ERROR(Status)) { 1503 | do_print_error("AllocatePool", Status); 1504 | return NULL; 1505 | } 1506 | 1507 | return r; 1508 | } 1509 | 1510 | static void zlib_free(void* opaque, void* ptr) { 1511 | UNUSED(opaque); 1512 | 1513 | bs->FreePool(ptr); 1514 | } 1515 | 1516 | static EFI_STATUS zlib_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen) { 1517 | z_stream c_stream; 1518 | int ret; 1519 | 1520 | c_stream.zalloc = zlib_alloc; 1521 | c_stream.zfree = zlib_free; 1522 | c_stream.opaque = (voidpf)0; 1523 | 1524 | ret = inflateInit(&c_stream); 1525 | 1526 | if (ret != Z_OK) { 1527 | char s[255], *p; 1528 | 1529 | p = stpcpy(s, "inflateInit returned "); 1530 | p = dec_to_str(p, ret); 1531 | p = stpcpy(p, "\n"); 1532 | 1533 | do_print(s); 1534 | 1535 | return EFI_INVALID_PARAMETER; 1536 | } 1537 | 1538 | c_stream.next_in = inbuf; 1539 | c_stream.avail_in = inlen; 1540 | 1541 | c_stream.next_out = outbuf; 1542 | c_stream.avail_out = outlen; 1543 | 1544 | do { 1545 | ret = inflate(&c_stream, Z_NO_FLUSH); 1546 | 1547 | if (ret != Z_OK && ret != Z_STREAM_END) { 1548 | char s[255], *p; 1549 | 1550 | p = stpcpy(s, "inflate returned "); 1551 | p = dec_to_str(p, ret); 1552 | p = stpcpy(p, "\n"); 1553 | 1554 | do_print(s); 1555 | 1556 | inflateEnd(&c_stream); 1557 | return EFI_INVALID_PARAMETER; 1558 | } 1559 | 1560 | if (c_stream.avail_out == 0) 1561 | break; 1562 | } while (ret != Z_STREAM_END); 1563 | 1564 | ret = inflateEnd(&c_stream); 1565 | 1566 | if (ret != Z_OK) { 1567 | char s[255], *p; 1568 | 1569 | p = stpcpy(s, "inflateEnd returned "); 1570 | p = dec_to_str(p, ret); 1571 | p = stpcpy(p, "\n"); 1572 | 1573 | do_print(s); 1574 | 1575 | return EFI_INVALID_PARAMETER; 1576 | } 1577 | 1578 | return EFI_SUCCESS; 1579 | } 1580 | 1581 | static void* zstd_malloc(void* opaque, size_t size) { 1582 | EFI_STATUS Status; 1583 | void* r; 1584 | 1585 | UNUSED(opaque); 1586 | 1587 | Status = bs->AllocatePool(EfiBootServicesData, size, &r); 1588 | if (EFI_ERROR(Status)) { 1589 | do_print_error("AllocatePool", Status); 1590 | return NULL; 1591 | } 1592 | 1593 | return r; 1594 | } 1595 | 1596 | static void zstd_free(void* opaque, void* address) { 1597 | UNUSED(opaque); 1598 | 1599 | bs->FreePool(address); 1600 | } 1601 | 1602 | static EFI_STATUS zstd_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen) { 1603 | EFI_STATUS Status; 1604 | ZSTD_DStream* stream; 1605 | size_t init_res, read; 1606 | ZSTD_inBuffer input; 1607 | ZSTD_outBuffer output; 1608 | 1609 | stream = ZSTD_createDStream_advanced(zstd_mem); 1610 | 1611 | if (!stream) { 1612 | do_print("ZSTD_createDStream failed.\n"); 1613 | return EFI_INVALID_PARAMETER; 1614 | } 1615 | 1616 | init_res = ZSTD_initDStream(stream); 1617 | 1618 | if (ZSTD_isError(init_res)) { 1619 | char s[255], *p; 1620 | 1621 | p = stpcpy(s, "ZSTD_initDStream failed: "); 1622 | p = stpcpy(p, ZSTD_getErrorName(init_res)); 1623 | p = stpcpy(p, "\n"); 1624 | 1625 | do_print(s); 1626 | 1627 | Status = EFI_INVALID_PARAMETER; 1628 | goto end; 1629 | } 1630 | 1631 | input.src = inbuf; 1632 | input.size = inlen; 1633 | input.pos = 0; 1634 | 1635 | output.dst = outbuf; 1636 | output.size = outlen; 1637 | output.pos = 0; 1638 | 1639 | do { 1640 | read = ZSTD_decompressStream(stream, &output, &input); 1641 | 1642 | if (ZSTD_isError(read)) { 1643 | char s[255], *p; 1644 | 1645 | p = stpcpy(s, "ZSTD_decompressStream failed: "); 1646 | p = stpcpy(p, ZSTD_getErrorName(init_res)); 1647 | p = stpcpy(p, "\n"); 1648 | 1649 | do_print(s); 1650 | 1651 | Status = EFI_INVALID_PARAMETER; 1652 | goto end; 1653 | } 1654 | 1655 | if (output.pos == output.size) 1656 | break; 1657 | } while (read != 0); 1658 | 1659 | Status = EFI_SUCCESS; 1660 | 1661 | end: 1662 | ZSTD_freeDStream(stream); 1663 | 1664 | return Status; 1665 | } 1666 | 1667 | static EFI_STATUS read_file(inode* ino, UINTN* bufsize, void* buf) { 1668 | EFI_STATUS Status; 1669 | unsigned int to_read, left; 1670 | uint64_t pos; 1671 | uint8_t* dest; 1672 | LIST_ENTRY* le; 1673 | 1674 | if (!ino->inode_loaded) { 1675 | Status = load_inode(ino); 1676 | if (EFI_ERROR(Status)) { 1677 | do_print_error("load_inode", Status); 1678 | return Status; 1679 | } 1680 | } 1681 | 1682 | // FIXME - check is actually file (check st_mode) 1683 | 1684 | if (ino->position >= ino->inode_item.st_size) { // past end of file 1685 | *bufsize = 0; 1686 | return EFI_SUCCESS; 1687 | } 1688 | 1689 | to_read = *bufsize; 1690 | 1691 | if (ino->position + to_read >= ino->inode_item.st_size) 1692 | to_read = ino->inode_item.st_size - ino->position; 1693 | 1694 | dest = (uint8_t*)buf; 1695 | left = to_read; 1696 | pos = ino->position; 1697 | 1698 | memset(dest, 0, to_read); 1699 | 1700 | le = ino->extents.Flink; 1701 | while (le != &ino->extents) { 1702 | extent* ext = _CR(le, extent, list_entry); 1703 | 1704 | if (ext->offset > pos + left) 1705 | break; 1706 | 1707 | if (ext->offset + ext->extent_data.decoded_size < pos) { 1708 | le = le->Flink; 1709 | continue; 1710 | } 1711 | 1712 | if (ext->extent_data.compression != BTRFS_COMPRESSION_NONE && 1713 | ext->extent_data.compression != BTRFS_COMPRESSION_ZLIB && 1714 | ext->extent_data.compression != BTRFS_COMPRESSION_LZO && 1715 | ext->extent_data.compression != BTRFS_COMPRESSION_ZSTD) { 1716 | char s[255], *p; 1717 | 1718 | p = stpcpy(s, "unsupported compression type "); 1719 | p = dec_to_str(p, ext->extent_data.compression); 1720 | p = stpcpy(p, "\n"); 1721 | 1722 | do_print(s); 1723 | 1724 | return EFI_UNSUPPORTED; 1725 | } 1726 | 1727 | if (ext->extent_data.encryption != 0) { 1728 | do_print("encryption not supported\n"); 1729 | return EFI_UNSUPPORTED; 1730 | } 1731 | 1732 | if (ext->extent_data.encoding != 0) { 1733 | do_print("other encodings not supported\n"); 1734 | return EFI_UNSUPPORTED; 1735 | } 1736 | 1737 | if (ext->extent_data.type == EXTENT_TYPE_INLINE) { 1738 | if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) 1739 | memcpy(dest, &ext->extent_data.data[pos - ext->offset], ext->extent_data.decoded_size - pos + ext->offset); 1740 | else { 1741 | uint8_t* decomp; 1742 | bool decomp_alloc; 1743 | uint32_t read; 1744 | uint16_t inlen = ext->size - (uint16_t)offsetof(EXTENT_DATA, data[0]); 1745 | 1746 | if (ext->extent_data.decoded_size == 0 || ext->extent_data.decoded_size > 0xffffffff) { 1747 | char s[255], *p; 1748 | 1749 | p = stpcpy(s, "ed->decoded_size was invalid ("); 1750 | p = hex_to_str(p, ext->extent_data.decoded_size); 1751 | p = stpcpy(p, ")\n"); 1752 | 1753 | do_print(p); 1754 | 1755 | return EFI_INVALID_PARAMETER; 1756 | } 1757 | 1758 | read = (uint32_t)ext->extent_data.decoded_size - pos; 1759 | 1760 | if (read > ino->inode_item.st_size) 1761 | read = ino->inode_item.st_size; 1762 | 1763 | if (pos > 0) { 1764 | Status = bs->AllocatePool(EfiBootServicesData, ext->extent_data.decoded_size, (void**)&decomp); 1765 | if (EFI_ERROR(Status)) { 1766 | do_print("out of memory\n"); 1767 | return Status; 1768 | } 1769 | 1770 | decomp_alloc = true; 1771 | } else { 1772 | decomp = dest; 1773 | decomp_alloc = false; 1774 | } 1775 | 1776 | if (ext->extent_data.compression == BTRFS_COMPRESSION_ZLIB) { 1777 | Status = zlib_decompress(ext->extent_data.data, inlen, decomp, (uint32_t)(read + pos)); 1778 | if (EFI_ERROR(Status)) { 1779 | do_print_error("zlib_decompress", Status); 1780 | if (decomp_alloc) bs->FreePool(decomp); 1781 | return Status; 1782 | } 1783 | } else if (ext->extent_data.compression == BTRFS_COMPRESSION_LZO) { 1784 | if (inlen < sizeof(uint32_t)) { 1785 | do_print("extent data was truncated\n"); 1786 | if (decomp_alloc) bs->FreePool(decomp); 1787 | return EFI_INVALID_PARAMETER; 1788 | } else 1789 | inlen -= sizeof(uint32_t); 1790 | 1791 | Status = lzo_decompress((uint8_t*)ext->extent_data.data + sizeof(uint32_t), inlen, decomp, (uint32_t)(read + pos), sizeof(uint32_t)); 1792 | if (EFI_ERROR(Status)) { 1793 | do_print_error("lzo_decompress", Status); 1794 | if (decomp_alloc) bs->FreePool(decomp); 1795 | return Status; 1796 | } 1797 | } else if (ext->extent_data.compression == BTRFS_COMPRESSION_ZSTD) { 1798 | Status = zstd_decompress(ext->extent_data.data, inlen, decomp, (uint32_t)(read + pos)); 1799 | if (EFI_ERROR(Status)) { 1800 | do_print_error("zstd_decompress", Status); 1801 | if (decomp_alloc) bs->FreePool(decomp); 1802 | return Status; 1803 | } 1804 | } 1805 | 1806 | if (decomp_alloc) { 1807 | memcpy(dest, decomp + pos, read); 1808 | bs->FreePool(decomp); 1809 | } 1810 | } 1811 | 1812 | dest += ext->extent_data.decoded_size - pos + ext->offset; 1813 | left -= ext->extent_data.decoded_size - pos + ext->offset; 1814 | pos = ext->extent_data.decoded_size + ext->offset; 1815 | 1816 | if (left == 0) 1817 | break; 1818 | } else if (ext->extent_data.type == EXTENT_TYPE_REGULAR) { 1819 | EXTENT_DATA2* ed2 = (EXTENT_DATA2*)&ext->extent_data.data[0]; 1820 | uint64_t size; 1821 | uint8_t* tmp; 1822 | 1823 | if (ext->offset > pos) { // account for holes 1824 | if (ext->offset - pos >= left) { 1825 | pos = ext->offset; 1826 | break; 1827 | } 1828 | 1829 | dest += ext->offset - pos; 1830 | left -= ext->offset - pos; 1831 | pos = ext->offset; 1832 | } 1833 | 1834 | // FIXME - only use tmp if necessary 1835 | // FIXME - unaligned reads 1836 | 1837 | size = ed2->num_bytes - pos + ext->offset; 1838 | if (size > left) 1839 | size = sector_align(left, ino->vol->block->Media->BlockSize); 1840 | 1841 | if (ext->extent_data.compression == BTRFS_COMPRESSION_NONE) { 1842 | Status = bs->AllocatePool(EfiBootServicesData, size, (void**)&tmp); 1843 | if (EFI_ERROR(Status)) { 1844 | do_print_error("AllocatePool", Status); 1845 | return Status; 1846 | } 1847 | 1848 | Status = read_data(ino->vol, ed2->address + ed2->offset + pos - ext->offset, size, tmp); 1849 | if (EFI_ERROR(Status)) { 1850 | do_print_error("read_data", Status); 1851 | bs->FreePool(tmp); 1852 | return Status; 1853 | } 1854 | 1855 | memcpy(dest, tmp, size); 1856 | } else { 1857 | uint8_t* comp; 1858 | 1859 | Status = bs->AllocatePool(EfiBootServicesData, ext->extent_data.decoded_size, (void**)&tmp); 1860 | if (EFI_ERROR(Status)) { 1861 | do_print_error("AllocatePool", Status); 1862 | return Status; 1863 | } 1864 | 1865 | Status = bs->AllocatePool(EfiBootServicesData, ed2->size, (void**)&comp); 1866 | if (EFI_ERROR(Status)) { 1867 | do_print_error("AllocatePool", Status); 1868 | bs->FreePool(tmp); 1869 | return Status; 1870 | } 1871 | 1872 | Status = read_data(ino->vol, ed2->address, ed2->size, comp); 1873 | if (EFI_ERROR(Status)) { 1874 | do_print_error("read_data", Status); 1875 | bs->FreePool(comp); 1876 | bs->FreePool(tmp); 1877 | return Status; 1878 | } 1879 | 1880 | if (ext->extent_data.compression == BTRFS_COMPRESSION_ZLIB) { 1881 | Status = zlib_decompress(comp, ed2->size, tmp, ext->extent_data.decoded_size); 1882 | if (EFI_ERROR(Status)) { 1883 | do_print_error("zlib_decompress", Status); 1884 | bs->FreePool(comp); 1885 | bs->FreePool(tmp); 1886 | return Status; 1887 | } 1888 | } else if (ext->extent_data.compression == BTRFS_COMPRESSION_LZO) { 1889 | if (ed2->size < sizeof(uint32_t)) { 1890 | do_print("extent data was truncated\n"); 1891 | bs->FreePool(comp); 1892 | bs->FreePool(tmp); 1893 | return EFI_INVALID_PARAMETER; 1894 | } 1895 | 1896 | Status = lzo_decompress(comp + sizeof(uint32_t), ed2->size - sizeof(uint32_t), tmp, ext->extent_data.decoded_size, sizeof(uint32_t)); 1897 | if (EFI_ERROR(Status)) { 1898 | do_print_error("lzo_decompress", Status); 1899 | bs->FreePool(comp); 1900 | bs->FreePool(tmp); 1901 | return Status; 1902 | } 1903 | } else if (ext->extent_data.compression == BTRFS_COMPRESSION_ZSTD) { 1904 | Status = zstd_decompress(comp, ed2->size, tmp, ext->extent_data.decoded_size); 1905 | if (EFI_ERROR(Status)) { 1906 | do_print_error("zstd_decompress", Status); 1907 | bs->FreePool(comp); 1908 | bs->FreePool(tmp); 1909 | return Status; 1910 | } 1911 | } 1912 | 1913 | memcpy(dest, tmp + ed2->offset + pos - ext->offset, size); 1914 | 1915 | bs->FreePool(comp); 1916 | } 1917 | 1918 | bs->FreePool(tmp); 1919 | 1920 | dest += size; 1921 | pos += size; 1922 | left -= size; 1923 | 1924 | if (left == 0) 1925 | break; 1926 | } 1927 | 1928 | le = le->Flink; 1929 | } 1930 | 1931 | ino->position = pos; 1932 | 1933 | *bufsize = to_read; 1934 | 1935 | return EFI_SUCCESS; 1936 | } 1937 | 1938 | static EFI_STATUS EFIAPI file_read(struct _EFI_FILE_HANDLE* File, UINTN* BufferSize, VOID* Buffer) { 1939 | EFI_STATUS Status; 1940 | inode* ino = _CR(File, inode, proto); 1941 | 1942 | if (!ino->inode_loaded) { 1943 | Status = load_inode(ino); 1944 | if (EFI_ERROR(Status)) { 1945 | do_print_error("load_inode", Status); 1946 | return Status; 1947 | } 1948 | } 1949 | 1950 | if (ino->inode_item.st_mode & __S_IFDIR) 1951 | return read_dir(ino, BufferSize, Buffer); 1952 | else 1953 | return read_file(ino, BufferSize, Buffer); 1954 | } 1955 | 1956 | static EFI_STATUS EFIAPI file_write(struct _EFI_FILE_HANDLE* File, UINTN* BufferSize, VOID* Buffer) { 1957 | UNUSED(File); 1958 | UNUSED(BufferSize); 1959 | UNUSED(Buffer); 1960 | 1961 | return EFI_UNSUPPORTED; 1962 | } 1963 | 1964 | static EFI_STATUS load_inode(inode* ino) { 1965 | EFI_STATUS Status; 1966 | KEY searchkey; 1967 | traverse_ptr tp; 1968 | 1969 | searchkey.obj_id = ino->inode; 1970 | searchkey.obj_type = TYPE_INODE_ITEM; 1971 | searchkey.offset = 0xffffffffffffffff; 1972 | 1973 | Status = find_item(ino->vol, ino->r, &tp, &searchkey); 1974 | if (EFI_ERROR(Status)) { 1975 | do_print_error("find_item", Status); 1976 | return Status; 1977 | } 1978 | 1979 | if (tp.key->obj_id != searchkey.obj_id || tp.key->obj_type != searchkey.obj_type) { 1980 | char s[100], *p; 1981 | 1982 | p = stpcpy(s, "Error finding INODE_ITEM for subvol "); 1983 | p = hex_to_str(p, ino->r->id); 1984 | p = stpcpy(p, ", inode "); 1985 | p = hex_to_str(p, ino->inode); 1986 | p = stpcpy(p, ".\n"); 1987 | 1988 | do_print(s); 1989 | 1990 | free_traverse_ptr(&tp); 1991 | 1992 | return EFI_VOLUME_CORRUPTED; 1993 | } 1994 | 1995 | if (tp.itemlen < sizeof(INODE_ITEM)) { 1996 | char s[100], *p; 1997 | 1998 | p = stpcpy(s, "INODE_ITEM length was "); 1999 | p = dec_to_str(p, tp.itemlen); 2000 | p = stpcpy(p, " bytes, expected "); 2001 | p = dec_to_str(p, sizeof(INODE_ITEM)); 2002 | p = stpcpy(p, ".\n"); 2003 | 2004 | do_print(s); 2005 | 2006 | free_traverse_ptr(&tp); 2007 | 2008 | return EFI_VOLUME_CORRUPTED; 2009 | } 2010 | 2011 | memcpy(&ino->inode_item, tp.item, sizeof(INODE_ITEM)); 2012 | ino->inode_loaded = true; 2013 | 2014 | InitializeListHead(&ino->extents); 2015 | 2016 | if (!(ino->inode_item.st_mode & __S_IFDIR)) { 2017 | while (tp.key->obj_id == ino->inode && tp.key->obj_type <= TYPE_EXTENT_DATA) { 2018 | if (tp.key->obj_type == TYPE_EXTENT_DATA && tp.itemlen >= offsetof(EXTENT_DATA, data[0])) { 2019 | EXTENT_DATA* ed = (EXTENT_DATA*)tp.item; 2020 | extent* ext; 2021 | bool skip = false; 2022 | 2023 | if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && 2024 | tp.itemlen < offsetof(EXTENT_DATA, data[0]) + sizeof(EXTENT_DATA2)) { 2025 | do_print("EXTENT_DATA was truncated\n"); 2026 | free_traverse_ptr(&tp); 2027 | return EFI_VOLUME_CORRUPTED; 2028 | } 2029 | 2030 | if (ed->type == EXTENT_TYPE_PREALLOC) 2031 | skip = true; 2032 | else if (ed->type == EXTENT_TYPE_REGULAR) { 2033 | EXTENT_DATA2* ed2 = (EXTENT_DATA2*)ed->data; 2034 | 2035 | skip = ed2->address == 0 && ed2->size == 0; // skip sparse 2036 | } 2037 | 2038 | if (!skip) { 2039 | Status = bs->AllocatePool(EfiBootServicesData, offsetof(extent, extent_data) + tp.itemlen, (void**)&ext); 2040 | if (EFI_ERROR(Status)) { 2041 | do_print_error("AllocatePool", Status); 2042 | free_traverse_ptr(&tp); 2043 | return Status; 2044 | } 2045 | 2046 | ext->offset = tp.key->offset; 2047 | ext->size = tp.itemlen; 2048 | memcpy(&ext->extent_data, tp.item, tp.itemlen); 2049 | 2050 | InsertTailList(&ino->extents, &ext->list_entry); 2051 | } 2052 | } 2053 | 2054 | Status = next_item(ino->vol, &tp); 2055 | if (Status == EFI_NOT_FOUND) 2056 | break; 2057 | else if (EFI_ERROR(Status)) { 2058 | do_print_error("next_item", Status); 2059 | free_traverse_ptr(&tp); 2060 | return Status; 2061 | } 2062 | } 2063 | } 2064 | 2065 | free_traverse_ptr(&tp); 2066 | 2067 | return EFI_SUCCESS; 2068 | } 2069 | 2070 | static EFI_STATUS EFIAPI file_set_position(struct _EFI_FILE_HANDLE* File, UINT64 Position) { 2071 | EFI_STATUS Status; 2072 | inode* ino = _CR(File, inode, proto); 2073 | 2074 | if (!ino->inode_loaded) { 2075 | Status = load_inode(ino); 2076 | if (EFI_ERROR(Status)) { 2077 | do_print_error("load_inode", Status); 2078 | return Status; 2079 | } 2080 | } 2081 | 2082 | if (ino->inode_item.st_mode & __S_IFDIR) { 2083 | if (Position != 0) 2084 | return EFI_UNSUPPORTED; 2085 | 2086 | ino->position = 0; 2087 | ino->dir_position = ino->children.Flink; 2088 | } else { 2089 | if (Position == 0xffffffffffffffff) 2090 | ino->position = ino->inode_item.st_size; 2091 | else 2092 | ino->position = Position; 2093 | } 2094 | 2095 | return EFI_SUCCESS; 2096 | } 2097 | 2098 | static EFI_STATUS EFIAPI file_get_position(struct _EFI_FILE_HANDLE* File, UINT64* Position) { 2099 | UNUSED(File); 2100 | UNUSED(Position); 2101 | 2102 | // FIXME 2103 | 2104 | return EFI_UNSUPPORTED; 2105 | } 2106 | 2107 | static EFI_STATUS EFIAPI file_get_info(struct _EFI_FILE_HANDLE* File, EFI_GUID* InformationType, UINTN* BufferSize, VOID* Buffer) { 2108 | EFI_STATUS Status; 2109 | inode* ino = _CR(File, inode, proto); 2110 | EFI_GUID guid = EFI_FILE_INFO_ID; 2111 | 2112 | // FIXME - EFI_FILE_SYSTEM_INFO 2113 | 2114 | if (!memcmp(InformationType, &guid, sizeof(EFI_GUID))) { 2115 | unsigned int size = offsetof(EFI_FILE_INFO, FileName[0]) + sizeof(CHAR16); 2116 | EFI_FILE_INFO* info = (EFI_FILE_INFO*)Buffer; 2117 | size_t bs = 0; 2118 | 2119 | if (ino->name) { 2120 | for (int i = wcslen(ino->name); i >= 0; i--) { 2121 | if (ino->name[i] == '\\') { 2122 | bs = i; 2123 | break; 2124 | } 2125 | } 2126 | 2127 | size += (wcslen(ino->name) - bs - 1) * sizeof(WCHAR); 2128 | } 2129 | 2130 | if (*BufferSize < size) { 2131 | *BufferSize = size; 2132 | return EFI_BUFFER_TOO_SMALL; 2133 | } 2134 | 2135 | if (!ino->inode_loaded) { 2136 | Status = load_inode(ino); 2137 | if (EFI_ERROR(Status)) { 2138 | do_print_error("load_inode", Status); 2139 | return Status; 2140 | } 2141 | } 2142 | 2143 | info->Size = size; 2144 | info->FileSize = ino->inode_item.st_size; 2145 | info->PhysicalSize = ino->inode_item.st_blocks; 2146 | // info->CreateTime; // FIXME 2147 | // info->LastAccessTime; // FIXME 2148 | // info->ModificationTime; // FIXME 2149 | info->Attribute = ino->inode_item.st_mode & __S_IFDIR ? EFI_FILE_DIRECTORY : 0; 2150 | 2151 | if (ino->name) 2152 | memcpy(info->FileName, &ino->name[bs + 1], (wcslen(ino->name) - bs) * sizeof(WCHAR)); 2153 | else 2154 | info->FileName[0] = 0; 2155 | 2156 | // FIXME - get other attributes from DOSATTRIB xattr? 2157 | 2158 | return EFI_SUCCESS; 2159 | } else { 2160 | do_print("Unrecognized file info GUID.\n"); 2161 | return EFI_UNSUPPORTED; 2162 | } 2163 | } 2164 | 2165 | static EFI_STATUS EFIAPI file_set_info(struct _EFI_FILE_HANDLE* File, EFI_GUID* InformationType, UINTN BufferSize, VOID* Buffer) { 2166 | UNUSED(File); 2167 | UNUSED(InformationType); 2168 | UNUSED(BufferSize); 2169 | UNUSED(Buffer); 2170 | 2171 | return EFI_UNSUPPORTED; 2172 | } 2173 | 2174 | static EFI_STATUS EFIAPI file_flush(struct _EFI_FILE_HANDLE* File) { 2175 | UNUSED(File); 2176 | 2177 | // nop 2178 | 2179 | return EFI_SUCCESS; 2180 | } 2181 | 2182 | __inline static void populate_file_handle(EFI_FILE_PROTOCOL* h) { 2183 | h->Revision = EFI_FILE_PROTOCOL_REVISION; 2184 | h->Open = file_open; 2185 | h->Close = file_close; 2186 | h->Delete = file_delete; 2187 | h->Read = file_read; 2188 | h->Write = file_write; 2189 | h->GetPosition = file_get_position; 2190 | h->SetPosition = file_set_position; 2191 | h->GetInfo = file_get_info; 2192 | h->SetInfo = file_set_info; 2193 | h->Flush = file_flush; 2194 | } 2195 | 2196 | static EFI_STATUS EFIAPI open_volume(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* This, EFI_FILE_PROTOCOL** Root) { 2197 | EFI_STATUS Status; 2198 | volume* vol = _CR(This, volume, proto); 2199 | inode* ino; 2200 | 2201 | if (!vol->chunks_loaded) { 2202 | Status = load_chunks(vol); 2203 | if (EFI_ERROR(Status)) { 2204 | do_print_error("load_chunks", Status); 2205 | return Status; 2206 | } 2207 | } 2208 | 2209 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(inode), (void**)&ino); 2210 | if (EFI_ERROR(Status)) { 2211 | do_print_error("AllocatePool", Status); 2212 | return Status; 2213 | } 2214 | 2215 | memset(ino, 0, sizeof(inode)); 2216 | 2217 | InitializeListHead(&ino->children); 2218 | 2219 | populate_file_handle(&ino->proto); 2220 | 2221 | ino->r = vol->fsroot; 2222 | ino->inode = SUBVOL_ROOT_INODE; 2223 | ino->vol = vol; 2224 | 2225 | *Root = &ino->proto; 2226 | 2227 | return EFI_SUCCESS; 2228 | } 2229 | 2230 | static EFI_STATUS EFIAPI get_arc_name(EFI_QUIBBLE_PROTOCOL* This, char* ArcName, UINTN* ArcNameLen) { 2231 | volume* vol = _CR(This, volume, quibble_proto); 2232 | char* s; 2233 | 2234 | static const char prefix[] = "btrfs("; 2235 | static const unsigned int needed_len = sizeof(prefix) - 1 + 37; 2236 | 2237 | if (*ArcNameLen < needed_len) { 2238 | *ArcNameLen = needed_len; 2239 | return EFI_BUFFER_TOO_SMALL; 2240 | } 2241 | 2242 | *ArcNameLen = needed_len; 2243 | 2244 | strcpy(ArcName, prefix); 2245 | memcpy(ArcName, prefix, sizeof(prefix) - 1); 2246 | s = &ArcName[sizeof(prefix) - 1]; 2247 | 2248 | for (unsigned int i = 0; i < 16; i++) { 2249 | if ((vol->sb->uuid.uuid[i] >> 4) < 0xa) 2250 | *s = (vol->sb->uuid.uuid[i] >> 4) + '0'; 2251 | else 2252 | *s = (vol->sb->uuid.uuid[i] >> 4) + 'a' - 0xa; 2253 | 2254 | s++; 2255 | 2256 | if ((vol->sb->uuid.uuid[i] & 0xf) < 0xa) 2257 | *s = (vol->sb->uuid.uuid[i] & 0xf) + '0'; 2258 | else 2259 | *s = (vol->sb->uuid.uuid[i] & 0xf) + 'a' - 0xa; 2260 | 2261 | s++; 2262 | 2263 | if (i == 3 || i == 5 || i == 7 || i == 9) { 2264 | *s = '-'; 2265 | s++; 2266 | } 2267 | } 2268 | 2269 | *s = ')'; 2270 | 2271 | return EFI_SUCCESS; 2272 | } 2273 | 2274 | static EFI_STATUS get_subvol_path(volume* vol, uint64_t subvol, LIST_ENTRY* pathbits, uint64_t* parent_subvol_num) { 2275 | EFI_STATUS Status; 2276 | KEY searchkey; 2277 | traverse_ptr tp; 2278 | ROOT_REF* rr; 2279 | path_segment* ps; 2280 | uint64_t dir_inode; 2281 | 2282 | searchkey.obj_id = subvol; 2283 | searchkey.obj_type = TYPE_ROOT_BACKREF; 2284 | searchkey.offset = 0xffffffffffffffff; 2285 | 2286 | Status = find_item(vol, vol->root_root, &tp, &searchkey); 2287 | if (EFI_ERROR(Status)) { 2288 | do_print_error("find_item", Status); 2289 | return Status; 2290 | } 2291 | 2292 | if (tp.key->obj_id != searchkey.obj_id || tp.key->obj_type != searchkey.obj_type) { 2293 | char s[100], *p; 2294 | 2295 | p = stpcpy(s, "ROOT_BACKREF not found for subvol "); 2296 | p = hex_to_str(p, subvol); 2297 | p = stpcpy(p, ".\n"); 2298 | 2299 | do_print(s); 2300 | 2301 | free_traverse_ptr(&tp); 2302 | return EFI_INVALID_PARAMETER; 2303 | } 2304 | 2305 | if (tp.itemlen < sizeof(ROOT_REF) || tp.itemlen < offsetof(ROOT_REF, name[0]) + ((ROOT_REF*)tp.item)->n) { 2306 | do_print("ROOT_BACKREF was truncated.\n"); 2307 | free_traverse_ptr(&tp); 2308 | return EFI_INVALID_PARAMETER; 2309 | } 2310 | 2311 | rr = (ROOT_REF*)tp.item; 2312 | 2313 | Status = bs->AllocatePool(EfiBootServicesData, offsetof(path_segment, name[0]) + rr->n + 1, (void**)&ps); 2314 | if (EFI_ERROR(Status)) { 2315 | do_print_error("AllocatePool", Status); 2316 | free_traverse_ptr(&tp); 2317 | return Status; 2318 | } 2319 | 2320 | memcpy(ps->name, rr->name, rr->n); 2321 | ps->name[rr->n] = 0; 2322 | InsertHeadList(pathbits, &ps->list_entry); 2323 | 2324 | *parent_subvol_num = tp.key->offset; 2325 | dir_inode = rr->dir; 2326 | 2327 | free_traverse_ptr(&tp); 2328 | 2329 | if (dir_inode != SUBVOL_ROOT_INODE) { 2330 | LIST_ENTRY* le; 2331 | root* parent_subvol = NULL; 2332 | INODE_REF* ir; 2333 | 2334 | le = vol->roots.Flink; 2335 | while (le != &vol->roots) { 2336 | root* r2 = _CR(le, root, list_entry); 2337 | 2338 | if (r2->id == *parent_subvol_num) { 2339 | parent_subvol = r2; 2340 | break; 2341 | } 2342 | 2343 | le = le->Flink; 2344 | } 2345 | 2346 | if (!parent_subvol) { 2347 | char s[100], *p; 2348 | 2349 | p = stpcpy(s, "Could not find subvol "); 2350 | p = hex_to_str(p, *parent_subvol_num); 2351 | p = stpcpy(p, ".\n"); 2352 | 2353 | do_print(s); 2354 | 2355 | return EFI_INVALID_PARAMETER; 2356 | } 2357 | 2358 | do { 2359 | searchkey.obj_id = dir_inode; 2360 | searchkey.obj_type = TYPE_INODE_REF; // no hardlinks for directories, so should never be INODE_EXTREF 2361 | searchkey.offset = 0xffffffffffffffff; 2362 | 2363 | Status = find_item(vol, parent_subvol, &tp, &searchkey); 2364 | if (EFI_ERROR(Status)) { 2365 | do_print_error("find_item", Status); 2366 | return Status; 2367 | } 2368 | 2369 | if (tp.key->obj_id != searchkey.obj_id || tp.key->obj_type != searchkey.obj_type) { 2370 | char s[100], *p; 2371 | 2372 | p = stpcpy(s, "INODE_REF not found for inode "); 2373 | p = hex_to_str(p, searchkey.obj_id); 2374 | p = stpcpy(p, " in subvol "); 2375 | p = hex_to_str(p, *parent_subvol_num); 2376 | p = stpcpy(p, ".\n"); 2377 | 2378 | do_print(s); 2379 | 2380 | free_traverse_ptr(&tp); 2381 | return EFI_INVALID_PARAMETER; 2382 | } 2383 | 2384 | if (tp.itemlen < sizeof(INODE_REF) || tp.itemlen < offsetof(INODE_REF, name[0]) + ((INODE_REF*)tp.item)->n) { 2385 | do_print("INODE_REF was truncated.\n"); 2386 | free_traverse_ptr(&tp); 2387 | return EFI_INVALID_PARAMETER; 2388 | } 2389 | 2390 | ir = (INODE_REF*)tp.item; 2391 | 2392 | Status = bs->AllocatePool(EfiBootServicesData, offsetof(path_segment, name[0]) + ir->n + 1, (void**)&ps); 2393 | if (EFI_ERROR(Status)) { 2394 | do_print_error("AllocatePool", Status); 2395 | free_traverse_ptr(&tp); 2396 | return Status; 2397 | } 2398 | 2399 | memcpy(ps->name, ir->name, ir->n); 2400 | ps->name[ir->n] = 0; 2401 | InsertHeadList(pathbits, &ps->list_entry); 2402 | 2403 | dir_inode = tp.key->offset; 2404 | 2405 | free_traverse_ptr(&tp); 2406 | } while (dir_inode != SUBVOL_ROOT_INODE); 2407 | } 2408 | 2409 | return EFI_SUCCESS; 2410 | } 2411 | 2412 | static EFI_STATUS EFIAPI open_subvol(EFI_OPEN_SUBVOL_PROTOCOL* This, UINT64 Subvol, EFI_FILE_HANDLE* File) { 2413 | EFI_STATUS Status; 2414 | volume* vol = _CR(This, volume, open_subvol_proto); 2415 | inode* ino; 2416 | root* r = NULL; 2417 | LIST_ENTRY* le; 2418 | WCHAR* name = NULL; 2419 | 2420 | if (!vol->chunks_loaded) { 2421 | Status = load_chunks(vol); 2422 | if (EFI_ERROR(Status)) { 2423 | do_print_error("load_chunks", Status); 2424 | return Status; 2425 | } 2426 | } 2427 | 2428 | le = vol->roots.Flink; 2429 | while (le != &vol->roots) { 2430 | root* r2 = _CR(le, root, list_entry); 2431 | 2432 | if (r2->id == Subvol) { 2433 | r = r2; 2434 | break; 2435 | } 2436 | 2437 | le = le->Flink; 2438 | } 2439 | 2440 | if (!r) 2441 | return EFI_NOT_FOUND; 2442 | 2443 | 2444 | if (Subvol != BTRFS_ROOT_FSTREE) { 2445 | LIST_ENTRY pathbits; 2446 | uint64_t root_num = Subvol; 2447 | uint64_t parent; 2448 | unsigned int len, left; 2449 | WCHAR* s; 2450 | 2451 | InitializeListHead(&pathbits); 2452 | 2453 | do { 2454 | Status = get_subvol_path(vol, root_num, &pathbits, &parent); 2455 | if (EFI_ERROR(Status)) { 2456 | do_print_error("get_subvol_path", Status); 2457 | 2458 | while (!IsListEmpty(&pathbits)) { 2459 | path_segment* ps = _CR(pathbits.Flink, path_segment, list_entry); 2460 | RemoveEntryList(&ps->list_entry); 2461 | bs->FreePool(ps); 2462 | } 2463 | 2464 | return Status; 2465 | } 2466 | 2467 | root_num = parent; 2468 | } while (parent != BTRFS_ROOT_FSTREE); 2469 | 2470 | len = 0; 2471 | 2472 | le = pathbits.Flink; 2473 | while (le != &pathbits) { 2474 | path_segment* ps = _CR(le, path_segment, list_entry); 2475 | unsigned int pslen; 2476 | 2477 | Status = utf8_to_utf16(NULL, 0, &pslen, ps->name, strlen(ps->name)); 2478 | if (EFI_ERROR(Status)) { 2479 | do_print_error("utf8_to_utf16", Status); 2480 | 2481 | while (!IsListEmpty(&pathbits)) { 2482 | path_segment* ps = _CR(pathbits.Flink, path_segment, list_entry); 2483 | RemoveEntryList(&ps->list_entry); 2484 | bs->FreePool(ps); 2485 | } 2486 | 2487 | return Status; 2488 | } 2489 | 2490 | len += pslen + sizeof(WCHAR); 2491 | 2492 | le = le->Flink; 2493 | } 2494 | 2495 | Status = bs->AllocatePool(EfiBootServicesData, len, (void**)&name); 2496 | if (EFI_ERROR(Status)) { 2497 | do_print_error("AllocatePool", Status); 2498 | 2499 | while (!IsListEmpty(&pathbits)) { 2500 | path_segment* ps = _CR(pathbits.Flink, path_segment, list_entry); 2501 | RemoveEntryList(&ps->list_entry); 2502 | bs->FreePool(ps); 2503 | } 2504 | 2505 | return Status; 2506 | } 2507 | 2508 | len -= sizeof(WCHAR); 2509 | 2510 | // assemble pathbits into path 2511 | 2512 | s = name; 2513 | left = len; 2514 | 2515 | while (!IsListEmpty(&pathbits)) { 2516 | path_segment* ps = _CR(pathbits.Flink, path_segment, list_entry); 2517 | unsigned int pslen; 2518 | 2519 | RemoveEntryList(&ps->list_entry); 2520 | 2521 | if (s != name) { // not first 2522 | *s = '\\'; 2523 | s++; 2524 | left -= sizeof(WCHAR); 2525 | } 2526 | 2527 | Status = utf8_to_utf16(s, left, &pslen, ps->name, strlen(ps->name)); 2528 | if (EFI_ERROR(Status)) { 2529 | do_print_error("utf8_to_utf16", Status); 2530 | 2531 | bs->FreePool(ps); 2532 | bs->FreePool(name); 2533 | 2534 | while (!IsListEmpty(&pathbits)) { 2535 | path_segment* ps = _CR(pathbits.Flink, path_segment, list_entry); 2536 | RemoveEntryList(&ps->list_entry); 2537 | bs->FreePool(ps); 2538 | } 2539 | 2540 | return Status; 2541 | } 2542 | 2543 | s += pslen / sizeof(WCHAR); 2544 | left -= pslen; 2545 | 2546 | bs->FreePool(ps); 2547 | } 2548 | 2549 | name[len / sizeof(WCHAR)] = 0; 2550 | } 2551 | 2552 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(inode), (void**)&ino); 2553 | if (EFI_ERROR(Status)) { 2554 | do_print_error("AllocatePool", Status); 2555 | 2556 | if (name) 2557 | bs->FreePool(name); 2558 | 2559 | return Status; 2560 | } 2561 | 2562 | memset(ino, 0, sizeof(inode)); 2563 | 2564 | InitializeListHead(&ino->children); 2565 | 2566 | populate_file_handle(&ino->proto); 2567 | 2568 | ino->r = r; 2569 | ino->inode = SUBVOL_ROOT_INODE; 2570 | ino->vol = vol; 2571 | ino->name = name; 2572 | 2573 | *File = &ino->proto; 2574 | 2575 | return EFI_SUCCESS; 2576 | } 2577 | 2578 | static EFI_STATUS EFIAPI get_driver_name(EFI_QUIBBLE_PROTOCOL* This, CHAR16* DriverName, UINTN* DriverNameLen) { 2579 | static const CHAR16 name[] = L"btrfs"; 2580 | 2581 | UNUSED(This); 2582 | 2583 | if (*DriverNameLen < sizeof(name)) { 2584 | *DriverNameLen = sizeof(name); 2585 | return EFI_BUFFER_TOO_SMALL; 2586 | } 2587 | 2588 | *DriverNameLen = sizeof(name); 2589 | 2590 | memcpy(DriverName, name, sizeof(name)); 2591 | 2592 | return EFI_SUCCESS; 2593 | } 2594 | 2595 | static EFI_STATUS EFIAPI drv_start(EFI_DRIVER_BINDING_PROTOCOL* This, EFI_HANDLE ControllerHandle, 2596 | EFI_DEVICE_PATH_PROTOCOL* RemainingDevicePath) { 2597 | EFI_STATUS Status; 2598 | EFI_GUID disk_guid = EFI_DISK_IO_PROTOCOL_GUID; 2599 | EFI_GUID block_guid = EFI_BLOCK_IO_PROTOCOL_GUID; 2600 | EFI_GUID fs_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; 2601 | EFI_GUID quibble_guid = EFI_QUIBBLE_PROTOCOL_GUID; 2602 | EFI_GUID open_subvol_guid = EFI_OPEN_SUBVOL_GUID; 2603 | EFI_BLOCK_IO_PROTOCOL* block; 2604 | uint32_t sblen; 2605 | superblock* sb; 2606 | volume* vol; 2607 | LIST_ENTRY* le; 2608 | EFI_DISK_IO_PROTOCOL* disk_io; 2609 | 2610 | UNUSED(RemainingDevicePath); 2611 | 2612 | le = volumes.Flink; 2613 | while (le != &volumes) { 2614 | volume* vol = _CR(le, volume, list_entry); 2615 | 2616 | if (vol->controller == ControllerHandle) // already set up 2617 | return EFI_SUCCESS; 2618 | 2619 | le = le->Flink; 2620 | } 2621 | 2622 | Status = bs->OpenProtocol(ControllerHandle, &block_guid, (void**)&block, This->DriverBindingHandle, 2623 | ControllerHandle, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 2624 | if (EFI_ERROR(Status)) 2625 | return Status; 2626 | 2627 | if (block->Media->BlockSize == 0) { 2628 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2629 | return EFI_UNSUPPORTED; 2630 | } 2631 | 2632 | Status = bs->OpenProtocol(ControllerHandle, &disk_guid, (void**)&disk_io, This->DriverBindingHandle, 2633 | ControllerHandle, EFI_OPEN_PROTOCOL_BY_DRIVER); 2634 | if (EFI_ERROR(Status)) { 2635 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2636 | return Status; 2637 | } 2638 | 2639 | // FIXME - FAT driver also claims DISK_IO 2 protocol - do we need to? 2640 | 2641 | sblen = sector_align(sizeof(superblock), block->Media->BlockSize); 2642 | 2643 | Status = bs->AllocatePool(EfiBootServicesData, sblen, (void**)&sb); 2644 | if (EFI_ERROR(Status)) { 2645 | do_print_error("AllocatePool", Status); 2646 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2647 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2648 | return Status; 2649 | } 2650 | 2651 | // read superblock 2652 | // FIXME - check other superblocks? 2653 | 2654 | Status = block->ReadBlocks(block, block->Media->MediaId, superblock_addrs[0] / block->Media->BlockSize, sblen, sb); 2655 | if (EFI_ERROR(Status)) { 2656 | bs->FreePool(sb); 2657 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2658 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2659 | return Status; 2660 | } 2661 | 2662 | if (sb->magic != BTRFS_MAGIC) { // not a Btrfs FS 2663 | bs->FreePool(sb); 2664 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2665 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2666 | return EFI_UNSUPPORTED; 2667 | } 2668 | 2669 | // FIXME - test CRC32 2670 | 2671 | Status = bs->AllocatePool(EfiBootServicesData, sizeof(volume), (void**)&vol); 2672 | if (EFI_ERROR(Status)) { 2673 | do_print_error("AllocatePool", Status); 2674 | bs->FreePool(sb); 2675 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2676 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2677 | return Status; 2678 | } 2679 | 2680 | memset(vol, 0, sizeof(volume)); 2681 | 2682 | if ((sb->incompat_flags & ~COMPAT_FLAGS) != 0) { 2683 | char s[100], *p; 2684 | 2685 | p = stpcpy(s, "Cannot mount as unsupported incompat_flags ("); 2686 | p = hex_to_str(p, sb->incompat_flags & ~COMPAT_FLAGS); 2687 | p = stpcpy(p, ").\r\n"); 2688 | 2689 | do_print(s); 2690 | 2691 | bs->FreePool(sb); 2692 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2693 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2694 | return EFI_UNSUPPORTED; 2695 | } 2696 | 2697 | // FIXME - check csum type (only needed if we do checksum checking) 2698 | 2699 | vol->proto.Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION; 2700 | vol->proto.OpenVolume = open_volume; 2701 | 2702 | vol->quibble_proto.GetArcName = get_arc_name; 2703 | vol->quibble_proto.GetWindowsDriverName = get_driver_name; 2704 | 2705 | vol->open_subvol_proto.OpenSubvol = open_subvol; 2706 | 2707 | Status = bs->InstallMultipleProtocolInterfaces(&ControllerHandle, &fs_guid, &vol->proto, 2708 | &quibble_guid, &vol->quibble_proto, 2709 | &open_subvol_guid, &vol->open_subvol_proto, NULL); 2710 | if (EFI_ERROR(Status)) { 2711 | do_print_error("InstallMultipleProtocolInterfaces", Status); 2712 | bs->FreePool(sb); 2713 | bs->FreePool(vol); 2714 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2715 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2716 | return Status; 2717 | } 2718 | 2719 | vol->sb = sb; 2720 | vol->controller = ControllerHandle; 2721 | vol->block = block; 2722 | vol->disk_io = disk_io; 2723 | 2724 | InsertTailList(&volumes, &vol->list_entry); 2725 | 2726 | return EFI_SUCCESS; 2727 | } 2728 | 2729 | static EFI_STATUS EFIAPI drv_stop(EFI_DRIVER_BINDING_PROTOCOL* This, EFI_HANDLE ControllerHandle, 2730 | UINTN NumberOfChildren, EFI_HANDLE* ChildHandleBuffer) { 2731 | EFI_GUID disk_guid = EFI_DISK_IO_PROTOCOL_GUID; 2732 | EFI_GUID block_guid = EFI_BLOCK_IO_PROTOCOL_GUID; 2733 | 2734 | // FIXME - free anything that needs freeing 2735 | 2736 | bs->CloseProtocol(ControllerHandle, &block_guid, This->DriverBindingHandle, ControllerHandle); 2737 | bs->CloseProtocol(ControllerHandle, &disk_guid, This->DriverBindingHandle, ControllerHandle); 2738 | 2739 | UNUSED(NumberOfChildren); 2740 | UNUSED(ChildHandleBuffer); 2741 | 2742 | return EFI_SUCCESS; 2743 | } 2744 | 2745 | static void get_info_protocol(EFI_HANDLE image_handle) { 2746 | EFI_GUID guid = EFI_QUIBBLE_INFO_PROTOCOL_GUID; 2747 | EFI_HANDLE* handles = NULL; 2748 | UINTN count; 2749 | EFI_STATUS Status; 2750 | 2751 | Status = bs->LocateHandleBuffer(ByProtocol, &guid, NULL, &count, &handles); 2752 | if (EFI_ERROR(Status)) 2753 | return; 2754 | 2755 | if (count == 0) { 2756 | bs->FreePool(handles); 2757 | return; 2758 | } 2759 | 2760 | for (unsigned int i = 0; i < count; i++) { 2761 | Status = bs->OpenProtocol(handles[i], &guid, (void**)&info_proto, image_handle, NULL, 2762 | EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); 2763 | if (EFI_ERROR(Status)) 2764 | continue; 2765 | 2766 | break; 2767 | } 2768 | 2769 | bs->FreePool(handles); 2770 | } 2771 | 2772 | EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) { 2773 | EFI_STATUS Status; 2774 | EFI_GUID guid = EFI_DRIVER_BINDING_PROTOCOL_GUID; 2775 | 2776 | zstd_mem.customAlloc = zstd_malloc; 2777 | zstd_mem.customFree = zstd_free; 2778 | zstd_mem.opaque = NULL; 2779 | 2780 | systable = SystemTable; 2781 | bs = SystemTable->BootServices; 2782 | 2783 | get_info_protocol(ImageHandle); 2784 | 2785 | InitializeListHead(&volumes); 2786 | 2787 | drvbind.Supported = drv_supported; 2788 | drvbind.Start = drv_start; 2789 | drvbind.Stop = drv_stop; 2790 | drvbind.Version = 0x10; 2791 | drvbind.ImageHandle = ImageHandle; 2792 | drvbind.DriverBindingHandle = ImageHandle; 2793 | 2794 | Status = bs->InstallProtocolInterface(&drvbind.DriverBindingHandle, &guid, 2795 | EFI_NATIVE_INTERFACE, &drvbind); 2796 | if (EFI_ERROR(Status)) { 2797 | do_print_error("InstallProtocolInterface", Status); 2798 | return Status; 2799 | } 2800 | 2801 | return EFI_SUCCESS; 2802 | } 2803 | 2804 | /* Work round binutils bug - objcopy will create an unrelocatable EFI image 2805 | * unless at least one input module has a section called .reloc */ 2806 | #ifdef NEED_DUMMY_RELOC 2807 | __asm(".pushsection .reloc"); 2808 | __asm(".popsection"); 2809 | #endif 2810 | -------------------------------------------------------------------------------- /src/btrfs.h: -------------------------------------------------------------------------------- 1 | /* btrfs.h 2 | * Generic btrfs header file. Thanks to whoever it was who wrote 3 | * https://btrfs.wiki.kernel.org/index.php/On-disk_Format - you saved me a lot of time! 4 | * 5 | * I release this file, and this file only, into the public domain - do whatever 6 | * you want with it. You don't have to, but I'd appreciate if you let me know if you 7 | * use it anything cool - mark@harmstone.com. */ 8 | 9 | #pragma once 10 | 11 | #include 12 | 13 | static const uint64_t superblock_addrs[] = { 0x10000, 0x4000000, 0x4000000000, 0x4000000000000, 0 }; 14 | 15 | #define BTRFS_MAGIC 0x4d5f53665248425f 16 | #define MAX_LABEL_SIZE 0x100 17 | #define SUBVOL_ROOT_INODE 0x100 18 | 19 | #define TYPE_INODE_ITEM 0x01 20 | #define TYPE_INODE_REF 0x0C 21 | #define TYPE_INODE_EXTREF 0x0D 22 | #define TYPE_XATTR_ITEM 0x18 23 | #define TYPE_ORPHAN_INODE 0x30 24 | #define TYPE_DIR_ITEM 0x54 25 | #define TYPE_DIR_INDEX 0x60 26 | #define TYPE_EXTENT_DATA 0x6C 27 | #define TYPE_EXTENT_CSUM 0x80 28 | #define TYPE_ROOT_ITEM 0x84 29 | #define TYPE_ROOT_BACKREF 0x90 30 | #define TYPE_ROOT_REF 0x9C 31 | #define TYPE_EXTENT_ITEM 0xA8 32 | #define TYPE_METADATA_ITEM 0xA9 33 | #define TYPE_TREE_BLOCK_REF 0xB0 34 | #define TYPE_EXTENT_DATA_REF 0xB2 35 | #define TYPE_EXTENT_REF_V0 0xB4 36 | #define TYPE_SHARED_BLOCK_REF 0xB6 37 | #define TYPE_SHARED_DATA_REF 0xB8 38 | #define TYPE_BLOCK_GROUP_ITEM 0xC0 39 | #define TYPE_FREE_SPACE_INFO 0xC6 40 | #define TYPE_FREE_SPACE_EXTENT 0xC7 41 | #define TYPE_FREE_SPACE_BITMAP 0xC8 42 | #define TYPE_DEV_EXTENT 0xCC 43 | #define TYPE_DEV_ITEM 0xD8 44 | #define TYPE_CHUNK_ITEM 0xE4 45 | #define TYPE_TEMP_ITEM 0xF8 46 | #define TYPE_DEV_STATS 0xF9 47 | #define TYPE_SUBVOL_UUID 0xFB 48 | #define TYPE_SUBVOL_REC_UUID 0xFC 49 | 50 | #define BTRFS_ROOT_ROOT 1 51 | #define BTRFS_ROOT_EXTENT 2 52 | #define BTRFS_ROOT_CHUNK 3 53 | #define BTRFS_ROOT_DEVTREE 4 54 | #define BTRFS_ROOT_FSTREE 5 55 | #define BTRFS_ROOT_TREEDIR 6 56 | #define BTRFS_ROOT_CHECKSUM 7 57 | #define BTRFS_ROOT_UUID 9 58 | #define BTRFS_ROOT_FREE_SPACE 0xa 59 | #define BTRFS_ROOT_DATA_RELOC 0xFFFFFFFFFFFFFFF7 60 | 61 | #define BTRFS_COMPRESSION_NONE 0 62 | #define BTRFS_COMPRESSION_ZLIB 1 63 | #define BTRFS_COMPRESSION_LZO 2 64 | #define BTRFS_COMPRESSION_ZSTD 3 65 | 66 | #define BTRFS_ENCRYPTION_NONE 0 67 | 68 | #define BTRFS_ENCODING_NONE 0 69 | 70 | #define EXTENT_TYPE_INLINE 0 71 | #define EXTENT_TYPE_REGULAR 1 72 | #define EXTENT_TYPE_PREALLOC 2 73 | 74 | #define BLOCK_FLAG_DATA 0x001 75 | #define BLOCK_FLAG_SYSTEM 0x002 76 | #define BLOCK_FLAG_METADATA 0x004 77 | #define BLOCK_FLAG_RAID0 0x008 78 | #define BLOCK_FLAG_RAID1 0x010 79 | #define BLOCK_FLAG_DUPLICATE 0x020 80 | #define BLOCK_FLAG_RAID10 0x040 81 | #define BLOCK_FLAG_RAID5 0x080 82 | #define BLOCK_FLAG_RAID6 0x100 83 | 84 | #define FREE_SPACE_CACHE_ID 0xFFFFFFFFFFFFFFF5 85 | #define EXTENT_CSUM_ID 0xFFFFFFFFFFFFFFF6 86 | #define BALANCE_ITEM_ID 0xFFFFFFFFFFFFFFFC 87 | 88 | #define BTRFS_INODE_NODATASUM 0x001 89 | #define BTRFS_INODE_NODATACOW 0x002 90 | #define BTRFS_INODE_READONLY 0x004 91 | #define BTRFS_INODE_NOCOMPRESS 0x008 92 | #define BTRFS_INODE_PREALLOC 0x010 93 | #define BTRFS_INODE_SYNC 0x020 94 | #define BTRFS_INODE_IMMUTABLE 0x040 95 | #define BTRFS_INODE_APPEND 0x080 96 | #define BTRFS_INODE_NODUMP 0x100 97 | #define BTRFS_INODE_NOATIME 0x200 98 | #define BTRFS_INODE_DIRSYNC 0x400 99 | #define BTRFS_INODE_COMPRESS 0x800 100 | 101 | #define BTRFS_SUBVOL_READONLY 0x1 102 | 103 | #define BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE 0x1 104 | #define BTRFS_COMPAT_RO_FLAGS_FREE_SPACE_CACHE_VALID 0x2 105 | 106 | #define BTRFS_INCOMPAT_FLAGS_MIXED_BACKREF 0x0001 107 | #define BTRFS_INCOMPAT_FLAGS_DEFAULT_SUBVOL 0x0002 108 | #define BTRFS_INCOMPAT_FLAGS_MIXED_GROUPS 0x0004 109 | #define BTRFS_INCOMPAT_FLAGS_COMPRESS_LZO 0x0008 110 | #define BTRFS_INCOMPAT_FLAGS_COMPRESS_ZSTD 0x0010 111 | #define BTRFS_INCOMPAT_FLAGS_BIG_METADATA 0x0020 112 | #define BTRFS_INCOMPAT_FLAGS_EXTENDED_IREF 0x0040 113 | #define BTRFS_INCOMPAT_FLAGS_RAID56 0x0080 114 | #define BTRFS_INCOMPAT_FLAGS_SKINNY_METADATA 0x0100 115 | #define BTRFS_INCOMPAT_FLAGS_NO_HOLES 0x0200 116 | #define BTRFS_INCOMPAT_FLAGS_METADATA_UUID 0x0400 117 | #define BTRFS_INCOMPAT_FLAGS_RAID1C34 0x0800 118 | 119 | #define BTRFS_SUPERBLOCK_FLAGS_SEEDING 0x100000000 120 | 121 | #define BTRFS_ORPHAN_INODE_OBJID 0xFFFFFFFFFFFFFFFB 122 | 123 | #pragma pack(push, 1) 124 | 125 | typedef struct { 126 | uint8_t uuid[16]; 127 | } BTRFS_UUID; 128 | 129 | typedef struct { 130 | uint64_t obj_id; 131 | uint8_t obj_type; 132 | uint64_t offset; 133 | } KEY; 134 | 135 | #define HEADER_FLAG_WRITTEN 0x000000000000001 136 | #define HEADER_FLAG_SHARED_BACKREF 0x000000000000002 137 | #define HEADER_FLAG_MIXED_BACKREF 0x100000000000000 138 | 139 | typedef struct { 140 | uint8_t csum[32]; 141 | BTRFS_UUID fs_uuid; 142 | uint64_t address; 143 | uint64_t flags; 144 | BTRFS_UUID chunk_tree_uuid; 145 | uint64_t generation; 146 | uint64_t tree_id; 147 | uint32_t num_items; 148 | uint8_t level; 149 | } tree_header; 150 | 151 | typedef struct { 152 | KEY key; 153 | uint32_t offset; 154 | uint32_t size; 155 | } leaf_node; 156 | 157 | typedef struct { 158 | KEY key; 159 | uint64_t address; 160 | uint64_t generation; 161 | } internal_node; 162 | 163 | typedef struct { 164 | uint64_t dev_id; 165 | uint64_t num_bytes; 166 | uint64_t bytes_used; 167 | uint32_t optimal_io_align; 168 | uint32_t optimal_io_width; 169 | uint32_t minimal_io_size; 170 | uint64_t type; 171 | uint64_t generation; 172 | uint64_t start_offset; 173 | uint32_t dev_group; 174 | uint8_t seek_speed; 175 | uint8_t bandwidth; 176 | BTRFS_UUID device_uuid; 177 | BTRFS_UUID fs_uuid; 178 | } DEV_ITEM; 179 | 180 | #define SYS_CHUNK_ARRAY_SIZE 0x800 181 | #define BTRFS_NUM_BACKUP_ROOTS 4 182 | 183 | typedef struct { 184 | uint64_t root_tree_addr; 185 | uint64_t root_tree_generation; 186 | uint64_t chunk_tree_addr; 187 | uint64_t chunk_tree_generation; 188 | uint64_t extent_tree_addr; 189 | uint64_t extent_tree_generation; 190 | uint64_t fs_tree_addr; 191 | uint64_t fs_tree_generation; 192 | uint64_t dev_root_addr; 193 | uint64_t dev_root_generation; 194 | uint64_t csum_root_addr; 195 | uint64_t csum_root_generation; 196 | uint64_t total_bytes; 197 | uint64_t bytes_used; 198 | uint64_t num_devices; 199 | uint64_t reserved[4]; 200 | uint8_t root_level; 201 | uint8_t chunk_root_level; 202 | uint8_t extent_root_level; 203 | uint8_t fs_root_level; 204 | uint8_t dev_root_level; 205 | uint8_t csum_root_level; 206 | uint8_t reserved2[10]; 207 | } superblock_backup; 208 | 209 | typedef struct { 210 | uint8_t checksum[32]; 211 | BTRFS_UUID uuid; 212 | uint64_t sb_phys_addr; 213 | uint64_t flags; 214 | uint64_t magic; 215 | uint64_t generation; 216 | uint64_t root_tree_addr; 217 | uint64_t chunk_tree_addr; 218 | uint64_t log_tree_addr; 219 | uint64_t log_root_transid; 220 | uint64_t total_bytes; 221 | uint64_t bytes_used; 222 | uint64_t root_dir_objectid; 223 | uint64_t num_devices; 224 | uint32_t sector_size; 225 | uint32_t node_size; 226 | uint32_t leaf_size; 227 | uint32_t stripe_size; 228 | uint32_t n; 229 | uint64_t chunk_root_generation; 230 | uint64_t compat_flags; 231 | uint64_t compat_ro_flags; 232 | uint64_t incompat_flags; 233 | uint16_t csum_type; 234 | uint8_t root_level; 235 | uint8_t chunk_root_level; 236 | uint8_t log_root_level; 237 | DEV_ITEM dev_item; 238 | char label[MAX_LABEL_SIZE]; 239 | uint64_t cache_generation; 240 | uint64_t uuid_tree_generation; 241 | uint64_t reserved[30]; 242 | uint8_t sys_chunk_array[SYS_CHUNK_ARRAY_SIZE]; 243 | superblock_backup backup[BTRFS_NUM_BACKUP_ROOTS]; 244 | uint8_t reserved2[565]; 245 | } superblock; 246 | 247 | #define BTRFS_TYPE_UNKNOWN 0 248 | #define BTRFS_TYPE_FILE 1 249 | #define BTRFS_TYPE_DIRECTORY 2 250 | #define BTRFS_TYPE_CHARDEV 3 251 | #define BTRFS_TYPE_BLOCKDEV 4 252 | #define BTRFS_TYPE_FIFO 5 253 | #define BTRFS_TYPE_SOCKET 6 254 | #define BTRFS_TYPE_SYMLINK 7 255 | #define BTRFS_TYPE_EA 8 256 | 257 | typedef struct { 258 | KEY key; 259 | uint64_t transid; 260 | uint16_t m; 261 | uint16_t n; 262 | uint8_t type; 263 | char name[1]; 264 | } DIR_ITEM; 265 | 266 | typedef struct { 267 | uint64_t seconds; 268 | uint32_t nanoseconds; 269 | } BTRFS_TIME; 270 | 271 | typedef struct { 272 | uint64_t generation; 273 | uint64_t transid; 274 | uint64_t st_size; 275 | uint64_t st_blocks; 276 | uint64_t block_group; 277 | uint32_t st_nlink; 278 | uint32_t st_uid; 279 | uint32_t st_gid; 280 | uint32_t st_mode; 281 | uint64_t st_rdev; 282 | uint64_t flags; 283 | uint64_t sequence; 284 | uint8_t reserved[32]; 285 | BTRFS_TIME st_atime; 286 | BTRFS_TIME st_ctime; 287 | BTRFS_TIME st_mtime; 288 | BTRFS_TIME otime; 289 | } INODE_ITEM; 290 | 291 | typedef struct { 292 | INODE_ITEM inode; 293 | uint64_t generation; 294 | uint64_t objid; 295 | uint64_t block_number; 296 | uint64_t byte_limit; 297 | uint64_t bytes_used; 298 | uint64_t last_snapshot_generation; 299 | uint64_t flags; 300 | uint32_t num_references; 301 | KEY drop_progress; 302 | uint8_t drop_level; 303 | uint8_t root_level; 304 | uint64_t generation2; 305 | BTRFS_UUID uuid; 306 | BTRFS_UUID parent_uuid; 307 | BTRFS_UUID received_uuid; 308 | uint64_t ctransid; 309 | uint64_t otransid; 310 | uint64_t stransid; 311 | uint64_t rtransid; 312 | BTRFS_TIME ctime; 313 | BTRFS_TIME otime; 314 | BTRFS_TIME stime; 315 | BTRFS_TIME rtime; 316 | uint64_t reserved[8]; 317 | } ROOT_ITEM; 318 | 319 | typedef struct { 320 | uint64_t size; 321 | uint64_t root_id; 322 | uint64_t stripe_length; 323 | uint64_t type; 324 | uint32_t opt_io_alignment; 325 | uint32_t opt_io_width; 326 | uint32_t sector_size; 327 | uint16_t num_stripes; 328 | uint16_t sub_stripes; 329 | } CHUNK_ITEM; 330 | 331 | typedef struct { 332 | uint64_t dev_id; 333 | uint64_t offset; 334 | BTRFS_UUID dev_uuid; 335 | } CHUNK_ITEM_STRIPE; 336 | 337 | typedef struct { 338 | uint64_t generation; 339 | uint64_t decoded_size; 340 | uint8_t compression; 341 | uint8_t encryption; 342 | uint16_t encoding; 343 | uint8_t type; 344 | uint8_t data[1]; 345 | } EXTENT_DATA; 346 | 347 | typedef struct { 348 | uint64_t address; 349 | uint64_t size; 350 | uint64_t offset; 351 | uint64_t num_bytes; 352 | } EXTENT_DATA2; 353 | 354 | typedef struct { 355 | uint64_t index; 356 | uint16_t n; 357 | char name[1]; 358 | } INODE_REF; 359 | 360 | typedef struct { 361 | uint64_t dir; 362 | uint64_t index; 363 | uint16_t n; 364 | char name[1]; 365 | } INODE_EXTREF; 366 | 367 | #define EXTENT_ITEM_DATA 0x001 368 | #define EXTENT_ITEM_TREE_BLOCK 0x002 369 | #define EXTENT_ITEM_SHARED_BACKREFS 0x100 370 | 371 | typedef struct { 372 | uint64_t refcount; 373 | uint64_t generation; 374 | uint64_t flags; 375 | } EXTENT_ITEM; 376 | 377 | typedef struct { 378 | KEY firstitem; 379 | uint8_t level; 380 | } EXTENT_ITEM2; 381 | 382 | typedef struct { 383 | uint32_t refcount; 384 | } EXTENT_ITEM_V0; 385 | 386 | typedef struct { 387 | EXTENT_ITEM extent_item; 388 | KEY firstitem; 389 | uint8_t level; 390 | } EXTENT_ITEM_TREE; 391 | 392 | typedef struct { 393 | uint64_t offset; 394 | } TREE_BLOCK_REF; 395 | 396 | typedef struct { 397 | uint64_t root; 398 | uint64_t objid; 399 | uint64_t offset; 400 | uint32_t count; 401 | } EXTENT_DATA_REF; 402 | 403 | typedef struct { 404 | uint64_t used; 405 | uint64_t chunk_tree; 406 | uint64_t flags; 407 | } BLOCK_GROUP_ITEM; 408 | 409 | typedef struct { 410 | uint64_t root; 411 | uint64_t gen; 412 | uint64_t objid; 413 | uint32_t count; 414 | } EXTENT_REF_V0; 415 | 416 | typedef struct { 417 | uint64_t offset; 418 | } SHARED_BLOCK_REF; 419 | 420 | typedef struct { 421 | uint64_t offset; 422 | uint32_t count; 423 | } SHARED_DATA_REF; 424 | 425 | #define FREE_SPACE_EXTENT 1 426 | #define FREE_SPACE_BITMAP 2 427 | 428 | typedef struct { 429 | uint64_t offset; 430 | uint64_t size; 431 | uint8_t type; 432 | } FREE_SPACE_ENTRY; 433 | 434 | typedef struct { 435 | KEY key; 436 | uint64_t generation; 437 | uint64_t num_entries; 438 | uint64_t num_bitmaps; 439 | } FREE_SPACE_ITEM; 440 | 441 | typedef struct { 442 | uint64_t dir; 443 | uint64_t index; 444 | uint16_t n; 445 | char name[1]; 446 | } ROOT_REF; 447 | 448 | typedef struct { 449 | uint64_t chunktree; 450 | uint64_t objid; 451 | uint64_t address; 452 | uint64_t length; 453 | BTRFS_UUID chunktree_uuid; 454 | } DEV_EXTENT; 455 | 456 | #define BALANCE_FLAGS_DATA 0x1 457 | #define BALANCE_FLAGS_SYSTEM 0x2 458 | #define BALANCE_FLAGS_METADATA 0x4 459 | 460 | #define BALANCE_ARGS_FLAGS_PROFILES 0x001 461 | #define BALANCE_ARGS_FLAGS_USAGE 0x002 462 | #define BALANCE_ARGS_FLAGS_DEVID 0x004 463 | #define BALANCE_ARGS_FLAGS_DRANGE 0x008 464 | #define BALANCE_ARGS_FLAGS_VRANGE 0x010 465 | #define BALANCE_ARGS_FLAGS_LIMIT 0x020 466 | #define BALANCE_ARGS_FLAGS_LIMIT_RANGE 0x040 467 | #define BALANCE_ARGS_FLAGS_STRIPES_RANGE 0x080 468 | #define BALANCE_ARGS_FLAGS_CONVERT 0x100 469 | #define BALANCE_ARGS_FLAGS_SOFT 0x200 470 | #define BALANCE_ARGS_FLAGS_USAGE_RANGE 0x400 471 | 472 | typedef struct { 473 | uint64_t profiles; 474 | 475 | union { 476 | uint64_t usage; 477 | struct { 478 | uint32_t usage_start; 479 | uint32_t usage_end; 480 | }; 481 | }; 482 | 483 | uint64_t devid; 484 | uint64_t drange_start; 485 | uint64_t drange_end; 486 | uint64_t vrange_start; 487 | uint64_t vrange_end; 488 | uint64_t convert; 489 | uint64_t flags; 490 | 491 | union { 492 | uint64_t limit; 493 | struct { 494 | uint32_t limit_start; 495 | uint32_t limit_end; 496 | }; 497 | }; 498 | 499 | uint32_t stripes_start; 500 | uint32_t stripes_end; 501 | uint8_t reserved[48]; 502 | } BALANCE_ARGS; 503 | 504 | typedef struct { 505 | uint64_t flags; 506 | BALANCE_ARGS data; 507 | BALANCE_ARGS metadata; 508 | BALANCE_ARGS system; 509 | uint8_t reserved[32]; 510 | } BALANCE_ITEM; 511 | 512 | #define BTRFS_FREE_SPACE_USING_BITMAPS 1 513 | 514 | typedef struct { 515 | uint32_t count; 516 | uint32_t flags; 517 | } FREE_SPACE_INFO; 518 | 519 | #define BTRFS_DEV_STAT_WRITE_ERRORS 0 520 | #define BTRFS_DEV_STAT_READ_ERRORS 1 521 | #define BTRFS_DEV_STAT_FLUSH_ERRORS 2 522 | #define BTRFS_DEV_STAT_CORRUPTION_ERRORS 3 523 | #define BTRFS_DEV_STAT_GENERATION_ERRORS 4 524 | 525 | #define BTRFS_SEND_CMD_SUBVOL 1 526 | #define BTRFS_SEND_CMD_SNAPSHOT 2 527 | #define BTRFS_SEND_CMD_MKFILE 3 528 | #define BTRFS_SEND_CMD_MKDIR 4 529 | #define BTRFS_SEND_CMD_MKNOD 5 530 | #define BTRFS_SEND_CMD_MKFIFO 6 531 | #define BTRFS_SEND_CMD_MKSOCK 7 532 | #define BTRFS_SEND_CMD_SYMLINK 8 533 | #define BTRFS_SEND_CMD_RENAME 9 534 | #define BTRFS_SEND_CMD_LINK 10 535 | #define BTRFS_SEND_CMD_UNLINK 11 536 | #define BTRFS_SEND_CMD_RMDIR 12 537 | #define BTRFS_SEND_CMD_SET_XATTR 13 538 | #define BTRFS_SEND_CMD_REMOVE_XATTR 14 539 | #define BTRFS_SEND_CMD_WRITE 15 540 | #define BTRFS_SEND_CMD_CLONE 16 541 | #define BTRFS_SEND_CMD_TRUNCATE 17 542 | #define BTRFS_SEND_CMD_CHMOD 18 543 | #define BTRFS_SEND_CMD_CHOWN 19 544 | #define BTRFS_SEND_CMD_UTIMES 20 545 | #define BTRFS_SEND_CMD_END 21 546 | #define BTRFS_SEND_CMD_UPDATE_EXTENT 22 547 | 548 | #define BTRFS_SEND_TLV_UUID 1 549 | #define BTRFS_SEND_TLV_TRANSID 2 550 | #define BTRFS_SEND_TLV_INODE 3 551 | #define BTRFS_SEND_TLV_SIZE 4 552 | #define BTRFS_SEND_TLV_MODE 5 553 | #define BTRFS_SEND_TLV_UID 6 554 | #define BTRFS_SEND_TLV_GID 7 555 | #define BTRFS_SEND_TLV_RDEV 8 556 | #define BTRFS_SEND_TLV_CTIME 9 557 | #define BTRFS_SEND_TLV_MTIME 10 558 | #define BTRFS_SEND_TLV_ATIME 11 559 | #define BTRFS_SEND_TLV_OTIME 12 560 | #define BTRFS_SEND_TLV_XATTR_NAME 13 561 | #define BTRFS_SEND_TLV_XATTR_DATA 14 562 | #define BTRFS_SEND_TLV_PATH 15 563 | #define BTRFS_SEND_TLV_PATH_TO 16 564 | #define BTRFS_SEND_TLV_PATH_LINK 17 565 | #define BTRFS_SEND_TLV_OFFSET 18 566 | #define BTRFS_SEND_TLV_DATA 19 567 | #define BTRFS_SEND_TLV_CLONE_UUID 20 568 | #define BTRFS_SEND_TLV_CLONE_CTRANSID 21 569 | #define BTRFS_SEND_TLV_CLONE_PATH 22 570 | #define BTRFS_SEND_TLV_CLONE_OFFSET 23 571 | #define BTRFS_SEND_TLV_CLONE_LENGTH 24 572 | 573 | #define BTRFS_SEND_MAGIC "btrfs-stream" 574 | 575 | typedef struct { 576 | uint8_t magic[13]; 577 | uint32_t version; 578 | } btrfs_send_header; 579 | 580 | typedef struct { 581 | uint32_t length; 582 | uint16_t cmd; 583 | uint32_t csum; 584 | } btrfs_send_command; 585 | 586 | typedef struct { 587 | uint16_t type; 588 | uint16_t length; 589 | } btrfs_send_tlv; 590 | 591 | #pragma pack(pop) 592 | -------------------------------------------------------------------------------- /src/crc32c.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | #include 19 | 20 | static const uint32_t crctable[] = { 21 | 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4, 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb, 22 | 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b, 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24, 23 | 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b, 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384, 24 | 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54, 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b, 25 | 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a, 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35, 26 | 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5, 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa, 27 | 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45, 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a, 28 | 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a, 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595, 29 | 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48, 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957, 30 | 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687, 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198, 31 | 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927, 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38, 32 | 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8, 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7, 33 | 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096, 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789, 34 | 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859, 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46, 35 | 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9, 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6, 36 | 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36, 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829, 37 | 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c, 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93, 38 | 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043, 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c, 39 | 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3, 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc, 40 | 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c, 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033, 41 | 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652, 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d, 42 | 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d, 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982, 43 | 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d, 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622, 44 | 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2, 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed, 45 | 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530, 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f, 46 | 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff, 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0, 47 | 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f, 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540, 48 | 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90, 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f, 49 | 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee, 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1, 50 | 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321, 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e, 51 | 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81, 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e, 52 | 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e, 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351, 53 | }; 54 | 55 | uint32_t calc_crc32c(uint32_t seed, uint8_t* msg, unsigned int msglen) { 56 | uint32_t rem; 57 | 58 | // FIXME - use asm instructions if available 59 | 60 | rem = seed; 61 | 62 | for (unsigned i = 0; i < msglen; i++) { 63 | rem = crctable[(rem ^ msg[i]) & 0xff] ^ (rem >> 8); 64 | } 65 | 66 | return rem; 67 | } 68 | -------------------------------------------------------------------------------- /src/lzo.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | // This LZO compression code comes from v0.22 of lzo, written way back in 19 | // 1996, and available here: 20 | // https://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996/libs/lzo-0.22.tar.gz 21 | // Modern versions of lzo are licensed under the GPL, but the very oldest 22 | // versions are under the LGPL and hence okay to use here. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "misc.h" 32 | 33 | #define LZO_PAGE_SIZE 4096 34 | 35 | typedef struct { 36 | uint8_t* in; 37 | uint32_t inlen; 38 | uint32_t inpos; 39 | uint8_t* out; 40 | uint32_t outlen; 41 | uint32_t outpos; 42 | bool error; 43 | void* wrkmem; 44 | } lzo_stream; 45 | 46 | void do_print(const char* s); 47 | void do_print_error(const char* func, EFI_STATUS Status); 48 | 49 | static uint8_t lzo_nextbyte(lzo_stream* stream) { 50 | uint8_t c; 51 | 52 | if (stream->inpos >= stream->inlen) { 53 | stream->error = true; 54 | return 0; 55 | } 56 | 57 | c = stream->in[stream->inpos]; 58 | stream->inpos++; 59 | 60 | return c; 61 | } 62 | 63 | static int lzo_len(lzo_stream* stream, int byte, int mask) { 64 | int len = byte & mask; 65 | 66 | if (len == 0) { 67 | while (!(byte = lzo_nextbyte(stream))) { 68 | if (stream->error) return 0; 69 | 70 | len += 255; 71 | } 72 | 73 | len += mask + byte; 74 | } 75 | 76 | return len; 77 | } 78 | 79 | static void lzo_copy(lzo_stream* stream, int len) { 80 | if (stream->inpos + len > stream->inlen) { 81 | stream->error = true; 82 | return; 83 | } 84 | 85 | if (stream->outpos + len > stream->outlen) { 86 | stream->error = true; 87 | return; 88 | } 89 | 90 | do { 91 | stream->out[stream->outpos] = stream->in[stream->inpos]; 92 | stream->inpos++; 93 | stream->outpos++; 94 | len--; 95 | } while (len > 0); 96 | } 97 | 98 | static void lzo_copyback(lzo_stream* stream, uint32_t back, int len) { 99 | if (stream->outpos < back) { 100 | stream->error = true; 101 | return; 102 | } 103 | 104 | if (stream->outpos + len > stream->outlen) { 105 | stream->error = true; 106 | return; 107 | } 108 | 109 | do { 110 | stream->out[stream->outpos] = stream->out[stream->outpos - back]; 111 | stream->outpos++; 112 | len--; 113 | } while (len > 0); 114 | } 115 | 116 | static __inline unsigned int min(unsigned int a, unsigned int b) { 117 | if (a < b) 118 | return a; 119 | else 120 | return b; 121 | } 122 | 123 | static EFI_STATUS do_lzo_decompress(lzo_stream* stream) { 124 | uint8_t byte; 125 | uint32_t len, back; 126 | bool backcopy = false; 127 | 128 | stream->error = false; 129 | 130 | byte = lzo_nextbyte(stream); 131 | if (stream->error) return EFI_INVALID_PARAMETER; 132 | 133 | if (byte > 17) { 134 | lzo_copy(stream, min((uint8_t)(byte - 17), (uint32_t)(stream->outlen - stream->outpos))); 135 | if (stream->error) return EFI_INVALID_PARAMETER; 136 | 137 | if (stream->outlen == stream->outpos) 138 | return EFI_SUCCESS; 139 | 140 | byte = lzo_nextbyte(stream); 141 | if (stream->error) return EFI_INVALID_PARAMETER; 142 | 143 | if (byte < 16) return EFI_INVALID_PARAMETER; 144 | } 145 | 146 | while (1) { 147 | if (byte >> 4) { 148 | backcopy = true; 149 | if (byte >> 6) { 150 | len = (byte >> 5) - 1; 151 | back = (lzo_nextbyte(stream) << 3) + ((byte >> 2) & 7) + 1; 152 | if (stream->error) return EFI_INVALID_PARAMETER; 153 | } else if (byte >> 5) { 154 | len = lzo_len(stream, byte, 31); 155 | if (stream->error) return EFI_INVALID_PARAMETER; 156 | 157 | byte = lzo_nextbyte(stream); 158 | if (stream->error) return EFI_INVALID_PARAMETER; 159 | 160 | back = (lzo_nextbyte(stream) << 6) + (byte >> 2) + 1; 161 | if (stream->error) return EFI_INVALID_PARAMETER; 162 | } else { 163 | len = lzo_len(stream, byte, 7); 164 | if (stream->error) return EFI_INVALID_PARAMETER; 165 | 166 | back = (1 << 14) + ((byte & 8) << 11); 167 | 168 | byte = lzo_nextbyte(stream); 169 | if (stream->error) return EFI_INVALID_PARAMETER; 170 | 171 | back += (lzo_nextbyte(stream) << 6) + (byte >> 2); 172 | if (stream->error) return EFI_INVALID_PARAMETER; 173 | 174 | if (back == (1 << 14)) { 175 | if (len != 1) 176 | return EFI_INVALID_PARAMETER; 177 | break; 178 | } 179 | } 180 | } else if (backcopy) { 181 | len = 0; 182 | back = (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1; 183 | if (stream->error) return EFI_INVALID_PARAMETER; 184 | } else { 185 | len = lzo_len(stream, byte, 15); 186 | if (stream->error) return EFI_INVALID_PARAMETER; 187 | 188 | lzo_copy(stream, min(len + 3, stream->outlen - stream->outpos)); 189 | if (stream->error) return EFI_INVALID_PARAMETER; 190 | 191 | if (stream->outlen == stream->outpos) 192 | return EFI_SUCCESS; 193 | 194 | byte = lzo_nextbyte(stream); 195 | if (stream->error) return EFI_INVALID_PARAMETER; 196 | 197 | if (byte >> 4) 198 | continue; 199 | 200 | len = 1; 201 | back = (1 << 11) + (lzo_nextbyte(stream) << 2) + (byte >> 2) + 1; 202 | if (stream->error) return EFI_INVALID_PARAMETER; 203 | 204 | break; 205 | } 206 | 207 | lzo_copyback(stream, back, min(len + 2, stream->outlen - stream->outpos)); 208 | if (stream->error) return EFI_INVALID_PARAMETER; 209 | 210 | if (stream->outlen == stream->outpos) 211 | return EFI_SUCCESS; 212 | 213 | len = byte & 3; 214 | 215 | if (len) { 216 | lzo_copy(stream, min(len, stream->outlen - stream->outpos)); 217 | if (stream->error) return EFI_INVALID_PARAMETER; 218 | 219 | if (stream->outlen == stream->outpos) 220 | return EFI_SUCCESS; 221 | } else 222 | backcopy = !backcopy; 223 | 224 | byte = lzo_nextbyte(stream); 225 | if (stream->error) return EFI_INVALID_PARAMETER; 226 | } 227 | 228 | return EFI_SUCCESS; 229 | } 230 | 231 | EFI_STATUS lzo_decompress(uint8_t* inbuf, uint32_t inlen, uint8_t* outbuf, uint32_t outlen, uint32_t inpageoff) { 232 | EFI_STATUS Status; 233 | uint32_t partlen, inoff, outoff; 234 | lzo_stream stream; 235 | 236 | inoff = 0; 237 | outoff = 0; 238 | 239 | do { 240 | partlen = *(uint32_t*)&inbuf[inoff]; 241 | 242 | if (partlen + inoff > inlen) { 243 | char s[255], *p; 244 | 245 | p = stpcpy(s, "overflow: "); 246 | p = dec_to_str(p, partlen); 247 | p = stpcpy(p, " + "); 248 | p = dec_to_str(p, inoff); 249 | p = stpcpy(p," > "); 250 | p = dec_to_str(p, inlen); 251 | p = stpcpy(p, "\n"); 252 | 253 | do_print(s); 254 | return EFI_INVALID_PARAMETER; 255 | } 256 | 257 | inoff += sizeof(uint32_t); 258 | 259 | stream.in = &inbuf[inoff]; 260 | stream.inlen = partlen; 261 | stream.inpos = 0; 262 | stream.out = &outbuf[outoff]; 263 | stream.outlen = min(outlen, LZO_PAGE_SIZE); 264 | stream.outpos = 0; 265 | 266 | Status = do_lzo_decompress(&stream); 267 | if (EFI_ERROR(Status)) { 268 | do_print_error("do_lzo_decompress", Status); 269 | return Status; 270 | } 271 | 272 | if (stream.outpos < stream.outlen) 273 | memset(&stream.out[stream.outpos], 0, stream.outlen - stream.outpos); 274 | 275 | inoff += partlen; 276 | outoff += stream.outlen; 277 | 278 | if (LZO_PAGE_SIZE - ((inpageoff + inoff) % LZO_PAGE_SIZE) < sizeof(uint32_t)) 279 | inoff = ((((inpageoff + inoff) / LZO_PAGE_SIZE) + 1) * LZO_PAGE_SIZE) - inpageoff; 280 | 281 | outlen -= stream.outlen; 282 | } while (inoff < inlen && outlen > 0); 283 | 284 | return EFI_SUCCESS; 285 | } 286 | -------------------------------------------------------------------------------- /src/misc.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | #include "misc.h" 19 | #include 20 | 21 | size_t wcslen(const wchar_t* s) { 22 | size_t i = 0; 23 | 24 | while (s[i] != 0) { 25 | i++; 26 | } 27 | 28 | return i; 29 | } 30 | 31 | size_t strlen(const char* s) { 32 | size_t i = 0; 33 | 34 | while (s[i] != 0) { 35 | i++; 36 | } 37 | 38 | return i; 39 | } 40 | 41 | int memcmp(const void* s1, const void* s2, size_t n) { 42 | #if __INTPTR_WIDTH__ == 64 43 | while (n > sizeof(uint64_t)) { 44 | uint64_t c1 = *(uint64_t*)s1; 45 | uint64_t c2 = *(uint64_t*)s2; 46 | 47 | if (c1 != c2) 48 | return c1 > c2 ? 1 : -1; 49 | 50 | s1 = (uint64_t*)s1 + 1; 51 | s2 = (uint64_t*)s2 + 1; 52 | n -= sizeof(uint64_t); 53 | } 54 | #endif 55 | 56 | while (n > sizeof(uint32_t)) { 57 | uint32_t c1 = *(uint32_t*)s1; 58 | uint32_t c2 = *(uint32_t*)s2; 59 | 60 | if (c1 != c2) 61 | return c1 > c2 ? 1 : -1; 62 | 63 | s1 = (uint32_t*)s1 + 1; 64 | s2 = (uint32_t*)s2 + 1; 65 | n -= sizeof(uint32_t); 66 | } 67 | 68 | while (n > 0) { 69 | uint8_t c1 = *(uint8_t*)s1; 70 | uint8_t c2 = *(uint8_t*)s2; 71 | 72 | if (c1 != c2) 73 | return c1 > c2 ? 1 : -1; 74 | 75 | s1 = (uint8_t*)s1 + 1; 76 | s2 = (uint8_t*)s2 + 1; 77 | n--; 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | void* memcpy(void* dest, const void* src, size_t n) { 84 | void* orig_dest = dest; 85 | 86 | #if __INTPTR_WIDTH__ == 64 87 | while (n >= sizeof(uint64_t)) { 88 | *(uint64_t*)dest = *(uint64_t*)src; 89 | 90 | dest = (uint8_t*)dest + sizeof(uint64_t); 91 | src = (uint8_t*)src + sizeof(uint64_t); 92 | 93 | n -= sizeof(uint64_t); 94 | } 95 | #endif 96 | 97 | while (n >= sizeof(uint32_t)) { 98 | *(uint32_t*)dest = *(uint32_t*)src; 99 | 100 | dest = (uint8_t*)dest + sizeof(uint32_t); 101 | src = (uint8_t*)src + sizeof(uint32_t); 102 | 103 | n -= sizeof(uint32_t); 104 | } 105 | 106 | while (n >= sizeof(uint16_t)) { 107 | *(uint16_t*)dest = *(uint16_t*)src; 108 | 109 | dest = (uint8_t*)dest + sizeof(uint16_t); 110 | src = (uint8_t*)src + sizeof(uint16_t); 111 | 112 | n -= sizeof(uint16_t); 113 | } 114 | 115 | while (n >= sizeof(uint8_t)) { 116 | *(uint8_t*)dest = *(uint8_t*)src; 117 | 118 | dest = (uint8_t*)dest + sizeof(uint8_t); 119 | src = (uint8_t*)src + sizeof(uint8_t); 120 | 121 | n -= sizeof(uint8_t); 122 | } 123 | 124 | return orig_dest; 125 | } 126 | 127 | void* memset(void* s, int c, size_t n) { 128 | void* orig_s = s; 129 | 130 | // FIXME - faster if we make sure we're aligned (also in memcpy)? 131 | 132 | #if __INTPTR_WIDTH__ == 64 133 | uint64_t v; 134 | 135 | v = 0; 136 | 137 | for (unsigned int i = 0; i < sizeof(uint64_t); i++) { 138 | v <<= 8; 139 | v |= c & 0xff; 140 | } 141 | 142 | while (n >= sizeof(uint64_t)) { 143 | *(uint64_t*)s = v; 144 | 145 | s = (uint8_t*)s + sizeof(uint64_t); 146 | n -= sizeof(uint64_t); 147 | } 148 | #else 149 | uint32_t v; 150 | 151 | v = 0; 152 | 153 | for (unsigned int i = 0; i < sizeof(uint32_t); i++) { 154 | v <<= 8; 155 | v |= c & 0xff; 156 | } 157 | 158 | while (n >= sizeof(uint32_t)) { 159 | *(uint32_t*)s = v; 160 | 161 | s = (uint8_t*)s + sizeof(uint32_t); 162 | n -= sizeof(uint32_t); 163 | } 164 | #endif 165 | 166 | while (n > 0) { 167 | *(uint8_t*)s = c; 168 | 169 | s = (uint8_t*)s + 1; 170 | n--; 171 | } 172 | 173 | return orig_s; 174 | } 175 | 176 | char* strcpy(char* dest, const char* src) { 177 | char* orig_dest = dest; 178 | 179 | while (*src != 0) { 180 | *dest = *src; 181 | src++; 182 | dest++; 183 | } 184 | 185 | *dest = 0; 186 | 187 | return orig_dest; 188 | } 189 | 190 | EFI_STATUS utf8_to_utf16(wchar_t* dest, unsigned int dest_max, unsigned int* dest_len, const char* src, unsigned int src_len) { 191 | EFI_STATUS Status = EFI_SUCCESS; 192 | uint8_t* in = (uint8_t*)src; 193 | uint16_t* out = (uint16_t*)dest; 194 | unsigned int needed = 0, left = dest_max / sizeof(uint16_t); 195 | 196 | for (unsigned int i = 0; i < src_len; i++) { 197 | uint32_t cp; 198 | 199 | if (!(in[i] & 0x80)) 200 | cp = in[i]; 201 | else if ((in[i] & 0xe0) == 0xc0) { 202 | if (i == src_len - 1 || (in[i+1] & 0xc0) != 0x80) { 203 | cp = 0xfffd; 204 | Status = EFI_INVALID_PARAMETER; 205 | } else { 206 | cp = ((in[i] & 0x1f) << 6) | (in[i+1] & 0x3f); 207 | i++; 208 | } 209 | } else if ((in[i] & 0xf0) == 0xe0) { 210 | if (i >= src_len - 2 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80) { 211 | cp = 0xfffd; 212 | Status = EFI_INVALID_PARAMETER; 213 | } else { 214 | cp = ((in[i] & 0xf) << 12) | ((in[i+1] & 0x3f) << 6) | (in[i+2] & 0x3f); 215 | i += 2; 216 | } 217 | } else if ((in[i] & 0xf8) == 0xf0) { 218 | if (i >= src_len - 3 || (in[i+1] & 0xc0) != 0x80 || (in[i+2] & 0xc0) != 0x80 || (in[i+3] & 0xc0) != 0x80) { 219 | cp = 0xfffd; 220 | Status = EFI_INVALID_PARAMETER; 221 | } else { 222 | cp = ((in[i] & 0x7) << 18) | ((in[i+1] & 0x3f) << 12) | ((in[i+2] & 0x3f) << 6) | (in[i+3] & 0x3f); 223 | i += 3; 224 | } 225 | } else { 226 | cp = 0xfffd; 227 | Status = EFI_INVALID_PARAMETER; 228 | } 229 | 230 | if (cp > 0x10ffff) { 231 | cp = 0xfffd; 232 | Status = EFI_INVALID_PARAMETER; 233 | } 234 | 235 | if (dest) { 236 | if (cp <= 0xffff) { 237 | if (left < 1) 238 | return EFI_BUFFER_TOO_SMALL; 239 | 240 | *out = (uint16_t)cp; 241 | out++; 242 | 243 | left--; 244 | } else { 245 | if (left < 2) 246 | return EFI_BUFFER_TOO_SMALL; 247 | 248 | cp -= 0x10000; 249 | 250 | *out = 0xd800 | ((cp & 0xffc00) >> 10); 251 | out++; 252 | 253 | *out = 0xdc00 | (cp & 0x3ff); 254 | out++; 255 | 256 | left -= 2; 257 | } 258 | } 259 | 260 | if (cp <= 0xffff) 261 | needed += sizeof(uint16_t); 262 | else 263 | needed += 2 * sizeof(uint16_t); 264 | } 265 | 266 | if (dest_len) 267 | *dest_len = needed; 268 | 269 | return Status; 270 | } 271 | 272 | EFI_STATUS utf16_to_utf8(char* dest, unsigned int dest_max, unsigned int* dest_len, const wchar_t* src, unsigned int src_len) { 273 | EFI_STATUS Status = EFI_SUCCESS; 274 | uint16_t* in = (uint16_t*)src; 275 | uint8_t* out = (uint8_t*)dest; 276 | unsigned int in_len = src_len / sizeof(uint16_t); 277 | unsigned int needed = 0, left = dest_max; 278 | 279 | for (unsigned int i = 0; i < in_len; i++) { 280 | uint32_t cp = *in; 281 | in++; 282 | 283 | if ((cp & 0xfc00) == 0xd800) { 284 | if (i == in_len - 1 || (*in & 0xfc00) != 0xdc00) { 285 | cp = 0xfffd; 286 | Status = EFI_INVALID_PARAMETER; 287 | } else { 288 | cp = (cp & 0x3ff) << 10; 289 | cp |= *in & 0x3ff; 290 | cp += 0x10000; 291 | 292 | in++; 293 | i++; 294 | } 295 | } else if ((cp & 0xfc00) == 0xdc00) { 296 | cp = 0xfffd; 297 | Status = EFI_INVALID_PARAMETER; 298 | } 299 | 300 | if (cp > 0x10ffff) { 301 | cp = 0xfffd; 302 | Status = EFI_INVALID_PARAMETER; 303 | } 304 | 305 | if (dest) { 306 | if (cp < 0x80) { 307 | if (left < 1) 308 | return EFI_BUFFER_TOO_SMALL; 309 | 310 | *out = (uint8_t)cp; 311 | out++; 312 | 313 | left--; 314 | } else if (cp < 0x800) { 315 | if (left < 2) 316 | return EFI_BUFFER_TOO_SMALL; 317 | 318 | *out = 0xc0 | ((cp & 0x7c0) >> 6); 319 | out++; 320 | 321 | *out = 0x80 | (cp & 0x3f); 322 | out++; 323 | 324 | left -= 2; 325 | } else if (cp < 0x10000) { 326 | if (left < 3) 327 | return EFI_BUFFER_TOO_SMALL; 328 | 329 | *out = 0xe0 | ((cp & 0xf000) >> 12); 330 | out++; 331 | 332 | *out = 0x80 | ((cp & 0xfc0) >> 6); 333 | out++; 334 | 335 | *out = 0x80 | (cp & 0x3f); 336 | out++; 337 | 338 | left -= 3; 339 | } else { 340 | if (left < 4) 341 | return EFI_BUFFER_TOO_SMALL; 342 | 343 | *out = 0xf0 | ((cp & 0x1c0000) >> 18); 344 | out++; 345 | 346 | *out = 0x80 | ((cp & 0x3f000) >> 12); 347 | out++; 348 | 349 | *out = 0x80 | ((cp & 0xfc0) >> 6); 350 | out++; 351 | 352 | *out = 0x80 | (cp & 0x3f); 353 | out++; 354 | 355 | left -= 4; 356 | } 357 | } 358 | 359 | if (cp < 0x80) 360 | needed++; 361 | else if (cp < 0x800) 362 | needed += 2; 363 | else if (cp < 0x10000) 364 | needed += 3; 365 | else 366 | needed += 4; 367 | } 368 | 369 | if (dest_len) 370 | *dest_len = needed; 371 | 372 | return Status; 373 | } 374 | 375 | char* stpcpy(char* dest, const char* src) { 376 | while (*src != 0) { 377 | *dest = *src; 378 | dest++; 379 | src++; 380 | } 381 | 382 | *dest = 0; 383 | 384 | return dest; 385 | } 386 | 387 | char* hex_to_str(char* s, uint64_t v) { 388 | char *end, *p; 389 | 390 | if (v == 0) { 391 | *s = '0'; 392 | s++; 393 | 394 | *s = 0; 395 | return s; 396 | } 397 | 398 | end = s; 399 | 400 | { 401 | uint64_t n = v; 402 | 403 | while (n != 0) { 404 | end++; 405 | n >>= 4; 406 | } 407 | } 408 | 409 | *end = 0; 410 | 411 | p = end; 412 | 413 | while (v != 0) { 414 | p = &p[-1]; 415 | 416 | if ((v & 0xf) >= 10) 417 | *p = (v & 0xf) - 10 + 'a'; 418 | else 419 | *p = (v & 0xf) + '0'; 420 | 421 | v >>= 4; 422 | } 423 | 424 | return end; 425 | } 426 | 427 | char* dec_to_str(char* s, uint64_t v) { 428 | char *end, *p; 429 | 430 | if (v == 0) { 431 | *s = '0'; 432 | s++; 433 | 434 | *s = 0; 435 | return s; 436 | } 437 | 438 | end = s; 439 | 440 | { 441 | uint64_t n = v; 442 | 443 | while (n != 0) { 444 | end++; 445 | n /= 10; 446 | } 447 | } 448 | 449 | *end = 0; 450 | 451 | p = end; 452 | 453 | while (v != 0) { 454 | p = &p[-1]; 455 | *p = (v % 10) + '0'; 456 | 457 | v /= 10; 458 | } 459 | 460 | return end; 461 | } 462 | 463 | const char* error_string(EFI_STATUS Status) { 464 | switch (Status) { 465 | case EFI_SUCCESS: 466 | return "EFI_SUCCESS"; 467 | 468 | case EFI_LOAD_ERROR: 469 | return "EFI_LOAD_ERROR"; 470 | 471 | case EFI_INVALID_PARAMETER: 472 | return "EFI_INVALID_PARAMETER"; 473 | 474 | case EFI_UNSUPPORTED: 475 | return "EFI_UNSUPPORTED"; 476 | 477 | case EFI_BAD_BUFFER_SIZE: 478 | return "EFI_BAD_BUFFER_SIZE"; 479 | 480 | case EFI_BUFFER_TOO_SMALL: 481 | return "EFI_BUFFER_TOO_SMALL"; 482 | 483 | case EFI_NOT_READY: 484 | return "EFI_NOT_READY"; 485 | 486 | case EFI_DEVICE_ERROR: 487 | return "EFI_DEVICE_ERROR"; 488 | 489 | case EFI_WRITE_PROTECTED: 490 | return "EFI_WRITE_PROTECTED"; 491 | 492 | case EFI_OUT_OF_RESOURCES: 493 | return "EFI_OUT_OF_RESOURCES"; 494 | 495 | case EFI_VOLUME_CORRUPTED: 496 | return "EFI_VOLUME_CORRUPTED"; 497 | 498 | case EFI_VOLUME_FULL: 499 | return "EFI_VOLUME_FULL"; 500 | 501 | case EFI_NO_MEDIA: 502 | return "EFI_NO_MEDIA"; 503 | 504 | case EFI_MEDIA_CHANGED: 505 | return "EFI_MEDIA_CHANGED"; 506 | 507 | case EFI_NOT_FOUND: 508 | return "EFI_NOT_FOUND"; 509 | 510 | case EFI_ACCESS_DENIED: 511 | return "EFI_ACCESS_DENIED"; 512 | 513 | case EFI_NO_RESPONSE: 514 | return "EFI_NO_RESPONSE"; 515 | 516 | case EFI_NO_MAPPING: 517 | return "EFI_NO_MAPPING"; 518 | 519 | case EFI_TIMEOUT: 520 | return "EFI_TIMEOUT"; 521 | 522 | case EFI_NOT_STARTED: 523 | return "EFI_NOT_STARTED"; 524 | 525 | case EFI_ALREADY_STARTED: 526 | return "EFI_ALREADY_STARTED"; 527 | 528 | case EFI_ABORTED: 529 | return "EFI_ABORTED"; 530 | 531 | case EFI_ICMP_ERROR: 532 | return "EFI_ICMP_ERROR"; 533 | 534 | case EFI_TFTP_ERROR: 535 | return "EFI_TFTP_ERROR"; 536 | 537 | case EFI_PROTOCOL_ERROR: 538 | return "EFI_PROTOCOL_ERROR"; 539 | 540 | case EFI_INCOMPATIBLE_VERSION: 541 | return "EFI_INCOMPATIBLE_VERSION"; 542 | 543 | case EFI_SECURITY_VIOLATION: 544 | return "EFI_SECURITY_VIOLATION"; 545 | 546 | case EFI_CRC_ERROR: 547 | return "EFI_CRC_ERROR"; 548 | 549 | case EFI_END_OF_MEDIA: 550 | return "EFI_END_OF_MEDIA"; 551 | 552 | case EFI_END_OF_FILE: 553 | return "EFI_END_OF_FILE"; 554 | 555 | case EFI_INVALID_LANGUAGE: 556 | return "EFI_INVALID_LANGUAGE"; 557 | 558 | case EFI_COMPROMISED_DATA: 559 | return "EFI_COMPROMISED_DATA"; 560 | 561 | default: 562 | return "(unknown error)"; 563 | } 564 | } 565 | 566 | void memmove(void* dest, const void* src, size_t n) { 567 | while (n > 0) { 568 | *(uint8_t*)dest = *(uint8_t*)src; 569 | 570 | dest = (uint8_t*)dest + 1; 571 | src = (uint8_t*)src + 1; 572 | 573 | n--; 574 | } 575 | } 576 | -------------------------------------------------------------------------------- /src/misc.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | char* stpcpy(char* dest, const char* src); 30 | char* hex_to_str(char* s, uint64_t v); 31 | char* dec_to_str(char* s, uint64_t v); 32 | EFI_STATUS utf8_to_utf16(wchar_t* dest, unsigned int dest_max, unsigned int* dest_len, const char* src, unsigned int src_len); 33 | EFI_STATUS utf16_to_utf8(char* dest, unsigned int dest_max, unsigned int* dest_len, const wchar_t* src, unsigned int src_len); 34 | const char* error_string(EFI_STATUS Status); 35 | -------------------------------------------------------------------------------- /src/quibbleproto.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Mark Harmstone 2020 2 | * 3 | * This file is part of btrfs-efi. 4 | * 5 | * btrfs-efi is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU Lesser General Public Licence as published by 7 | * the Free Software Foundation, either version 3 of the Licence, or 8 | * (at your option) any later version. 9 | * 10 | * btrfs-efi is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU Lesser General Public Licence for more details. 14 | * 15 | * You should have received a copy of the GNU Lesser General Public Licence 16 | * along with btrfs-efi. If not, see . */ 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define EFI_QUIBBLE_PROTOCOL_GUID { 0x98BCC8FF, 0xD212, 0x4B09, {0x84, 0x0C, 0x43, 0x19, 0xAD, 0x2E, 0xD3, 0x6A } } 26 | 27 | typedef struct _EFI_QUIBBLE_PROTOCOL EFI_QUIBBLE_PROTOCOL; 28 | 29 | typedef EFI_STATUS (EFIAPI* EFI_QUIBBLE_GET_ARC_NAME) ( 30 | IN EFI_QUIBBLE_PROTOCOL* This, 31 | OUT char* ArcName, 32 | IN OUT UINTN* ArcNameLen 33 | ); 34 | 35 | typedef EFI_STATUS (EFIAPI* EFI_QUIBBLE_GET_WINDOWS_DRIVER_NAME) ( 36 | IN EFI_QUIBBLE_PROTOCOL* This, 37 | OUT CHAR16* DriverName, 38 | IN OUT UINTN* DriverNameLen 39 | ); 40 | 41 | typedef struct _EFI_QUIBBLE_PROTOCOL { 42 | EFI_QUIBBLE_GET_ARC_NAME GetArcName; 43 | EFI_QUIBBLE_GET_WINDOWS_DRIVER_NAME GetWindowsDriverName; 44 | } EFI_QUIBBLE_PROTOCOL; 45 | 46 | #define EFI_OPEN_SUBVOL_GUID { 0x5861E4D5, 0xC7F1, 0x4932, {0xA0, 0x81, 0xF2, 0x2A, 0xAE, 0x8A, 0x82, 0x98 } } 47 | 48 | typedef struct _EFI_OPEN_SUBVOL_PROTOCOL EFI_OPEN_SUBVOL_PROTOCOL; 49 | 50 | typedef EFI_STATUS (EFIAPI* EFI_OPEN_SUBVOL_FUNC) ( 51 | IN EFI_OPEN_SUBVOL_PROTOCOL* This, 52 | IN UINT64 Subvol, 53 | OUT EFI_FILE_HANDLE* File 54 | ); 55 | 56 | typedef struct _EFI_OPEN_SUBVOL_PROTOCOL { 57 | EFI_OPEN_SUBVOL_FUNC OpenSubvol; 58 | } EFI_OPEN_SUBVOL_PROTOCOL; 59 | 60 | #define EFI_QUIBBLE_INFO_PROTOCOL_GUID { 0x89498E00, 0xAE8F, 0x4B23, {0x86, 0x11, 0x71, 0x2A, 0xE1, 0x2F, 0xC8, 0xD9 } } 61 | 62 | typedef void (EFIAPI* EFI_QUIBBLE_INFO_PRINT) ( 63 | IN const char* s 64 | ); 65 | 66 | typedef struct _EFI_QUIBBLE_INFO_PROTOCOL { 67 | EFI_QUIBBLE_INFO_PRINT Print; 68 | } EFI_QUIBBLE_INFO_PROTOCOL; 69 | -------------------------------------------------------------------------------- /src/zstd-shim.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static void* ZSTD_malloc(size_t size) { 5 | return NULL; 6 | } 7 | 8 | static void* ZSTD_calloc(size_t nmemb, size_t size) { 9 | return NULL; 10 | } 11 | 12 | static void ZSTD_free(void* ptr) { 13 | } 14 | 15 | #ifdef _MSC_VER 16 | 17 | #include 18 | 19 | #pragma intrinsic(_byteswap_uint64) 20 | #pragma intrinsic(_byteswap_ulong) 21 | #pragma intrinsic(_rotl) 22 | #pragma intrinsic(_rotl64) 23 | 24 | #endif 25 | --------------------------------------------------------------------------------