First version of apfs-fuse.

This commit is contained in:
Simon Gander
2017-10-14 00:45:32 +02:00
commit dc6caad927
48 changed files with 7986 additions and 0 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

270
.gitignore vendored Normal file
View File

@@ -0,0 +1,270 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Eigene Files
Data/
# Linux
build/
.kdev4/
data/

4
Apfs.kdev4 Normal file
View File

@@ -0,0 +1,4 @@
[Project]
CreatedFrom=CMakeLists.txt
Manager=KDevCMakeManager
Name=Apfs

61
Apfs.sln Normal file
View File

@@ -0,0 +1,61 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.16
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApfsDump", "ApfsDump\ApfsDump.vcxproj", "{636F1F23-E335-4199-9359-2EEB64D32827}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ApfsLib", "ApfsLib\ApfsLib.vcxproj", "{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test\Test.vcxproj", "{51D229BD-E3DC-4490-AA71-634454739287}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tools", "Tools\Tools.vcxproj", "{E27D174E-1C74-436A-95C5-4801024A1964}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{636F1F23-E335-4199-9359-2EEB64D32827}.Debug|x64.ActiveCfg = Debug|x64
{636F1F23-E335-4199-9359-2EEB64D32827}.Debug|x64.Build.0 = Debug|x64
{636F1F23-E335-4199-9359-2EEB64D32827}.Debug|x86.ActiveCfg = Debug|Win32
{636F1F23-E335-4199-9359-2EEB64D32827}.Debug|x86.Build.0 = Debug|Win32
{636F1F23-E335-4199-9359-2EEB64D32827}.Release|x64.ActiveCfg = Release|x64
{636F1F23-E335-4199-9359-2EEB64D32827}.Release|x64.Build.0 = Release|x64
{636F1F23-E335-4199-9359-2EEB64D32827}.Release|x86.ActiveCfg = Release|Win32
{636F1F23-E335-4199-9359-2EEB64D32827}.Release|x86.Build.0 = Release|Win32
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Debug|x64.ActiveCfg = Debug|x64
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Debug|x64.Build.0 = Debug|x64
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Debug|x86.ActiveCfg = Debug|Win32
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Debug|x86.Build.0 = Debug|Win32
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Release|x64.ActiveCfg = Release|x64
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Release|x64.Build.0 = Release|x64
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Release|x86.ActiveCfg = Release|Win32
{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}.Release|x86.Build.0 = Release|Win32
{51D229BD-E3DC-4490-AA71-634454739287}.Debug|x64.ActiveCfg = Debug|x64
{51D229BD-E3DC-4490-AA71-634454739287}.Debug|x64.Build.0 = Debug|x64
{51D229BD-E3DC-4490-AA71-634454739287}.Debug|x86.ActiveCfg = Debug|Win32
{51D229BD-E3DC-4490-AA71-634454739287}.Debug|x86.Build.0 = Debug|Win32
{51D229BD-E3DC-4490-AA71-634454739287}.Release|x64.ActiveCfg = Release|x64
{51D229BD-E3DC-4490-AA71-634454739287}.Release|x64.Build.0 = Release|x64
{51D229BD-E3DC-4490-AA71-634454739287}.Release|x86.ActiveCfg = Release|Win32
{51D229BD-E3DC-4490-AA71-634454739287}.Release|x86.Build.0 = Release|Win32
{E27D174E-1C74-436A-95C5-4801024A1964}.Debug|x64.ActiveCfg = Debug|x64
{E27D174E-1C74-436A-95C5-4801024A1964}.Debug|x64.Build.0 = Debug|x64
{E27D174E-1C74-436A-95C5-4801024A1964}.Debug|x86.ActiveCfg = Debug|Win32
{E27D174E-1C74-436A-95C5-4801024A1964}.Debug|x86.Build.0 = Debug|Win32
{E27D174E-1C74-436A-95C5-4801024A1964}.Release|x64.ActiveCfg = Release|x64
{E27D174E-1C74-436A-95C5-4801024A1964}.Release|x64.Build.0 = Release|x64
{E27D174E-1C74-436A-95C5-4801024A1964}.Release|x86.ActiveCfg = Release|Win32
{E27D174E-1C74-436A-95C5-4801024A1964}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {88E05318-DE9F-47DE-BBCB-30C8F6B96898}
EndGlobalSection
EndGlobal

185
ApfsDump/Apfs.cpp Normal file
View File

@@ -0,0 +1,185 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <fstream>
#include <iostream>
#include <iomanip>
#include <ApfsLib/Util.h>
#include <ApfsLib/DiskStruct.h>
#include <ApfsLib/BlockDumper.h>
#undef RAW_VERBOSE
constexpr size_t BLOCKSIZE = 0x1000;
void DumpBlockTrunc(std::ostream &os, const byte_t *data)
{
unsigned int sz = BLOCKSIZE - 1;
while (sz > 0 && data[sz] == 0)
sz--;
sz = (sz + 0x10) & 0xFFFFFFF0;
DumpHex(os, data, sz);
}
void MapBlocks(std::ostream &os, std::istream &dev)
{
using namespace std;
uint64_t size;
uint64_t blk_nr;
uint8_t block[BLOCKSIZE];
const APFS_BlockHeader * const blk = reinterpret_cast<const APFS_BlockHeader *>(block);
const APFS_TableHeader * const tbl = reinterpret_cast<const APFS_TableHeader *>(block + sizeof(APFS_BlockHeader));
bool last_was_used = false;
os << hex << uppercase << setfill('0');
os << "[Block] | Node ID | Version | Type | Subtype | Flgs | Levl | Entries | Description" << endl;
os << "---------+----------+----------+----------+----------+------+------+----------+---------------------------------" << endl;
dev.seekg(0, std::ios::end);
size = dev.tellg();
dev.seekg(0);
size /= BLOCKSIZE;
for (blk_nr = 0; blk_nr < size; blk_nr++)
{
dev.read(reinterpret_cast<char *>(block), BLOCKSIZE);
if (IsEmptyBlock(block, BLOCKSIZE))
{
if (last_was_used)
os << "---------+----------+----------+----------+----------+------+------+----------+ Empty" << endl;
last_was_used = false;
continue;
}
if (VerifyBlock(block, BLOCKSIZE))
{
os << setw(8) << blk_nr << " | ";
os << setw(8) << blk->node_id << " | ";
os << setw(8) << blk->version << " | ";
os << setw(8) << blk->type << " | ";
os << setw(8) << blk->subtype << " | ";
os << setw(4) << tbl->page << " | ";
os << setw(4) << tbl->level << " | ";
os << setw(8) << tbl->entries_cnt << " | ";
os << BlockDumper::GetNodeType(blk->type, blk->subtype);
if ((blk->type & 0xFFFFFFF) == 2)
os << " [Root]";
os << endl;
last_was_used = true;
}
else
{
os << setw(8) << blk_nr;
os << " | | | | | | | | Data" << endl;
last_was_used = true;
}
}
os << endl;
}
void ScanBlocks(std::ostream &os, std::istream &dev)
{
BlockDumper bd(os, BLOCKSIZE);
uint64_t size;
uint64_t blk_nr;
uint8_t block[BLOCKSIZE];
dev.seekg(0, std::ios::end);
size = dev.tellg();
dev.seekg(0);
size /= BLOCKSIZE;
for (blk_nr = 0; blk_nr < size; blk_nr++)
{
dev.read(reinterpret_cast<char *>(block), BLOCKSIZE);
if (IsEmptyBlock(block, BLOCKSIZE))
continue;
if (VerifyBlock(block, BLOCKSIZE))
bd.DumpNode(block, blk_nr);
else
{
#if 0
os << std::hex << std::setw(16) << blk_nr << std::endl;
DumpBlockTrunc(os, block);
os << std::endl;
os << "========================================================================================================================" << std::endl;
os << std::endl;
#endif
}
}
}
int main(int argc, const char *argv[])
{
if (argc < 3)
{
std::cerr << "Syntax: Apfs file.img output.txt [map.txt]" << std::endl;
return 1;
}
std::ifstream dev;
std::ofstream os;
dev.open(argv[1], std::ios::binary);
if (!dev.is_open())
{
std::cerr << "File " << argv[1] << " not found." << std::endl;
return 2;
}
if (argc >= 3)
{
os.open(argv[3]);
if (!os.is_open())
{
std::cerr << "Could not open output file " << argv[3] << std::endl;
return 3;
}
MapBlocks(os, dev);
os.close();
}
os.open(argv[2]);
if (!os.is_open())
{
std::cerr << "Could not open output file " << argv[2] << std::endl;
return 3;
}
ScanBlocks(os, dev);
dev.close();
os.close();
return 0;
}

163
ApfsDump/ApfsDump.vcxproj Normal file
View File

@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{636F1F23-E335-4199-9359-2EEB64D32827}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Apfs</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Text Include="Doku.txt" />
<Text Include="ReadMe.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Apfs.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Apfs.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApfsLib\ApfsLib.vcxproj">
<Project>{ab308f5e-e5d3-4231-b619-cab09eb5b81a}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Quelldateien">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Headerdateien">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Ressourcendateien">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<Text Include="ReadMe.txt" />
<Text Include="Doku.txt" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Apfs.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Apfs.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
</Project>

2
ApfsDump/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
add_executable(apfs-dump Apfs.cpp)
target_link_libraries(apfs-dump ApfsLib icuuc)

169
ApfsLib/ApfsContainer.cpp Normal file
View File

@@ -0,0 +1,169 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <iostream>
#include "ApfsContainer.h"
#include "ApfsVolume.h"
#include "Util.h"
#include "BlockDumper.h"
#include "Global.h"
int g_debug = 0;
ApfsContainer::ApfsContainer(Disk &disk, uint64_t start, uint64_t len) :
m_disk(disk),
m_part_start(start),
m_part_len(len),
m_nodemap_vol(*this),
m_nidmap_bt(*this),
m_oldmgr_bt(*this),
m_oldvol_bt(*this)
{
}
ApfsContainer::~ApfsContainer()
{
}
bool ApfsContainer::Init()
{
std::vector<byte_t> blk;
blk.resize(0x1000);
if (!m_disk.Read(blk.data(), m_part_start, 0x1000))
return false;
memcpy(&m_sb, blk.data(), sizeof(APFS_Superblock_NXSB));
if (m_sb.signature != 0x4253584E)
return false;
if (m_sb.block_size != 0x1000)
{
blk.resize(m_sb.block_size);
m_disk.Read(blk.data(), m_part_start, blk.size());
}
if (!VerifyBlock(blk.data(), blk.size()))
return false;
memcpy(&m_sb, blk.data(), sizeof(APFS_Superblock_NXSB));
m_nodemap_vol.Init(m_sb.blockid_volhdr, m_sb.hdr.version);
return true;
}
ApfsVolume * ApfsContainer::GetVolume(int index)
{
ApfsVolume *vol = nullptr;
uint64_t nodeid;
uint64_t blkid;
if (index >= 100)
return nullptr;
nodeid = m_sb.nodeid_apsb[index];
if (nodeid == 0)
return nullptr;
blkid = m_nodemap_vol.GetBlockID(nodeid, m_sb.hdr.version);
// std::cout << std::hex << "Loading Volume " << index << ", nodeid = " << nodeid << ", version = " << m_sb.hdr.version << ", blkid = " << blkid << std::endl;
if (blkid == 0)
return nullptr;
vol = new ApfsVolume(*this);
vol->Init(blkid);
return vol;
}
int ApfsContainer::GetVolumeCnt() const
{
int k;
for (k = 0; k < 100; k++)
{
if (m_sb.nodeid_apsb[k] == 0)
break;
}
return k;
}
bool ApfsContainer::ReadBlocks(byte_t * data, uint64_t blkid, uint64_t blkcnt) const
{
uint64_t offs;
uint64_t size;
if ((blkid + blkcnt) > m_sb.block_count)
return false;
offs = m_sb.block_size * blkid;
size = m_sb.block_size * blkcnt;
return m_disk.Read(data, offs, size);
}
bool ApfsContainer::ReadAndVerifyHeaderBlock(byte_t * data, uint64_t blkid) const
{
if (!ReadBlocks(data, blkid))
return false;
if (!VerifyBlock(data, m_sb.block_size))
return false;
return true;
}
void ApfsContainer::dump(BlockDumper& bd)
{
std::vector<byte_t> blk;
uint64_t blkid;
blk.resize(GetBlocksize());
ReadAndVerifyHeaderBlock(blk.data(), 0);
bd.DumpNode(blk.data(), 0);
for (blkid = m_sb.blockid_sb_area_start; blkid < (m_sb.blockid_sb_area_start + m_sb.sb_area_cnt); blkid++)
{
ReadAndVerifyHeaderBlock(blk.data(), blkid);
bd.DumpNode(blk.data(), blkid);
}
#if 0
for (blkid = m_sb.blockid_spaceman_area_start; blkid < (m_sb.blockid_spaceman_area_start + m_sb.spaceman_area_cnt); blkid++)
{
ReadAndVerifyHeaderBlock(blk.data(), blkid);
bd.DumpNode(blk.data(), blkid);
}
#endif
m_nodemap_vol.dump(bd);
// m_nidmap_bt.dump(bd);
// m_oldmgr_bt.dump(bd);
// m_oldvol_bt.dump(bd);
}

66
ApfsLib/ApfsContainer.h Normal file
View File

@@ -0,0 +1,66 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "Global.h"
#include "BTree.h"
#include "DiskStruct.h"
#include "Disk.h"
#include "ApfsNodeMapperBTree.h"
#include <cstdint>
class ApfsVolume;
class BlockDumper;
class ApfsContainer
{
public:
ApfsContainer(Disk &disk, uint64_t start, uint64_t len);
~ApfsContainer();
bool Init();
ApfsVolume *GetVolume(int index);
int GetVolumeCnt() const;
bool ReadBlocks(byte_t *data, uint64_t blkid, uint64_t blkcnt = 1) const;
bool ReadAndVerifyHeaderBlock(byte_t *data, uint64_t blkid) const;
uint32_t GetBlocksize() const { return m_sb.block_size; }
void dump(BlockDumper& bd);
private:
Disk &m_disk;
const uint64_t m_part_start;
const uint64_t m_part_len;
APFS_Superblock_NXSB m_sb;
APFS_Block_8_5_Spaceman m_spaceman_hdr;
// Block_8_11 ?
ApfsNodeMapperBTree m_nodemap_vol;
BTree m_nidmap_bt; // 4_2/B
BTree m_oldmgr_bt; // 8_2/9
BTree m_oldvol_bt; // 8_2/9
};

498
ApfsLib/ApfsDir.cpp Normal file
View File

@@ -0,0 +1,498 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <cassert>
#include <cstring>
#include "ApfsDir.h"
#include "ApfsVolume.h"
#include "ApfsContainer.h"
#include "BTree.h"
#include "Util.h"
// static uint32_t g_txt_fmt; // Hack
#ifndef _MSC_VER
template<size_t L>
void strcpy_s(char (&dst)[L], const char *src)
{
strncpy(dst, src, L);
}
#endif
ApfsDir::Inode::Inode()
{
id = 0;
memset(&ino, 0, sizeof(ino));
memset(&sizes, 0, sizeof(sizes));
unk_param = 0;
}
ApfsDir::Inode::Inode(const ApfsDir::Inode& o)
{
id = o.id;
ino = o.ino;
name = o.name;
sizes = o.sizes;
unk_param = o.unk_param;
}
ApfsDir::Name::Name()
{
parent_id = 0;
hash = 0;
inode_id = 0;
timestamp = 0;
}
ApfsDir::Name::Name(const ApfsDir::Name& o)
{
parent_id = o.parent_id;
hash = o.hash;
name = o.name;
inode_id = o.inode_id;
timestamp = o.timestamp;
}
ApfsDir::ApfsDir(ApfsVolume &vol) :
m_vol(vol),
m_bt(vol.getDirectory())
{
m_txt_fmt = vol.getTextFormat();
// m_bt.EnableDebugOutput();
}
ApfsDir::~ApfsDir()
{
}
bool ApfsDir::GetInode(ApfsDir::Inode& res, uint64_t inode)
{
BTreeEntry bte;
uint64_t key;
bool rc;
key = inode | KeyType_Object;
rc = m_bt.Lookup(bte, &key, sizeof(uint64_t), CompareStdDirKey, this, true);
if (!rc || (bte.val == nullptr))
return false;
const uint8_t *idata = reinterpret_cast<const uint8_t *>(bte.val);
const APFS_Inode *obj = reinterpret_cast<const APFS_Inode *>(bte.val);
const APFS_InodeEntry *ie = reinterpret_cast<const APFS_InodeEntry *>(idata + sizeof(APFS_Inode));
res.id = inode;
res.ino = *obj;
res.name.clear();
memset(&res.sizes, 0, sizeof(res.sizes));
res.unk_param = 0;
uint16_t entry_base = sizeof(APFS_Inode) + (obj->entries_cnt * sizeof(APFS_InodeEntry));
uint16_t k;
for (k = 0; k < obj->entries_cnt; k++)
{
switch (ie[k].type)
{
case 0x0204:
res.name = reinterpret_cast<const char *>(idata + entry_base);
break;
case 0x2008:
res.sizes = *reinterpret_cast<const APFS_Inode_Sizes *>(idata + entry_base);
break;
case 0x280D:
res.unk_param = *reinterpret_cast<const uint64_t *>(idata + entry_base);
break;
default:
std::cerr << std::hex << "WARNING!!!: Unknown Inode Attribute " << std::setw(4) << ie[k].type << " at inode " << inode << std::endl;
break;
}
entry_base += ((ie[k].len + 7) & 0xFFF8);
}
return true;
}
bool ApfsDir::ListDirectory(std::vector<Name> &dir, uint64_t inode)
{
BTreeIterator it;
BTreeEntry res;
bool rc;
uint64_t skey;
const APFS_Name *vdata;
const uint8_t *kdata;
skey = inode | KeyType_Name;
dir.clear();
if (m_txt_fmt & 9)
{
APFS_Key_Name key;
key.parent_id = skey;
key.hash = 0;
key.name[0] = 0;
rc = m_bt.GetIterator(it, &key, 12, CompareStdDirKey, this);
}
else
{
APFS_Key_Name_Old key;
key.parent_id = skey;
key.name_len = 0;
key.name[0] = 0;
rc = m_bt.GetIterator(it, &key, 10, CompareStdDirKey, this);
}
if (!rc)
return false;
for (;;)
{
Name e;
rc = it.GetEntry(res);
if (!rc)
break;
kdata = reinterpret_cast<const uint8_t *>(res.key);
e.parent_id = *reinterpret_cast<const uint64_t *>(kdata);
if (e.parent_id != skey)
break;
e.parent_id &= 0x0FFFFFFFFFFFFFFFULL;
if (m_txt_fmt != 0)
{
e.hash = *reinterpret_cast<const uint32_t *>(kdata + 8);
e.name = reinterpret_cast<const char *>(kdata + 12);
}
else
{
e.hash = 0;
e.name = reinterpret_cast<const char *>(res.key) + 10;
}
// assert(res.val_len == sizeof(APFS_Name));
vdata = reinterpret_cast<const APFS_Name *>(res.val);
e.inode_id = vdata->id;
e.timestamp = vdata->timestamp;
dir.push_back(e);
it.next();
}
return true;
}
bool ApfsDir::LookupName(ApfsDir::Name& res, uint64_t parent_id, const char* name)
{
bool rc;
BTreeEntry e;
res.parent_id = parent_id;
res.name = name;
if (m_txt_fmt & 9)
{
APFS_Key_Name key;
key.parent_id = parent_id | KeyType_Name;
key.hash = HashFilename(name, strlen(name) + 1, (m_txt_fmt & 0x09) == 0x01);
strcpy_s(key.name, name);
// printf("Lookup key: key=%016lX hash=%08X name=%s\n", key.parent_id, key.hash, key.name);
res.hash = key.hash;
rc = m_bt.Lookup(e, &key, 12 + (key.hash & 0x3FF), CompareStdDirKey, this, true);
}
else
{
APFS_Key_Name_Old key;
key.parent_id = parent_id | KeyType_Name;
key.name_len = strlen(name) + 1;
strcpy_s(key.name, name);
// printf("Lookup old key: key=%016lX nlen=%04X name=%s\n", key.parent_id, key.name_len, key.name);
res.hash = 0;
rc = m_bt.Lookup(e, &key, 10 + key.name_len, CompareStdDirKey, this, true);
}
if (!rc)
return false;
const APFS_Name *v = reinterpret_cast<const APFS_Name *>(e.val);
res.inode_id = v->id;
res.timestamp = v->timestamp;
// v->unk ?
return true;
}
bool ApfsDir::ReadFile(void* data, uint64_t inode, uint64_t offs, size_t size)
{
const ApfsContainer &cont = m_vol.getContainer();
BTreeEntry e;
APFS_Key_Extent key;
const APFS_Key_Extent *ext_key = nullptr;
const APFS_Extent *ext_val = nullptr;
bool rc;
assert((offs & 0xFFF) == 0);
assert((size & 0xFFF) == 0);
uint8_t *bdata = reinterpret_cast<uint8_t *>(data);
size_t cur_size;
unsigned idx;
while (size > 0)
{
key.inode = inode | KeyType_Extent;
key.offset = offs;
rc = m_bt.Lookup(e, &key, sizeof(key), CompareStdDirKey, this, false);
if (!rc)
return false;
ext_key = reinterpret_cast<const APFS_Key_Extent *>(e.key);
ext_val = reinterpret_cast<const APFS_Extent *>(e.val);
if (ext_key->inode != key.inode)
return false;
idx = (offs - ext_key->offset) >> 12;
cur_size = size;
if (((idx << 12) + cur_size) > ext_val->size)
cur_size = ext_val->size - (idx << 12);
if (cur_size == 0)
break; // Die Freuden von Fuse ...
if (ext_val->block != 0)
cont.ReadBlocks(bdata, ext_val->block + idx, cur_size >> 12);
else
memset(bdata, 0, cur_size);
bdata += cur_size;
offs += cur_size;
size -= cur_size;
// printf("ReadFile: offs=%016lX size=%016lX\n", offs, size);
}
return true;
}
bool ApfsDir::ListAttributes(std::vector<std::string>& names, uint64_t inode)
{
APFS_Key_Attribute skey;
const APFS_Key_Attribute *ekey;
BTreeIterator it;
BTreeEntry res;
bool rc;
skey.inode_key = inode | KeyType_Attribute;
skey.name_len = 0;
skey.name[0] = 0;
rc = m_bt.GetIterator(it, &skey, 10 + skey.name_len, CompareStdDirKey, this);
if (!rc)
return false;
for (;;)
{
rc = it.GetEntry(res);
if (!rc)
break;
ekey = reinterpret_cast<const APFS_Key_Attribute *>(res.key);
if (ekey->inode_key != skey.inode_key)
break;
names.push_back(ekey->name);
it.next();
}
return true;
}
bool ApfsDir::GetAttribute(std::vector<uint8_t>& data, uint64_t inode, const char* name)
{
APFS_Key_Attribute skey;
const APFS_Attribute *attr;
const APFS_AttributeLink *alnk = nullptr;
const uint8_t *adata;
BTreeEntry res;
bool rc;
skey.inode_key = inode | KeyType_Attribute;
skey.name_len = strlen(name) + 1;
strcpy_s(skey.name, name);
rc = m_bt.Lookup(res, &skey, 10 + skey.name_len, CompareStdDirKey, this, true);
if (!rc)
return false;
attr = reinterpret_cast<const APFS_Attribute *>(res.val);
adata = reinterpret_cast<const uint8_t *>(res.val) + sizeof(APFS_Attribute);
if (attr->type == 1)
{
assert(attr->size == 0x30);
alnk = reinterpret_cast<const APFS_AttributeLink *>(adata);
data.resize(alnk->size_on_disk);
ReadFile(data.data(), alnk->object_id, 0, data.size()); // Read must be multiple of 4K ...
data.resize(alnk->size);
}
else // if (attr->type == 2)
{
data.assign(adata, adata + attr->size);
}
return true;
}
int ApfsDir::CompareStdDirKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context)
{
// assert(skey_len == 8);
// assert(ekey_len == 8);
ApfsDir *dir = reinterpret_cast<ApfsDir *>(context);
uint64_t ks = *reinterpret_cast<const uint64_t *>(skey);
uint64_t ke = *reinterpret_cast<const uint64_t *>(ekey);
// std::cout << std::hex << std::uppercase << std::setfill('0');
ks = (ks << 4) | (ks >> 60);
ke = (ke << 4) | (ke >> 60);
// std::cout << std::setw(16) << ks << " : " << std::setw(16) << ke << std::endl;
if (ke < ks)
return -1;
if (ke > ks)
return 1;
if (skey_len > 8)
{
switch ((ks & 0xF) << 60)
{
case KeyType_Name:
if (dir->m_txt_fmt & 9)
{
const APFS_Key_Name *s = reinterpret_cast<const APFS_Key_Name *>(skey);
const APFS_Key_Name *e = reinterpret_cast<const APFS_Key_Name *>(ekey);
if ((e->hash & 0xFFFFFC00) < (s->hash & 0xFFFFFC00))
return -1;
if ((e->hash & 0xFFFFFC00) > (s->hash & 0xFFFFFC00))
return 1;
for (size_t k = 0; k < (e->hash & 0x3FF); k++)
{
if (e->name[k] < s->name[k])
return -1;
if (e->name[k] > s->name[k])
return 1;
}
}
else
{
const APFS_Key_Name_Old *s = reinterpret_cast<const APFS_Key_Name_Old *>(skey);
const APFS_Key_Name_Old *e = reinterpret_cast<const APFS_Key_Name_Old *>(ekey);
size_t cnt = std::min(e->name_len, s->name_len);
for (size_t k = 0; k < cnt; k++)
{
if (e->name[k] < s->name[k])
return -1;
if (e->name[k] > s->name[k])
return 1;
}
if (e->name_len < s->name_len)
return -1;
if (e->name_len > s->name_len)
return 1;
}
break;
case KeyType_Extent:
{
const APFS_Key_Extent *s = reinterpret_cast<const APFS_Key_Extent *>(skey);
const APFS_Key_Extent *e = reinterpret_cast<const APFS_Key_Extent *>(ekey);
assert(skey_len == sizeof(APFS_Key_Extent));
assert(ekey_len == sizeof(APFS_Key_Extent));
if (e->offset < s->offset)
return -1;
if (e->offset > s->offset)
return 1;
}
break;
case KeyType_Attribute:
{
const APFS_Key_Attribute *s = reinterpret_cast<const APFS_Key_Attribute *>(skey);
const APFS_Key_Attribute *e = reinterpret_cast<const APFS_Key_Attribute *>(ekey);
size_t cnt = std::max(e->name_len, s->name_len);
for (size_t k = 0; k < cnt; k++)
{
if (e->name[k] < s->name[k])
return -1;
if (e->name[k] > s->name[k])
return 1;
}
if (e->name_len < s->name_len)
return -1;
if (e->name_len > s->name_len)
return 1;
}
break;
}
}
return 0;
}

