├── .gitattributes
├── .gitignore
├── .gitmodules
├── ApfsDump
├── Apfs.cpp
├── Dumper.cpp
└── Dumper.h
├── ApfsDumpQuick
└── ApfsDumpQuick.cpp
├── ApfsLib
├── ApfsContainer.cpp
├── ApfsContainer.h
├── ApfsDir.cpp
├── ApfsDir.h
├── ApfsNodeMapper.cpp
├── ApfsNodeMapper.h
├── ApfsNodeMapperBTree.cpp
├── ApfsNodeMapperBTree.h
├── ApfsTypes.h
├── ApfsVolume.cpp
├── ApfsVolume.h
├── BTree.cpp
├── BTree.h
├── BlockDumper.cpp
├── BlockDumper.h
├── CheckPointMap.cpp
├── CheckPointMap.h
├── Crc32.cpp
├── Crc32.h
├── Decmpfs.cpp
├── Decmpfs.h
├── Device.cpp
├── Device.h
├── DeviceDMG.cpp
├── DeviceDMG.h
├── DeviceLinux.cpp
├── DeviceLinux.h
├── DeviceMac.cpp
├── DeviceMac.h
├── DeviceSparseImage.cpp
├── DeviceSparseImage.h
├── DeviceVDI.cpp
├── DeviceVDI.h
├── DeviceWinFile.cpp
├── DeviceWinFile.h
├── DeviceWinPhys.cpp
├── DeviceWinPhys.h
├── DiskImageFile.cpp
├── DiskImageFile.h
├── DiskStruct.h
├── Endian.h
├── Global.h
├── GptPartitionMap.cpp
├── GptPartitionMap.h
├── KeyMgmt.cpp
├── KeyMgmt.h
├── PList.cpp
├── PList.h
├── Unicode.cpp
├── Unicode.h
├── UnicodeTables_v10.h
├── Util.cpp
└── Util.h
├── ApfsUtil
└── ApfsUtil.cpp
├── CMakeLists.txt
├── Crypto
├── Aes.cpp
├── Aes.h
├── AesXts.cpp
├── AesXts.h
├── Asn1Der.cpp
├── Asn1Der.h
├── Crypto.cpp
├── Crypto.h
├── Des.cpp
├── Des.h
├── Sha1.cpp
├── Sha1.h
├── Sha256.cpp
├── Sha256.h
├── TripleDes.cpp
└── TripleDes.h
├── LICENSE
├── README.md
└── apfsfuse
└── ApfsFuse.cpp
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 |
263 | # Own Files
264 | Data/
265 |
266 | # Linux
267 | build/
268 | .kdev4/
269 | data/
270 | doc/
271 |
272 | # Mac
273 | .DS_Store
274 | xcode/
275 |
276 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "3rdparty/lzfse"]
2 | path = 3rdparty/lzfse
3 | url = https://github.com/lzfse/lzfse.git
4 |
--------------------------------------------------------------------------------
/ApfsDump/Apfs.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | #include "Dumper.h"
33 |
34 | #ifdef __linux__
35 | #include
36 | #endif
37 |
38 | #undef RAW_VERBOSE
39 |
40 |
41 | constexpr size_t BLOCKSIZE = 0x1000;
42 |
43 | volatile bool g_abort = 0;
44 |
45 | void DumpBlockTrunc(std::ostream &os, const uint8_t *data)
46 | {
47 | unsigned int sz = BLOCKSIZE - 1;
48 |
49 | while (sz > 0 && data[sz] == 0)
50 | sz--;
51 |
52 | sz = (sz + 0x10) & 0xFFFFFFF0;
53 |
54 | DumpHex(os, data, sz);
55 | }
56 |
57 | #if 0
58 |
59 | void MapBlocks(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt)
60 | {
61 | using namespace std;
62 |
63 | uint64_t bid;
64 | uint8_t block[BLOCKSIZE];
65 | const APFS_ObjHeader * const blk = reinterpret_cast(block);
66 | const APFS_TableHeader * const tbl = reinterpret_cast(block + sizeof(APFS_ObjHeader));
67 | bool last_was_used = false;
68 |
69 | os << hex << uppercase << setfill('0');
70 |
71 | os << "[Block] | oid | xid | type | subtype | Page | Levl | Entries | Description" << endl;
72 | os << "---------+----------+----------+----------+----------+------+------+----------+---------------------------------" << endl;
73 |
74 | for (bid = 0; bid < bcnt && !g_abort; bid++)
75 | {
76 | if ((bid & 0xFFF) == 0)
77 | {
78 | std::cout << '.';
79 | std::cout.flush();
80 | }
81 |
82 | dev.Read(block, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
83 |
84 | if (IsEmptyBlock(block, BLOCKSIZE))
85 | {
86 | if (last_was_used)
87 | os << "---------+----------+----------+----------+----------+------+------+----------+ Empty" << endl;
88 | last_was_used = false;
89 | continue;
90 | }
91 |
92 | if (VerifyBlock(block, BLOCKSIZE))
93 | {
94 | os << setw(8) << bid << " | ";
95 | os << setw(8) << blk->oid << " | ";
96 | os << setw(8) << blk->xid << " | ";
97 | os << setw(8) << blk->type << " | ";
98 | os << setw(8) << blk->subtype << " | ";
99 | os << setw(4) << tbl->page << " | ";
100 | os << setw(4) << tbl->level << " | ";
101 | os << setw(8) << tbl->entries_cnt << " | ";
102 | os << BlockDumper::GetNodeType(blk->type, blk->subtype);
103 | if (APFS_OBJ_TYPE(blk->type) == 2)
104 | os << " [Root]";
105 | os << endl;
106 | last_was_used = true;
107 | }
108 | else
109 | {
110 | os << setw(8) << bid;
111 | os << " | | | | | | | | Data" << endl;
112 | last_was_used = true;
113 | }
114 | }
115 |
116 | os << endl;
117 | }
118 |
119 | void ScanBlocks(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt)
120 | {
121 | BlockDumper bd(os, BLOCKSIZE);
122 | uint64_t bid;
123 | uint8_t block[BLOCKSIZE];
124 |
125 | bd.SetTextFlags(0x00);
126 |
127 | for (bid = 0; bid < bcnt && !g_abort; bid++)
128 | {
129 | if ((bid & 0xFFF) == 0)
130 | {
131 | std::cout << '.';
132 | std::cout.flush();
133 | }
134 |
135 | dev.Read(block, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
136 |
137 | if (IsEmptyBlock(block, BLOCKSIZE))
138 | continue;
139 |
140 | if (VerifyBlock(block, BLOCKSIZE))
141 | bd.DumpNode(block, bid);
142 | else
143 | {
144 | #if 0
145 | os << std::hex << std::setw(16) << blk_nr << std::endl;
146 | DumpBlockTrunc(os, block);
147 | os << std::endl;
148 | os << "===========================================================================================================================" << std::endl;
149 | os << std::endl;
150 | #endif
151 | }
152 | }
153 | }
154 |
155 | void DumpSpaceman(std::ostream &os, Device &dev, uint64_t bid_start, uint64_t bcnt)
156 | {
157 | uint8_t sb[BLOCKSIZE];
158 | uint8_t data[BLOCKSIZE];
159 | BlockDumper bd(os, BLOCKSIZE);
160 | const APFS_ObjHeader *bhdr;
161 | const APFS_Spaceman *sm;
162 | const APFS_NX_Superblock *nxsb;
163 | uint64_t bid;
164 | uint64_t cnt;
165 | uint64_t smbmp_bid = 0;
166 | uint64_t smbmp_cnt = 0;
167 | uint64_t volbmp_bid = 0;
168 | uint64_t volbmp_cnt = 0;
169 |
170 | (void)bcnt;
171 |
172 | if (!dev.Read(sb, bid_start * BLOCKSIZE, BLOCKSIZE))
173 | return;
174 |
175 | if (!VerifyBlock(sb, BLOCKSIZE))
176 | return;
177 |
178 | nxsb = reinterpret_cast(sb);
179 | if (nxsb->hdr.type != 0x80000001)
180 | return;
181 |
182 | if (nxsb->nx_magic != 0x4253584E)
183 | return;
184 |
185 | bd.DumpNode(sb, 0);
186 |
187 | os << std::endl;
188 | os << "Dumping Superblock Area" << std::endl;
189 | os << std::endl;
190 |
191 | bid = nxsb->nx_xp_desc_base;
192 | cnt = nxsb->nx_xp_desc_blocks;
193 |
194 | while (cnt > 0)
195 | {
196 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
197 | bd.DumpNode(data, bid);
198 | cnt--;
199 | bid++;
200 | }
201 |
202 | os << std::endl;
203 | os << "Dumping Spaceman Area" << std::endl;
204 | os << std::endl;
205 |
206 | bid = nxsb->nx_xp_data_base;
207 | cnt = nxsb->nx_xp_data_blocks;
208 |
209 | while (cnt > 0)
210 | {
211 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
212 | bd.DumpNode(data, bid);
213 | cnt--;
214 | bid++;
215 |
216 | bhdr = reinterpret_cast(data);
217 | if (bhdr->type == 0x80000005)
218 | {
219 | sm = reinterpret_cast(data);
220 | smbmp_bid = sm->ip_bm_base_address;
221 | smbmp_cnt = sm->ip_bitmap_block_count;
222 | volbmp_bid = sm->ip_base_address;
223 | volbmp_cnt = sm->ip_block_count;
224 | }
225 | }
226 |
227 | bid = smbmp_bid;
228 | cnt = smbmp_cnt;
229 |
230 | while (cnt > 0)
231 | {
232 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
233 | bd.DumpNode(data, bid);
234 | cnt--;
235 | bid++;
236 | }
237 |
238 | bid = volbmp_bid;
239 | cnt = volbmp_cnt;
240 |
241 | while (cnt > 0)
242 | {
243 | dev.Read(data, (bid_start + bid) * BLOCKSIZE, BLOCKSIZE);
244 | bd.DumpNode(data, bid);
245 | cnt--;
246 | bid++;
247 | }
248 | }
249 |
250 | #endif
251 |
252 | static void ctrl_c_handler(int sig)
253 | {
254 | (void)sig;
255 | g_abort = true;
256 | }
257 |
258 | #if 0
259 |
260 | int main(int argc, const char *argv[])
261 | {
262 | if (argc < 3)
263 | {
264 | std::cerr << "Syntax: apfs-dump file.img output.txt [map.txt]" << std::endl;
265 | return 1;
266 | }
267 |
268 | Device *dev;
269 | std::ofstream os;
270 | uint64_t bid_start = 0;
271 | uint64_t bcnt = 0;
272 |
273 | g_debug = 255;
274 |
275 | #if defined(__linux__) || defined(__APPLE__)
276 | signal(SIGINT, ctrl_c_handler);
277 | #endif
278 |
279 | dev = Device::OpenDevice(argv[1]);
280 |
281 | if (!dev)
282 | {
283 | std::cerr << "Device " << argv[1] << " not found." << std::endl;
284 | return 2;
285 | }
286 |
287 | {
288 | GptPartitionMap pmap;
289 | if (pmap.LoadAndVerify(*dev))
290 | {
291 | int partid = pmap.FindFirstAPFSPartition();
292 | if (partid >= 0)
293 | {
294 | std::cout << "Dumping EFI partition" << std::endl;
295 | pmap.GetPartitionOffsetAndSize(partid, bid_start, bcnt);
296 | bid_start /= BLOCKSIZE;
297 | bcnt /= BLOCKSIZE;
298 | }
299 | }
300 | }
301 |
302 | if (bcnt == 0)
303 | bcnt = dev->GetSize() / BLOCKSIZE;
304 |
305 | if (argc > 3)
306 | {
307 | os.open(argv[3]);
308 | if (!os.is_open())
309 | {
310 | std::cerr << "Could not open output file " << argv[3] << std::endl;
311 | dev->Close();
312 | delete dev;
313 | return 3;
314 | }
315 |
316 | MapBlocks(os, *dev, bid_start, bcnt);
317 | os.close();
318 | }
319 |
320 | os.open(argv[2]);
321 | if (!os.is_open())
322 | {
323 | std::cerr << "Could not open output file " << argv[2] << std::endl;
324 | dev->Close();
325 | delete dev;
326 | return 3;
327 | }
328 |
329 | DumpSpaceman(os, *dev, bid_start, bcnt);
330 | ScanBlocks(os, *dev, bid_start, bcnt);
331 |
332 | dev->Close();
333 | os.close();
334 |
335 | delete dev;
336 |
337 | return 0;
338 | }
339 |
340 | #else
341 |
342 | void usage()
343 | {
344 | std::cerr << "Syntax:" << std::endl;
345 | std::cerr << "apfs-dump [-map mapfile.txt] file.img output.txt" << std::endl;
346 | std::cerr << "apfs-dump [-map mapfile.txt] -fusion main.img tier2.img output.txt" << std::endl;
347 | }
348 |
349 | int main(int argc, const char *argv[])
350 | {
351 | const char *name_dev_main = 0;
352 | const char *name_dev_tier2 = 0;
353 | const char *name_map = 0;
354 | const char *name_output = 0;
355 | bool use_fusion = false;
356 |
357 | int n;
358 | int idx = 0;
359 |
360 | for (n = 1; n < argc; n++)
361 | {
362 | if (!strcmp(argv[n], "-map"))
363 | {
364 | n++;
365 | if (n >= argc)
366 | {
367 | usage();
368 | return 1;
369 | }
370 | name_map = argv[n];
371 | }
372 |
373 | else if (!strcmp(argv[n], "-fusion"))
374 | use_fusion = true;
375 |
376 | else
377 | {
378 | switch (idx)
379 | {
380 | case 0:
381 | name_dev_main = argv[n];
382 | printf("main: %s\n", name_dev_main);
383 | if (use_fusion)
384 | idx++;
385 | else
386 | idx += 2;
387 | break;
388 | case 1:
389 | name_dev_tier2 = argv[n];
390 | printf("tier2: %s\n", name_dev_tier2);
391 | idx++;
392 | break;
393 | case 2:
394 | name_output = argv[n];
395 | printf("out: %s\n", name_output);
396 | idx++;
397 | break;
398 | default:
399 | usage();
400 | return 1;
401 | break;
402 | }
403 | }
404 | }
405 |
406 | if ((name_output == 0) || (use_fusion && name_dev_tier2 == 0))
407 | {
408 | usage();
409 | return 1;
410 | }
411 |
412 | std::unique_ptr dev_main;
413 | std::unique_ptr dev_tier2;
414 | std::ofstream os;
415 |
416 | g_debug = 255;
417 |
418 | #if defined(__linux__) || defined(__APPLE__)
419 | signal(SIGINT, ctrl_c_handler);
420 | #endif
421 |
422 | dev_main.reset(Device::OpenDevice(name_dev_main));
423 | if (use_fusion)
424 | dev_tier2.reset(Device::OpenDevice(name_dev_tier2));
425 |
426 | if (!dev_main)
427 | {
428 | std::cerr << "Device " << name_dev_main << " not found." << std::endl;
429 | return 2;
430 | }
431 |
432 | if (use_fusion && !dev_tier2)
433 | {
434 | std::cerr << "Device " << name_dev_tier2 << " not found." << std::endl;
435 | return 2;
436 | }
437 |
438 | {
439 | Dumper dmp(dev_main.get(), dev_tier2.get());
440 |
441 | if (!dmp.Initialize())
442 | return -1;
443 |
444 | #if 1
445 | if (name_map)
446 | {
447 | os.open(name_map);
448 | if (!os.is_open())
449 | {
450 | std::cerr << "Could not open map file " << name_map << std::endl;
451 | dev_main->Close();
452 | if (dev_tier2)
453 | dev_tier2->Close();
454 | return 3;
455 | }
456 |
457 | dmp.DumpBlockList(os);
458 | os.close();
459 | }
460 | #endif
461 |
462 | os.open(name_output);
463 | if (!os.is_open())
464 | {
465 | std::cerr << "Could not open output file " << name_output << std::endl;
466 | dev_main->Close();
467 | if (dev_tier2)
468 | dev_tier2->Close();
469 | return 3;
470 | }
471 |
472 | dmp.DumpContainer(os);
473 | }
474 |
475 | dev_main->Close();
476 | if (dev_tier2)
477 | dev_tier2->Close();
478 | os.close();
479 |
480 | return 0;
481 | }
482 |
483 | #endif
484 |
--------------------------------------------------------------------------------
/ApfsDump/Dumper.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include
7 |
8 | extern volatile bool g_abort; // In Apfs.cpp
9 |
10 | class Device;
11 |
12 | class Dumper
13 | {
14 | public:
15 | Dumper(Device *dev_main, Device *dev_tier2);
16 | ~Dumper();
17 |
18 | bool Initialize();
19 | bool DumpContainer(std::ostream &os);
20 | bool DumpBlockList(std::ostream &os);
21 |
22 | private:
23 | bool Read(void *data, uint64_t paddr, uint64_t cnt);
24 | bool Read(std::vector &data, uint64_t paddr, uint64_t cnt);
25 |
26 | void Decrypt(uint8_t *data, size_t size, uint64_t paddr);
27 |
28 | const checkpoint_mapping_t* cpm_lookup(const checkpoint_map_phys_t *cpm, uint64_t oid);
29 |
30 | Device *m_dev_main;
31 | Device *m_dev_tier2;
32 | uint64_t m_base_main;
33 | uint64_t m_size_main;
34 | uint64_t m_base_tier2;
35 | uint64_t m_size_tier2;
36 | uint32_t m_blocksize;
37 |
38 | AesXts m_aes;
39 | bool m_is_encrypted;
40 | };
41 |
--------------------------------------------------------------------------------
/ApfsDumpQuick/ApfsDumpQuick.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 | #include
24 |
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 |
32 | int main(int argc, char *argv[])
33 | {
34 | bool rc;
35 | int volume_id;
36 | int n;
37 | std::unique_ptr main_disk;
38 | std::unique_ptr tier2_disk;
39 | uint64_t main_offset;
40 | uint64_t tier2_offset;
41 | uint64_t main_size;
42 | uint64_t tier2_size;
43 |
44 | const char *main_name;
45 | const char *tier2_name;
46 | const char *output_name;
47 |
48 | if (argc < 3)
49 | {
50 | std::cerr << "Syntax: apfs-dump-quick [-f fusion-device] " << std::endl;
51 | return -1;
52 | }
53 |
54 | main_name = argv[1];
55 | if (!strcmp(argv[2], "-f"))
56 | {
57 | if (argc < 5)
58 | std::cerr << "Syntax: apfs-dump-quick [-f fusion-secondary-device] " << std::endl;
59 |
60 | tier2_name = argv[3];
61 | output_name = argv[4];
62 | }
63 | else
64 | {
65 | tier2_name = nullptr;
66 | output_name = argv[2];
67 | }
68 |
69 | g_debug = 255;
70 |
71 | main_disk.reset(Device::OpenDevice(main_name));
72 | if (tier2_name)
73 | tier2_disk.reset(Device::OpenDevice(tier2_name));
74 |
75 | if (!main_disk)
76 | {
77 | std::cerr << "Unable to open device " << main_name << std::endl;
78 | return -1;
79 | }
80 |
81 | if (tier2_name && !tier2_disk)
82 | {
83 | std::cerr << "Unable to open secondary device " << tier2_name << std::endl;
84 | return -1;
85 | }
86 |
87 | std::ofstream st;
88 | st.open(output_name);
89 |
90 | if (!st.is_open())
91 | {
92 | std::cerr << "Unable to open output file " << output_name << std::endl;
93 | main_disk->Close();
94 | return -1;
95 | }
96 |
97 | main_offset = 0;
98 | main_size = main_disk->GetSize();
99 |
100 | tier2_offset = 0;
101 | if (tier2_disk)
102 | tier2_size = tier2_disk->GetSize();
103 | else
104 | tier2_size = 0;
105 |
106 | GptPartitionMap gpt;
107 | if (gpt.LoadAndVerify(*main_disk.get()))
108 | {
109 | std::cout << "Info: Found valid GPT partition table on main device. Dumping first APFS partition." << std::endl;
110 |
111 | n = gpt.FindFirstAPFSPartition();
112 | if (n != -1)
113 | gpt.GetPartitionOffsetAndSize(n, main_offset, main_size);
114 | }
115 |
116 | if (tier2_disk && gpt.LoadAndVerify(*tier2_disk.get()))
117 | {
118 | std::cout << "Info: Found valid GPT partition table on tier2 device. Dumping first APFS partition." << std::endl;
119 |
120 | n = gpt.FindFirstAPFSPartition();
121 | if (n != -1)
122 | gpt.GetPartitionOffsetAndSize(n, tier2_offset, tier2_size);
123 | }
124 |
125 | std::unique_ptr container(new ApfsContainer(main_disk.get(), main_offset, main_size, tier2_disk.get(), tier2_offset, tier2_size));
126 |
127 | rc = container->Init();
128 |
129 | if (!rc)
130 | {
131 | std::cerr << "Unable to init container." << std::endl;
132 | container.reset();
133 | main_disk->Close();
134 | return -1;
135 | }
136 |
137 | #if 1
138 | BlockDumper bd(st, container->GetBlocksize());
139 |
140 | container->dump(bd);
141 |
142 | #if 1
143 | for (volume_id = 0; volume_id < NX_MAX_FILE_SYSTEMS; volume_id++)
144 | {
145 | ApfsVolume *vol;
146 |
147 | vol = container->GetVolume(volume_id);
148 |
149 | if (vol)
150 | {
151 | std::cout << "Volume " << volume_id << ": " << vol->name() << std::endl;
152 |
153 | vol->dump(bd);
154 |
155 | delete vol;
156 | }
157 | }
158 | #endif
159 | #endif
160 |
161 | #if 0
162 | ApfsVolume *vol;
163 |
164 | vol = container->GetVolume(0);
165 |
166 | if (vol)
167 | {
168 | ApfsDir dir(*vol);
169 | ApfsDir::Inode ino;
170 | std::vector root_list;
171 |
172 | #if 0
173 | dir.GetInode(ino, 0x15);
174 |
175 | std::cout << "Directory is called " << ino.name << std::endl;
176 |
177 | dir.ListDirectory(root_list, 0x15);
178 |
179 | for (auto it = root_list.cbegin(); it != root_list.cend(); ++it)
180 | {
181 | std::cout << it->name << " : " << it->inode_id << std::endl;
182 | }
183 | #endif
184 |
185 | #if 1
186 | std::vector buf;
187 | buf.resize(0x29000);
188 |
189 | dir.ReadFile(buf.data(), 0x39, 0, 0x29000);
190 | #endif
191 | }
192 |
193 | #endif
194 |
195 | container.reset();
196 |
197 | st.close();
198 |
199 | main_disk->Close();
200 |
201 | return 0;
202 | }
203 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsContainer.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "Global.h"
23 | #include "BTree.h"
24 | #include "DiskStruct.h"
25 | #include "Device.h"
26 | #include "CheckPointMap.h"
27 | #include "ApfsNodeMapperBTree.h"
28 | #include "KeyMgmt.h"
29 |
30 | #include
31 | #include
32 |
33 | class ApfsVolume;
34 | class BlockDumper;
35 |
36 | class ApfsContainer
37 | {
38 | public:
39 | ApfsContainer(Device *disk_main, uint64_t main_start, uint64_t main_len, Device *disk_tier2 = 0, uint64_t tier2_start = 0, uint64_t tier2_len = 0);
40 | ~ApfsContainer();
41 |
42 | bool Init(xid_t req_xid = 0);
43 |
44 | ApfsVolume *GetVolume(unsigned int fsid, const std::string &passphrase = std::string(), xid_t snap_xid = 0);
45 | bool GetVolumeInfo(unsigned int fsid, apfs_superblock_t &apsb);
46 |
47 | bool ReadBlocks(uint8_t *data, paddr_t paddr, uint64_t blkcnt = 1) const;
48 | bool ReadAndVerifyHeaderBlock(uint8_t *data, paddr_t paddr) const;
49 |
50 | uint32_t GetBlocksize() const { return m_nx.nx_block_size; }
51 | uint64_t GetBlockCount() const { return m_nx.nx_block_count; }
52 | uint64_t GetFreeBlocks() const { return m_sm->sm_dev[SD_MAIN].sm_free_count + m_sm->sm_dev[SD_TIER2].sm_free_count; }
53 |
54 | bool GetVolumeKey(uint8_t *key, const apfs_uuid_t &vol_uuid, const char *password = nullptr);
55 | bool GetPasswordHint(std::string &hint, const apfs_uuid_t &vol_uuid);
56 | bool IsUnencrypted() const { return m_keymgr.IsUnencrypted(); }
57 |
58 | void dump(BlockDumper& bd);
59 |
60 | private:
61 | Device *m_main_disk;
62 | const uint64_t m_main_part_start;
63 | const uint64_t m_main_part_len;
64 |
65 | Device *m_tier2_disk;
66 | const uint64_t m_tier2_part_start;
67 | const uint64_t m_tier2_part_len;
68 |
69 | std::string m_passphrase;
70 |
71 | nx_superblock_t m_nx;
72 |
73 | CheckPointMap m_cpm;
74 | ApfsNodeMapperBTree m_omap;
75 |
76 | std::vector m_sm_data;
77 | const spaceman_phys_t *m_sm;
78 | // Block_8_11 -> omap
79 |
80 | // BTree m_omap_tree; // see ApfsNodeMapperBTree
81 | BTree m_fq_tree_mgr;
82 | BTree m_fq_tree_vol;
83 |
84 | KeyManager m_keymgr;
85 | };
86 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsDir.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | #include "DiskStruct.h"
26 |
27 | class BTree;
28 | class ApfsVolume;
29 |
30 | class ApfsDir
31 | {
32 | public:
33 | struct Inode
34 | {
35 | Inode();
36 | Inode(const Inode &other);
37 |
38 | /* TODO */
39 | uint64_t obj_id;
40 |
41 | uint64_t parent_id;
42 | uint64_t private_id;
43 |
44 | uint64_t create_time;
45 | uint64_t mod_time;
46 | uint64_t change_time;
47 | uint64_t access_time;
48 |
49 | uint64_t internal_flags;
50 |
51 | uint64_t nchildren_nlink;
52 |
53 | cp_key_class_t default_protection_class;
54 | uint32_t write_generation_counter;
55 | uint32_t bsd_flags;
56 | uint32_t owner;
57 | uint32_t group;
58 | uint16_t mode;
59 |
60 | uint64_t uncompressed_size;
61 |
62 | uint64_t snap_xid;
63 | uint64_t delta_tree_oid;
64 | uint64_t prev_fsize;
65 | // FinderInfo
66 | uint64_t ds_size;
67 | uint64_t ds_alloced_size;
68 | uint64_t ds_default_crypto_id;
69 | uint64_t ds_total_bytes_written;
70 | uint64_t ds_total_bytes_read;
71 | // j_dir_stats_val_t dir_stats;
72 | apfs_uuid_t fs_uuid;
73 | uint64_t sparse_bytes;
74 | uint32_t document_id;
75 | uint32_t rdev;
76 | std::string name;
77 |
78 | uint32_t optional_present_flags;
79 |
80 | enum PresentFlags {
81 | INO_HAS_SNAP_XID = 1,
82 | INO_HAS_DELTA_TREE_OID = 2,
83 | INO_HAS_DOCUMENT_ID = 4,
84 | INO_HAS_NAME = 8,
85 | INO_HAS_PREV_FSIZE = 16,
86 | INO_HAS_FINDER_INFO = 64,
87 | INO_HAS_DSTREAM = 128,
88 | INO_HAS_DIR_STATS = 512,
89 | INO_HAS_FS_UUID = 1024,
90 | INO_HAS_SPARSE_BYTES = 4096,
91 | INO_HAS_RDEV = 8192
92 | };
93 | };
94 |
95 | struct DirRec
96 | {
97 | DirRec();
98 | DirRec(const DirRec &other);
99 |
100 | uint64_t parent_id;
101 | uint32_t hash;
102 | std::string name;
103 |
104 | uint64_t file_id;
105 | uint64_t date_added;
106 |
107 | uint64_t sibling_id;
108 | uint16_t flags;
109 | bool has_sibling_id;
110 | };
111 |
112 | struct XAttr
113 | {
114 | XAttr();
115 | XAttr(const XAttr &other);
116 |
117 | uint16_t flags;
118 | uint16_t xdata_len;
119 | j_xattr_dstream_t xstrm;
120 | };
121 |
122 |
123 | ApfsDir(ApfsVolume &vol);
124 | ~ApfsDir();
125 |
126 | bool GetInode(Inode &res, uint64_t inode);
127 |
128 | bool ListDirectory(std::vector &dir, uint64_t inode);
129 | bool LookupName(DirRec &res, uint64_t parent_id, const char *name);
130 | bool ReadFile(void *data, uint64_t inode, uint64_t offs, size_t size);
131 | bool ListAttributes(std::vector &names, uint64_t inode);
132 | bool GetAttribute(std::vector &data, uint64_t inode, const char *name);
133 | bool GetAttributeInfo(XAttr &attr, uint64_t inode, const char *name);
134 |
135 | private:
136 | static int CompareStdDirKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
137 | static int CompareFextKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
138 |
139 | ApfsVolume &m_vol;
140 | BTree &m_fs_tree;
141 | uint32_t m_txt_fmt;
142 | uint32_t m_blksize;
143 | uint64_t m_blksize_mask_hi;
144 | uint64_t m_blksize_mask_lo;
145 | int m_blksize_sh;
146 | std::vector m_tmp_blk;
147 | };
148 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsNodeMapper.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #include "ApfsNodeMapper.h"
21 |
22 | ApfsNodeMapper::ApfsNodeMapper()
23 | {
24 | }
25 |
26 |
27 | ApfsNodeMapper::~ApfsNodeMapper()
28 | {
29 | }
30 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsNodeMapper.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "ApfsTypes.h"
23 |
24 | struct omap_res_t
25 | {
26 | oid_t oid;
27 | xid_t xid;
28 | uint32_t flags;
29 | uint32_t size;
30 | paddr_t paddr;
31 | };
32 |
33 | class ApfsNodeMapper
34 | {
35 | public:
36 | ApfsNodeMapper();
37 | virtual ~ApfsNodeMapper();
38 |
39 | virtual bool Lookup(omap_res_t &res, oid_t oid, xid_t xid) = 0;
40 | };
41 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsNodeMapperBTree.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | #include "ApfsNodeMapperBTree.h"
25 | #include "ApfsContainer.h"
26 |
27 | static int CompareOMapKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context)
28 | {
29 | (void)context;
30 | (void)skey_len;
31 | (void)ekey_len;
32 |
33 | assert(skey_len == sizeof(omap_key_t));
34 | assert(ekey_len == sizeof(omap_val_t));
35 |
36 | const omap_key_t *skey_map = reinterpret_cast(skey);
37 | const omap_key_t *ekey_map = reinterpret_cast(ekey);
38 |
39 | if (ekey_map->ok_oid < skey_map->ok_oid)
40 | return -1;
41 | if (ekey_map->ok_oid > skey_map->ok_oid)
42 | return 1;
43 | if (ekey_map->ok_xid < skey_map->ok_xid)
44 | return -1;
45 | if (ekey_map->ok_xid > skey_map->ok_xid)
46 | return 1;
47 | return 0;
48 | }
49 |
50 | ApfsNodeMapperBTree::ApfsNodeMapperBTree(ApfsContainer &container) :
51 | m_tree(container),
52 | m_container(container)
53 | {
54 | }
55 |
56 | ApfsNodeMapperBTree::~ApfsNodeMapperBTree()
57 | {
58 | }
59 |
60 | bool ApfsNodeMapperBTree::Init(oid_t omap_oid, xid_t xid)
61 | {
62 | std::vector blk;
63 |
64 | blk.resize(m_container.GetBlocksize());
65 |
66 | if (!m_container.ReadAndVerifyHeaderBlock(blk.data(), omap_oid))
67 | {
68 | std::cerr << "ERROR: Invalid omap block @ oid 0x" << std::hex << omap_oid << std::endl;
69 | return false;
70 | }
71 |
72 | memcpy(&m_omap, blk.data(), sizeof(omap_phys_t));
73 |
74 | if ((m_omap.om_o.o_type & OBJECT_TYPE_MASK) == OBJECT_TYPE_BTREE)
75 | {
76 | std::cerr << "ERROR: Wrong omap type 0x" << std::hex << m_omap.om_o.o_type << std::endl;
77 | return false;
78 | }
79 |
80 | return m_tree.Init(m_omap.om_tree_oid, xid);
81 | }
82 |
83 | bool ApfsNodeMapperBTree::Lookup(omap_res_t &omr, oid_t oid, xid_t xid)
84 | {
85 | omap_key_t key;
86 |
87 | const omap_key_t *res_key = nullptr;
88 | const omap_val_t *res_val = nullptr;
89 |
90 | BTreeEntry res;
91 |
92 | key.ok_oid = oid;
93 | key.ok_xid = xid;
94 |
95 | // std::cout << std::hex << "Omap Lookup: oid = " << oid << ", xid = " << xid << " => ";
96 |
97 | if (!m_tree.Lookup(res, &key, sizeof(key), CompareOMapKey, this, false))
98 | {
99 | // std::cout << "NOT FOUND" << std::endl;
100 | std::cerr << std::hex << "oid " << oid << " xid " << xid << " NOT FOUND!!!" << std::endl;
101 | return false;
102 | }
103 |
104 | assert(res.val_len == sizeof(omap_val_t));
105 |
106 | res_key = reinterpret_cast(res.key);
107 | res_val = reinterpret_cast(res.val);
108 |
109 | #if 0
110 | if (g_debug & Dbg_Info) {
111 | std::cout << std::hex << "Omap Lookup: oid=" << oid << " xid=" << xid << ": ";
112 | std::cout << "oid=" << res_key->ok_oid << " xid=" << res_key->ok_xid << " => flags=" << res_val->ov_flags << " size=" << res_val->ov_size << " paddr=" << res_val->ov_paddr << std::endl;
113 | }
114 | #endif
115 |
116 | if (key.ok_oid != res_key->ok_oid)
117 | {
118 | // std::cout << "NOT FOUND" << std::endl;
119 | std::cerr << std::hex << "oid " << oid << " xid " << xid << " NOT FOUND!!!" << std::endl;
120 | omr.oid = oid;
121 | omr.xid = xid;
122 | omr.flags = 0;
123 | omr.size = 0;
124 | omr.paddr = 0;
125 | return false;
126 | }
127 |
128 | // std::cout << val->blockid << std::endl;
129 |
130 | omr.oid = res_key->ok_oid;
131 | omr.xid = res_key->ok_xid;
132 | omr.flags = res_val->ov_flags;
133 | omr.size = res_val->ov_size;
134 | omr.paddr = res_val->ov_paddr;
135 |
136 | return true;
137 | }
138 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsNodeMapperBTree.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "DiskStruct.h"
23 |
24 | #include "ApfsNodeMapper.h"
25 | #include "BTree.h"
26 |
27 | class BlockDumper;
28 |
29 | class ApfsNodeMapperBTree : public ApfsNodeMapper
30 | {
31 | public:
32 | ApfsNodeMapperBTree(ApfsContainer &container);
33 | virtual ~ApfsNodeMapperBTree();
34 |
35 | bool Init(oid_t omap_oid, xid_t xid);
36 | bool Lookup(omap_res_t & omr, oid_t oid, xid_t xid) override;
37 |
38 | void dump(BlockDumper &bd) { m_tree.dump(bd); }
39 |
40 | private:
41 | omap_phys_t m_omap;
42 | BTree m_tree;
43 |
44 | ApfsContainer &m_container;
45 | };
46 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsTypes.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 |
24 | // Commonly used FS types
25 | typedef unsigned char apfs_uuid_t[16];
26 | typedef uint64_t paddr_t; // Apple: int64_t
27 | typedef uint64_t oid_t;
28 | typedef uint64_t xid_t;
29 |
30 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsVolume.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include
23 |
24 | #include "Global.h"
25 |
26 | #include "ApfsContainer.h"
27 | #include "ApfsVolume.h"
28 | #include "BlockDumper.h"
29 | #include "Util.h"
30 |
31 | ApfsVolume::ApfsVolume(ApfsContainer &container) :
32 | m_container(container),
33 | m_omap(container),
34 | m_fs_tree(container, this),
35 | m_extentref_tree(container, this),
36 | m_snap_meta_tree(container, this),
37 | m_fext_tree(container, this)
38 | {
39 | m_apsb_paddr = 0;
40 | m_is_encrypted = false;
41 | }
42 |
43 | ApfsVolume::~ApfsVolume()
44 | {
45 | }
46 |
47 | bool ApfsVolume::Init(paddr_t apsb_paddr)
48 | {
49 | std::vector blk;
50 |
51 | m_apsb_paddr = apsb_paddr;
52 |
53 | blk.resize(m_container.GetBlocksize());
54 |
55 | if (!ReadBlocks(blk.data(), apsb_paddr, 1, 0))
56 | return false;
57 |
58 | if (!VerifyBlock(blk.data(), blk.size()))
59 | return false;
60 |
61 | memcpy(&m_sb, blk.data(), sizeof(m_sb));
62 |
63 | if (m_sb.apfs_magic != APFS_MAGIC)
64 | return false;
65 |
66 | if (!m_omap.Init(m_sb.apfs_omap_oid, m_sb.apfs_o.o_xid)) {
67 | std::cerr << "WARNING: Volume omap tree init failed." << std::endl;
68 | return false;
69 | }
70 |
71 | if ((m_sb.apfs_fs_flags & 3) != APFS_FS_UNENCRYPTED && !m_container.IsUnencrypted())
72 | {
73 | uint8_t vek[0x20];
74 | std::string str;
75 |
76 | std::cout << "Volume " << m_sb.apfs_volname << " is encrypted." << std::endl;
77 |
78 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid))
79 | {
80 | if (m_container.GetPasswordHint(str, m_sb.apfs_vol_uuid))
81 | std::cout << "Hint: " << str << std::endl;
82 |
83 | std::cout << "Enter Password: ";
84 | GetPassword(str);
85 |
86 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid, str.c_str()))
87 | {
88 | std::cout << "Wrong password!" << std::endl;
89 | return false;
90 | }
91 | }
92 |
93 | m_aes.SetKey(vek, vek + 0x10);
94 | m_is_encrypted = true;
95 | }
96 |
97 | if (!m_fs_tree.Init(m_sb.apfs_root_tree_oid, m_sb.apfs_o.o_xid, &m_omap))
98 | std::cerr << "ERROR: root tree init failed" << std::endl;
99 |
100 | if (!m_extentref_tree.Init(m_sb.apfs_extentref_tree_oid, m_sb.apfs_o.o_xid))
101 | std::cerr << "WARNING: extentref tree init failed" << std::endl;
102 |
103 | if (!m_snap_meta_tree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid))
104 | std::cerr << "WARNING: snap meta tree init failed" << std::endl;
105 |
106 | if (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME)
107 | {
108 | if (!m_fext_tree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid))
109 | std::cerr << "ERROR: fext tree init failed" << std::endl;
110 | }
111 |
112 | return true;
113 | }
114 |
115 | bool ApfsVolume::MountSnapshot(paddr_t apsb_paddr, xid_t snap_xid)
116 | {
117 | BTree snap_btree(m_container);
118 | BTreeEntry snap_entry;
119 | j_snap_metadata_key_t snap_key;
120 | const j_snap_metadata_val_t *snap_val = nullptr;
121 | std::vector blk;
122 |
123 | m_apsb_paddr = apsb_paddr;
124 |
125 | blk.resize(m_container.GetBlocksize());
126 |
127 | if (!ReadBlocks(blk.data(), apsb_paddr, 1, 0))
128 | return false;
129 |
130 | if (!VerifyBlock(blk.data(), blk.size()))
131 | return false;
132 |
133 | memcpy(&m_sb, blk.data(), sizeof(m_sb));
134 |
135 | if (m_sb.apfs_magic != APFS_MAGIC)
136 | return false;
137 |
138 | if (m_sb.apfs_snap_meta_tree_oid == 0)
139 | return false;
140 |
141 | if (!snap_btree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid)) {
142 | std::cerr << "snap meta tree init failed" << std::endl;
143 | return false;
144 | }
145 |
146 | snap_key.hdr.obj_id_and_type = APFS_TYPE_ID(APFS_TYPE_SNAP_METADATA, snap_xid);
147 | if (!snap_btree.Lookup(snap_entry, &snap_key, sizeof(snap_key), CompareSnapMetaKey, nullptr, true)) {
148 | std::cerr << "snap xid not found" << std::endl;
149 | return false;
150 | }
151 |
152 | snap_val = reinterpret_cast(snap_entry.val);
153 |
154 | if (!m_omap.Init(m_sb.apfs_omap_oid, m_sb.apfs_o.o_xid)) {
155 | std::cerr << "WARNING: Volume omap tree init failed." << std::endl;
156 | return false;
157 | }
158 |
159 | if (!ReadBlocks(blk.data(), snap_val->sblock_oid, 1, 0)) {
160 | std::cerr << "failed to read snapshot superblock" << std::endl;
161 | return false;
162 | }
163 | if (!VerifyBlock(blk.data(), blk.size())) {
164 | std::cerr << "snap superblock checksum error" << std::endl;
165 | return false;
166 | }
167 |
168 | memcpy(&m_sb, blk.data(), sizeof(m_sb));
169 |
170 | if (m_sb.apfs_magic != APFS_MAGIC)
171 | return false;
172 |
173 | if ((m_sb.apfs_fs_flags & 3) != APFS_FS_UNENCRYPTED)
174 | {
175 | uint8_t vek[0x20];
176 | std::string str;
177 |
178 | std::cout << "Volume " << m_sb.apfs_volname << " is encrypted." << std::endl;
179 |
180 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid))
181 | {
182 | if (m_container.GetPasswordHint(str, m_sb.apfs_vol_uuid))
183 | std::cout << "Hint: " << str << std::endl;
184 |
185 | std::cout << "Enter Password: ";
186 | GetPassword(str);
187 |
188 | if (!m_container.GetVolumeKey(vek, m_sb.apfs_vol_uuid, str.c_str()))
189 | {
190 | std::cout << "Wrong password!" << std::endl;
191 | return false;
192 | }
193 | }
194 |
195 | m_aes.SetKey(vek, vek + 0x10);
196 | m_is_encrypted = true;
197 | }
198 |
199 | if (!m_fs_tree.Init(m_sb.apfs_root_tree_oid, m_sb.apfs_o.o_xid, &m_omap))
200 | std::cerr << "WARNING: root tree init failed" << std::endl;
201 |
202 | if (!m_extentref_tree.Init(m_sb.apfs_extentref_tree_oid, m_sb.apfs_o.o_xid))
203 | std::cerr << "WARNING: extentref tree init failed" << std::endl;
204 |
205 | if (!m_snap_meta_tree.Init(m_sb.apfs_snap_meta_tree_oid, m_sb.apfs_o.o_xid))
206 | std::cerr << "WARNING: snap meta tree init failed" << std::endl;
207 |
208 | if (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME)
209 | {
210 | if (!m_fext_tree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid))
211 | std::cerr << "ERROR: fext tree init failed" << std::endl;
212 | }
213 |
214 | return true;
215 | }
216 |
217 | void ApfsVolume::dump(BlockDumper& bd)
218 | {
219 | std::vector blk;
220 | omap_res_t om;
221 | oid_t omap_snapshot_tree_oid = 0;
222 |
223 | blk.resize(m_container.GetBlocksize());
224 |
225 | if (!ReadBlocks(blk.data(), m_apsb_paddr, 1, 0))
226 | return;
227 |
228 | if (!VerifyBlock(blk.data(), blk.size()))
229 | return;
230 |
231 | bd.SetTextFlags(m_sb.apfs_incompatible_features & 0xFF);
232 |
233 | bd.DumpNode(blk.data(), m_apsb_paddr);
234 |
235 | ReadBlocks(blk.data(), m_sb.apfs_omap_oid, 1, 0);
236 | bd.DumpNode(blk.data(), m_sb.apfs_omap_oid);
237 |
238 | {
239 | const omap_phys_t *om = reinterpret_cast(blk.data());
240 | omap_snapshot_tree_oid = om->om_snapshot_tree_oid;
241 |
242 | ReadBlocks(blk.data(), omap_snapshot_tree_oid, 1, 0);
243 | bd.DumpNode(blk.data(), omap_snapshot_tree_oid);
244 | }
245 |
246 | if (m_sb.apfs_er_state_oid) {
247 | ReadBlocks(blk.data(), m_sb.apfs_er_state_oid, 1, 0);
248 | bd.DumpNode(blk.data(), m_sb.apfs_er_state_oid);
249 | }
250 |
251 | m_omap.dump(bd);
252 | m_fs_tree.dump(bd);
253 | // m_extentref_tree.dump(bd);
254 | m_snap_meta_tree.dump(bd);
255 |
256 | if (m_sb.apfs_integrity_meta_oid != 0) {
257 | if (m_omap.Lookup(om, m_sb.apfs_integrity_meta_oid, m_sb.apfs_o.o_xid))
258 | {
259 | ReadBlocks(blk.data(), om.paddr, 1, 0);
260 | bd.DumpNode(blk.data(), om.paddr);
261 | }
262 | }
263 |
264 | if (m_sb.apfs_snap_meta_ext_oid != 0) {
265 | if (m_omap.Lookup(om, m_sb.apfs_snap_meta_ext_oid, m_sb.apfs_o.o_xid))
266 | {
267 | ReadBlocks(blk.data(), om.paddr, 1, 0);
268 | bd.DumpNode(blk.data(), om.paddr);
269 | }
270 | }
271 |
272 | #if 1
273 | if (m_sb.apfs_fext_tree_oid != 0) {
274 | BTree fxtree(m_container, this);
275 | fxtree.Init(m_sb.apfs_fext_tree_oid, m_sb.apfs_o.o_xid);
276 | fxtree.dump(bd);
277 | }
278 | #endif
279 |
280 | BTreeEntry bte;
281 | #if 0
282 | BTreeIterator it;
283 | const j_snap_metadata_key_t *sm_key;
284 | const j_snap_metadata_val_t *sm_val;
285 |
286 | if (m_snap_meta_tree.GetIteratorBegin(it)) {
287 | for (;;) {
288 | if (!it.GetEntry(bte)) break;
289 |
290 | sm_key = reinterpret_cast(bte.key);
291 | sm_val = reinterpret_cast(bte.val);
292 |
293 | if ((sm_key->hdr.obj_id_and_type >> OBJ_TYPE_SHIFT) != APFS_TYPE_SNAP_METADATA) break;
294 |
295 | ReadBlocks(blk.data(), sm_val->sblock_oid, 1, 0);
296 | bd.DumpNode(blk.data(), sm_val->sblock_oid);
297 |
298 | apfs_superblock_t apsb;
299 | memcpy(&apsb, blk.data(), sizeof(apfs_superblock_t));
300 |
301 | if (apsb.apfs_omap_oid) {
302 | ReadBlocks(blk.data(), apsb.apfs_omap_oid, 1, 0);
303 | bd.DumpNode(blk.data(), apsb.apfs_omap_oid);
304 | }
305 |
306 | if (!it.next()) break;
307 | }
308 | }
309 |
310 | bte.clear();
311 | #endif
312 |
313 | #if 0
314 | {
315 | BTree omap_tree(m_container, this);
316 | BTreeIterator oit;
317 | omap_phys_t om;
318 |
319 | ReadBlocks(blk.data(), m_sb.apfs_omap_oid, 1, 0);
320 | memcpy(&om, blk.data(), sizeof(om));
321 |
322 | omap_tree.Init(om.om_tree_oid, om.om_o.o_xid);
323 | if (omap_tree.GetIteratorBegin(oit)) {
324 | const omap_key_t *ok;
325 | const omap_val_t *ov;
326 | for (;;) {
327 | if (!oit.GetEntry(bte)) break;
328 | ok = reinterpret_cast(bte.key);
329 | ov = reinterpret_cast(bte.val);
330 |
331 | bd.st() << "omap: " << ok->ok_oid << " " << ok->ok_xid << " => " << ov->ov_flags << " " << ov->ov_size << " " << ov->ov_paddr << std::endl;
332 | if (ov->ov_flags & OMAP_VAL_NOHEADER) {
333 | ReadBlocks(blk.data(), ov->ov_paddr, 1, 0);
334 | bd.DumpNode(blk.data(), ov->ov_paddr);
335 | }
336 |
337 | if (!oit.next()) break;
338 | }
339 | } else {
340 | bd.st() << "Failed getting omap iterator" << std::endl;
341 | }
342 | }
343 | #endif
344 | }
345 |
346 | bool ApfsVolume::ReadBlocks(uint8_t * data, paddr_t paddr, uint64_t blkcnt, uint64_t xts_tweak)
347 | {
348 | constexpr int encryption_block_size = 0x200;
349 |
350 | if (!m_container.ReadBlocks(data, paddr, blkcnt))
351 | return false;
352 |
353 | if (!m_is_encrypted || (xts_tweak == 0))
354 | return true;
355 |
356 | uint64_t cs_factor = m_container.GetBlocksize() / encryption_block_size;
357 | uint64_t uno = xts_tweak * cs_factor;
358 | size_t size = blkcnt * m_container.GetBlocksize();
359 | size_t k;
360 |
361 | for (k = 0; k < size; k += encryption_block_size)
362 | {
363 | m_aes.Decrypt(data + k, data + k, encryption_block_size, uno);
364 | uno++;
365 | }
366 |
367 | return true;
368 | }
369 |
370 | int ApfsVolume::CompareSnapMetaKey(const void* skey, size_t skey_len, const void* ekey, size_t ekey_len, void* context)
371 | {
372 | const j_key_t *ks = reinterpret_cast(skey);
373 | const j_key_t *ke = reinterpret_cast(ekey);
374 | const j_snap_name_key_t *sks;
375 | const j_snap_name_key_t *ske;
376 |
377 | if (ke->obj_id_and_type < ks->obj_id_and_type)
378 | return -1;
379 | if (ke->obj_id_and_type > ks->obj_id_and_type)
380 | return 1;
381 |
382 | switch (ks->obj_id_and_type >> OBJ_TYPE_SHIFT)
383 | {
384 | case APFS_TYPE_SNAP_METADATA:
385 | break;
386 | case APFS_TYPE_SNAP_NAME:
387 | sks = reinterpret_cast(skey);
388 | ske = reinterpret_cast(ekey);
389 | return apfs_strncmp(ske->name, ske->name_len, sks->name, sks->name_len);
390 | break;
391 | }
392 |
393 | return 0;
394 | }
395 |
--------------------------------------------------------------------------------
/ApfsLib/ApfsVolume.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 |
24 | #include "DiskStruct.h"
25 | #include "ApfsNodeMapperBTree.h"
26 | #include "BTree.h"
27 | #include
28 |
29 | class ApfsContainer;
30 | class BlockDumper;
31 |
32 | class ApfsVolume
33 | {
34 | public:
35 | ApfsVolume(ApfsContainer &container);
36 | ~ApfsVolume();
37 |
38 | bool Init(paddr_t apsb_paddr);
39 | bool MountSnapshot(paddr_t apsb_paddr, xid_t snap_xid);
40 |
41 | const char *name() const { return reinterpret_cast(m_sb.apfs_volname); }
42 |
43 | void dump(BlockDumper &bd);
44 |
45 | BTree &fstree() { return m_fs_tree; }
46 | BTree &fexttree() { return m_fext_tree; }
47 | uint32_t getTextFormat() const { return m_sb.apfs_incompatible_features & 0x9; }
48 |
49 | ApfsContainer &getContainer() const { return m_container; }
50 |
51 | bool ReadBlocks(uint8_t *data, paddr_t paddr, uint64_t blkcnt, uint64_t xts_tweak);
52 | bool isSealed() const { return (m_sb.apfs_incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) != 0; }
53 |
54 | private:
55 | static int CompareSnapMetaKey(const void *skey, size_t skey_len, const void *ekey, size_t ekey_len, void *context);
56 |
57 | ApfsContainer &m_container;
58 |
59 | apfs_superblock_t m_sb;
60 |
61 | ApfsNodeMapperBTree m_omap;
62 | BTree m_fs_tree;
63 | BTree m_extentref_tree;
64 | BTree m_snap_meta_tree;
65 | BTree m_fext_tree;
66 |
67 | paddr_t m_apsb_paddr;
68 |
69 | bool m_is_encrypted;
70 | AesXts m_aes;
71 | };
72 |
--------------------------------------------------------------------------------
/ApfsLib/BTree.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of apfs-fuse, a read-only implementation of APFS
3 | (Apple File System) for FUSE.
4 | Copyright (C) 2017 Simon Gander
5 |
6 | Apfs-fuse is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 2 of the License, or
9 | (at your option) any later version.
10 |
11 | Apfs-fuse is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with apfs-fuse. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include