mirror of
https://github.com/sgan81/apfs-fuse.git
synced 2025-12-14 20:35:44 +01:00
First version of apfs-fuse.
This commit is contained in:
63
.gitattributes
vendored
Normal file
63
.gitattributes
vendored
Normal 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
270
.gitignore
vendored
Normal 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
4
Apfs.kdev4
Normal file
@@ -0,0 +1,4 @@
|
||||
[Project]
|
||||
CreatedFrom=CMakeLists.txt
|
||||
Manager=KDevCMakeManager
|
||||
Name=Apfs
|
||||
61
Apfs.sln
Normal file
61
Apfs.sln
Normal 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
185
ApfsDump/Apfs.cpp
Normal 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
163
ApfsDump/ApfsDump.vcxproj
Normal 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>
|
||||
31
ApfsDump/ApfsDump.vcxproj.filters
Normal file
31
ApfsDump/ApfsDump.vcxproj.filters
Normal 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
2
ApfsDump/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_executable(apfs-dump Apfs.cpp)
|
||||
target_link_libraries(apfs-dump ApfsLib icuuc)
|
||||
169
ApfsLib/ApfsContainer.cpp
Normal file
169
ApfsLib/ApfsContainer.cpp
Normal 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
66
ApfsLib/ApfsContainer.h
Normal 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
498
ApfsLib/ApfsDir.cpp
Normal 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
78
ApfsLib/ApfsDir.h
Normal 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
170
ApfsLib/ApfsLib.vcxproj
Normal 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>
|
||||
87
ApfsLib/ApfsLib.vcxproj.filters
Normal file
87
ApfsLib/ApfsLib.vcxproj.filters
Normal 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>
|
||||
29
ApfsLib/ApfsNodeMapper.cpp
Normal file
29
ApfsLib/ApfsNodeMapper.cpp
Normal 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
31
ApfsLib/ApfsNodeMapper.h
Normal 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;
|
||||
};
|
||||
113
ApfsLib/ApfsNodeMapperBTree.cpp
Normal file
113
ApfsLib/ApfsNodeMapperBTree.cpp
Normal 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;
|
||||
}
|
||||
45
ApfsLib/ApfsNodeMapperBTree.h
Normal file
45
ApfsLib/ApfsNodeMapperBTree.h
Normal 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
84
ApfsLib/ApfsVolume.cpp
Normal 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
59
ApfsLib/ApfsVolume.h
Normal 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
584
ApfsLib/BTree.cpp
Normal 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
197
ApfsLib/BTree.h
Normal 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
1191
ApfsLib/BlockDumper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
81
ApfsLib/BlockDumper.h
Normal file
81
ApfsLib/BlockDumper.h
Normal 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
2
ApfsLib/CMakeLists.txt
Normal 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
97
ApfsLib/Crc32.cpp
Normal 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
47
ApfsLib/Crc32.h
Normal 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
216
ApfsLib/Decmpfs.cpp
Normal 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
36
ApfsLib/Decmpfs.h
Normal 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
129
ApfsLib/Disk.cpp
Normal 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
66
ApfsLib/Disk.h
Normal 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
449
ApfsLib/DiskStruct.h
Normal 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
18
ApfsLib/FastCompression.h
Normal 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
25
ApfsLib/Global.h
Normal 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
327
ApfsLib/Inflate.cpp
Normal 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
61
ApfsLib/Inflate.h
Normal 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
310
ApfsLib/Util.cpp
Normal 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
33
ApfsLib/Util.h
Normal 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
546
ApfsLib/lzvn_decode.s
Normal 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
20
CMakeLists.txt
Normal 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
339
LICENSE
Normal 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
58
README.md
Normal 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
2
Test/CMakeLists.txt
Normal 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
124
Test/Test.cpp
Normal 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
164
Test/Test.vcxproj
Normal 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
22
Test/Test.vcxproj.filters
Normal 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
631
apfsfuse/ApfsFuse.cpp
Normal 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
3
apfsfuse/CMakeLists.txt
Normal 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)
|
||||
Reference in New Issue
Block a user