78
ApfsLib/ApfsDir.h Normal file
View File

@@ -0,0 +1,78 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
#include "DiskStruct.h"
class BTree;
class ApfsVolume;
class ApfsDir
{
public:
struct Inode
{
Inode();
Inode(const Inode &other);
uint64_t id;
APFS_Inode ino;
std::string name;
APFS_Inode_Sizes sizes;
uint64_t unk_param;
};
struct Name
{
Name();
Name(const Name &other);
uint64_t parent_id;
uint32_t hash;
std::string name;
uint64_t inode_id;
uint64_t timestamp;
// TBD
};
ApfsDir(ApfsVolume &vol);
~ApfsDir();
bool GetInode(Inode &res, uint64_t inode);
bool ListDirectory(std::vector<Name> &dir, uint64_t inode);
bool LookupName(Name &res, uint64_t parent_id, const char *name);
bool ReadFile(void *data, uint64_t inode, uint64_t offs, size_t size);
bool ListAttributes(std::vector<std::string> &names, uint64_t inode);
bool GetAttribute(std::vector<uint8_t> &data, uint64_t inode, const char *name);
private:
static int CompareStdDirKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
ApfsVolume &m_vol;
BTree &m_bt;
uint32_t m_txt_fmt;
};

170
ApfsLib/ApfsLib.vcxproj Normal file
View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{AB308F5E-E5D3-4231-B619-CAB09EB5B81A}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ApfsLib</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ApfsContainer.cpp" />
<ClCompile Include="ApfsDir.cpp" />
<ClCompile Include="ApfsNodeMapper.cpp" />
<ClCompile Include="ApfsNodeMapperBTree.cpp" />
<ClCompile Include="ApfsVolume.cpp" />
<ClCompile Include="BlockDumper.cpp" />
<ClCompile Include="BTree.cpp" />
<ClCompile Include="Crc32.cpp" />
<ClCompile Include="Disk.cpp" />
<ClCompile Include="Util.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ApfsContainer.h" />
<ClInclude Include="ApfsDir.h" />
<ClInclude Include="ApfsNodeMapper.h" />
<ClInclude Include="ApfsNodeMapperBTree.h" />
<ClInclude Include="ApfsVolume.h" />
<ClInclude Include="BlockDumper.h" />
<ClInclude Include="BTree.h" />
<ClInclude Include="Crc32.h" />
<ClInclude Include="Disk.h" />
<ClInclude Include="DiskStruct.h" />
<ClInclude Include="Global.h" />
<ClInclude Include="Util.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Quelldateien">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Headerdateien">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Ressourcendateien">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="ApfsContainer.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="BlockDumper.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="BTree.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Crc32.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Disk.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Util.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="ApfsVolume.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="ApfsNodeMapper.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="ApfsNodeMapperBTree.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="ApfsDir.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ApfsContainer.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="BlockDumper.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="BTree.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Crc32.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Disk.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="DiskStruct.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Global.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Util.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="ApfsVolume.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="ApfsNodeMapper.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="ApfsNodeMapperBTree.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="ApfsDir.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,29 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ApfsNodeMapper.h"
ApfsNodeMapper::ApfsNodeMapper()
{
}
ApfsNodeMapper::~ApfsNodeMapper()
{
}

31
ApfsLib/ApfsNodeMapper.h Normal file
View File

@@ -0,0 +1,31 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
class ApfsNodeMapper
{
public:
ApfsNodeMapper();
virtual ~ApfsNodeMapper();
virtual uint64_t GetBlockID(uint64_t nodeid, uint64_t version) = 0;
};

View File

@@ -0,0 +1,113 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring>
#include <iostream>
#include "ApfsNodeMapperBTree.h"
#include "ApfsContainer.h"
static int CompareNodeMapKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context)
{
(void)context;
assert(skey_len == sizeof(APFS_Key_B_NodeID_Map));
assert(ekey_len == sizeof(APFS_Key_B_NodeID_Map));
const APFS_Key_B_NodeID_Map *skey_map = reinterpret_cast<const APFS_Key_B_NodeID_Map *>(skey);
const APFS_Key_B_NodeID_Map *ekey_map = reinterpret_cast<const APFS_Key_B_NodeID_Map *>(ekey);
if (ekey_map->nodeid < skey_map->nodeid)
return -1;
if (ekey_map->nodeid > skey_map->nodeid)
return 1;
if (ekey_map->version < skey_map->version)
return -1;
if (ekey_map->version > skey_map->version)
return 1;
return 0;
}
ApfsNodeMapperBTree::ApfsNodeMapperBTree(ApfsContainer &container) :
m_tree(container),
m_container(container)
{
}
ApfsNodeMapperBTree::~ApfsNodeMapperBTree()
{
}
bool ApfsNodeMapperBTree::Init(uint64_t hdr_block_id, uint64_t version)
{
std::vector<byte_t> blk;
blk.resize(m_container.GetBlocksize());
if (!m_container.ReadAndVerifyHeaderBlock(blk.data(), hdr_block_id))
return false;
memcpy(&m_root_ptr, blk.data(), sizeof(APFS_Block_4_B_BTreeRootPtr));
if (m_root_ptr.hdr.type != 0x4000000B)
return false;
m_tree.Init(m_root_ptr.entry[0].blk, version);
return true;
}
uint64_t ApfsNodeMapperBTree::GetBlockID(uint64_t nodeid, uint64_t version)
{
APFS_Key_B_NodeID_Map key;
const APFS_Key_B_NodeID_Map *rkey = nullptr;
const APFS_Value_B_NodeID_Map *val = nullptr;
BTreeEntry res;
key.nodeid = nodeid;
key.version = version;
// std::cout << std::hex << "GetBlockID: nodeid = " << nodeid << ", version = " << version << " => blockid = ";
if (!m_tree.Lookup(res, &key, sizeof(key), CompareNodeMapKey, this, false))
{
// std::cout << "NOT FOUND" << std::endl;
std::cerr << std::hex << "nodeid " << nodeid << " version " << version << " NOT FOUND!!!" << std::endl;
return 0;
}
assert(res.val_len == sizeof(APFS_Value_B_NodeID_Map));
rkey = reinterpret_cast<const APFS_Key_B_NodeID_Map *>(res.key);
val = reinterpret_cast<const APFS_Value_B_NodeID_Map *>(res.val);
if (key.nodeid != rkey->nodeid)
{
// std::cout << "NOT FOUND" << std::endl;
std::cerr << std::hex << "nodeid " << nodeid << " version " << version << " NOT FOUND!!!" << std::endl;
return 0;
}
// std::cout << val->blockid << std::endl;
return val->blockid;
}

View File

@@ -0,0 +1,45 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "DiskStruct.h"
#include "ApfsNodeMapper.h"
#include "BTree.h"
class BlockDumper;
class ApfsNodeMapperBTree : public ApfsNodeMapper
{
public:
ApfsNodeMapperBTree(ApfsContainer &container);
virtual ~ApfsNodeMapperBTree();
bool Init(uint64_t hdr_block_id, uint64_t version);
uint64_t GetBlockID(uint64_t nodeid, uint64_t version) override;
void dump(BlockDumper &bd) { m_tree.dump(bd); }
private:
APFS_Block_4_B_BTreeRootPtr m_root_ptr;
BTree m_tree;
ApfsContainer &m_container;
};

84
ApfsLib/ApfsVolume.cpp Normal file
View File

@@ -0,0 +1,84 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstring>
#include <vector>
#include "Global.h"
#include "ApfsContainer.h"
#include "ApfsVolume.h"
#include "BlockDumper.h"
ApfsVolume::ApfsVolume(ApfsContainer &container) :
m_container(container),
m_nodemap_dir(container),
m_bt_directory(container),
m_bt_blockmap(container),
m_bt_snapshots(container)
{
m_blockid_sb = 0;
}
ApfsVolume::~ApfsVolume()
{
}
bool ApfsVolume::Init(uint64_t blkid_volhdr)
{
std::vector<byte_t> blk;
m_blockid_sb = blkid_volhdr;
blk.resize(m_container.GetBlocksize());
if (!m_container.ReadAndVerifyHeaderBlock(blk.data(), blkid_volhdr))
return false;
memcpy(&m_sb, blk.data(), sizeof(m_sb));
if (m_sb.signature != 0x42535041)
return false;
m_nodemap_dir.Init(m_sb.blockid_nodemap, m_sb.hdr.version);
m_bt_directory.Init(m_sb.nodeid_rootdir, m_sb.hdr.version, &m_nodemap_dir);
m_bt_blockmap.Init(m_sb.blockid_blockmap, m_sb.hdr.version);
m_bt_snapshots.Init(m_sb.blockid_4xBx10_map, m_sb.hdr.version);
return true;
}
void ApfsVolume::dump(BlockDumper& bd)
{
std::vector<byte_t> blk;
blk.resize(m_container.GetBlocksize());
if (!m_container.ReadAndVerifyHeaderBlock(blk.data(), m_blockid_sb))
return;
bd.SetTextFlags(m_sb.unk_38 & 0xFF);
bd.DumpNode(blk.data(), m_blockid_sb);
// m_nodemap_dir.dump(bd);
m_bt_directory.dump(bd);
// m_bt_blockmap.dump(bd);
// m_bt_snapshots.dump(bd);
}

59
ApfsLib/ApfsVolume.h Normal file
View File

@@ -0,0 +1,59 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include "DiskStruct.h"
#include "ApfsNodeMapperBTree.h"
#include "BTree.h"
class ApfsContainer;
class BlockDumper;
class ApfsVolume
{
public:
ApfsVolume(ApfsContainer &container);
~ApfsVolume();
bool Init(uint64_t blkid_volhdr);
const char *name() const { return m_sb.vol_name; }
void dump(BlockDumper &bd);
BTree &getDirectory() { return m_bt_directory; }
uint32_t getTextFormat() const { return m_sb.unk_38 & 0x9; }
ApfsContainer &getContainer() const { return m_container; }
private:
ApfsContainer &m_container;
APFS_Superblock_APSB m_sb;
ApfsNodeMapperBTree m_nodemap_dir;
BTree m_bt_directory;
BTree m_bt_blockmap;
BTree m_bt_snapshots;
uint64_t m_blockid_sb;
};

584
ApfsLib/BTree.cpp Normal file
View File

@@ -0,0 +1,584 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring>
#include <iostream>
#include <iomanip>
#include "ApfsContainer.h"
#include "BTree.h"
#include "Util.h"
#include "BlockDumper.h"
int CompareStdKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context)
{
// assert(skey_len == 8);
// assert(ekey_len == 8);
(void)skey_len;
(void)ekey_len;
(void)context;
uint64_t ks = *reinterpret_cast<const uint64_t *>(skey);
uint64_t ke = *reinterpret_cast<const uint64_t *>(ekey);
if (ke < ks)
return -1;
if (ke > ks)
return 1;
return 0;
}
BTreeEntry::BTreeEntry()
{
key = nullptr;
val = nullptr;
key_len = 0;
val_len = 0;
}
BTreeEntry::~BTreeEntry()
{
}
void BTreeEntry::clear()
{
key = nullptr;
val = nullptr;
key_len = 0;
val_len = 0;
m_node.reset();
}
BTreeNode::BTreeNode(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid) :
m_tree(tree),
m_nodeid_parent(nodeid_parent),
m_block_id(blkid)
{
m_block.assign(block, block + blocksize);
m_hdr = reinterpret_cast<const APFS_BlockHeader *>(m_block.data());
m_bt = reinterpret_cast<const APFS_BTHeader *>(m_block.data() + sizeof(APFS_BlockHeader));
assert(m_bt->keys_offs == 0);
m_keys_start = 0x38 + m_bt->keys_len;
m_vals_start = (nodeid_parent != 0) ? blocksize : blocksize - sizeof(APFS_BTFooter);
}
std::shared_ptr<BTreeNode> BTreeNode::CreateNode(BTree & tree, const uint8_t * block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid)
{
const APFS_BTHeader *bt = reinterpret_cast<const APFS_BTHeader *>(block + sizeof(APFS_BlockHeader));
if (bt->flags & 4)
return std::make_shared<BTreeNodeFix>(tree, block, blocksize, nodeid_parent, blkid);
else
return std::make_shared<BTreeNodeVar>(tree, block, blocksize, nodeid_parent, blkid);
}
BTreeNode::~BTreeNode()
{
}
BTreeNodeFix::BTreeNodeFix(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid) :
BTreeNode(tree, block, blocksize, nodeid_parent, blkid)
{
m_entries = reinterpret_cast<const APFS_BTEntryFixed *>(m_block.data() + 0x38);
}
bool BTreeNodeFix::GetEntry(BTreeEntry & result, uint32_t index) const
{
result.clear();
if (index >= m_bt->entries_cnt)
return false;
result.key = m_block.data() + m_keys_start + m_entries[index].key_offs;
result.key_len = m_tree.GetKeyLen();
if (m_entries[index].value_offs != 0xFFFF)
{
result.val = m_block.data() + m_vals_start - m_entries[index].value_offs;
result.val_len = (m_bt->level > 0) ? sizeof(uint64_t) : m_tree.GetValLen();
}
else
{
result.val = nullptr;
result.val_len = 0;
}
return true;
}
BTreeNodeVar::BTreeNodeVar(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid) :
BTreeNode(tree, block, blocksize, nodeid_parent, blkid)
{
m_entries = reinterpret_cast<const APFS_BTEntry *>(m_block.data() + 0x38);
}
bool BTreeNodeVar::GetEntry(BTreeEntry & result, uint32_t index) const
{
result.clear();
if (index >= m_bt->entries_cnt)
return false;
result.key = m_block.data() + m_keys_start + m_entries[index].key_offs;
result.key_len = m_entries[index].key_len;
if (m_entries[index].value_offs != 0xFFFF)
{
result.val = m_block.data() + m_vals_start - m_entries[index].value_offs;
result.val_len = m_entries[index].value_len;
}
else
{
result.val = nullptr;
result.val_len = 0;
}
return true;
}
BTree::BTree(ApfsContainer &container) :
m_container(container)
{
m_root_node = nullptr;
m_nodeid_map = nullptr;
m_version = 0;
m_debug = false;
}
BTree::~BTree()
{
#ifdef BTREE_USE_MAP
m_nodes.clear();
#endif
}
void BTree::Init(uint64_t root_node_id, uint64_t version, ApfsNodeMapper *node_map)
{
m_nodeid_map = node_map;
m_version = version;
m_root_node = GetNode(root_node_id, 0);
memcpy(&m_treeinfo, m_root_node->block().data() + m_root_node->block().size() - sizeof(APFS_BTFooter), sizeof(APFS_BTFooter));
}
bool BTree::Lookup(BTreeEntry &result, const void *key, size_t key_size, BTCompareFunc func, void *context, bool exact)
{
uint64_t nodeid;
int index;
std::shared_ptr<BTreeNode> node(m_root_node);
BTreeEntry e;
if (m_debug)
{
// std::cout << std::hex << "BTree::Lookup: key=" << *reinterpret_cast<const uint64_t *>(key) << " root=" << node->nodeid() << std::endl;
std::cout << "BTree::Lookup: ";
DumpHex(std::cout, reinterpret_cast<const byte_t *>(key), key_size, key_size);
}
while (node->level() > 0)
{
index = FindBin(node, key, key_size, func, context, FindMode::LE);
if (index < 0)
return false;
node->GetEntry(e, index);
assert(e.val_len == sizeof(uint64_t));
nodeid = *reinterpret_cast<const uint64_t *>(e.val);
node = GetNode(nodeid, node->nodeid());
}
index = FindBin(node, key, key_size, func, context, exact ? FindMode::EQ : FindMode::LE);
if (m_debug)
std::cout << "Result = " << node->nodeid() << ":" << index << std::endl;
if (index < 0)
return false;
node->GetEntry(result, index);
result.m_node = node;
return true;
}
bool BTree::GetIterator(BTreeIterator& it, const void* key, size_t key_size, BTCompareFunc func, void *context)
{
uint64_t nodeid;
int index;
std::shared_ptr<BTreeNode> node(m_root_node);
BTreeEntry e;
it.Init(this, m_root_node->level());
if (m_debug)
std::cout << std::hex << "BTree::GetIterator: key=" << *reinterpret_cast<const uint64_t *>(key) << " root=" << node->nodeid() << std::endl;
while (node->level() > 0)
{
index = FindBin(node, key, key_size, func, context, FindMode::LE);
if (index < 0)
index = 0;
node->GetEntry(e, index);
assert(e.val_len == sizeof(uint64_t));
nodeid = *reinterpret_cast<const uint64_t *>(e.val);
it.Set(node->level(), node, index);
node = GetNode(nodeid, node->nodeid());
}
index = FindBin(node, key, key_size, func, context, FindMode::GE);
if (m_debug)
std::cout << "Result = " << node->nodeid() << ":" << index << std::endl;
if (index < 0)
{
index = node->entries_cnt() - 1;
it.Set(node->level(), node, index);
it.next();
if (m_debug)
std::cout << "Iterator next entry" << std::endl;
}
else
{
it.Set(node->level(), node, index);
}
#if 0
it.GetEntry(e);
if (func(key, key_size, e.key, e.key_len) < 0)
it.next();
#endif
return true;
}
void BTree::dump(BlockDumper& out)
{
DumpTreeInternal(out, m_root_node);
}
void BTree::DumpTreeInternal(BlockDumper& out, const std::shared_ptr<BTreeNode> &node)
{
size_t k;
size_t cnt;
BTreeEntry e;
std::shared_ptr<BTreeNode> child;
uint64_t nodeid_child;
if (!node)
return;
out.DumpNode(node->block().data(), node->blockid());
if (node->level() > 0)
{
cnt = node->entries_cnt();
for (k = 0; k < cnt; k++)
{
node->GetEntry(e, k);
if (e.val_len != sizeof(uint64_t))
continue; // assert ...
nodeid_child = *reinterpret_cast<const uint64_t *>(e.val);
child = GetNode(nodeid_child, node->nodeid());
DumpTreeInternal(out, child);
}
}
}
std::shared_ptr<BTreeNode> BTree::GetNode(uint64_t nodeid, uint64_t parentid)
{
std::shared_ptr<BTreeNode> node;
#ifdef BTREE_USE_MAP
std::map<uint64_t, std::shared_ptr<BTreeNode>>::iterator it = m_nodes.find(nodeid);
if (it != m_nodes.end())
{
node = it->second;
}
else
#endif
{
uint64_t blkid = nodeid;
std::vector<byte_t> blk;
if (m_nodeid_map)
blkid = m_nodeid_map->GetBlockID(nodeid, m_version);
blk.resize(m_container.GetBlocksize());
if (m_container.ReadAndVerifyHeaderBlock(blk.data(), blkid))
{
node = BTreeNode::CreateNode(*this, blk.data(), blk.size(), parentid, blkid);
#ifdef BTREE_USE_MAP
m_nodes[nodeid] = node;
#endif
}
}
return node;
}
uint32_t BTree::Find(const std::shared_ptr<BTreeNode> &node, const void *key, size_t key_size, BTCompareFunc func, void *context)
{
uint32_t k;
uint32_t cnt;
int res_cur = 0;
int res_prev = 0;
BTreeEntry e;
cnt = node->entries_cnt();
if (cnt == 0)
return 0;
// Erst mal linear suchen ...
for (k = 0; k < cnt; k++)
{
node->GetEntry(e, k);
res_cur = func(key, key_size, e.key, e.key_len, context);
if (res_cur > 0 && res_prev <= 0)
break;
res_prev = res_cur;
}
return k;
}
int BTree::FindBin(const std::shared_ptr<BTreeNode>& node, const void* key, size_t key_size, BTCompareFunc func, void *context, FindMode mode)
{
static const char resstr[3] = { '<', '=', '>' };
int beg;
int end;
int mid;
int cnt = node->entries_cnt();
int res;
BTreeEntry e;
int rc;
if (cnt == 0)
return -1;
beg = 0;
end = cnt - 1;
if (m_debug)
{
std::cout << "FindBin : ";
DumpHex(std::cout, reinterpret_cast<const byte_t *>(key), key_size, key_size);
}
while (beg <= end)
{
mid = (beg + end) / 2;
node->GetEntry(e, mid);
rc = func(key, key_size, e.key, e.key_len, context);
if (m_debug)
{
std::cout << std::dec << std::setfill(' ');
std::cout << std::setw(2) << beg << " [" << std::setw(2) << mid << "] " << std::setw(2) << end << " : " << resstr[rc + 1] << " : ";
DumpHex(std::cout, reinterpret_cast<const byte_t *>(e.key), e.key_len, e.key_len);
}
if (rc == 0)
break;
if (rc == -1)
beg = mid + 1;
else if (rc == 1)
end = mid - 1;
}
if (m_debug)
{
std::cout << std::dec << std::setfill(' ');
std::cout << " => " << resstr[rc + 1] << ", " << mid;
}
switch (mode)
{
case FindMode::EQ:
res = (rc == 0) ? mid : -1;
break;
case FindMode::LE:
res = (rc <= 0) ? mid : (mid - 1);
break;
case FindMode::LT:
res = (rc < 0) ? mid : (mid - 1);
break;
case FindMode::GE:
res = (rc >= 0) ? mid : (mid + 1);
break;
case FindMode::GT:
res = (rc > 0) ? mid : (mid + 1);
break;
}
if (res == cnt)
res = -1;
if (m_debug)
std::cout << " => " << res << std::endl;
return res;
}
BTreeIterator::BTreeIterator()
{
m_tree = nullptr;
m_max_level = 0;
std::fill(std::begin(m_bt_index), std::end(m_bt_index), 0);
}
BTreeIterator::~BTreeIterator()
{
}
bool BTreeIterator::next()
{
m_bt_index[0]++;
if (m_bt_index[0] < m_bt_node[0]->entries_cnt())
{
return true;
}
else
{
std::shared_ptr<BTreeNode> node = next_internal(1);
if (node)
{
m_bt_index[0] = 0;
m_bt_node[0] = node;
return true;
}
}
return false;
}
bool BTreeIterator::prev()
{
// Not implemented
return false;
}
bool BTreeIterator::GetEntry(BTreeEntry& res) const
{
if (m_bt_node[0] == nullptr)
return false;
return m_bt_node[0]->GetEntry(res, m_bt_index[0]);
}
void BTreeIterator::Init(BTree* tree, uint16_t max_level)
{
m_tree = tree;
m_max_level = max_level;
}
void BTreeIterator::Set(uint16_t level, const std::shared_ptr<BTreeNode> &node, uint32_t index)
{
m_bt_node[level] = node;
m_bt_index[level] = index;
}
std::shared_ptr<BTreeNode> BTreeIterator::next_internal(uint16_t level)
{
if (level > m_max_level)
return nullptr;
std::shared_ptr<BTreeNode> node;
BTreeEntry bte;
uint64_t nodeid;
uint64_t parentid;
bool rc;
m_bt_index[level]++;
if (m_bt_index[level] >= m_bt_node[level]->entries_cnt())
{
node = next_internal(level + 1);
if (node)
{
m_bt_index[level] = 0;
m_bt_node[level] = node;
}
else
m_bt_index[level]--;
}
rc = m_bt_node[level]->GetEntry(bte, m_bt_index[level]);
if (!rc)
{
std::cerr << "BTreeIterator next_internal getting entry failed. Node-ID " << m_bt_node[level]->nodeid() << ", Entry " << m_bt_index[level] << ", Level " << level << std::endl;
return std::shared_ptr<BTreeNode>();
}
assert(bte.val_len == sizeof(uint64_t));
nodeid = *reinterpret_cast<const uint64_t *>(bte.val);
parentid = (level <= m_max_level) ? m_bt_node[level]->nodeid() : 0;
return m_tree->GetNode(nodeid, parentid);
}
std::shared_ptr<BTreeNode> BTreeIterator::prev_internal(uint16_t level)
{
// Not implemented
(void) level;
return std::shared_ptr<BTreeNode>();
}

197
ApfsLib/BTree.h Normal file
View File

@@ -0,0 +1,197 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vector>
#include <map>
#include <memory>
#include "Global.h"
#include "DiskStruct.h"
#include "ApfsNodeMapper.h"
class BTree;
class BTreeNode;
class BTreeIterator;
class BlockDumper;
class ApfsContainer;
#define BTREE_USE_MAP
// ekey < skey: -1, ekey > skey: 1, ekey == skey: 0
typedef int(*BTCompareFunc)(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
int CompareStdKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
class BTreeEntry
{
friend class BTree;
public:
BTreeEntry();
~BTreeEntry();
BTreeEntry(const BTreeEntry &o) = delete;
BTreeEntry &operator=(const BTreeEntry &o) = delete;
void clear();
const void *key;
const void *val;
size_t key_len;
size_t val_len;
private:
std::shared_ptr<BTreeNode> m_node;
};
class BTreeNode
{
protected:
BTreeNode(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid);
public:
static std::shared_ptr<BTreeNode> CreateNode(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid);
virtual ~BTreeNode();
uint64_t nodeid() const { return m_hdr->node_id; }
uint32_t entries_cnt() const { return m_bt->entries_cnt; }
uint16_t level() const { return m_bt->level; }
uint64_t blockid() const { return m_block_id; }
virtual bool GetEntry(BTreeEntry &result, uint32_t index) const = 0;
// virtual uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const = 0;
const std::vector<byte_t> &block() const { return m_block; }
protected:
std::vector<uint8_t> m_block;
BTree &m_tree;
uint16_t m_keys_start; // Up
uint16_t m_vals_start; // Dn
const uint64_t m_nodeid_parent;
const uint64_t m_block_id;
const APFS_BlockHeader *m_hdr;
const APFS_BTHeader *m_bt;
};
class BTreeNodeFix : public BTreeNode
{
public:
BTreeNodeFix(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid);
bool GetEntry(BTreeEntry &result, uint32_t index) const override;
// uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const override;
private:
const APFS_BTEntryFixed *m_entries;
};
class BTreeNodeVar : public BTreeNode
{
public:
BTreeNodeVar(BTree &tree, const uint8_t *block, size_t blocksize, uint64_t nodeid_parent, uint64_t blkid);
bool GetEntry(BTreeEntry &result, uint32_t index) const override;
// uint32_t Find(const void *key, size_t key_size, BTCompareFunc func) const override;
private:
const APFS_BTEntry *m_entries;
};
class BTree
{
enum class FindMode
{
EQ,
LE,
LT,
GE,
GT
};
friend class BTreeIterator;
public:
BTree(ApfsContainer &container);
~BTree();
void Init(uint64_t root_node_id, uint64_t version, ApfsNodeMapper *node_map = nullptr);
bool Lookup(BTreeEntry &result, const void *key, size_t key_size, BTCompareFunc func, void *context, bool exact);
bool GetIterator(BTreeIterator &it, const void *key, size_t key_size, BTCompareFunc func, void *context);
uint16_t GetKeyLen() const { return m_treeinfo.min_key_size; }
uint16_t GetValLen() const { return m_treeinfo.min_val_size; }
void dump(BlockDumper &out);
void EnableDebugOutput() { m_debug = true; }
private:
void DumpTreeInternal(BlockDumper &out, const std::shared_ptr<BTreeNode> &node);
uint32_t Find(const std::shared_ptr<BTreeNode> &node, const void *key, size_t key_size, BTCompareFunc func, void *context);
int FindBin(const std::shared_ptr<BTreeNode> &node, const void *key, size_t key_size, BTCompareFunc func, void *context, FindMode mode);
std::shared_ptr<BTreeNode> GetNode(uint64_t nodeid, uint64_t parentid);
ApfsContainer &m_container;
std::shared_ptr<BTreeNode> m_root_node;
ApfsNodeMapper *m_nodeid_map;
APFS_BTFooter m_treeinfo;
uint64_t m_version;
bool m_debug;
#ifdef BTREE_USE_MAP
std::map<uint64_t, std::shared_ptr<BTreeNode>> m_nodes;
#endif
};
class BTreeIterator
{
public:
BTreeIterator();
~BTreeIterator();
bool next();
bool prev();
bool GetEntry(BTreeEntry &res) const;
void Init(BTree *tree, uint16_t max_level);
void Set(uint16_t level, const std::shared_ptr<BTreeNode> &node, uint32_t index);
private:
std::shared_ptr<BTreeNode> next_internal(uint16_t level);
std::shared_ptr<BTreeNode> prev_internal(uint16_t level);
std::shared_ptr<BTreeNode> m_bt_node[8];
uint32_t m_bt_index[8];
BTree *m_tree;
uint16_t m_max_level;
};

1191
ApfsLib/BlockDumper.cpp Normal file

File diff suppressed because it is too large Load Diff

81
ApfsLib/BlockDumper.h Normal file
View File

@@ -0,0 +1,81 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vector>
#include <ostream>
#include "Global.h"
#include "DiskStruct.h"
class BlockDumper
{
public:
BlockDumper(std::ostream &os, size_t blocksize);
~BlockDumper();
void SetTextFlags(uint32_t flags) { m_text_flags = flags; }
void DumpNode(const byte_t *block, uint64_t blk_nr);
private:
typedef void(BlockDumper::*DumpFunc)(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpNodeHeader(const APFS_BlockHeader *blk, uint64_t blk_nr);
void DumpBTNode(DumpFunc func, uint16_t key_size = 0, uint16_t value_size = 0);
void DumpBTHeader(bool dump_offsets = false);
void DumpBTFooter();
void DumpTableHeader(const APFS_TableHeader &tbl);
void DumpBlk_0_D();
void DumpBlk_4_7();
void DumpBlk_4_B();
void DumpBlk_4_C();
void DumpBlk_8_1();
void DumpBlk_8_5();
void DumpBlk_8_11();
void DumpBTNode_0();
void DumpBTNode_4();
void DumpBTNode_8();
void DumpBTEntry_0_E(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_4_B(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_4_F(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_4_10(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_4_13(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_8_9(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBTEntry_Unk(const byte_t *key_data, size_t key_length, const byte_t *value_data, size_t value_length, bool index);
void DumpBlockHex();
void DumpHex(const byte_t *data, size_t size, size_t line_size = 16);
public:
static const char * GetNodeType(uint32_t type, uint32_t subtype);
private:
static const std::string tstamp(uint64_t apfs_time);
uint32_t m_text_flags; // 00 - Alt, 01 - insensitive, 08 - sensitive
std::ostream &m_os;
const byte_t *m_block;
const size_t m_blocksize;
};

2
ApfsLib/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
enable_language(C ASM)
add_library(ApfsLib ApfsContainer.cpp ApfsDir.cpp ApfsNodeMapper.cpp ApfsNodeMapperBTree.cpp ApfsVolume.cpp BlockDumper.cpp BTree.cpp Crc32.cpp Decmpfs.cpp Disk.cpp Inflate.cpp Util.cpp lzvn_decode.s)

97
ApfsLib/Crc32.cpp Normal file
View File

@@ -0,0 +1,97 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Crc32.h"
Crc32::Crc32(bool reflect, uint32_t poly)
{
unsigned int i;
uint32_t r;
unsigned int b;
m_reflect = reflect;
m_crc = 0;
if (reflect) {
poly = ((poly << 16) & 0xFFFF0000) | ((poly >> 16) & 0x0000FFFF);
poly = ((poly << 8) & 0xFF00FF00) | ((poly >> 8) & 0x00FF00FF);
poly = ((poly << 4) & 0xF0F0F0F0) | ((poly >> 4) & 0x0F0F0F0F);
poly = ((poly << 2) & 0xCCCCCCCC) | ((poly >> 2) & 0x33333333);
poly = ((poly << 1) & 0xAAAAAAAA) | ((poly >> 1) & 0x55555555);
for (i = 0; i < 256; i++) {
r = i;
for (b = 0; b < 8; b++) {
if (r & 1)
r = (r >> 1) ^ poly;
else
r = (r >> 1);
}
m_table[i] = r;
}
}
else {
for (i = 0; i < 256; i++) {
r = (i << 24);
for (b = 0; b < 8; b++) {
if (r & 0x80000000)
r = (r << 1) ^ poly;
else
r = (r << 1);
}
m_table[i] = r;
}
}
}
Crc32::~Crc32()
{
}
void Crc32::Calc(const byte_t *data, size_t size)
{
size_t i;
for (i = 0; i < size; i++) {
if (m_reflect) {
CalcLE(data[i]);
}
else {
CalcBE(data[i]);
}
}
}
void Crc32::CalcLE(byte_t b)
{
m_crc = m_table[b ^ (m_crc & 0xFF)] ^ (m_crc >> 8);
}
void Crc32::CalcBE(byte_t b)
{
m_crc = m_table[b ^ ((m_crc >> 24) & 0xFF)] ^ (m_crc << 8);
}
uint32_t Crc32::GetDataCRC(const byte_t *data, size_t size, uint32_t initialXor, uint32_t finalXor)
{
m_crc = initialXor;
Calc(data, size);
return m_crc ^ finalXor;
}

47
ApfsLib/Crc32.h Normal file
View File

@@ -0,0 +1,47 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include "Global.h"
class Crc32
{
public:
Crc32(bool reflect, uint32_t poly = 0x04C11DB7);
~Crc32();
void SetCRC(uint32_t crc) { m_crc = crc; }
uint32_t GetCRC() const { return m_crc; }
void Calc(const byte_t *data, size_t size);
uint32_t GetDataCRC(const byte_t *data, size_t size, uint32_t initialXor, uint32_t finalXor);
private:
void CalcLE(byte_t b);
void CalcBE(byte_t b);
uint32_t m_table[256];
uint32_t m_crc;
bool m_reflect;
};

216
ApfsLib/Decmpfs.cpp Normal file
View File

@@ -0,0 +1,216 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <cstring>
#include <cassert>
#include "Decmpfs.h"
#include "Inflate.h"
#ifdef __linux__
#include <byteswap.h>
#endif
#include "FastCompression.h"
#include "Global.h"
struct RsrcForkHeader
{
uint32_t data_off_be;
uint32_t mgmt_off_be;
uint32_t data_size_be;
uint32_t mgmt_size_be;
};
struct CmpfRsrcEntry
{
// 1 64K-Block
uint32_t off;
uint32_t size;
};
struct CmpfRsrc
{
uint32_t entries;
CmpfRsrcEntry entry[32];
};
static size_t DecompressZLib(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size)
{
size_t nwr = 0;
if (src[0] == 0x78)
{
Inflate inf;
nwr = inf.Decompress(dst, dst_size, src + 2, src_size - 2);
// assert(nwr == dst_size);
}
return nwr;
}
bool IsDecompAlgoSupported(uint16_t algo)
{
switch (algo)
{
case 3:
case 4:
case 7:
case 8:
return true;
default:
return false;
}
}
bool IsDecompAlgoInRsrc(uint16_t algo)
{
switch (algo)
{
case 4:
case 8:
return true;
default:
return false;
}
}
bool DecompressFile(ApfsDir &dir, uint64_t ino, std::vector<uint8_t> &decompressed, const std::vector<uint8_t> &compressed)
{
#if 1
if (compressed.size() < sizeof(CompressionHeader))
return false;
const CompressionHeader *hdr = reinterpret_cast<const CompressionHeader *>(compressed.data());
const uint8_t *cdata = compressed.data() + sizeof(CompressionHeader);
size_t csize = compressed.size() - sizeof(CompressionHeader);
if (hdr->algo == 3)
{
if (g_debug > 0)
std::cout << "DecompressFile " << compressed.size() << " => " << hdr->size << std::endl;
decompressed.resize(hdr->size);
if (compressed[0x10] == 0x78)
{
DecompressZLib(decompressed.data(), decompressed.size(), cdata, csize);
}
else if (compressed[0x10] == 0xFF)
{
decompressed.assign(cdata + 1, cdata + csize);
}
}
else if (hdr->algo == 4)
{
std::vector<uint8_t> rsrc;
bool rc = dir.GetAttribute(rsrc, ino, "com.apple.ResourceFork");
if (!rc)
{
decompressed.clear();
return false;
}
RsrcForkHeader rsrc_hdr;
memcpy(&rsrc_hdr, rsrc.data(), sizeof(rsrc_hdr));
rsrc_hdr.data_off_be = bswap_32(rsrc_hdr.data_off_be);
rsrc_hdr.data_size_be = bswap_32(rsrc_hdr.data_size_be);
rsrc_hdr.mgmt_off_be = bswap_32(rsrc_hdr.mgmt_off_be);
rsrc_hdr.mgmt_size_be = bswap_32(rsrc_hdr.mgmt_size_be);
uint32_t rsrc_size = bswap_32(*reinterpret_cast<uint32_t *>(rsrc.data() + rsrc_hdr.data_off_be));
const uint8_t *cmpf_rsrc_base = rsrc.data() + rsrc_hdr.data_off_be + sizeof(uint32_t);
const CmpfRsrc *cmpf_rsrc = reinterpret_cast<const CmpfRsrc *>(cmpf_rsrc_base);
decompressed.resize((hdr->size + 0xFFFF) & 0xFFFF0000);
uint8_t blk[0x10000];
size_t k;
size_t off = 0;
for (k = 0; k < cmpf_rsrc->entries; k++)
{
// DecompressZLib(decompressed.data() + 0x10000 * k, 0x10000, cmpf_rsrc_base + cmpf_rsrc->entry[k].off, cmpf_rsrc->entry[k].size);
off = DecompressZLib(blk, 0x10000, cmpf_rsrc_base + cmpf_rsrc->entry[k].off, cmpf_rsrc->entry[k].size);
if (g_debug > 0)
std::cout << "DecompressZLib dst = " << (0x10000 * k) << " / 10000 src = " << cmpf_rsrc->entry[k].off << " / " << cmpf_rsrc->entry[k].size << " => " << off << std::endl;
std::copy(blk, blk + 0x10000, decompressed.begin() + (0x10000 * k));
}
}
else if (hdr->algo == 7)
{
if (g_debug > 0)
std::cout << "Decompress LZVN compressed file " << compressed.size() << " => " << hdr->size << std::endl;
decompressed.resize(hdr->size);
if (cdata[0] == 0x06)
decompressed.assign(cdata + 1, cdata + csize);
else
lzvn_decode(decompressed.data(), decompressed.size(), cdata, csize);
}
else if (hdr->algo == 8)
{
if (g_debug > 0)
std::cout << "Decompress LZVN compressed resource file ..." << std::endl;
std::vector<uint8_t> rsrc;
size_t k;
bool rc = dir.GetAttribute(rsrc, ino, "com.apple.ResourceFork");
if (!rc)
{
decompressed.clear();
return false;
}
const uint32_t *off_list = reinterpret_cast<const uint32_t *>(rsrc.data());
decompressed.resize((hdr->size + 0xFFFF) & 0xFFFF0000);
for (k = 0; (k << 16) < decompressed.size(); k++)
lzvn_decode(decompressed.data() + (k << 16), 0x10000, rsrc.data() + off_list[k], off_list[k + 1] - off_list[k]);
decompressed.resize(hdr->size);
return rc;
}
else
{
if (g_debug > 0)
std::cout << "DecompressFile: Unknown Algorithm " << hdr->algo << std::endl;
decompressed = compressed;
}
#else
decompressed = compressed;
#endif
return true;
}

36
ApfsLib/Decmpfs.h Normal file
View File

@@ -0,0 +1,36 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vector>
#include "ApfsDir.h"
struct CompressionHeader
{
uint32_t signature;
uint32_t algo;
uint64_t size;
};
bool IsDecompAlgoSupported(uint16_t algo);
bool IsDecompAlgoInRsrc(uint16_t algo);
bool DecompressFile(ApfsDir &dir, uint64_t ino, std::vector<uint8_t> &decompressed, const std::vector<uint8_t> &compressed);

129
ApfsLib/Disk.cpp Normal file
View File

@@ -0,0 +1,129 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __unix
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <linux/fs.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include "Disk.h"
#include "Global.h"
#ifdef __unix
Disk::Disk()
{
m_device = -1;
m_size = 0;
}
Disk::~Disk()
{
Close();
}
bool Disk::Open(const char* name)
{
m_device = open(name, O_RDONLY | O_LARGEFILE);
if (m_device == -1)
return false;
struct stat64 st;
fstat64(m_device, &st);
if (S_ISREG(st.st_mode))
{
m_size = st.st_size;
}
else if (S_ISBLK(st.st_mode))
{
// Hmmm ...
ioctl(m_device, BLKGETSIZE64, &m_size);
}
if (g_debug > 0)
printf("Device %s opened. Size is %zu bytes.\n", name, m_size);
return m_device != -1;
}
void Disk::Close()
{
if (m_device != -1)
close(m_device);
m_device = -1;
m_size = 0;
}
bool Disk::Read(void* data, uint64_t offs, uint64_t len)
{
size_t nread;
nread = pread64(m_device, data, len, offs);
// TODO: Bessere Fehlerbehandlung ...
return nread == len;
}
#else
Disk::Disk()
{
m_size = 0;
}
Disk::~Disk()
{
}
bool Disk::Open(const char * name)
{
m_vol.open(name, std::ios::binary);
if (!m_vol.is_open())
return false;
m_vol.seekg(0, std::ios::end);
m_size = m_vol.tellg();
m_vol.seekg(0);
return m_vol.is_open();
}
void Disk::Close()
{
m_vol.close();
}
bool Disk::Read(void *data, uint64_t offs, uint64_t len)
{
m_vol.seekg(offs);
m_vol.read(reinterpret_cast<char *>(data), len);
// TODO: Fehlerbehandlung ...
return true;
}
#endif

66
ApfsLib/Disk.h Normal file
View File

@@ -0,0 +1,66 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#ifdef __unix
class Disk
{
public:
Disk();
~Disk();
bool Open(const char *name);
void Close();
bool Read(void *data, uint64_t offs, uint64_t len);
uint64_t GetSize() const { return m_size; }
private:
int m_device;
size_t m_size;
};
#else
#include <fstream>
class Disk
{
public:
Disk();
~Disk();
bool Open(const char *name);
void Close();
bool Read(void *data, uint64_t offs, uint64_t len);
uint64_t GetSize() const { return m_size; }
private:
std::ifstream m_vol;
size_t m_size;
};
#endif

449
ApfsLib/DiskStruct.h Normal file
View File

@@ -0,0 +1,449 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
constexpr uint64_t KeyType_Snapshot_1 = 0x1000000000000000ULL;
constexpr uint64_t KeyType_BlockMap = 0x2000000000000000ULL;
constexpr uint64_t KeyType_Object = 0x3000000000000000ULL;
constexpr uint64_t KeyType_Attribute = 0x4000000000000000ULL;
constexpr uint64_t KeyType_HardLink_5 = 0x5000000000000000ULL;
constexpr uint64_t KeyType_RefCount = 0x6000000000000000ULL;
constexpr uint64_t KeyType_Unknown_7 = 0x7000000000000000ULL;
constexpr uint64_t KeyType_Extent = 0x8000000000000000ULL;
constexpr uint64_t KeyType_Name = 0x9000000000000000ULL;
constexpr uint64_t KeyType_Unknown_A = 0xA000000000000000ULL;
constexpr uint64_t KeyType_Snapshot_B = 0xB000000000000000ULL;
constexpr uint64_t KeyType_Hardlink_C = 0xC000000000000000ULL;
#pragma pack(push)
#pragma pack(1)
struct APFS_BlockHeader
{
uint64_t checksum;
uint64_t node_id;
uint64_t version;
uint32_t type;
uint32_t subtype;
};
static_assert(sizeof(APFS_BlockHeader) == 0x20, "BlockHeader size wrong");
struct APFS_TableHeader // at 0x20
{
uint16_t page;
uint16_t level;
uint32_t entries_cnt;
};
static_assert(sizeof(APFS_TableHeader) == 0x8, "TableHeader size wrong");
struct APFS_BTHeader // at 0x20
{
uint16_t flags; // Bit 0x4 : Fixed
uint16_t level; // 0 = Leaf
uint32_t entries_cnt;
uint16_t keys_offs;
uint16_t keys_len;
uint16_t free_offs;
uint16_t free_len;
uint16_t unk_30;
uint16_t unk_32;
uint16_t unk_34;
uint16_t unk_36;
};
static_assert(sizeof(APFS_BTHeader) == 0x18, "BTHeader size wrong");
struct APFS_BTFooter
{
uint32_t unk_FD8;
uint32_t unk_FDC; // 0x1000 - Node Size?
uint32_t min_key_size; // Key Size - or 0
uint32_t min_val_size; // Value Size - or 0
uint32_t max_key_size; // (Max) Key Size - or 0
uint32_t max_val_size; // (Max) Value Size - or 0
uint64_t entries_cnt; // Total entries in B*-Tree
uint64_t nodes_cnt; // Total nodes in B*-Tree
};
static_assert(sizeof(APFS_BTFooter) == 0x28, "BTFooter size wrong");
struct APFS_BTEntry
{
uint16_t key_offs; // offs = Base + key_offs
uint16_t key_len;
uint16_t value_offs; // offs = 0x1000 - value_offs
uint16_t value_len;
};
struct APFS_BTEntryFixed
{
uint16_t key_offs;
uint16_t value_offs;
};
// Entries in Bitmap Table
struct APFS_BitmapPtr
{
uint64_t version;
uint64_t offset;
uint32_t bits_total;
uint32_t bits_avail;
uint64_t block;
};
// Entries in BT 0/E
struct APFS_Inode
{
uint64_t parent_id;
uint64_t object_id;
uint64_t birthtime;
uint64_t mtime;
uint64_t ctime;
uint64_t atime;
uint64_t unk_30; // ? meistens 0x8000
uint64_t refcnt;
uint32_t unk_40;
uint32_t flags; // 0x20: compressed
uint32_t uid; // uid (somehow modified)
uint32_t gid; // gid (somehow modified)
uint64_t mode;
uint32_t unk_58;
uint16_t entries_cnt;
uint16_t entries_len;
};
struct APFS_InodeEntry
{
uint16_t type; // Keine Ahnung ...
uint16_t len; // Padden auf vielfaches von 8 ...
};
struct APFS_Inode_Sizes // Object, after Filename
{
uint64_t size;
uint64_t size_on_disk;
uint64_t unk_10;
uint64_t size_2;
uint64_t unk_20;
};
struct APFS_Key_Name_Old
{
uint64_t parent_id;
uint16_t name_len;
char name[0x400];
};
struct APFS_Key_Name
{
uint64_t parent_id;
uint32_t hash;
char name[0x400];
};
struct APFS_Name
{
uint64_t id;
uint64_t timestamp;
uint16_t unk;
};
struct APFS_Name_SLink
{
uint16_t unk_12;
uint16_t unk_14;
uint8_t unk_16;
uint8_t unk_17;
uint16_t unk_18;
uint64_t obj_id;
};
struct APFS_Key_Extent
{
uint64_t inode;
uint64_t offset;
};
struct APFS_Extent
{
uint64_t size;
uint64_t block;
uint64_t unk;
};
struct APFS_Key_Attribute
{
uint64_t inode_key;
uint16_t name_len;
char name[0x400];
};
struct APFS_Attribute
{
uint16_t type;
uint16_t size;
};
struct APFS_AttributeLink
{
uint64_t object_id;
uint64_t size;
uint64_t size_on_disk;
uint64_t unk[3];
};
struct APFS_IDMapping
{
uint32_t type; // type?
uint32_t subtype;
uint64_t unk_08; // size?
uint64_t unk_10;
uint64_t nodeid;
uint64_t block;
};
struct APFS_Key_B_NodeID_Map
{
uint64_t nodeid;
uint64_t version;
};
struct APFS_Value_B_NodeID_Map
{
uint32_t flags;
uint32_t size;
uint64_t blockid;
};
struct APFS_Value_F
{
uint64_t block_cnt;
uint64_t obj_id;
uint32_t unk_10;
};
struct APFS_Value_10_1
{
uint64_t unk_00;
uint64_t unk_08;
uint64_t tstamp_10;
uint64_t tstamp_18;
uint64_t unk_20;
uint32_t type_28;
uint32_t unk_2C;
uint16_t namelen;
};
struct APFS_Value_10_B
{
uint64_t id;
};
struct APFS_Key_8_9
{
uint64_t version;
uint64_t blk_id; // Block-ID
// => Anzahl Bloecke
// Wenn kein Value, dann Anzahl = 1
};
struct APFS_Superblock_NXSB // Ab 0x20
{
APFS_BlockHeader hdr;
uint32_t signature; // 'NXSB' / 0x4253584E
uint32_t block_size;
uint64_t block_count;
uint64_t unk_30;
uint64_t unk_38;
uint64_t unk_40;
uint64_t container_guid_1;
uint64_t container_guid_2;
uint64_t next_nodeid; // Next node id (?)
uint64_t next_version; // Next version number (?)
uint32_t sb_area_cnt; // Anzahl Bloecke fuer NXSB + 4_C ?
uint32_t spaceman_area_cnt; // Anzahl Bloecke fuer Rest
uint64_t blockid_sb_area_start; // Block-ID (0x4000000C) - Nein
uint64_t blockid_spaceman_area_start; // Block-ID (0x80000005) => Node-ID 0x400
uint32_t next_sb; // Naechster 4_C + NXSB? (+sb_area_start)
uint32_t next_spaceman; // Naechster 8_5/2/11? (+blockid_spaceman_area_start)
uint32_t current_sb_start; // Start 4_C+NXSB Block (+sb_area_start)
uint32_t current_sb_len; // Laenge 4_C+NXSB Block
uint32_t current_spaceman_start; // Start 8_5/2/11 Bloecke (+blockid_spaceman_area_start)
uint32_t current_spaceman_len; // Anzahl 8_5/2/11 Bloecke
uint64_t nodeid_8x5; // Node-ID (0x400) => (0x80000005)
uint64_t blockid_volhdr; // Block-ID => (0x4000000B) => B*-Tree fuer Mapping Node-ID -> Volume APSB Superblocks
uint64_t nodeid_8x11; // Node-ID (0x401) => (0x80000011)
uint32_t unk_B0;
uint32_t unk_B4;
uint64_t nodeid_apsb[100]; // Liste der Node-ID's der Volume Superblocks (Anzahl geraten, koennte aber hinkommen ...)
// Hier kaeme noch ein wenig mehr ... aber keine Ahnung was es bedeutet ...
};
static_assert(sizeof(APFS_Superblock_NXSB) == 0x3D8, "NXSB Superblock size wrong");
struct APFS_Superblock_APSB_AccessInfo
{
char accessor[0x20];
uint64_t timestamp;
uint64_t version;
};
struct APFS_Superblock_APSB
{
APFS_BlockHeader hdr;
uint32_t signature; // APSB, 0x42535041
uint32_t unk_24;
uint64_t unk_28;
uint64_t unk_30;
uint64_t unk_38;
uint64_t unk_40;
uint64_t blocks_reserved;
uint64_t blocks_quota;
uint64_t unk_58; // Node-ID
uint64_t unk_60;
uint32_t unk_68;
uint32_t unk_6C;
uint32_t unk_70;
uint32_t unk_74;
uint32_t unk_78; // 40000002
uint32_t unk_7C; // 40000002
uint64_t blockid_nodemap; // Block ID -> 4000000B -> Node Map Tree Root (40000002/B) - Node Map nur fuer Directory!
uint64_t nodeid_rootdir; // Node ID -> Root Directory
uint64_t blockid_blockmap; // Block ID -> 40000002/F Block Map Tree Root
uint64_t blockid_4xBx10_map; // Block ID -> Root of 40000002/10 Tree
uint64_t unk_A0;
uint64_t unk_A8;
uint64_t unk_B0;
uint64_t unk_B8;
uint64_t unk_C0;
uint64_t unk_C8;
uint64_t unk_D0;
uint64_t unk_D8;
uint64_t unk_E0;
uint64_t unk_E8;
uint64_t guid_1;
uint64_t guid_2;
uint64_t timestamp_100;
uint64_t version_108;
APFS_Superblock_APSB_AccessInfo access_info[9];
char vol_name[0x100];
uint64_t unk_3C0;
uint64_t unk_3C8;
};
static_assert(sizeof(APFS_Superblock_APSB) == 0x3D0, "APSB Superblock size wrong");
struct APFS_Block_4_7_Bitmaps
{
APFS_BlockHeader hdr;
APFS_TableHeader tbl;
APFS_BitmapPtr bmp[0x7E];
};
static_assert(sizeof(APFS_Block_4_7_Bitmaps) == 0xFE8, "Block-40000007 size wrong");
struct APFS_Entry_4_B
{
uint32_t type_1;
uint32_t type_2;
uint64_t blk;
};
struct APFS_Block_4_B_BTreeRootPtr
{
APFS_BlockHeader hdr;
APFS_TableHeader tbl;
APFS_Entry_4_B entry[0xFD];
};
static_assert(sizeof(APFS_Block_4_B_BTreeRootPtr) == 0xFF8, "Block-4000000B size wrong");
struct APFS_Block_4_C
{
APFS_BlockHeader hdr;
APFS_TableHeader tbl;
APFS_IDMapping entry[0x65];
};
static_assert(sizeof(APFS_Block_4_C) == 0xFF0, "Block-4000000C size wrong");
struct APFS_Block_8_5_Spaceman
{
APFS_BlockHeader hdr;
uint32_t unk_20;
uint32_t unk_24;
uint32_t unk_28;
uint32_t unk_2C;
uint64_t blocks_total;
uint64_t unk_38;
uint64_t unk_40;
uint64_t blocks_free;
uint64_t unk_50;
uint64_t unk_58;
uint64_t unk_60;
uint64_t unk_68;
uint64_t unk_70;
uint64_t unk_78;
uint64_t unk_80;
uint64_t unk_88;
uint32_t unk_90;
uint32_t unk_94;
uint64_t blockcnt_bitmaps_98; // Container-Bitmaps
uint32_t unk_A0;
uint32_t blockcnt_bitmaps_A4; // Mgr-Bitmaps?
uint64_t blockid_begin_bitmaps_A8; // Mgr-Bitmaps
uint64_t blockid_bitmaps_B0; // Container-Bitmaps
uint64_t unk_B8;
uint64_t unk_C0;
uint64_t unk_C8;
uint64_t nodeid_obsolete_1; // Obsolete B*-Tree fuer Mgr-Bitmap-Blocks
uint64_t unk_D8;
uint64_t unk_E0;
uint64_t unk_E8;
uint64_t unk_F0;
uint64_t nodeid_obsolete_2; // Obsolete B*-Tree fuer Volume;
uint64_t unk_100;
uint64_t unk_108;
uint64_t unk_110;
uint64_t unk_118;
uint64_t unk_120;
uint64_t unk_128;
uint64_t unk_130;
uint64_t unk_138;
uint16_t unk_140;
uint16_t unk_142;
uint32_t unk_144;
uint32_t unk_148;
uint32_t unk_14C;
uint64_t unk_150;
uint64_t unk_158;
uint16_t unk_160[0x10];
uint64_t blockid_vol_bitmap_hdr;
};
static_assert(sizeof(APFS_Block_8_5_Spaceman) == 0x188, "Spaceman Header wrong size");
#pragma pack(pop)

18
ApfsLib/FastCompression.h Normal file
View File

@@ -0,0 +1,18 @@
/*
* Created..: 31 October 2014
* Filename.: FastCompression.h
* Author...: Pike R. Alpha
* Purpose..: Command line tool to LZVN compress a file.
*/
#ifdef __cplusplus
extern "C" {
#endif
extern size_t lzvn_encode(void * dst, size_t dst_size, const void * src, size_t src_size, void * work_space);
extern size_t lzvn_decode(void * dst, size_t dst_size, const void * src, size_t src_size);
extern size_t lzvn_encode_work_size(void);
#ifdef __cplusplus
}
#endif

25
ApfsLib/Global.h Normal file
View File

@@ -0,0 +1,25 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
typedef unsigned char byte_t;
// Debug level - defined in ApfsContainer.cpp
extern int g_debug;

327
ApfsLib/Inflate.cpp Normal file
View File

@@ -0,0 +1,327 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include "Inflate.h"
Inflate::Inflate()
{
}
Inflate::~Inflate()
{
}
unsigned int Inflate::GetBit()
{
unsigned int bit;
if (bit_pos >= 8)
{
bit_buffer = buffer[buffer_ptr++];
bit_pos -= 8;
}
bit = bit_buffer & 1;
bit_pos++;
bit_buffer >>= 1;
return bit;
}
unsigned int Inflate::GetBits(int bits)
{
unsigned int v = 0;
int i;
for (i = 0; i < bits; i++)
v = v | (GetBit() << i);
return v;
}
unsigned int Inflate::DecodeValue(const unsigned short *hcnt, const unsigned short *hval)
{
unsigned int b;
int p;
int i;
p = 0;
i = 1;
b = 0;
while (1)
{
b = (b << 1) | GetBit();
if (b < hcnt[i])
return hval[b + p];
p += hcnt[i];
b -= hcnt[i];
i++;
}
}
void Inflate::CreateHuffmanTable(unsigned short *count, unsigned short count_sz, unsigned short *value, unsigned short value_sz, const unsigned char *sym_size)
{
unsigned short i, j;
unsigned int sym_pos;
for (i = 0; i < count_sz; i++)
count[i] = 0;
for (i = 0; i < value_sz; i++)
value[i] = 0;
sym_pos = 0;
for (i = 1; i < count_sz; i++)
{
for (j = 0; j < value_sz; j++)
{
if (sym_size[j] == i)
{
count[i] ++;
value[sym_pos] = j;
sym_pos++;
}
}
}
}
void Inflate::CreateStaticTables()
{
unsigned char sym_size[288];
int i;
for (i = 0; i <= 143; i++)
sym_size[i] = 8;
for (i = 144; i <= 255; i++)
sym_size[i] = 9;
for (i = 256; i <= 279; i++)
sym_size[i] = 7;
for (i = 280; i <= 287; i++)
sym_size[i] = 8;
CreateHuffmanTable(lit_len_count, 16, lit_len_value, 288, sym_size);
for (i = 0; i < 31; i++)
sym_size[i] = 5;
CreateHuffmanTable(dist_count, 16, dist_value, 32, sym_size);
}
void Inflate::ReadDynamicTables()
{
unsigned char sym_size[288];
int hlit;
int hdist;
int hclen;
int i;
unsigned int v;
unsigned int len;
static const unsigned short values[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
hlit = GetBits(5) + 257;
hdist = GetBits(5) + 1;
hclen = GetBits(4) + 4;
for (i = 0; i < hclen; i++)
sym_size[values[i]] = GetBits(3);
for (; i < 19; i++)
sym_size[values[i]] = 0;
CreateHuffmanTable(clen_count, 8, clen_value, 19, sym_size);
for (i = 0; i < hlit; i++)
{
v = DecodeValue(clen_count, clen_value);
if (v < 16)
sym_size[i] = v;
else if (v == 16)
{
len = 3 + GetBits(2);
for (; len > 0; len--)
{
sym_size[i] = sym_size[i - 1];
i++;
}
i--;
}
else
{
if (v == 17)
len = 3 + GetBits(3);
if (v == 18)
len = 11 + GetBits(7);
for (; len > 0; len--)
sym_size[i++] = 0;
i--;
}
}
for (; i < 288; i++)
sym_size[i] = 0;
CreateHuffmanTable(lit_len_count, 16, lit_len_value, 288, sym_size);
for (i = 0; i < hdist; i++)
{
v = DecodeValue(clen_count, clen_value);
if (v < 16)
sym_size[i] = v;
else if (v == 16)
{
len = 3 + GetBits(2);
for (; len > 0; len--)
{
sym_size[i] = sym_size[i - 1];
i++;
}
i--;
}
else
{
if (v == 17)
len = 3 + GetBits(3);
if (v == 18)
len = 11 + GetBits(7);
for (; len > 0; len--)
sym_size[i++] = 0;
i--;
}
}
for (; i < 32; i++)
sym_size[i] = 0;
CreateHuffmanTable(dist_count, 16, dist_value, 32, sym_size);
}
void Inflate::DecodeData()
{
unsigned int v;
unsigned int exbits;
unsigned int length;
unsigned int distance;
static const int lengths[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 };
static const int distances[] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 };
while (true)
{
v = DecodeValue(lit_len_count, lit_len_value);
if (v < 256)
dbuffer[dbuffer_ptr++] = v;
else if (v == 256)
return;
else
{
length = lengths[v - 257];
if (v >= 265 && v < 285)
{
exbits = (v - 261) >> 2;
length = length + GetBits(exbits);
}
v = DecodeValue(dist_count, dist_value);
distance = distances[v];
if (v >= 4)
{
exbits = (v - 2) >> 1;
distance = distance + GetBits(exbits);
}
for (v = 0; v < length; v++)
{
dbuffer[dbuffer_ptr] = dbuffer[dbuffer_ptr - distance];
dbuffer_ptr++;
}
}
}
}
size_t Inflate::Decompress(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size)
{
int bfinal;
int btype;
int len;
int nlen;
buffer = src;
buffer_size = src_size;
buffer_ptr = 0;
dbuffer = dst;
dbuffer_size = dst_size;
dbuffer_ptr = 0;
do
{
bfinal = GetBit();
btype = GetBits(2);
switch (btype)
{
case 0:
len = buffer[buffer_ptr] | (buffer[buffer_ptr + 1] << 8);
nlen = buffer[buffer_ptr + 2] | (buffer[buffer_ptr + 3] << 8);
buffer_ptr += 4;
bit_pos = 8;
memcpy(dbuffer + dbuffer_ptr, buffer + buffer_ptr, len);
dbuffer_ptr += len;
buffer_ptr += len;
break;
case 1:
CreateStaticTables();
DecodeData();
break;
case 2:
ReadDynamicTables();
DecodeData();
break;
default:
assert(false);
bfinal = true;
break;
}
} while (!bfinal);
return dbuffer_ptr;
}

61
ApfsLib/Inflate.h Normal file
View File

@@ -0,0 +1,61 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstddef>
#include <cstdint>
class Inflate
{
public:
Inflate();
~Inflate();
size_t Decompress(uint8_t *dst, size_t dst_size, const uint8_t *src, size_t src_size);
size_t GetUsedInputBytes() const { return buffer_ptr; }
private:
unsigned int GetBit();
unsigned int GetBits(int bits);
unsigned int DecodeValue(const unsigned short *hcnt, const unsigned short *hval);
void CreateHuffmanTable(unsigned short *count, unsigned short count_sz, unsigned short *value, unsigned short value_sz, const unsigned char *sym_size);
void CreateStaticTables();
void ReadDynamicTables();
void DecodeData();
const uint8_t *buffer;
size_t buffer_size;
size_t buffer_ptr;
uint8_t *dbuffer;
size_t dbuffer_size;
size_t dbuffer_ptr;
uint8_t bit_buffer = 0;
uint8_t bit_pos = 8;
uint16_t lit_len_count[16];
uint16_t lit_len_value[288];
uint16_t dist_count[16];
uint16_t dist_value[32];
uint16_t clen_count[8];
uint16_t clen_value[19];
};

310
ApfsLib/Util.cpp Normal file
View File

@@ -0,0 +1,310 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <unicode/utypes.h>
#include <unicode/normalizer2.h>
#include <iostream>
#include <iomanip>
#include <vector>
#include "Util.h"
#include "Crc32.h"
static const uint16_t lowercase_table[0x500] =
{
// 0000
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
// 0080
0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x03BC, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00D7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00DF,
0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
// 0100
0x0101, 0x0101, 0x0103, 0x0103, 0x0105, 0x0105, 0x0107, 0x0107, 0x0109, 0x0109, 0x010B, 0x010B, 0x010D, 0x010D, 0x010F, 0x010F,
0x0111, 0x0111, 0x0113, 0x0113, 0x0115, 0x0115, 0x0117, 0x0117, 0x0119, 0x0119, 0x011B, 0x011B, 0x011D, 0x011D, 0x011F, 0x011F,
0x0121, 0x0121, 0x0123, 0x0123, 0x0125, 0x0125, 0x0127, 0x0127, 0x0129, 0x0129, 0x012B, 0x012B, 0x012D, 0x012D, 0x012F, 0x012F,
0x0130, 0x0131, 0x0133, 0x0133, 0x0135, 0x0135, 0x0137, 0x0137, 0x0138, 0x013A, 0x013A, 0x013C, 0x013C, 0x013E, 0x013E, 0x0140,
0x0140, 0x0142, 0x0142, 0x0144, 0x0144, 0x0146, 0x0146, 0x0148, 0x0148, 0x0149, 0x014B, 0x014B, 0x014D, 0x014D, 0x014F, 0x014F,
0x0151, 0x0151, 0x0153, 0x0153, 0x0155, 0x0155, 0x0157, 0x0157, 0x0159, 0x0159, 0x015B, 0x015B, 0x015D, 0x015D, 0x015F, 0x015F,
0x0161, 0x0161, 0x0163, 0x0163, 0x0165, 0x0165, 0x0167, 0x0167, 0x0169, 0x0169, 0x016B, 0x016B, 0x016D, 0x016D, 0x016F, 0x016F,
0x0171, 0x0171, 0x0173, 0x0173, 0x0175, 0x0175, 0x0177, 0x0177, 0x00FF, 0x017A, 0x017A, 0x017C, 0x017C, 0x017E, 0x017E, 0x0073,
// 0180
0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188, 0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268, 0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
0x01A1, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x0280, 0x01A8, 0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01B0,
0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292, 0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9, 0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CE, 0x01CE, 0x01D0,
0x01D0, 0x01D2, 0x01D2, 0x01D4, 0x01D4, 0x01D6, 0x01D6, 0x01D8, 0x01D8, 0x01DA, 0x01DA, 0x01DC, 0x01DC, 0x01DD, 0x01DF, 0x01DF,
0x01E1, 0x01E1, 0x01E3, 0x01E3, 0x01E5, 0x01E5, 0x01E7, 0x01E7, 0x01E9, 0x01E9, 0x01EB, 0x01EB, 0x01ED, 0x01ED, 0x01EF, 0x01EF,
0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F5, 0x01F5, 0x0195, 0x01BF, 0x01F9, 0x01F9, 0x01FB, 0x01FB, 0x01FD, 0x01FD, 0x01FF, 0x01FF,
// 0200
0x0201, 0x0201, 0x0203, 0x0203, 0x0205, 0x0205, 0x0207, 0x0207, 0x0209, 0x0209, 0x020B, 0x020B, 0x020D, 0x020D, 0x020F, 0x020F,
0x0211, 0x0211, 0x0213, 0x0213, 0x0215, 0x0215, 0x0217, 0x0217, 0x0219, 0x0219, 0x021B, 0x021B, 0x021D, 0x021D, 0x021F, 0x021F,
0x019E, 0x0221, 0x0223, 0x0223, 0x0225, 0x0225, 0x0227, 0x0227, 0x0229, 0x0229, 0x022B, 0x022B, 0x022D, 0x022D, 0x022F, 0x022F,
0x0231, 0x0231, 0x0233, 0x0233, 0x0234, 0x0235, 0x0236, 0x0237, 0x0238, 0x0239, 0x2C65, 0x023C, 0x023C, 0x019A, 0x2C66, 0x023F,
0x0240, 0x0242, 0x0242, 0x0180, 0x0289, 0x028C, 0x0247, 0x0247, 0x0249, 0x0249, 0x024B, 0x024B, 0x024D, 0x024D, 0x024F, 0x024F,
0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 0x0258, 0x0259, 0x025A, 0x025B, 0x025C, 0x025D, 0x025E, 0x025F,
0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026A, 0x026B, 0x026C, 0x026D, 0x026E, 0x026F,
0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278, 0x0279, 0x027A, 0x027B, 0x027C, 0x027D, 0x027E, 0x027F,
// 0280
0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028A, 0x028B, 0x028C, 0x028D, 0x028E, 0x028F,
0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 0x0298, 0x0299, 0x029A, 0x029B, 0x029C, 0x029D, 0x029E, 0x029F,
0x02A0, 0x02A1, 0x02A2, 0x02A3, 0x02A4, 0x02A5, 0x02A6, 0x02A7, 0x02A8, 0x02A9, 0x02AA, 0x02AB, 0x02AC, 0x02AD, 0x02AE, 0x02AF,
0x02B0, 0x02B1, 0x02B2, 0x02B3, 0x02B4, 0x02B5, 0x02B6, 0x02B7, 0x02B8, 0x02B9, 0x02BA, 0x02BB, 0x02BC, 0x02BD, 0x02BE, 0x02BF,
0x02C0, 0x02C1, 0x02C2, 0x02C3, 0x02C4, 0x02C5, 0x02C6, 0x02C7, 0x02C8, 0x02C9, 0x02CA, 0x02CB, 0x02CC, 0x02CD, 0x02CE, 0x02CF,
0x02D0, 0x02D1, 0x02D2, 0x02D3, 0x02D4, 0x02D5, 0x02D6, 0x02D7, 0x02D8, 0x02D9, 0x02DA, 0x02DB, 0x02DC, 0x02DD, 0x02DE, 0x02DF,
0x02E0, 0x02E1, 0x02E2, 0x02E3, 0x02E4, 0x02E5, 0x02E6, 0x02E7, 0x02E8, 0x02E9, 0x02EA, 0x02EB, 0x02EC, 0x02ED, 0x02EE, 0x02EF,
0x02F0, 0x02F1, 0x02F2, 0x02F3, 0x02F4, 0x02F5, 0x02F6, 0x02F7, 0x02F8, 0x02F9, 0x02FA, 0x02FB, 0x02FC, 0x02FD, 0x02FE, 0x02FF,
// 0300
0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x03B9, 0x0346, 0x0347, 0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
0x0371, 0x0371, 0x0373, 0x0373, 0x0374, 0x0375, 0x0377, 0x0377, 0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x03F3,
// 0380
0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x03AC, 0x0387, 0x03AD, 0x03AE, 0x03AF, 0x038B, 0x03CC, 0x038D, 0x03CD, 0x03CE,
0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
0x03C0, 0x03C1, 0x03C3, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03D7,
0x03B2, 0x03B8, 0x03D2, 0x03D3, 0x03D4, 0x03C6, 0x03C0, 0x03D7, 0x03D9, 0x03D9, 0x03DB, 0x03DB, 0x03DD, 0x03DD, 0x03DF, 0x03DF,
0x03E1, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7, 0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
0x03BA, 0x03C1, 0x03F2, 0x03F3, 0x03B8, 0x03B5, 0x03F6, 0x03F8, 0x03F8, 0x03F2, 0x03FB, 0x03FB, 0x03FC, 0x037B, 0x037C, 0x037D,
// 0400
0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467, 0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0477, 0x0477, 0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
// 0480
0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048B, 0x048B, 0x048D, 0x048D, 0x048F, 0x048F,
0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497, 0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7, 0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7, 0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
0x04CF, 0x04C2, 0x04C2, 0x04C4, 0x04C4, 0x04C6, 0x04C6, 0x04C8, 0x04C8, 0x04CA, 0x04CA, 0x04CC, 0x04CC, 0x04CE, 0x04CE, 0x04CF,
0x04D1, 0x04D1, 0x04D3, 0x04D3, 0x04D5, 0x04D5, 0x04D7, 0x04D7, 0x04D9, 0x04D9, 0x04DB, 0x04DB, 0x04DD, 0x04DD, 0x04DF, 0x04DF,
0x04E1, 0x04E1, 0x04E3, 0x04E3, 0x04E5, 0x04E5, 0x04E7, 0x04E7, 0x04E9, 0x04E9, 0x04EB, 0x04EB, 0x04ED, 0x04ED, 0x04EF, 0x04EF,
0x04F1, 0x04F1, 0x04F3, 0x04F3, 0x04F5, 0x04F5, 0x04F7, 0x04F7, 0x04F9, 0x04F9, 0x04FB, 0x04FB, 0x04FD, 0x04FD, 0x04FF, 0x04FF
};
static Crc32 g_crc(true, 0x1EDC6F41);
uint64_t Fletcher64(const uint32_t *data, size_t cnt, uint64_t init)
{
size_t k;
uint64_t sum1 = init & 0xFFFFFFFFU;
uint64_t sum2 = (init >> 32);
for (k = 0; k < cnt; k++)
{
sum1 = (sum1 + data[k]);
sum2 = (sum2 + sum1);
}
sum1 = sum1 % 0xFFFFFFFF;
sum2 = sum2 % 0xFFFFFFFF;
return (static_cast<uint64_t>(sum2) << 32) | static_cast<uint64_t>(sum1);
}
bool VerifyBlock(const void *block, size_t size)
{
uint64_t cs;
const uint32_t * const data = reinterpret_cast<const uint32_t *>(block);
size /= sizeof(uint32_t);
cs = *reinterpret_cast<const uint64_t *>(block);
if (cs == 0)
return false;
if (cs == 0xFFFFFFFFFFFFFFFFULL)
return false;
cs = Fletcher64(data + 2, size - 2, 0);
cs = Fletcher64(data, 2, cs);
return cs == 0;
}
bool IsZero(const byte_t *data, size_t size)
{
for (size_t k = 0; k < size; k++)
{
if (data[k] != 0)
return false;
}
return true;
}
bool IsEmptyBlock(const void *data, size_t blksize)
{
blksize /= sizeof(uint64_t);
const uint64_t *qdata = reinterpret_cast<const uint64_t *>(data);
for (size_t k = 0; k < blksize; k++)
{
if (qdata[k] != 0)
return false;
}
return true;
}
void DumpHex(std::ostream &os, const byte_t *data, size_t size, size_t lineSize)
{
using namespace std;
size_t i, j;
byte_t b;
if (size == 0)
return;
os << hex << uppercase << setfill('0');
for (i = 0; i < size; i += lineSize)
{
os << setw(4) << i << ": ";
for (j = 0; (j < lineSize) && ((i + j) < size); j++)
os << setw(2) << static_cast<unsigned int>(data[i + j]) << ' ';
for (; j < lineSize; j++)
os << " ";
os << "- ";
for (j = 0; (j < lineSize) && ((i + j) < size); j++)
{
b = data[i + j];
if (b >= 0x20 && b < 0x7F)
os << b;
else
os << '.';
}
os << endl;
}
// os << dec;
}
#undef DEBUG_OUT
uint32_t HashFilename(const char *utf8str, uint16_t name_len, bool case_insensitive)
{
icu::StringPiece utf8(utf8str);
icu::UnicodeString str = icu::UnicodeString::fromUTF8(utf8);
icu::UnicodeString normalized;
UErrorCode err = U_ZERO_ERROR;
std::vector<UChar32> hashdata;
int n;
uint32_t hash;
const icu::Normalizer2 *norm = icu::Normalizer2::getNFDInstance(err);
#ifdef DEBUG_OUT
if (U_FAILURE(err))
{
std::cerr << "getNFDInstance failed." << std::endl;
return 0;
}
else
{
std::cerr << "norm = " << norm << ", err = " << err << std::endl;
}
#endif
normalized = norm->normalize(str, err);
#ifdef DEBUG_OUT
if (U_FAILURE(err))
{
std::cerr << "Normalization failed." << std::endl;
return 0;
}
else
{
std::cerr << "Normalization ok, err = " << err << std::endl;
}
#endif
hashdata.resize(normalized.countChar32() + 1);
n = normalized.toUTF32(hashdata.data(), hashdata.size(), err);
#ifdef DEBUG_OUT
if (U_FAILURE(err))
{
std::cerr << "Conversion to UTF32 failed." << std::endl;
return 0;
}
else
{
std::cerr << "Conversion to UTF32 ok, err = " << err << std::endl;
}
#endif
if (case_insensitive)
{
for (size_t k = 0; k < hashdata.size(); k++)
{
if (hashdata[k] < 0x500)
hashdata[k] = lowercase_table[hashdata[k]];
}
}
#ifdef DEBUG_OUT
for (size_t k = 0; k < hashdata.size(); k++)
{
if (hashdata[k] > 0x100)
std::cerr << '?';
else
std::cerr << (char)hashdata[k];
}
std::cerr << std::endl;
#endif
hashdata.pop_back();
g_crc.SetCRC(0xFFFFFFFF);
g_crc.Calc(reinterpret_cast<const byte_t *>(hashdata.data()), hashdata.size() * sizeof(UChar32));
hash = g_crc.GetCRC();
hash = ((hash & 0x3FFFFF) << 10) | (name_len & 0x3FF);
return hash;
}

33
ApfsLib/Util.h Normal file
View File

@@ -0,0 +1,33 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <ostream>
#include "Global.h"
uint64_t Fletcher64(const uint32_t *data, size_t cnt, uint64_t init);
bool VerifyBlock(const void *block, size_t size);
bool IsZero(const byte_t *data, size_t size);
bool IsEmptyBlock(const void *data, size_t blksize);
void DumpHex(std::ostream &os, const byte_t *data, size_t size, size_t line_size = 16);
uint32_t HashFilename(const char *utf8str, uint16_t name_len, bool case_insensitive);

546
ApfsLib/lzvn_decode.s Normal file
View File

@@ -0,0 +1,546 @@
/*
* Filename.: lzvn_decode.s
* Author...: Pike R. Alpha
*/
.text
.globl lzvn_decode
# rdi: void *dst
# rsi: size_t dst_size
# rdx: const void *src
# rcx: size_t src_size
# rax: return size_t
lzvn_decode:
pushq %rbp
movq %rsp, %rbp
pushq %rbx
pushq %r12
leaq Lzvn_decode.opcode_table(%rip), %rbx
# leaq 0x2f2(%rip), %rbx
xorq %rax, %rax
xorq %r12, %r12
# Has to be at least 8 bytes output (?)
subq $8, %rsi
jb L_return_0
# Last 8 bytes are 06 00 00 00 00 00 00 00
leaq -8(%rdx,%rcx), %rcx # src + src_size - 8
cmpq %rcx, %rdx
ja L_return_0
# r9 = next byte
movzbq (%rdx), %r9
# r8 = next 8 bytes
movq (%rdx), %r8
# Execute operation
jmpq *(%rbx,%r9,8)
L_op_0E:
addq $1, %rdx # src++
cmpq %rcx, %rdx # src >= end?
ja L_return_0
# Get next op
movzbq (%rdx), %r9
movq (%rdx), %r8
jmpq *(%rbx,%r9,8)
# Alignment stuff
nopw %cs:(%rax,%rax)
nop
L_op_00:
shrq $6, %r9 # byte >>= 6
leaq 2(%rdx,%r9), %rdx # src += (2 + (byte >> 6))
cmpq %rcx, %rdx
ja L_return_0
movq %r8, %r12 # qword
bswapq %r12
movq %r12, %r10
shlq $5, %r12
shlq $2, %r10
shrq $53, %r12 # r12 = (qword.swp << 5) >> 53 = (qword.swp >> 48) & 0x07FF
shrq $61, %r10 # r10 = (qword.swp << 2) >> 61 = (qword.swp >> 59) & 0x1F
shrq $16, %r8 # qword >>= 16
addq $3, %r10 # r10 += 3
L_0x089:
leaq (%rax,%r9), %r11 # r11 = dst_written + (byte >> 6) ...
addq %r10, %r11 # + r10
cmpq %rsi, %r11 # r11 > dst_size?
jae L_0x0d2
movq %r8, (%rdi,%rax) # dst[dst_written] = r8
addq %r9, %rax
movq %rax, %r8
subq %r12, %r8
jb L_return_0
cmpq $8, %r12
jb L_0x102
L_0x0ae:
movq (%rdi,%r8), %r9
addq $8, %r8
movq %r9, (%rdi,%rax)
addq $8, %rax
subq $8, %r10
ja L_0x0ae
addq %r10, %rax
# Get op
movzbq (%rdx), %r9
movq (%rdx), %r8
jmpq *(%rbx,%r9,8)
L_0x0d2:
testq %r9, %r9
je L_0x0f6
leaq 8(%rsi), %r11
L_0x0db:
movb %r8b, (%rdi,%rax)
addq $1, %rax
cmpq %rax, %r11
je L_return_size
shrq $8, %r8
subq $1, %r9
jne L_0x0db
L_0x0f6:
movq %rax, %r8
subq %r12, %r8
jb L_return_0
L_0x102:
leaq 8(%rsi), %r11
L_0x106:
movzbq (%rdi,%r8), %r9
addq $1, %r8
movb %r9b, (%rdi,%rax)
addq $1, %rax
cmpq %rax, %r11
je L_return_size
subq $1, %r10
jne L_0x106
movzbq (%rdx), %r9
movq (%rdx), %r8
jmpq *(%rbx,%r9,8)
L_op_46:
shrq $6, %r9
leaq 1(%rdx,%r9), %rdx
cmpq %rcx, %rdx
ja L_return_0
movq $56, %r10
andq %r8, %r10
shrq $8, %r8
shrq $3, %r10
addq $3, %r10
jmp L_0x089
L_op_07:
shrq $6, %r9
leaq 3(%rdx,%r9), %rdx
cmpq %rcx, %rdx
ja L_return_0
movq $56, %r10
movq $65535, %r12
andq %r8, %r10
shrq $8, %r8
shrq $3, %r10
andq %r8, %r12
shrq $16, %r8
addq $3, %r10
jmp L_0x089
L_op_A0:
shrq $3, %r9
andq $3, %r9
leaq 3(%rdx,%r9), %rdx
cmpq %rcx, %rdx
ja L_return_0
movq %r8, %r10
andq $775, %r10
shrq $10, %r8
movzbq %r10b, %r12
shrq $8, %r10
shlq $2, %r12
orq %r12, %r10
movq $16383, %r12
addq $3, %r10
andq %r8, %r12
shrq $14, %r8
jmp L_0x089
L_op_Fx:
addq $1, %rdx
cmpq %rcx, %rdx
ja L_return_0
movq %r8, %r10
andq $15, %r10
jmp L_0x218
L_op_F0:
addq $2, %rdx
cmpq %rcx, %rdx
ja L_return_0
movq %r8, %r10
shrq $8, %r10
andq $255, %r10
addq $16, %r10
L_0x218:
movq %rax, %r8
subq %r12, %r8
leaq (%rax,%r10), %r11
cmpq %rsi, %r11
jae L_0x102
cmpq $8, %r12
jae L_0x0ae
jmp L_0x102
L_op_Ex:
# Ex [LIT] : copy x bytes
andq $15, %r8
leaq 1(%rdx,%r8), %rdx
jmp L_copy_literals
L_op_E0:
# E0 (cnt-16) [LIT] : copy cnt bytes
shrq $8, %r8 # r8 = next byte + 16
andq $255, %r8
addq $16, %r8
leaq 2(%rdx,%r8), %rdx # src += 2 + r8
L_copy_literals:
cmpq %rcx, %rdx
ja L_return_0
leaq (%rax,%r8), %r11 # r11 = r8 + dst_written
negq %r8 # r8 = -r8
cmpq %rsi, %r11 # r11 > dst_size?
ja L_copy_literals_remainder
leaq (%rdi,%r11), %r11 # r11 = dst + r11
L_copy_literals_qword:
# Copy literals ...
movq (%rdx,%r8), %r9
movq %r9, (%r11,%r8)
addq $8, %r8
jae L_copy_literals_qword
# Update dst_written
movq %r11, %rax
subq %rdi, %rax
# Get op
movzbq (%rdx), %r9
movq (%rdx), %r8
jmpq *(%rbx,%r9,8)
L_copy_literals_remainder:
leaq 8(%rsi), %r11
L_copy_literals_bytes_loop:
movzbq (%rdx,%r8), %r9
movb %r9b, (%rdi,%rax)
addq $1, %rax
cmpq %rax, %r11
je L_return_size
addq $1, %r8
jne L_copy_literals_bytes_loop
# Get op
movzbq (%rdx), %r9
movq (%rdx), %r8
jmpq *(%rbx,%r9,8)
L_return_0:
xorq %rax, %rax
L_return_size:
popq %r12
popq %rbx
popq %rbp
ret
.data
.align 8
Lzvn_decode.opcode_table:
# 00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_size
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_0E
.quad L_op_07
# 10
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_0E
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_0
.quad L_op_07
# 20
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_0
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_0
.quad L_op_07
# 30
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_0
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_return_0
.quad L_op_07
# 40
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# 50
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# 60
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# 70
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
# 80
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# 90
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
# B0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
.quad L_op_A0
# C0
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_00
.quad L_op_46
.quad L_op_07
# D0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
.quad L_return_0
# E0
.quad L_op_E0
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
.quad L_op_Ex
# F0
.quad L_op_F0
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx
.quad L_op_Fx

20
CMakeLists.txt Normal file
View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.5)
# set(CMAKE_C_COMPILER "clang")
# set(CMAKE_CXX_COMPILER "clang++")
project(Apfs)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -march=native")
set(CMAKE_VERBOSE_MAKEFILE ON)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
include_directories(.)
add_subdirectory(ApfsLib)
add_subdirectory(ApfsDump)
add_subdirectory(Test)
add_subdirectory(apfsfuse)

339
LICENSE Normal file
View File

@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

58
README.md Normal file
View File

@@ -0,0 +1,58 @@
# APFS FUSE Driver
This project is a read-only FUSE driver for the new Apple File System.
## Usage
### Compile the source code
The following libraries are needed:
* FUSE 2.6 or greater
* ICU
```
mkdir build
cd build
cmake ..
make
```
After compilation, the binaries are located in `bin`.
### Mount a drive
```
apfs-fuse <device> <mount-directory>
```
Supported options:
* `-d n`: If n > 0, enable debug output.
* `-o opts`: Comma-separated list of mount options.
* `-v n`: Instead of mounting the first volume in a container, mount volume n (starting at 0).
The device has to be the one containing the APFS container. If a container contains more than one volume,
the volume can be specified by the `-v` option.
### Unmount a drive
```
fusermount -u <mount-directory>
```
## Features
The following features are implemented:
* Can read macOS 10.13 case sensitive and insensitive volumes, as well as iOS 11 / macOS 10.12 volumes
* Transparent decompression of ZLib and LZVN
* Extended attributes
## Limitations
These things are not supported (yet):
* Encryption
* Transparent decompression of LZFSE
* Writing
## Final Words
**This driver is experimental**
Please be aware that this code is based on reverse engineering (looking at disk images in a hex-editor), since
Apple didn't document the disk format yet. The driver is read-only, so your data should be safe. However, there
may still be quite a few bugs left, including potential crashes. So use it at your own risk.

2
Test/CMakeLists.txt Normal file
View File

@@ -0,0 +1,2 @@
add_executable(apfs-dump-quick Test.cpp)
target_link_libraries(apfs-dump-quick ApfsLib icuuc)

124
Test/Test.cpp Normal file
View File

@@ -0,0 +1,124 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <fstream>
#include <ApfsLib/Disk.h>
#include <ApfsLib/ApfsContainer.h>
#include <ApfsLib/ApfsVolume.h>
#include <ApfsLib/BlockDumper.h>
#include <ApfsLib/ApfsDir.h>
int main(int argc, char *argv[])
{
Disk disk;
int volumes_cnt;
int volume_id;
if (argc < 3)
{
std::cerr << "Syntax: Test <filename.dmg> <Logfile.txt>" << std::endl;
return -1;
}
if (!disk.Open(argv[1]))
{
std::cerr << "Unable to open file " << argv[1] << std::endl;
return -1;
}
std::ofstream st;
st.open(argv[2]);
if (!st.is_open())
{
std::cerr << "Unable to open output file " << argv[2] << std::endl;
return -1;
}
ApfsContainer *container = new ApfsContainer(disk, 0, disk.GetSize());
container->Init();
#if 1
BlockDumper bd(st, container->GetBlocksize());
// container->dump(bd);
volumes_cnt = container->GetVolumeCnt();
for (volume_id = 0; volume_id < volumes_cnt; volume_id++)
{
ApfsVolume *vol;
vol = container->GetVolume(volume_id);
if (vol)
{
std::cout << "Volume " << volume_id << ": " << vol->name() << std::endl;
vol->dump(bd);
}
delete vol;
}
#endif
#if 0
ApfsVolume *vol;
vol = container->GetVolume(0);
if (vol)
{
ApfsDir dir(*vol);
ApfsDir::Inode ino;
std::vector<ApfsDir::Name> root_list;
#if 0
dir.GetInode(ino, 0x15);
std::cout << "Directory is called " << ino.name << std::endl;
dir.ListDirectory(root_list, 0x15);
for (auto it = root_list.cbegin(); it != root_list.cend(); ++it)
{
std::cout << it->name << " : " << it->inode_id << std::endl;
}
#endif
#if 1
std::vector<uint8_t> buf;
buf.resize(0x29000);
dir.ReadFile(buf.data(), 0x39, 0, 0x29000);
#endif
}
#endif
delete container;
st.close();
disk.Close();
return 0;
}

164
Test/Test.vcxproj Normal file
View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{51D229BD-E3DC-4490-AA71-634454739287}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>Test</RootNamespace>
<WindowsTargetPlatformVersion>10.0.15063.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Test.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApfsLib\ApfsLib.vcxproj">
<Project>{ab308f5e-e5d3-4231-b619-cab09eb5b81a}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

22
Test/Test.vcxproj.filters Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Quelldateien">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Headerdateien">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Ressourcendateien">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Test.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
</Project>

631
apfsfuse/ApfsFuse.cpp Normal file
View File

@@ -0,0 +1,631 @@
/*
This file is part of apfs-fuse, a read-only implementation of APFS
(Apple File System) for FUSE.
Copyright (C) 2017 Simon Gander
Apfs-fuse is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
Apfs-fuse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with apfs-fuse. If not, see <http://www.gnu.org/licenses/>.
*/
#define FUSE_USE_VERSION 26
#include <fuse/fuse.h>
#include <fuse/fuse_lowlevel.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <attr/xattr.h>
#include <ApfsLib/ApfsContainer.h>
#include <ApfsLib/ApfsVolume.h>
#include <ApfsLib/ApfsDir.h>
#include <ApfsLib/Decmpfs.h>
#include <cassert>
#include <cstring>
#include <cstddef>
#include <iostream>
static struct fuse_lowlevel_ops ops;
static Disk g_disk;
static ApfsContainer *g_container = nullptr;
static ApfsVolume *g_volume = nullptr;
struct Directory
{
Directory() {}
~Directory() {}
std::vector<char> dirbuf;
};
struct File
{
File() {}
~File() {}
bool IsCompressed() const { return (ino.ino.flags & 0x20) != 0; }
ApfsDir::Inode ino;
std::vector<uint8_t> decomp_data;
};
static bool apfs_stat_internal(fuse_ino_t ino, struct stat &st)
{
ApfsDir dir(*g_volume);
ApfsDir::Inode rec;
bool rc = false;
memset(&st, 0, sizeof(st));
if (ino == 1)
{
st.st_ino = ino;
st.st_mode = S_IFDIR | 0755;
st.st_nlink = 2;
return true;
}
rc = dir.GetInode(rec, ino);
if (!rc)
{
return false;
}
else
{
constexpr uint64_t div_nsec = 1000000000;
// st_dev?
st.st_ino = ino;
st.st_mode = rec.ino.mode;
// st.st_nlink = rec.ino.refcnt;
st.st_nlink = 1;
// st_uid
// st_gid
// st_rdev?
if (S_ISREG(st.st_mode))
{
if (rec.ino.flags & 0x20) // Compressed
{
std::vector<uint8_t> data;
rc = dir.GetAttribute(data, ino, "com.apple.decmpfs");
if (rc)
{
const CompressionHeader *decmpfs = reinterpret_cast<const CompressionHeader *>(data.data());
if (IsDecompAlgoSupported(decmpfs->algo))
{
st.st_size = decmpfs->size;
// st.st_blocks = data.size() / 512;
if (g_debug > 0)
std::cout << "Compressed size " << decmpfs->size << " bytes." << std::endl;
}
else if (IsDecompAlgoInRsrc(decmpfs->algo))
{
rc = dir.GetAttribute(data, ino, "com.apple.ResourceFork");
if (!rc)
st.st_size = 0;
else
// Compressed size
st.st_size = data.size();
}
else
{
st.st_size = data.size();
std::cerr << "Unknown compression algorithm " << decmpfs->algo << std::endl;
}
}
else
{
std::cerr << "Flag 0x20 set but no com.apple.decmpfs attribute!!!" << std::endl;
}
}
else
{
st.st_size = rec.sizes.size;
// st_blksize
// st.st_blocks = rec.sizes.size_on_disk / 512;
}
}
else if (S_ISDIR(st.st_mode))
{
st.st_size = rec.ino.refcnt;
}
// What about this?
// st.st_birthtime.tv_sec = rec.ino.birthtime / div_nsec;
// st.st_birthtime.tv_nsec = rec.ino.birthtime % div_nsec;
st.st_mtim.tv_sec = rec.ino.mtime / div_nsec;
st.st_mtim.tv_nsec = rec.ino.mtime % div_nsec;
st.st_ctim.tv_sec = rec.ino.ctime / div_nsec;
st.st_ctim.tv_nsec = rec.ino.ctime % div_nsec;
st.st_atim.tv_sec = rec.ino.atime / div_nsec;
st.st_atim.tv_nsec = rec.ino.atime % div_nsec;
return true;
}
}
static void apfs_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize, uint64_t idx)
{
// fuse_reply_bmap(req, idx);
}
static void apfs_destroy(void *userdata)
{
}
static void apfs_getattr(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
{
ApfsDir dir(*g_volume);
ApfsDir::Inode rec;
bool rc = false;
struct stat st;
if (g_debug > 0)
std::cout << "apfs_getattr: ino=" << ino << " => ";
rc = apfs_stat_internal(ino, st);
if (g_debug > 0)
std::cout << (rc ? "OK" : "FAIL") << std::endl;
if (rc)
fuse_reply_attr(req, &st, 1.0);
else
fuse_reply_err(req, ENOENT);
}
static void apfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size)
{
ApfsDir dir(*g_volume);
bool rc = false;
std::vector<uint8_t> data;
if (g_debug > 0)
std::cout << "apfs_getxattr: " << std::hex << ino << " " << name << " => ";
rc = dir.GetAttribute(data, ino, name);
if (g_debug > 0)
std::cout << (rc ? "OK" : "FAIL") << std::endl;
if (!rc)
fuse_reply_err(req, ENOATTR);
else
{
if (size == 0)
fuse_reply_xattr(req, data.size()); // xattr size
else
fuse_reply_buf(req, reinterpret_cast<const char *>(data.data()), std::min(data.size(), size));
}
}
static void apfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size)
{
ApfsDir dir(*g_volume);
bool rc = false;
std::vector<std::string> names;
std::string reply;
rc = dir.ListAttributes(names, ino);
if (g_debug > 0)
std::cout << "apfs_listxattr:" << std::endl;
if (rc)
{
for (size_t k = 0; k < names.size(); k++)
{
std::cout << names[k] << std::endl;
reply.append(names[k]);
reply.push_back(0);
}
}
if (size == 0)
fuse_reply_xattr(req, reply.size());
else if (size < reply.size())
fuse_reply_err(req, ERANGE);
else
fuse_reply_buf(req, reply.c_str(), reply.size());
}
static void apfs_lookup(fuse_req_t req, fuse_ino_t ino, const char *name)
{
if (g_debug > 0)
std::cout << "apfs_lookup: ino=" << ino << " name=" << name << " => ";
ApfsDir dir(*g_volume);
ApfsDir::Name res;
bool rc;
rc = dir.LookupName(res, ino, name);
if (g_debug > 0)
std::cout << (rc ? "OK" : "FAIL") << std::endl;
if (!rc)
{
fuse_reply_err(req, ENOENT);
}
else
{
fuse_entry_param e;
e.ino = res.inode_id;
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
rc = apfs_stat_internal(res.inode_id, e.attr);
if (g_debug > 0)
std::cout << " apfs_stat_internal => " << (rc ? "OK" : "FAIL") << std::endl;
fuse_reply_entry(req, &e);
}
}
static void apfs_open(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
{
if (g_debug > 0)
std::cout << std::hex << "apfs_open: " << ino << std::endl;
bool rc;
if ((fi->flags & 3) != O_RDONLY)
fuse_reply_err(req, EACCES);
else
{
File *f = new File();
ApfsDir dir(*g_volume);
rc = dir.GetInode(f->ino, ino);
if (!rc)
{
std::cerr << "Couldn't get inode " << ino << std::endl;
fuse_reply_err(req, ENOENT);
delete f;
return;
}
if (f->IsCompressed())
{
std::vector<uint8_t> attr;
rc = dir.GetAttribute(attr, ino, "com.apple.decmpfs");
if (!rc)
{
std::cerr << "Couldn't get attribute com.apple.decmpfs for " << ino << std::endl;
fuse_reply_err(req, ENOENT);
delete f;
return;
}
DecompressFile(dir, ino, f->decomp_data, attr);
}
fi->fh = reinterpret_cast<uint64_t>(f);
fuse_reply_open(req, fi);
}
}
static void apfs_opendir(fuse_req_t req, fuse_ino_t ino, fuse_file_info *fi)
{
if (g_debug > 0)
std::cout << std::hex << "apfs_opendir: " << ino << std::endl;
Directory *dir = new Directory();
fi->fh = reinterpret_cast<uint64_t>(dir);
fuse_reply_open(req, fi);
}
static void apfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
ApfsDir dir(*g_volume);
File *file = reinterpret_cast<File *>(fi->fh);
if (g_debug > 0)
printf("apfs_read: ino=%016lX size=%016lX off=%016lX\n", ino, size, off);
if (!file->IsCompressed())
{
bool rc;
std::vector<char> buf(size, 0);
rc = dir.ReadFile(buf.data(), file->ino.ino.object_id, off, size);
// std::cerr << "apfs_read: fuse_reply_buf(req, " << reinterpret_cast<uint64_t>(buf.data()) << ", " << size << ")" << std::endl;
fuse_reply_buf(req, buf.data(), buf.size());
}
else
{
if (off >= file->decomp_data.size())
size = 0;
else if (off + size > file->decomp_data.size())
size = file->decomp_data.size() - off;
// std::cerr << "apfs_read: fuse_reply_buf(req, " << reinterpret_cast<uint64_t>(file->decomp_data.data()) + off << ", " << size << ")" << std::endl;
fuse_reply_buf(req, reinterpret_cast<const char *>(file->decomp_data.data()) + off, size);
}
}
static void dirbuf_add(fuse_req_t req, std::vector<char> &dirbuf, const char *name, fuse_ino_t ino)
{
struct stat st;
size_t oldsize;
memset(&st, 0, sizeof(st));
oldsize = dirbuf.size();
dirbuf.resize(oldsize + fuse_add_direntry(req, nullptr, 0, name, nullptr, 0));
st.st_ino = ino;
fuse_add_direntry(req, &dirbuf[oldsize], dirbuf.size() - oldsize, name, &st, dirbuf.size());
}
#define min(x, y) ((x) < (y) ? (x) : (y))
static int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, off_t off, size_t maxsize)
{
if (off < bufsize)
return fuse_reply_buf(req, buf + off, min(bufsize - off, maxsize));
else
return fuse_reply_buf(req, NULL, 0);
}
static void apfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi)
{
ApfsDir dir(*g_volume);
std::vector<ApfsDir::Name> dir_list;
Directory *dirptr = reinterpret_cast<Directory *>(fi->fh);
std::vector<char> &dirbuf = dirptr->dirbuf;
bool rc;
if (g_debug > 0)
std::cout << "apfs_readdir: " << std::hex << ino << std::endl;
if (dirbuf.size() == 0)
{
dirbuf.reserve(0x1000);
#if 0 // Not needed
if (ino != 1)
{
ApfsDir::Inode dirrec;
rc = dir.GetInode(dirrec, ino);
if (!rc)
{
fuse_reply_err(req, ENOENT);
return;
}
dirbuf_add(req, dirbuf, ".", ino);
dirbuf_add(req, dirbuf, "..", dirrec.ino.parent_id);
}
#endif
rc = dir.ListDirectory(dir_list, ino);
if (!rc)
{
fuse_reply_err(req, ENOENT);
return;
}
for (size_t k = 0; k < dir_list.size(); k++)
dirbuf_add(req, dirbuf, dir_list[k].name.c_str(), dir_list[k].inode_id);
}
reply_buf_limited(req, dirbuf.data(), dirbuf.size(), off, size);
}
static void apfs_readlink(fuse_req_t req, fuse_ino_t ino)
{
ApfsDir dir(*g_volume);
bool rc = false;
std::vector<uint8_t> data;
rc = dir.GetAttribute(data, ino, "com.apple.fs.symlink");
if (!rc)
fuse_reply_err(req, ENOENT);
else
fuse_reply_readlink(req, reinterpret_cast<const char *>(data.data()));
}
static void apfs_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
if (g_debug > 0)
std::cout << std::hex << "apfs_release " << ino << std::endl;
File *file = reinterpret_cast<File *>(fi->fh);
delete file;
fuse_reply_err(req, 0);
}
static void apfs_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
if (g_debug > 0)
std::cout << std::hex << "apfs_releasedir " << ino << std::endl;
Directory *dir = reinterpret_cast<Directory *>(fi->fh);
delete dir;
fuse_reply_err(req, 0);
}
static void apfs_statfs(fuse_req_t req, fuse_ino_t ino)
{
}
struct apfs {
char *disk;
char *logfile;
int vol_id;
};
static fuse_opt apfs_opfs[] =
{
// { "logfile=%s", offsetof(apfs, logfile), 0 },
{ "volume=%d", offsetof(apfs, vol_id), 0 },
FUSE_OPT_END
};
void usage(const char *name)
{
std::cout << name << " [-d level] [-o options] [-v volume-id] <device> <dir>" << std::endl;
}
bool add_option(std::string &optstr, const char *name, const char *value)
{
if (!optstr.empty())
optstr.push_back(',');
optstr.append(name);
if (value)
{
optstr.push_back('=');
optstr.append(value);
}
return true;
}
int main(int argc, char *argv[])
{
struct fuse_args args = FUSE_ARGS_INIT(0, nullptr);
struct fuse_chan *ch;
const char *mountpoint = nullptr;
const char *dev_path = nullptr;
int err = -1;
int opt;
std::string mount_options;
unsigned int volume_id = 0;
// static const char *dev_path = "/mnt/data/Projekte/VS17/Apfs/Data/ios_11_0_1.img";
// static const char *dev_path = "/mnt/data/Projekte/VS17/Apfs/Data/apfs_2vol_test_rw.dmg";
// static const char *dev_path = "/dev/sdd2";
// static const char *dev_path = "/mnt/data/Projekte/VS17/Apfs/Data/apfs_clone_test.img";
memset(&ops, 0, sizeof(ops));
// ops.bmap = apfs_bmap;
// ops.destroy = apfs_destroy;
ops.getattr = apfs_getattr;
ops.getxattr = apfs_getxattr;
ops.listxattr = apfs_listxattr;
ops.lookup = apfs_lookup;
ops.open = apfs_open;
ops.opendir = apfs_opendir;
ops.read = apfs_read;
ops.readdir = apfs_readdir;
ops.readlink = apfs_readlink;
ops.release = apfs_release;
ops.releasedir = apfs_releasedir;
// ops.statfs = apfs_statfs;
while ((opt = getopt(argc, argv, "d:o:v:")) != -1)
{
switch (opt)
{
case 'd':
g_debug = strtoul(optarg, nullptr, 10);
break;
case 'o':
add_option(mount_options, optarg, nullptr);
break;
case 'v':
volume_id = strtoul(optarg, nullptr, 10);
break;
default:
usage(argv[0]);
return 1;
break;
}
}
if ((argc - optind) != 2)
{
usage(argv[0]);
return 1;
}
dev_path = argv[optind];
mountpoint = argv[optind + 1];
add_option(mount_options, "ro", nullptr);
if (!g_disk.Open(dev_path))
{
std::cerr << "Device not found!" << std::endl;
return 1;
}
g_container = new ApfsContainer(g_disk, 0, g_disk.GetSize());
g_container->Init();
g_volume = g_container->GetVolume(volume_id);
if (!g_volume)
{
std::cerr << "Unable to get volume!" << std::endl;
delete g_container;
g_disk.Close();
return 1;
}
add_option(mount_options, "fsname", dev_path);
fuse_opt_add_arg(&args, "apfs-fuse");
fuse_opt_add_arg(&args, "-o");
fuse_opt_add_arg(&args, mount_options.c_str());
if ((ch = fuse_mount(mountpoint, &args)) != NULL)
{
struct fuse_session *se;
se = fuse_lowlevel_new(&args, &ops, sizeof(ops), NULL);
if (se != NULL) {
if (fuse_set_signal_handlers(se) != -1) {
fuse_session_add_chan(se, ch);
err = fuse_session_loop(se);
fuse_remove_signal_handlers(se);
fuse_session_remove_chan(ch);
}
fuse_session_destroy(se);
}
fuse_unmount(mountpoint, ch);
}
fuse_opt_free_args(&args);
delete g_volume;
delete g_container;
g_disk.Close();
return err ? 1 : 0;
}

3
apfsfuse/CMakeLists.txt Normal file
View File

@@ -0,0 +1,3 @@
add_executable(apfs-fuse ApfsFuse.cpp)
target_link_libraries(apfs-fuse ApfsLib fuse icuuc)
target_compile_definitions(apfs-fuse PRIVATE _FILE_OFFSET_BITS=64)