xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/ApfsHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // ApfsHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 // #define SHOW_DEBUG_INFO
6 
7 #ifdef SHOW_DEBUG_INFO
8 #include <stdio.h>
9 #define PRF(x) x
10 #else
11 #define PRF(x)
12 #endif
13 
14 #include "../../../C/CpuArch.h"
15 #include "../../../C/Sha256.h"
16 
17 #include "../../Common/ComTry.h"
18 #include "../../Common/IntToString.h"
19 #include "../../Common/MyBuffer2.h"
20 #include "../../Common/MyLinux.h"
21 #include "../../Common/UTFConvert.h"
22 
23 #include "../../Windows/PropVariantConv.h"
24 #include "../../Windows/PropVariantUtils.h"
25 #include "../../Windows/TimeUtils.h"
26 
27 #include "../Common/LimitedStreams.h"
28 #include "../Common/ProgressUtils.h"
29 #include "../Common/RegisterArc.h"
30 #include "../Common/StreamObjects.h"
31 #include "../Common/StreamUtils.h"
32 
33 #include "../Compress/CopyCoder.h"
34 
35 #include "Common/ItemNameUtils.h"
36 
37 #include "HfsHandler.h"
38 
39 // if APFS_SHOW_ALT_STREAMS is defined, the handler will show attribute files.
40 #define APFS_SHOW_ALT_STREAMS
41 
42 #define VI_MINUS1   ((unsigned)(int)-1)
43 #define IsViDef(x)      ((int)(x) != -1)
44 #define IsViNotDef(x)   ((int)(x) == -1)
45 
46 #define Get16(p) GetUi16(p)
47 #define Get32(p) GetUi32(p)
48 #define Get64(p) GetUi64(p)
49 
50 #define G16(_offs_, dest) dest = Get16(p + (_offs_));
51 #define G32(_offs_, dest) dest = Get32(p + (_offs_));
52 #define G64(_offs_, dest) dest = Get64(p + (_offs_));
53 
54 namespace NArchive {
55 namespace NApfs {
56 
57 struct CUuid
58 {
59   Byte Data[16];
60 
SetHex_To_strNArchive::NApfs::CUuid61   void SetHex_To_str(char *s) const
62   {
63     ConvertDataToHex_Lower(s, Data, sizeof(Data));
64   }
65 
AddHexToStringNArchive::NApfs::CUuid66   void AddHexToString(UString &dest) const
67   {
68     char temp[sizeof(Data) * 2 + 4];
69     SetHex_To_str(temp);
70     dest += temp;
71   }
72 
SetFromNArchive::NApfs::CUuid73   void SetFrom(const Byte *p) { memcpy(Data, p, 16); }
74 };
75 
76 
77 typedef UInt64  oid_t;
78 typedef UInt64  xid_t;
79 // typedef Int64   paddr_t;
80 typedef UInt64   paddr_t_unsigned;
81 
82 #define G64o G64
83 #define G64x G64
84 // #define G64a G64
85 
86 /*
87 struct prange_t
88 {
89   paddr_t start_paddr;
90   UInt64 block_count;
91 
92   void Parse(const Byte *p)
93   {
94     G64a (0, start_paddr);
95     G64 (8, block_count);
96   }
97 };
98 */
99 
100 #define OBJECT_TYPE_NX_SUPERBLOCK       0x1
101 #define OBJECT_TYPE_BTREE               0x2
102 #define OBJECT_TYPE_BTREE_NODE          0x3
103 /*
104 #define OBJECT_TYPE_SPACEMAN            0x5
105 #define OBJECT_TYPE_SPACEMAN_CAB        0x6
106 #define OBJECT_TYPE_SPACEMAN_CIB        0x7
107 #define OBJECT_TYPE_SPACEMAN_BITMAP     0x8
108 #define OBJECT_TYPE_SPACEMAN_FREE_QUEUE 0x9
109 #define OBJECT_TYPE_EXTENT_LIST_TREE    0xa
110 */
111 #define OBJECT_TYPE_OMAP                0xb
112 /*
113 #define OBJECT_TYPE_CHECKPOINT_MAP      0xc
114 */
115 #define OBJECT_TYPE_FS                  0xd
116 #define OBJECT_TYPE_FSTREE              0xe
117 /*
118 #define OBJECT_TYPE_BLOCKREFTREE        0xf
119 #define OBJECT_TYPE_SNAPMETATREE        0x10
120 #define OBJECT_TYPE_NX_REAPER           0x11
121 #define OBJECT_TYPE_NX_REAP_LIST        0x12
122 #define OBJECT_TYPE_OMAP_SNAPSHOT       0x13
123 #define OBJECT_TYPE_EFI_JUMPSTART       0x14
124 #define OBJECT_TYPE_FUSION_MIDDLE_TREE  0x15
125 #define OBJECT_TYPE_NX_FUSION_WBC       0x16
126 #define OBJECT_TYPE_NX_FUSION_WBC_LIST  0x17
127 #define OBJECT_TYPE_ER_STATE            0x18
128 #define OBJECT_TYPE_GBITMAP             0x19
129 #define OBJECT_TYPE_GBITMAP_TREE        0x1a
130 #define OBJECT_TYPE_GBITMAP_BLOCK       0x1b
131 #define OBJECT_TYPE_ER_RECOVERY_BLOCK   0x1c
132 #define OBJECT_TYPE_SNAP_META_EXT       0x1d
133 */
134 #define OBJECT_TYPE_INTEGRITY_META      0x1e
135 #define OBJECT_TYPE_FEXT_TREE           0x1f
136 /*
137 #define OBJECT_TYPE_RESERVED_20         0x20
138 
139 #define OBJECT_TYPE_INVALID       0x0
140 #define OBJECT_TYPE_TEST          0xff
141 #define OBJECT_TYPE_CONTAINER_KEYBAG  'keys'
142 #define OBJECT_TYPE_VOLUME_KEYBAG     'recs'
143 #define OBJECT_TYPE_MEDIA_KEYBAG      'mkey'
144 
145 #define OBJ_VIRTUAL       0x0
146 #define OBJ_EPHEMERAL     0x80000000
147 */
148 #define OBJ_PHYSICAL      0x40000000
149 /*
150 #define OBJ_NOHEADER      0x20000000
151 #define OBJ_ENCRYPTED     0x10000000
152 #define OBJ_NONPERSISTENT 0x08000000
153 */
154 #define OBJECT_TYPE_MASK                0x0000ffff
155 /*
156 #define OBJECT_TYPE_FLAGS_MASK          0xffff0000
157 #define OBJ_STORAGETYPE_MASK            0xc0000000
158 #define OBJECT_TYPE_FLAGS_DEFINED_MASK  0xf8000000
159 */
160 
161 // #define MAX_CKSUM_SIZE 8
162 
163 static const unsigned k_obj_phys_Size = 0x20;
164 
165 // obj_phys_t
166 struct CPhys
167 {
168   // Byte cksum[MAX_CKSUM_SIZE];
169   oid_t oid;
170   xid_t xid;
171   UInt32 type;
172   UInt32 subtype;
173 
GetTypeNArchive::NApfs::CPhys174   UInt32 GetType() const { return type & OBJECT_TYPE_MASK; }
175   void Parse(const Byte *p);
176 };
177 
Parse(const Byte * p)178 void CPhys::Parse(const Byte *p)
179 {
180   // memcpy(cksum, p, MAX_CKSUM_SIZE);
181   G64o (8, oid)
182   G64x (0x10, xid)
183   G32 (0x18, type)
184   G32 (0x1C, subtype)
185 }
186 
187 #define NX_MAX_FILE_SYSTEMS   100
188 /*
189 #define NX_EPH_INFO_COUNT   4
190 #define NX_EPH_MIN_BLOCK_COUNT  8
191 #define NX_MAX_FILE_SYSTEM_EPH_STRUCTS  4
192 #define NX_TX_MIN_CHECKPOINT_COUNT  4
193 #define NX_EPH_INFO_VERSION_1   1
194 */
195 
196 /*
197 typedef enum
198 {
199   NX_CNTR_OBJ_CKSUM_SET = 0,
200   NX_CNTR_OBJ_CKSUM_FAIL = 1,
201   NX_NUM_COUNTERS = 32
202 } counter_id_t;
203 */
204 
205 /* Incompatible volume feature flags */
206 #define APFS_INCOMPAT_CASE_INSENSITIVE          (1 << 0)
207 /*
208 #define APFS_INCOMPAT_DATALESS_SNAPS            (1 << 1)
209 #define APFS_INCOMPAT_ENC_ROLLED                (1 << 2)
210 */
211 #define APFS_INCOMPAT_NORMALIZATION_INSENSITIVE (1 << 3)
212 /*
213 #define APFS_INCOMPAT_INCOMPLETE_RESTORE        (1 << 4)
214 */
215 #define APFS_INCOMPAT_SEALED_VOLUME             (1 << 5)
216 /*
217 #define APFS_INCOMPAT_RESERVED_40               (1 << 6)
218 */
219 
220 static const char * const g_APFS_INCOMPAT_Flags[] =
221 {
222     "CASE_INSENSITIVE"
223   , "DATALESS_SNAPS"
224   , "ENC_ROLLED"
225   , "NORMALIZATION_INSENSITIVE"
226   , "INCOMPLETE_RESTORE"
227   , "SEALED_VOLUME"
228 };
229 
230 /*
231 #define APFS_SUPPORTED_INCOMPAT_MASK \
232     ( APFS_INCOMPAT_CASE_INSENSITIVE \
233     | APFS_INCOMPAT_DATALESS_SNAPS \
234     | APFS_INCOMPAT_ENC_ROLLED \
235     | APFS_INCOMPAT_NORMALIZATION_INSENSITIVE \
236     | APFS_INCOMPAT_INCOMPLETE_RESTORE \
237     | APFS_INCOMPAT_SEALED_VOLUME \
238     | APFS_INCOMPAT_RESERVED_40 \
239 )
240 */
241 
242 // superblock_t
243 struct CSuperBlock
244 {
245   // CPhys o;
246   // UInt32 magic;
247   UInt32 block_size;
248   unsigned block_size_Log;
249   UInt64 block_count;
250   // UInt64 features;
251   // UInt64 readonly_compatible_features;
252   // UInt64 incompatible_features;
253   CUuid uuid;
254   /*
255   oid_t next_oid;
256   xid_t next_xid;
257   UInt32 xp_desc_blocks;
258   UInt32 xp_data_blocks;
259   paddr_t xp_desc_base;
260   paddr_t xp_data_base;
261   UInt32 xp_desc_next;
262   UInt32 xp_data_next;
263   UInt32 xp_desc_index;
264   UInt32 xp_desc_len;
265   UInt32 xp_data_index;
266   UInt32 xp_data_len;
267   oid_t spaceman_oid;
268   */
269   oid_t omap_oid;
270   // oid_t reaper_oid;
271   // UInt32 test_type;
272   UInt32 max_file_systems;
273   // oid_t fs_oid[NX_MAX_FILE_SYSTEMS];
274   /*
275   UInt64 counters[NX_NUM_COUNTERS]; // counter_id_t
276   prange_t blocked_out_prange;
277   oid_t evict_mapping_tree_oid;
278   UInt64 flags;
279   paddr_t efi_jumpstart;
280   CUuid fusion_uuid;
281   prange_t keylocker;
282   UInt64 ephemeral_info[NX_EPH_INFO_COUNT];
283   oid_t test_oid;
284   oid_t fusion_mt_oid;
285   oid_t fusion_wbc_oid;
286   prange_t fusion_wbc;
287   UInt64 newest_mounted_version;
288   prange_t mkb_locker;
289   */
290 
291   bool Parse(const Byte *p);
292 };
293 
294 struct CSuperBlock2
295 {
296   oid_t fs_oid[NX_MAX_FILE_SYSTEMS];
ParseNArchive::NApfs::CSuperBlock2297   void Parse(const Byte *p)
298   {
299     for (unsigned i = 0; i < NX_MAX_FILE_SYSTEMS; i++)
300     {
301       G64o (0xb8 + i * 8, fs_oid[i])
302     }
303   }
304 };
305 
306 
307 // we include one additional byte of next field (block_size)
308 static const unsigned k_SignatureOffset = 32;
309 static const Byte k_Signature[] = { 'N', 'X', 'S', 'B', 0 };
310 
311 // size must be 4 bytes aligned
Fletcher64(const Byte * data,size_t size)312 static UInt64 Fletcher64(const Byte *data, size_t size)
313 {
314   const UInt32 kMax32 = 0xffffffff;
315   const UInt64 val = 0; // startVal
316   UInt64 a = val & kMax32;
317   UInt64 b = (val >> 32) & kMax32;
318   for (size_t i = 0; i < size; i += 4)
319   {
320     a += GetUi32(data + i);
321     b += a;
322   }
323   a %= kMax32;
324   b %= kMax32;
325   b = (UInt32)(kMax32 - ((a + b) % kMax32));
326   a = (UInt32)(kMax32 - ((a + b) % kMax32));
327   return (a << 32) | b;
328 }
329 
CheckFletcher64(const Byte * p,size_t size)330 static bool CheckFletcher64(const Byte *p, size_t size)
331 {
332   const UInt64 calculated_checksum = Fletcher64(p + 8, size - 8);
333   const UInt64 stored_checksum = Get64(p);
334   return (stored_checksum == calculated_checksum);
335 }
336 
337 
GetLogSize(UInt32 size)338 static unsigned GetLogSize(UInt32 size)
339 {
340   unsigned k;
341   for (k = 0; k < 32; k++)
342     if (((UInt32)1 << k) == size)
343       return k;
344   return k;
345 }
346 
347 static const unsigned kApfsHeaderSize = 1 << 12;
348 
349 // #define OID_INVALID         0
350 #define OID_NX_SUPERBLOCK   1
351 // #define OID_RESERVED_COUNT  1024
352 // This range of identifiers is reserved for physical, virtual, and ephemeral objects
353 
Parse(const Byte * p)354 bool CSuperBlock::Parse(const Byte *p)
355 {
356   CPhys o;
357   o.Parse(p);
358   if (o.oid != OID_NX_SUPERBLOCK)
359     return false;
360   if (o.GetType() != OBJECT_TYPE_NX_SUPERBLOCK)
361     return false;
362   if (o.subtype != 0)
363     return false;
364   if (memcmp(p + k_SignatureOffset, k_Signature, 4) != 0)
365     return false;
366   if (!CheckFletcher64(p, kApfsHeaderSize))
367     return false;
368 
369   G32 (0x24, block_size)
370   {
371     const unsigned logSize = GetLogSize(block_size);
372     // don't change it. Some code requires (block_size <= 16)
373     if (logSize < 12 || logSize > 16)
374       return false;
375     block_size_Log = logSize;
376   }
377 
378   G64 (0x28, block_count)
379 
380   const UInt64 kArcSize_MAX = (UInt64)1 << 62;
381   if (block_count > (kArcSize_MAX >> block_size_Log))
382     return false;
383 
384   // G64 (0x30, features);
385   // G64 (0x38, readonly_compatible_features);
386   // G64 (0x40, incompatible_features);
387   uuid.SetFrom(p + 0x48);
388   /*
389   G64o (0x58, next_oid);
390   G64x (0x60, next_xid);
391   G32 (0x68, xp_desc_blocks);
392   G32 (0x6c, xp_data_blocks);
393   G64a (0x70, xp_desc_base);
394   G64a (0x78, xp_data_base);
395   G32 (0x80, xp_desc_next);
396   G32 (0x84, xp_data_next);
397   G32 (0x88, xp_desc_index);
398   G32 (0x8c, xp_desc_len);
399   G32 (0x90, xp_data_index);
400   G32 (0x94, xp_data_len);
401   G64o (0x98, spaceman_oid);
402   */
403   G64o (0xa0, omap_oid)
404   // G64o (0xa8, reaper_oid);
405   // G32 (0xb0, test_type);
406   G32 (0xb4, max_file_systems)
407   if (max_file_systems > NX_MAX_FILE_SYSTEMS)
408     return false;
409   /*
410   {
411     for (unsigned i = 0; i < NX_MAX_FILE_SYSTEMS; i++)
412     {
413       G64o (0xb8 + i * 8, fs_oid[i]);
414     }
415   }
416   */
417   /*
418   {
419     for (unsigned i = 0; i < NX_NUM_COUNTERS; i++)
420     {
421       G64 (0x3d8 + i * 8, counters[i]);
422     }
423   }
424   blocked_out_prange.Parse(p + 0x4d8);
425   G64o (0x4e8, evict_mapping_tree_oid);
426   #define NX_CRYPTO_SW 0x00000004LL
427   G64 (0x4f0, flags);
428   G64a (0x4f8, efi_jumpstart);
429   fusion_uuid.SetFrom(p + 0x500);
430   keylocker.Parse(p + 0x510);
431   {
432     for (unsigned i = 0; i < NX_EPH_INFO_COUNT; i++)
433     {
434       G64 (0x520 + i * 8, ephemeral_info[i]);
435     }
436   }
437   G64o (0x540, test_oid);
438   G64o (0x548, fusion_mt_oid);
439   G64o (0x550, fusion_wbc_oid);
440   fusion_wbc.Parse(p + 0x558);
441   G64 (0x568, newest_mounted_version); // decimal 1412141 001 000 000
442   mkb_locker.Parse(p + 0x570);
443   */
444 
445   return true;
446 }
447 
448 
449 enum apfs_hash_type_t
450 {
451   APFS_HASH_INVALID = 0,
452   APFS_HASH_SHA256 = 1,
453   APFS_HASH_SHA512_256 = 2,
454   APFS_HASH_SHA384 = 3,
455   APFS_HASH_SHA512 = 4,
456 
457   APFS_HASH_MIN = APFS_HASH_SHA256,
458   APFS_HASH_MAX = APFS_HASH_SHA512,
459 
460   APFS_HASH_DEFAULT = APFS_HASH_SHA256
461 };
462 
GetHashSize(unsigned hashType)463 static unsigned GetHashSize(unsigned hashType)
464 {
465   if (hashType > APFS_HASH_MAX) return 0;
466   if (hashType < APFS_HASH_MIN) return 0;
467   if (hashType == APFS_HASH_SHA256) return 32;
468   return hashType * 16;
469 }
470 
471 #define APFS_HASH_MAX_SIZE 64
472 
473 static const char * const g_hash_types[] =
474 {
475     NULL
476   , "SHA256"
477   , "SHA512_256"
478   , "SHA384"
479   , "SHA512"
480 };
481 
482 
483 struct C_integrity_meta_phys
484 {
485   // CPhys im_o;
486   // UInt32 im_version;
487   UInt32 im_flags;
488   // apfs_hash_type_t
489   UInt32 im_hash_type;
490   // UInt32 im_root_hash_offset;
491   // xid_t im_broken_xid;
492   // UInt64 im_reserved[9];
493 
494   unsigned HashSize;
495   Byte Hash[APFS_HASH_MAX_SIZE];
496 
Is_SHA256NArchive::NApfs::C_integrity_meta_phys497   bool Is_SHA256() const { return im_hash_type == APFS_HASH_SHA256; }
498 
C_integrity_meta_physNArchive::NApfs::C_integrity_meta_phys499   C_integrity_meta_phys()
500   {
501     memset(this, 0, sizeof(*this));
502   }
503   bool Parse(const Byte *p, size_t size, oid_t oid);
504 };
505 
Parse(const Byte * p,size_t size,oid_t oid)506 bool C_integrity_meta_phys::Parse(const Byte *p, size_t size, oid_t oid)
507 {
508   CPhys o;
509   if (!CheckFletcher64(p, size))
510     return false;
511   o.Parse(p);
512   if (o.GetType() != OBJECT_TYPE_INTEGRITY_META)
513     return false;
514   if (o.oid != oid)
515     return false;
516   // G32 (0x20, im_version);
517   G32 (0x24, im_flags)
518   G32 (0x28, im_hash_type)
519   UInt32 im_root_hash_offset;
520   G32 (0x2C, im_root_hash_offset)
521   // G64x (0x30, im_broken_xid);
522   const unsigned hashSize = GetHashSize(im_hash_type);
523   HashSize = hashSize;
524   if (im_root_hash_offset >= size || size - im_root_hash_offset < hashSize)
525     return false;
526   memcpy(Hash, p + im_root_hash_offset, hashSize);
527   return true;
528 }
529 
530 
531 struct C_omap_phys
532 {
533   // om_ prefix
534   // CPhys o;
535   /*
536   UInt32 flags;
537   UInt32 snap_count;
538   UInt32 tree_type;
539   UInt32 snapshot_tree_type;
540   */
541   oid_t tree_oid;
542   /*
543   oid_t snapshot_tree_oid;
544   xid_t most_recent_snap;
545   xid_t pending_revert_min;
546   xid_t pending_revert_max;
547   */
548   bool Parse(const Byte *p, size_t size, oid_t oid);
549 };
550 
Parse(const Byte * p,size_t size,oid_t oid)551 bool C_omap_phys::Parse(const Byte *p, size_t size, oid_t oid)
552 {
553   CPhys o;
554   if (!CheckFletcher64(p, size))
555     return false;
556   o.Parse(p);
557   if (o.GetType() != OBJECT_TYPE_OMAP)
558     return false;
559   if (o.oid != oid)
560     return false;
561   /*
562   G32 (0x20, flags);
563   G32 (0x24, snap_count);
564   G32 (0x28, tree_type);
565   G32 (0x2C, snapshot_tree_type);
566   */
567   G64o (0x30, tree_oid)
568   /*
569   G64o (0x38, snapshot_tree_oid);
570   G64x (0x40, most_recent_snap);
571   G64x (0x48, pending_revert_min);
572   G64x (0x50, pending_revert_max);
573   */
574   return true;
575 }
576 
577 
578 // #define BTOFF_INVALID 0xffff
579 /* This value is stored in the off field of nloc_t to indicate that
580 there's no offset. For example, the last entry in a free
581 list has no entry after it, so it uses this value for its off field. */
582 
583 // A location within a B-tree node
584 struct nloc
585 {
586   UInt16 off;
587   UInt16 len;
588 
ParseNArchive::NApfs::nloc589   void Parse(const Byte *p)
590   {
591     G16 (0, off)
592     G16 (2, len)
593   }
GetEndNArchive::NApfs::nloc594   UInt32 GetEnd() const { return (UInt32)off + len; }
CheckOverLimitNArchive::NApfs::nloc595   bool CheckOverLimit(UInt32 limit)
596   {
597     return off < limit && len <= limit - off;
598   }
599 };
600 
601 
602 // The location, within a B-tree node, of a key and value
603 struct kvloc
604 {
605   nloc k;
606   nloc v;
607 
ParseNArchive::NApfs::kvloc608   void Parse(const Byte *p)
609   {
610     k.Parse(p);
611     v.Parse(p + 4);
612   }
613 };
614 
615 
616 // The location, within a B-tree node, of a fixed-size key and value
617 struct kvoff
618 {
619   UInt16 k;
620   UInt16 v;
621 
ParseNArchive::NApfs::kvoff622   void Parse(const Byte *p)
623   {
624     G16 (0, k)
625     G16 (2, v)
626   }
627 };
628 
629 
630 #define BTNODE_ROOT             (1 << 0)
631 #define BTNODE_LEAF             (1 << 1)
632 #define BTNODE_FIXED_KV_SIZE    (1 << 2)
633 #define BTNODE_HASHED           (1 << 3)
634 #define BTNODE_NOHEADER         (1 << 4)
635 /*
636 #define BTNODE_CHECK_KOFF_INVAL (1 << 15)
637 */
638 
639 static const unsigned k_Toc_offset = 0x38;
640 
641 // btree_node_phys
642 struct CBTreeNodePhys
643 {
644   // btn_ prefix
645   CPhys ophys;
646   UInt16 flags;
647   UInt16 level;       // the number of child levels below this node. 0 -  for a leaf node, 1 for the immediate parent of a leaf node
648   UInt32 nkeys;       // The number of keys stored in this node.
649   nloc table_space;
650   /*
651   nloc free_space;
652   nloc key_free_list;
653   nloc val_free_list;
654   */
655 
Is_FIXED_KV_SIZENArchive::NApfs::CBTreeNodePhys656   bool Is_FIXED_KV_SIZE() const { return (flags & BTNODE_FIXED_KV_SIZE) != 0; }
Is_NOHEADERNArchive::NApfs::CBTreeNodePhys657   bool Is_NOHEADER() const { return (flags & BTNODE_NOHEADER) != 0; }
Is_HASHEDNArchive::NApfs::CBTreeNodePhys658   bool Is_HASHED() const { return (flags & BTNODE_HASHED) != 0; }
659 
ParseNArchive::NApfs::CBTreeNodePhys660   bool Parse(const Byte *p, size_t size, bool noHeader = false)
661   {
662     G16 (0x20, flags)
663     G16 (0x22, level)
664     G32 (0x24, nkeys)
665     table_space.Parse(p + 0x28);
666     /*
667     free_space.Parse(p + 0x2C);
668     key_free_list.Parse(p + 0x30);
669     val_free_list.Parse(p + 0x34);
670     */
671     memset(&ophys, 0, sizeof(ophys));
672     if (noHeader)
673     {
674       for (unsigned i = 0; i < k_obj_phys_Size; i++)
675         if (p[i] != 0)
676           return false;
677     }
678     else
679     {
680       if (!CheckFletcher64(p, size))
681         return false;
682       ophys.Parse(p);
683     }
684     if (Is_NOHEADER() != noHeader)
685       return false;
686     return true;
687   }
688 };
689 
690 /*
691 #define BTREE_UINT64_KEYS       (1 << 0)
692 #define BTREE_SEQUENTIAL_INSERT (1 << 1)
693 #define BTREE_ALLOW_GHOSTS      (1 << 2)
694 */
695 #define BTREE_EPHEMERAL         (1 << 3)
696 #define BTREE_PHYSICAL          (1 << 4)
697 /*
698 #define BTREE_NONPERSISTENT     (1 << 5)
699 #define BTREE_KV_NONALIGNED     (1 << 6)
700 #define BTREE_HASHED            (1 << 7)
701 */
702 #define BTREE_NOHEADER          (1 << 8)
703 
704 /*
705   BTREE_EPHEMERAL: The nodes in the B-tree use ephemeral object identifiers to link to child nodes
706   BTREE_PHYSICAL : The nodes in the B-tree use physical object identifiers to link to child nodes.
707   If neither flag is set, nodes in the B-tree use virtual object
708   identifiers to link to their child nodes.
709 */
710 
711 // Static information about a B-tree.
712 struct btree_info_fixed
713 {
714   UInt32 flags;
715   UInt32 node_size;
716   UInt32 key_size;
717   UInt32 val_size;
718 
ParseNArchive::NApfs::btree_info_fixed719   void Parse(const Byte *p)
720   {
721     G32 (0, flags)
722     G32 (4, node_size)
723     G32 (8, key_size)
724     G32 (12, val_size)
725   }
726 };
727 
728 static const unsigned k_btree_info_Size = 0x28;
729 
730 struct btree_info
731 {
732   btree_info_fixed fixed;
733   UInt32 longest_key;
734   UInt32 longest_val;
735   UInt64 key_count;
736   UInt64 node_count;
737 
Is_EPHEMERALNArchive::NApfs::btree_info738   bool Is_EPHEMERAL() const { return (fixed.flags & BTREE_EPHEMERAL) != 0; }
Is_PHYSICALNArchive::NApfs::btree_info739   bool Is_PHYSICAL()  const { return (fixed.flags & BTREE_PHYSICAL) != 0; }
Is_NOHEADERNArchive::NApfs::btree_info740   bool Is_NOHEADER()  const { return (fixed.flags & BTREE_NOHEADER) != 0; }
741 
ParseNArchive::NApfs::btree_info742   void Parse(const Byte *p)
743   {
744     fixed.Parse(p);
745     G32 (0x10, longest_key)
746     G32 (0x14, longest_val)
747     G64 (0x18, key_count)
748     G64 (0x20, node_count)
749   }
750 };
751 
752 
753 /*
754 typedef UInt32  cp_key_class_t;
755 typedef UInt32  cp_key_os_version_t;
756 typedef UInt16  cp_key_revision_t;
757 typedef UInt32  crypto_flags_t;
758 
759 struct wrapped_meta_crypto_state
760 {
761   UInt16 major_version;
762   UInt16 minor_version;
763   crypto_flags_t cpflags;
764   cp_key_class_t persistent_class;
765   cp_key_os_version_t key_os_version;
766   cp_key_revision_t key_revision;
767   // UInt16 unused;
768 
769   void Parse(const Byte *p)
770   {
771     G16 (0, major_version);
772     G16 (2, minor_version);
773     G32 (4, cpflags);
774     G32 (8, persistent_class);
775     G32 (12, key_os_version);
776     G16 (16, key_revision);
777   }
778 };
779 */
780 
781 
782 #define APFS_MODIFIED_NAMELEN 32
783 #define sizeof_apfs_modified_by_t (APFS_MODIFIED_NAMELEN + 16)
784 
785 struct apfs_modified_by_t
786 {
787   Byte id[APFS_MODIFIED_NAMELEN];
788   UInt64 timestamp;
789   xid_t last_xid;
790 
ParseNArchive::NApfs::apfs_modified_by_t791   void Parse(const Byte *p)
792   {
793     memcpy(id, p, APFS_MODIFIED_NAMELEN);
794     p += APFS_MODIFIED_NAMELEN;
795     G64 (0, timestamp)
796     G64x (8, last_xid)
797   }
798 };
799 
800 
801 #define APFS_MAX_HIST 8
802 #define APFS_VOLNAME_LEN 256
803 
804 struct CApfs
805 {
806   // apfs_
807   CPhys o;
808   // UInt32 magic;
809   UInt32 fs_index; // index of the object identifier for this volume's file system in the container's array of file systems.
810   // UInt64 features;
811   // UInt64 readonly_compatible_features;
812   UInt64 incompatible_features;
813   UInt64 unmount_time;
814   // UInt64 fs_reserve_block_count;
815   // UInt64 fs_quota_block_count;
816   UInt64 fs_alloc_count;
817   // wrapped_meta_crypto_state meta_crypto;
818   // UInt32 root_tree_type;
819     /* The type of the root file-system tree.
820         The value is typically OBJ_VIRTUAL | OBJECT_TYPE_BTREE,
821         with a subtype of OBJECT_TYPE_FSTREE */
822 
823   // UInt32 extentref_tree_type;
824   // UInt32 snap_meta_tree_type;
825   oid_t omap_oid;
826   oid_t root_tree_oid;
827   /*
828   oid_t extentref_tree_oid;
829   oid_t snap_meta_tree_oid;
830   xid_t revert_to_xid;
831   oid_t revert_to_sblock_oid;
832   UInt64 next_obj_id;
833   */
834   UInt64 num_files;
835   UInt64 num_directories;
836   UInt64 num_symlinks;
837   UInt64 num_other_fsobjects;
838   UInt64 num_snapshots;
839   UInt64 total_blocks_alloced;
840   UInt64 total_blocks_freed;
841   CUuid vol_uuid;
842   UInt64 last_mod_time;
843   UInt64 fs_flags;
844   apfs_modified_by_t formatted_by;
845   apfs_modified_by_t modified_by[APFS_MAX_HIST];
846   Byte volname[APFS_VOLNAME_LEN];
847   /*
848   UInt32 next_doc_id;
849   UInt16 role; //  APFS_VOL_ROLE_NONE   APFS_VOL_ROLE_SYSTEM ....
850   UInt16 reserved;
851   xid_t root_to_xid;
852   oid_t er_state_oid;
853   UInt64 cloneinfo_id_epoch;
854   UInt64 cloneinfo_xid;
855   oid_t snap_meta_ext_oid;
856   CUuid volume_group_id;
857   */
858   oid_t integrity_meta_oid; // virtual object identifier of the integrity metadata object
859   oid_t fext_tree_oid;      // virtual object identifier of the file extent tree.
860   UInt32 fext_tree_type;    // The type of the file extent tree.
861   /*
862   UInt32 reserved_type;
863   oid_t reserved_oid;
864   */
865 
GetTotalItemsNArchive::NApfs::CApfs866   UInt64 GetTotalItems() const
867   {
868     return num_files + num_directories + num_symlinks + num_other_fsobjects;
869   }
870 
IsHashedNameNArchive::NApfs::CApfs871   bool IsHashedName() const
872   {
873     return
874       (incompatible_features & APFS_INCOMPAT_CASE_INSENSITIVE) != 0 ||
875       (incompatible_features & APFS_INCOMPAT_NORMALIZATION_INSENSITIVE) != 0;
876   }
877 
878   bool Parse(const Byte *p, size_t size);
879 };
880 
881 
Parse(const Byte * p,size_t size)882 bool CApfs::Parse(const Byte *p, size_t size)
883 {
884   o.Parse(p);
885   if (Get32(p + 32) != 0x42535041) //  { 'A', 'P', 'S', 'B' };
886     return false;
887   if (o.GetType() != OBJECT_TYPE_FS)
888     return false;
889   if (!CheckFletcher64(p, size))
890     return false;
891   // if (o.GetType() != OBJECT_TYPE_NX_SUPERBLOCK) return false;
892 
893   G32 (0x24, fs_index)
894   // G64 (0x28, features);
895   // G64 (0x30, readonly_compatible_features);
896   G64 (0x38, incompatible_features)
897   G64 (0x40, unmount_time)
898   // G64 (0x48, fs_reserve_block_count);
899   // G64 (0x50, fs_quota_block_count);
900   G64 (0x58, fs_alloc_count)
901   // meta_crypto.Parse(p + 0x60);
902   // G32 (0x74, root_tree_type);
903   // G32 (0x78, extentref_tree_type);
904   // G32 (0x7C, snap_meta_tree_type);
905 
906   G64o (0x80, omap_oid)
907   G64o (0x88, root_tree_oid)
908   /*
909   G64o (0x90, extentref_tree_oid);
910   G64o (0x98, snap_meta_tree_oid);
911   G64x (0xa0, revert_to_xid);
912   G64o (0xa8, revert_to_sblock_oid);
913   G64 (0xb0, next_obj_id);
914   */
915   G64 (0xb8, num_files)
916   G64 (0xc0, num_directories)
917   G64 (0xc8, num_symlinks)
918   G64 (0xd0, num_other_fsobjects)
919   G64 (0xd8, num_snapshots)
920   G64 (0xe0, total_blocks_alloced)
921   G64 (0xe8, total_blocks_freed)
922   vol_uuid.SetFrom(p + 0xf0);
923   G64 (0x100, last_mod_time)
924   G64 (0x108, fs_flags)
925   p += 0x110;
926   formatted_by.Parse(p);
927   p += sizeof_apfs_modified_by_t;
928   for (unsigned i = 0; i < APFS_MAX_HIST; i++)
929   {
930     modified_by[i].Parse(p);
931     p += sizeof_apfs_modified_by_t;
932   }
933   memcpy(volname, p, APFS_VOLNAME_LEN);
934   p += APFS_VOLNAME_LEN;
935   /*
936   G32 (0, next_doc_id);
937   G16 (4, role);
938   G16 (6, reserved);
939   G64x (8, root_to_xid);
940   G64o (0x10, er_state_oid);
941   G64 (0x18, cloneinfo_id_epoch);
942   G64 (0x20, cloneinfo_xid);
943   G64o (0x28, snap_meta_ext_oid);
944   volume_group_id.SetFrom(p + 0x30);
945   */
946   G64o (0x40, integrity_meta_oid)
947   G64o (0x48, fext_tree_oid)
948   G32 (0x50, fext_tree_type)
949   /*
950   G32 (0x54, reserved_type);
951   G64o (0x58, reserved_oid);
952   */
953   return true;
954 }
955 
956 
957 #define OBJ_ID_MASK         0x0fffffffffffffff
958 /*
959 #define OBJ_TYPE_MASK       0xf000000000000000
960 #define SYSTEM_OBJ_ID_MARK  0x0fffffff00000000
961 */
962 #define OBJ_TYPE_SHIFT      60
963 
964 typedef enum
965 {
966   APFS_TYPE_ANY           = 0,
967   APFS_TYPE_SNAP_METADATA = 1,
968   APFS_TYPE_EXTENT        = 2,
969   APFS_TYPE_INODE         = 3,
970   APFS_TYPE_XATTR         = 4,
971   APFS_TYPE_SIBLING_LINK  = 5,
972   APFS_TYPE_DSTREAM_ID    = 6,
973   APFS_TYPE_CRYPTO_STATE  = 7,
974   APFS_TYPE_FILE_EXTENT   = 8,
975   APFS_TYPE_DIR_REC       = 9,
976   APFS_TYPE_DIR_STATS     = 10,
977   APFS_TYPE_SNAP_NAME     = 11,
978   APFS_TYPE_SIBLING_MAP   = 12,
979   APFS_TYPE_FILE_INFO     = 13,
980   APFS_TYPE_MAX_VALID     = 13,
981   APFS_TYPE_MAX           = 15,
982   APFS_TYPE_INVALID       = 15
983 } j_obj_types;
984 
985 
986 struct j_key_t
987 {
988   UInt64 obj_id_and_type;
989 
ParseNArchive::NApfs::j_key_t990   void Parse(const Byte *p) { G64(0, obj_id_and_type) }
GetTypeNArchive::NApfs::j_key_t991   unsigned GetType() const { return (unsigned)(obj_id_and_type >> OBJ_TYPE_SHIFT); }
GetIDNArchive::NApfs::j_key_t992   UInt64 GetID() const { return obj_id_and_type & OBJ_ID_MASK; }
993 };
994 
995 
996 
997 #define J_DREC_LEN_MASK   0x000003ff
998 /*
999 #define J_DREC_HASH_MASK  0xfffff400
1000 #define J_DREC_HASH_SHIFT 10
1001 */
1002 
1003 static const unsigned k_SizeOf_j_drec_val = 0x12;
1004 
1005 struct j_drec_val
1006 {
1007   UInt64 file_id;
1008   UInt64 date_added; /* The time that this directory entry was added to the directory.
1009        It's not updated when modifying the directory entry for example,
1010        by renaming a file without moving it to a different directory. */
1011   UInt16 flags;
1012 
1013   // bool IsFlags_File() const { return flags == MY_LIN_DT_REG; }
IsFlags_UnknownNArchive::NApfs::j_drec_val1014   bool IsFlags_Unknown() const { return flags == MY_LIN_DT_UNKNOWN; }
IsFlags_DirNArchive::NApfs::j_drec_val1015   bool IsFlags_Dir()     const { return flags == MY_LIN_DT_DIR; }
1016 
1017   // uint8_t xfields[];
ParseNArchive::NApfs::j_drec_val1018   void Parse(const Byte *p)
1019   {
1020     G64 (0, file_id)
1021     G64 (8, date_added)
1022     G16 (0x10, flags)
1023   }
1024 };
1025 
1026 
1027 struct CItem
1028 {
1029   // j_key_t hdr;
1030   UInt64 ParentId;
1031   AString Name;
1032   j_drec_val Val;
1033 
1034   unsigned ParentItemIndex;
1035   unsigned RefIndex;
1036   // unsigned  iNode_Index;
1037 
ClearNArchive::NApfs::CItem1038   void Clear()
1039   {
1040     Name.Empty();
1041     ParentItemIndex = VI_MINUS1;
1042     RefIndex = VI_MINUS1;
1043   }
1044 };
1045 
1046 
1047 /*
1048 #define INVALID_INO_NUM       0
1049 #define ROOT_DIR_PARENT       1  // parent for "root" and "private-dir", there's no inode on disk with this inode number.
1050 */
1051 #define ROOT_DIR_INO_NUM      2  // "root" - parent for all main files
1052 #define PRIV_DIR_INO_NUM      3  // "private-dir"
1053 /*
1054 #define SNAP_DIR_INO_NUM      6  //  the directory where snapshot metadata is stored. Snapshot inodes are stored in the snapshot metedata tree.
1055 #define PURGEABLE_DIR_INO_NUM 7
1056 #define MIN_USER_INO_NUM      16
1057 
1058 #define UNIFIED_ID_SPACE_MARK 0x0800000000000000
1059 */
1060 
1061 //typedef enum
1062 // {
1063 /*
1064 INODE_IS_APFS_PRIVATE         = 0x00000001,
1065 INODE_MAINTAIN_DIR_STATS      = 0x00000002,
1066 INODE_DIR_STATS_ORIGIN        = 0x00000004,
1067 INODE_PROT_CLASS_EXPLICIT     = 0x00000008,
1068 INODE_WAS_CLONED              = 0x00000010,
1069 INODE_FLAG_UNUSED             = 0x00000020,
1070 INODE_HAS_SECURITY_EA         = 0x00000040,
1071 INODE_BEING_TRUNCATED         = 0x00000080,
1072 INODE_HAS_FINDER_INFO         = 0x00000100,
1073 INODE_IS_SPARSE               = 0x00000200,
1074 INODE_WAS_EVER_CLONED         = 0x00000400,
1075 INODE_ACTIVE_FILE_TRIMMED     = 0x00000800,
1076 INODE_PINNED_TO_MAIN          = 0x00001000,
1077 INODE_PINNED_TO_TIER2         = 0x00002000,
1078 */
1079 // INODE_HAS_RSRC_FORK           = 0x00004000,
1080 /*
1081 INODE_NO_RSRC_FORK            = 0x00008000,
1082 INODE_ALLOCATION_SPILLEDOVER  = 0x00010000,
1083 INODE_FAST_PROMOTE            = 0x00020000,
1084 */
1085 #define INODE_HAS_UNCOMPRESSED_SIZE  0x00040000
1086 /*
1087 INODE_IS_PURGEABLE            = 0x00080000,
1088 INODE_WANTS_TO_BE_PURGEABLE   = 0x00100000,
1089 INODE_IS_SYNC_ROOT            = 0x00200000,
1090 INODE_SNAPSHOT_COW_EXEMPTION  = 0x00400000,
1091 
1092 
1093 INODE_INHERITED_INTERNAL_FLAGS = \
1094     ( INODE_MAINTAIN_DIR_STATS \
1095     | INODE_SNAPSHOT_COW_EXEMPTION),
1096 
1097 INODE_CLONED_INTERNAL_FLAGS = \
1098     ( INODE_HAS_RSRC_FORK \
1099     | INODE_NO_RSRC_FORK \
1100     | INODE_HAS_FINDER_INFO \
1101     | INODE_SNAPSHOT_COW_EXEMPTION),
1102 */
1103 // }
1104 // j_inode_flags;
1105 
1106 
1107 /*
1108 #define APFS_VALID_INTERNAL_INODE_FLAGS \
1109 ( INODE_IS_APFS_PRIVATE \
1110 | INODE_MAINTAIN_DIR_STATS \
1111 | INODE_DIR_STATS_ORIGIN \
1112 | INODE_PROT_CLASS_EXPLICIT \
1113 | INODE_WAS_CLONED \
1114 | INODE_HAS_SECURITY_EA \
1115 | INODE_BEING_TRUNCATED \
1116 | INODE_HAS_FINDER_INFO \
1117 | INODE_IS_SPARSE \
1118 | INODE_WAS_EVER_CLONED \
1119 | INODE_ACTIVE_FILE_TRIMMED \
1120 | INODE_PINNED_TO_MAIN \
1121 | INODE_PINNED_TO_TIER2 \
1122 | INODE_HAS_RSRC_FORK \
1123 | INODE_NO_RSRC_FORK \
1124 | INODE_ALLOCATION_SPILLEDOVER \
1125 | INODE_FAST_PROMOTE \
1126 | INODE_HAS_UNCOMPRESSED_SIZE \
1127 | INODE_IS_PURGEABLE \
1128 | INODE_WANTS_TO_BE_PURGEABLE \
1129 | INODE_IS_SYNC_ROOT \
1130 | INODE_SNAPSHOT_COW_EXEMPTION)
1131 
1132 #define APFS_INODE_PINNED_MASK (INODE_PINNED_TO_MAIN | INODE_PINNED_TO_TIER2)
1133 */
1134 
1135 static const char * const g_INODE_Flags[] =
1136 {
1137     "IS_APFS_PRIVATE"
1138   , "MAINTAIN_DIR_STATS"
1139   , "DIR_STATS_ORIGIN"
1140   , "PROT_CLASS_EXPLICIT"
1141   , "WAS_CLONED"
1142   , "FLAG_UNUSED"
1143   , "HAS_SECURITY_EA"
1144   , "BEING_TRUNCATED"
1145   , "HAS_FINDER_INFO"
1146   , "IS_SPARSE"
1147   , "WAS_EVER_CLONED"
1148   , "ACTIVE_FILE_TRIMMED"
1149   , "PINNED_TO_MAIN"
1150   , "PINNED_TO_TIER2"
1151   , "HAS_RSRC_FORK"
1152   , "NO_RSRC_FORK"
1153   , "ALLOCATION_SPILLEDOVER"
1154   , "FAST_PROMOTE"
1155   , "HAS_UNCOMPRESSED_SIZE"
1156   , "IS_PURGEABLE"
1157   , "WANTS_TO_BE_PURGEABLE"
1158   , "IS_SYNC_ROOT"
1159   , "SNAPSHOT_COW_EXEMPTION"
1160 };
1161 
1162 
1163 // bsd stat.h
1164 /*
1165 #define MY_UF_SETTABLE   0x0000ffff  // mask of owner changeable flags
1166 #define MY_UF_NODUMP     0x00000001  // do not dump file
1167 #define MY_UF_IMMUTABLE  0x00000002  // file may not be changed
1168 #define MY_UF_APPEND     0x00000004  // writes to file may only append
1169 #define MY_UF_OPAQUE     0x00000008  // directory is opaque wrt. union
1170 #define MY_UF_NOUNLINK   0x00000010  // file entry may not be removed or renamed Not implement in MacOS
1171 #define MY_UF_COMPRESSED 0x00000020  // file entry is compressed
1172 #define MY_UF_TRACKED    0x00000040  // notify about file entry changes
1173 #define MY_UF_DATAVAULT  0x00000080  // entitlement required for reading and writing
1174 #define MY_UF_HIDDEN     0x00008000  // file entry is hidden
1175 
1176 #define MY_SF_SETTABLE   0xffff0000  // mask of superuser changeable flags
1177 #define MY_SF_ARCHIVED   0x00010000  // file is archived
1178 #define MY_SF_IMMUTABLE  0x00020000  // file may not be changed
1179 #define MY_SF_APPEND     0x00040000  // writes to file may only append
1180 #define MY_SF_RESTRICTED 0x00080000  // entitlement required for writing
1181 #define MY_SF_NOUNLINK   0x00100000  // file entry may not be removed, renamed or used as mount point
1182 #define MY_SF_SNAPSHOT   0x00200000  // snapshot inode
1183 Not implement in MacOS
1184 */
1185 
1186 static const char * const g_INODE_BSD_Flags[] =
1187 {
1188     "UF_NODUMP"
1189   , "UF_IMMUTABLE"
1190   , "UF_APPEND"
1191   , "UF_OPAQUE"
1192   , "UF_NOUNLINK"
1193   , "UF_COMPRESSED"
1194   , "UF_TRACKED"
1195   , "UF_DATAVAULT"
1196   , NULL, NULL, NULL, NULL
1197   , NULL, NULL, NULL
1198   , "UF_HIDDEN"
1199 
1200   , "SF_ARCHIVE"
1201   , "SF_IMMUTABLE"
1202   , "SF_APPEND"
1203   , "SF_RESTRICTED"
1204   , "SF_NOUNLINK"
1205   , "SF_SNAPSHOT"
1206 };
1207 
1208 /*
1209 #define INO_EXT_TYPE_SNAP_XID       1
1210 #define INO_EXT_TYPE_DELTA_TREE_OID 2
1211 #define INO_EXT_TYPE_DOCUMENT_ID    3
1212 */
1213 #define INO_EXT_TYPE_NAME           4
1214 /*
1215 #define INO_EXT_TYPE_PREV_FSIZE     5
1216 #define INO_EXT_TYPE_RESERVED_6     6
1217 #define INO_EXT_TYPE_FINDER_INFO    7
1218 */
1219 #define INO_EXT_TYPE_DSTREAM        8
1220 /*
1221 #define INO_EXT_TYPE_RESERVED_9     9
1222 #define INO_EXT_TYPE_DIR_STATS_KEY  10
1223 #define INO_EXT_TYPE_FS_UUID        11
1224 #define INO_EXT_TYPE_RESERVED_12    12
1225 #define INO_EXT_TYPE_SPARSE_BYTES   13
1226 #define INO_EXT_TYPE_RDEV           14
1227 #define INO_EXT_TYPE_PURGEABLE_FLAGS 15
1228 #define INO_EXT_TYPE_ORIG_SYNC_ROOT_ID 16
1229 */
1230 
1231 
1232 static const unsigned k_SizeOf_j_dstream = 8 * 5;
1233 
1234 struct j_dstream
1235 {
1236   UInt64 size;
1237   UInt64 alloced_size;
1238   UInt64 default_crypto_id;
1239   UInt64 total_bytes_written;
1240   UInt64 total_bytes_read;
1241 
ParseNArchive::NApfs::j_dstream1242   void Parse(const Byte *p)
1243   {
1244     G64 (0, size)
1245     G64 (0x8, alloced_size)
1246     G64 (0x10, default_crypto_id)
1247     G64 (0x18, total_bytes_written)
1248     G64 (0x20, total_bytes_read)
1249   }
1250 };
1251 
1252 static const unsigned k_SizeOf_j_file_extent_val = 8 * 3;
1253 
1254 #define J_FILE_EXTENT_LEN_MASK    0x00ffffffffffffffU
1255 // #define J_FILE_EXTENT_FLAG_MASK   0xff00000000000000U
1256 // #define J_FILE_EXTENT_FLAG_SHIFT  56
1257 
1258 #define EXTENT_GET_LEN(x) ((x) & J_FILE_EXTENT_LEN_MASK)
1259 
1260 struct j_file_extent_val
1261 {
1262   UInt64 len_and_flags;   // The length must be a multiple of the block size defined by the nx_block_size field of nx_superblock_t.
1263                           // There are currently no flags defined
1264   UInt64 phys_block_num;  // The physical block address that the extent starts at
1265   // UInt64 crypto_id;       // The encryption key or the encryption tweak used in this extent.
1266 
ParseNArchive::NApfs::j_file_extent_val1267   void Parse(const Byte *p)
1268   {
1269     G64 (0, len_and_flags)
1270     G64 (0x8, phys_block_num)
1271     // G64 (0x10, crypto_id);
1272   }
1273 };
1274 
1275 
1276 struct CExtent
1277 {
1278   UInt64 logical_offset;
1279   UInt64 len_and_flags;   // The length must be a multiple of the block size defined by the nx_block_size field of nx_superblock_t.
1280                           // There are currently no flags defined
1281   UInt64 phys_block_num;  // The physical block address that the extent starts at
1282 
GetEndOffsetNArchive::NApfs::CExtent1283   UInt64 GetEndOffset() const { return logical_offset + EXTENT_GET_LEN(len_and_flags); }
1284 };
1285 
1286 
1287 typedef UInt32  MY_uid_t;
1288 typedef UInt32  MY_gid_t;
1289 typedef UInt16  MY_mode_t;
1290 
1291 
1292 typedef enum
1293 {
1294   XATTR_DATA_STREAM       = 1 << 0,
1295   XATTR_DATA_EMBEDDED     = 1 << 1,
1296   XATTR_FILE_SYSTEM_OWNED = 1 << 2,
1297   XATTR_RESERVED_8        = 1 << 3
1298 } j_xattr_flags;
1299 
1300 
1301 struct CAttr
1302 {
1303   AString Name;
1304   UInt32 flags;
1305   bool dstream_defined;
1306   bool NeedShow;
1307   CByteBuffer Data;
1308 
1309   j_dstream dstream;
1310   UInt64 Id;
1311 
Is_dstream_OK_for_SymLinkNArchive::NApfs::CAttr1312   bool Is_dstream_OK_for_SymLink() const
1313   {
1314     return dstream_defined && dstream.size <= (1 << 12) && dstream.size != 0;
1315   }
1316 
GetSizeNArchive::NApfs::CAttr1317   UInt64 GetSize() const
1318   {
1319     if (dstream_defined) // dstream has more priority
1320       return dstream.size;
1321     return Data.Size();
1322   }
1323 
ClearNArchive::NApfs::CAttr1324   void Clear()
1325   {
1326     dstream_defined = false;
1327     NeedShow = true;
1328     Data.Free();
1329     Name.Empty();
1330   }
1331 
Is_STREAMNArchive::NApfs::CAttr1332   bool Is_STREAM()   const { return (flags & XATTR_DATA_STREAM) != 0; }
Is_EMBEDDEDNArchive::NApfs::CAttr1333   bool Is_EMBEDDED() const { return (flags & XATTR_DATA_EMBEDDED) != 0; }
1334 };
1335 
1336 
1337 // j_inode_val_t
1338 struct CNode
1339 {
1340   unsigned ItemIndex;      // index to CItem. We set it only if Node is directory.
1341   unsigned NumCalcedLinks; // Num links to that node
1342   // unsigned NumItems;    // Num Items in that node
1343 
1344   UInt64 parent_id;    // The identifier of the file system record for the parent directory.
1345   UInt64 private_id;
1346   UInt64 create_time;
1347   UInt64 mod_time;
1348   UInt64 change_time;
1349   UInt64 access_time;
1350   UInt64 internal_flags;
1351   union
1352   {
1353     UInt32 nchildren; /* The number of directory entries.
1354                          is valid only if the inode is a directory */
1355     UInt32 nlink;     /* The number of hard links whose target is this inode.
1356                          is valid only if the inode isn't a directory.
1357     Inodes with multiple hard links (nlink > 1)
1358        - The parent_id field refers to the parent directory of the primary link.
1359        - The name field contains the name of the primary link.
1360        - The INO_EXT_TYPE_NAME extended field contains the name of this link.
1361     */
1362   };
1363   // cp_key_class_t default_protection_class;
1364   UInt32 write_generation_counter;
1365   UInt32 bsd_flags;
1366   MY_uid_t owner;
1367   MY_gid_t group;
1368   MY_mode_t mode;
1369   UInt16 pad1;
1370   UInt64 uncompressed_size;
1371 
1372   j_dstream dstream;
1373   AString PrimaryName;
1374 
1375   bool dstream_defined;
1376   bool refcnt_defined;
1377 
1378   UInt32 refcnt; // j_dstream_id_val_t
1379   CRecordVector<CExtent> Extents;
1380   CObjectVector<CAttr> Attrs;
1381   unsigned SymLinkIndex; // index in Attrs
1382   unsigned DecmpfsIndex; // index in Attrs
1383   unsigned ResourceIndex; // index in Attrs
1384 
1385   NHfs::CCompressHeader CompressHeader;
1386 
CNodeNArchive::NApfs::CNode1387   CNode():
1388       ItemIndex(VI_MINUS1),
1389       NumCalcedLinks(0),
1390       // NumItems(0),
1391       dstream_defined(false),
1392       refcnt_defined(false),
1393       SymLinkIndex(VI_MINUS1),
1394       DecmpfsIndex(VI_MINUS1),
1395       ResourceIndex(VI_MINUS1)
1396       {}
1397 
IsDirNArchive::NApfs::CNode1398   bool IsDir() const { return MY_LIN_S_ISDIR(mode); }
IsSymLinkNArchive::NApfs::CNode1399   bool IsSymLink() const { return MY_LIN_S_ISLNK(mode); }
1400 
Has_UNCOMPRESSED_SIZENArchive::NApfs::CNode1401   bool Has_UNCOMPRESSED_SIZE() const { return (internal_flags & INODE_HAS_UNCOMPRESSED_SIZE) != 0; }
1402 
Get_Type_From_modeNArchive::NApfs::CNode1403   unsigned Get_Type_From_mode() const { return mode >> 12; }
1404 
GetSizeNArchive::NApfs::CNode1405   bool GetSize(unsigned attrIndex, UInt64 &size) const
1406   {
1407     if (IsViNotDef(attrIndex))
1408     {
1409       if (dstream_defined)
1410       {
1411         size = dstream.size;
1412         return true;
1413       }
1414       size = 0;
1415       if (Has_UNCOMPRESSED_SIZE())
1416       {
1417         size = uncompressed_size;
1418         return true;
1419       }
1420       if (!IsSymLink())
1421         return false;
1422       attrIndex = SymLinkIndex;
1423       if (IsViNotDef(attrIndex))
1424         return false;
1425     }
1426     const CAttr &attr = Attrs[(unsigned)attrIndex];
1427     if (attr.dstream_defined)
1428       size = attr.dstream.size;
1429     else
1430       size = attr.Data.Size();
1431     return true;
1432   }
1433 
GetPackSizeNArchive::NApfs::CNode1434   bool GetPackSize(unsigned attrIndex, UInt64 &size) const
1435   {
1436     if (IsViNotDef(attrIndex))
1437     {
1438       if (dstream_defined)
1439       {
1440         size = dstream.alloced_size;
1441         return true;
1442       }
1443       size = 0;
1444 
1445       if (IsSymLink())
1446         attrIndex = SymLinkIndex;
1447       else
1448       {
1449         if (!CompressHeader.IsCorrect ||
1450             !CompressHeader.IsSupported)
1451           return false;
1452         const CAttr &attr = Attrs[DecmpfsIndex];
1453         if (!CompressHeader.IsMethod_Resource())
1454         {
1455           size = attr.Data.Size() - CompressHeader.DataPos;
1456           return true;
1457         }
1458         attrIndex = ResourceIndex;
1459       }
1460       if (IsViNotDef(attrIndex))
1461         return false;
1462     }
1463     const CAttr &attr = Attrs[(unsigned)attrIndex];
1464     if (attr.dstream_defined)
1465       size = attr.dstream.alloced_size;
1466     else
1467       size = attr.Data.Size();
1468     return true;
1469   }
1470 
1471   void Parse(const Byte *p);
1472 };
1473 
1474 
1475 // it's used for Attr streams
1476 struct CSmallNode
1477 {
1478   CRecordVector<CExtent> Extents;
1479   // UInt32 NumLinks;
1480   // CSmallNode(): NumLinks(0) {}
1481 };
1482 
1483 static const unsigned k_SizeOf_j_inode_val = 0x5c;
1484 
Parse(const Byte * p)1485 void CNode::Parse(const Byte *p)
1486 {
1487   G64 (0, parent_id)
1488   G64 (0x8, private_id)
1489   G64 (0x10, create_time)
1490   G64 (0x18, mod_time)
1491   G64 (0x20, change_time)
1492   G64 (0x28, access_time)
1493   G64 (0x30, internal_flags)
1494   {
1495     G32 (0x38, nchildren)
1496     //  G32 (0x38, nlink);
1497   }
1498   // G32 (0x3c, default_protection_class);
1499   G32 (0x40, write_generation_counter)
1500   G32 (0x44, bsd_flags)
1501   G32 (0x48, owner)
1502   G32 (0x4c, group)
1503   G16 (0x50, mode)
1504   // G16 (0x52, pad1);
1505   G64 (0x54, uncompressed_size)
1506 }
1507 
1508 
1509 struct CRef
1510 {
1511   unsigned ItemIndex;
1512   unsigned NodeIndex;
1513   unsigned ParentRefIndex;
1514 
1515  #ifdef APFS_SHOW_ALT_STREAMS
1516   unsigned AttrIndex;
IsAltStreamNArchive::NApfs::CRef1517   bool IsAltStream() const { return IsViDef(AttrIndex); }
GetAttrIndexNArchive::NApfs::CRef1518   unsigned GetAttrIndex() const { return AttrIndex; }
1519  #else
1520   // bool IsAltStream() const { return false; }
GetAttrIndexNArchive::NApfs::CRef1521   unsigned GetAttrIndex() const { return VI_MINUS1; }
1522  #endif
1523 };
1524 
1525 
1526 struct CRef2
1527 {
1528   unsigned VolIndex;
1529   unsigned RefIndex;
1530 };
1531 
1532 
1533 struct CHashChunk
1534 {
1535   UInt64 lba;
1536   UInt32 hashed_len; // the value is UInt16
1537   Byte hash[APFS_HASH_MAX_SIZE];
1538 };
1539 
1540 typedef CRecordVector<CHashChunk> CStreamHashes;
1541 
1542 
1543 Z7_CLASS_IMP_NOQIB_1(
1544   COutStreamWithHash
1545   , ISequentialOutStream
1546 )
1547   bool _hashError;
1548   CAlignedBuffer1 _sha;
1549   CMyComPtr<ISequentialOutStream> _stream;
1550   const CStreamHashes *_hashes;
1551   unsigned _blockSizeLog;
1552   unsigned _chunkIndex;
1553   UInt32 _offsetInChunk;
1554   // UInt64 _size;
1555 
Sha()1556   CSha256 *Sha() { return (CSha256 *)(void *)(Byte *)_sha; }
1557 public:
1558   COutStreamWithHash(): _sha(sizeof(CSha256)) {}
1559 
1560   void SetStream(ISequentialOutStream *stream) { _stream = stream; }
1561   // void ReleaseStream() { _stream.Release(); }
1562   void Init(const CStreamHashes *hashes, unsigned blockSizeLog)
1563   {
1564     _hashes = hashes;
1565     _blockSizeLog = blockSizeLog;
1566     _chunkIndex = 0;
1567     _offsetInChunk = 0;
1568     _hashError = false;
1569     // _size = 0;
1570   }
1571   // UInt64 GetSize() const { return _size; }
1572   bool FinalCheck();
1573 };
1574 
1575 
1576 static bool Sha256_Final_and_CheckDigest(CSha256 *sha256, const Byte *digest)
1577 {
1578   MY_ALIGN (16)
1579   UInt32 temp[SHA256_NUM_DIGEST_WORDS];
1580   Sha256_Final(sha256, (Byte *)temp);
1581   return memcmp(temp, digest, SHA256_DIGEST_SIZE) == 0;
1582 }
1583 
1584 
1585 Z7_COM7F_IMF(COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize))
1586 {
1587   HRESULT result = S_OK;
1588   if (_stream)
1589     result = _stream->Write(data, size, &size);
1590   if (processedSize)
1591     *processedSize = size;
1592   while (size != 0)
1593   {
1594     if (_hashError)
1595       break;
1596     if (_chunkIndex >= _hashes->Size())
1597     {
1598       _hashError = true;
1599       break;
1600     }
1601     if (_offsetInChunk == 0)
1602       Sha256_Init(Sha());
1603     const CHashChunk &chunk = (*_hashes)[_chunkIndex];
1604     /* (_blockSizeLog <= 16) && chunk.hashed_len is 16-bit.
1605        so we can use 32-bit chunkSize here */
1606     const UInt32 chunkSize = ((UInt32)chunk.hashed_len << _blockSizeLog);
1607     const UInt32 rem = chunkSize - _offsetInChunk;
1608     UInt32 cur = size;
1609     if (cur > rem)
1610       cur = (UInt32)rem;
1611     Sha256_Update(Sha(), (const Byte *)data, cur);
1612     data = (const void *)((const Byte *)data + cur);
1613     size -= cur;
1614     // _size += cur;
1615     _offsetInChunk += cur;
1616     if (chunkSize == _offsetInChunk)
1617     {
1618       if (!Sha256_Final_and_CheckDigest(Sha(), chunk.hash))
1619         _hashError = true;
1620       _offsetInChunk = 0;
1621       _chunkIndex++;
1622     }
1623   }
1624   return result;
1625 }
1626 
1627 
1628 bool COutStreamWithHash::FinalCheck()
1629 {
1630   if (_hashError)
1631     return false;
1632 
1633   if (_offsetInChunk != 0)
1634   {
1635     const CHashChunk &chunk = (*_hashes)[_chunkIndex];
1636     {
1637       const UInt32 chunkSize = ((UInt32)chunk.hashed_len << _blockSizeLog);
1638       const UInt32 rem = chunkSize - _offsetInChunk;
1639       Byte b = 0;
1640       for (UInt32 i = 0; i < rem; i++)
1641         Sha256_Update(Sha(), &b, 1);
1642     }
1643     if (!Sha256_Final_and_CheckDigest(Sha(), chunk.hash))
1644       _hashError = true;
1645     _offsetInChunk = 0;
1646     _chunkIndex++;
1647   }
1648   if (_chunkIndex != _hashes->Size())
1649     _hashError = true;
1650   return !_hashError;
1651 }
1652 
1653 
1654 
1655 struct CVol
1656 {
1657   CObjectVector<CNode> Nodes;
1658   CRecordVector<UInt64> NodeIDs;
1659   CObjectVector<CItem> Items;
1660   CRecordVector<CRef> Refs;
1661 
1662   CObjectVector<CSmallNode> SmallNodes;
1663   CRecordVector<UInt64> SmallNodeIDs;
1664 
1665   CObjectVector<CSmallNode> FEXT_Nodes;
1666   CRecordVector<UInt64> FEXT_NodeIDs;
1667 
1668   CObjectVector<CStreamHashes> Hash_Vectors;
1669   CRecordVector<UInt64> Hash_IDs;
1670 
1671   unsigned StartRef2Index;  // ref2_Index for Refs[0] item
1672   unsigned RootRef2Index;   // ref2_Index of virtual root folder (Volume1)
1673   CApfs apfs;
1674   C_integrity_meta_phys integrity;
1675 
1676   bool NodeNotFound;
1677   bool ThereAreUnlinkedNodes;
1678   bool WrongInodeLink;
1679   bool UnsupportedFeature;
1680   bool UnsupportedMethod;
1681 
1682   unsigned NumItems_In_PrivateDir;
1683   unsigned NumAltStreams;
1684 
1685   UString RootName;
1686 
1687   bool ThereAreErrors() const
1688   {
1689     return NodeNotFound || ThereAreUnlinkedNodes || WrongInodeLink;
1690   }
1691 
1692   void AddComment(UString &s) const;
1693 
1694   HRESULT FillRefs();
1695 
1696   CVol():
1697       StartRef2Index(0),
1698       RootRef2Index(VI_MINUS1),
1699       NodeNotFound(false),
1700       ThereAreUnlinkedNodes(false),
1701       WrongInodeLink(false),
1702       UnsupportedFeature(false),
1703       UnsupportedMethod(false),
1704       NumItems_In_PrivateDir(0),
1705       NumAltStreams(0)
1706       {}
1707 };
1708 
1709 
1710 static void ApfsTimeToFileTime(UInt64 apfsTime, FILETIME &ft, UInt32 &ns100)
1711 {
1712   const UInt64 s = apfsTime / 1000000000;
1713   const UInt32 ns = (UInt32)(apfsTime % 1000000000);
1714   ns100 = (ns % 100);
1715   const UInt64 v = NWindows::NTime::UnixTime64_To_FileTime64((Int64)s) + ns / 100;
1716   ft.dwLowDateTime = (DWORD)v;
1717   ft.dwHighDateTime = (DWORD)(v >> 32);
1718 }
1719 
1720 static void AddComment_Name(UString &s, const char *name)
1721 {
1722   s += name;
1723   s += ": ";
1724 }
1725 
1726 /*
1727 static void AddComment_Bool(UString &s, const char *name, bool val)
1728 {
1729   AddComment_Name(s, name);
1730   s += val ? "+" : "-";
1731   s.Add_LF();
1732 }
1733 */
1734 
1735 static void AddComment_UInt64(UString &s, const char *name, UInt64 v)
1736 {
1737   AddComment_Name(s, name);
1738   s.Add_UInt64(v);
1739   s.Add_LF();
1740 }
1741 
1742 
1743 static void AddComment_Time(UString &s, const char *name, UInt64 v)
1744 {
1745   AddComment_Name(s, name);
1746 
1747   FILETIME ft;
1748   UInt32 ns100;
1749   ApfsTimeToFileTime(v, ft, ns100);
1750   char temp[64];
1751   ConvertUtcFileTimeToString2(ft, ns100, temp
1752     // , kTimestampPrintLevel_SEC);
1753     , kTimestampPrintLevel_NS);
1754   s += temp;
1755   s.Add_LF();
1756 }
1757 
1758 
1759 static void AddComment_modified_by_t(UString &s, const char *name, const apfs_modified_by_t &v)
1760 {
1761   AddComment_Name(s, name);
1762   AString s2;
1763   s2.SetFrom_CalcLen((const char *)v.id, sizeof(v.id));
1764   s += s2;
1765   s.Add_LF();
1766   s += "  ";
1767   AddComment_Time(s, "timestamp", v.timestamp);
1768   s += "  ";
1769   AddComment_UInt64(s, "last_xid", v.last_xid);
1770 }
1771 
1772 
1773 static void AddVolInternalName_toString(UString &s, const CApfs &apfs)
1774 {
1775   AString temp;
1776   temp.SetFrom_CalcLen((const char *)apfs.volname, sizeof(apfs.volname));
1777   UString unicode;
1778   ConvertUTF8ToUnicode(temp, unicode);
1779   s += unicode;
1780 }
1781 
1782 
1783 void CVol::AddComment(UString &s) const
1784 {
1785   AddComment_UInt64(s, "fs_index", apfs.fs_index);
1786   {
1787     AddComment_Name(s, "volume_name");
1788     AddVolInternalName_toString(s, apfs);
1789     s.Add_LF();
1790   }
1791   AddComment_Name(s, "vol_uuid");
1792   apfs.vol_uuid.AddHexToString(s);
1793   s.Add_LF();
1794 
1795   AddComment_Name(s, "incompatible_features");
1796   s += FlagsToString(g_APFS_INCOMPAT_Flags,
1797       Z7_ARRAY_SIZE(g_APFS_INCOMPAT_Flags),
1798       (UInt32)apfs.incompatible_features);
1799   s.Add_LF();
1800 
1801 
1802   if (apfs.integrity_meta_oid != 0)
1803   {
1804     /*
1805     AddComment_Name(s, "im_version");
1806     s.Add_UInt32(integrity.im_version);
1807     s.Add_LF();
1808     */
1809     AddComment_Name(s, "im_flags");
1810     s.Add_UInt32(integrity.im_flags);
1811     s.Add_LF();
1812     AddComment_Name(s, "im_hash_type");
1813     const char *p = NULL;
1814     if (integrity.im_hash_type < Z7_ARRAY_SIZE(g_hash_types))
1815       p = g_hash_types[integrity.im_hash_type];
1816     if (p)
1817       s += p;
1818     else
1819       s.Add_UInt32(integrity.im_hash_type);
1820     s.Add_LF();
1821   }
1822 
1823 
1824   // AddComment_UInt64(s, "reserve_block_count", apfs.fs_reserve_block_count, false);
1825   // AddComment_UInt64(s, "quota_block_count", apfs.fs_quota_block_count);
1826   AddComment_UInt64(s, "fs_alloc_count", apfs.fs_alloc_count);
1827 
1828   AddComment_UInt64(s, "num_files", apfs.num_files);
1829   AddComment_UInt64(s, "num_directories", apfs.num_directories);
1830   AddComment_UInt64(s, "num_symlinks", apfs.num_symlinks);
1831   AddComment_UInt64(s, "num_other_fsobjects", apfs.num_other_fsobjects);
1832 
1833   AddComment_UInt64(s, "Num_Attr_Streams", NumAltStreams);
1834 
1835   AddComment_UInt64(s, "num_snapshots", apfs.num_snapshots);
1836   AddComment_UInt64(s, "total_blocks_alloced", apfs.total_blocks_alloced);
1837   AddComment_UInt64(s, "total_blocks_freed", apfs.total_blocks_freed);
1838 
1839   AddComment_Time(s, "unmounted", apfs.unmount_time);
1840   AddComment_Time(s, "last_modified", apfs.last_mod_time);
1841   AddComment_modified_by_t(s, "formatted_by", apfs.formatted_by);
1842   for (unsigned i = 0; i < Z7_ARRAY_SIZE(apfs.modified_by); i++)
1843   {
1844     const apfs_modified_by_t &v = apfs.modified_by[i];
1845     if (v.last_xid == 0 && v.timestamp == 0 && v.id[0] == 0)
1846       continue;
1847     AString name ("modified_by[");
1848     name.Add_UInt32(i);
1849     name += ']';
1850     AddComment_modified_by_t(s, name.Ptr(), v);
1851   }
1852 }
1853 
1854 
1855 
1856 struct CKeyValPair
1857 {
1858   CByteBuffer Key;
1859   CByteBuffer Val;
1860   // unsigned ValPos; // for alognment
1861 };
1862 
1863 
1864 struct omap_key
1865 {
1866   oid_t oid; // The object identifier
1867   xid_t xid; // The transaction identifier
1868   void Parse(const Byte *p)
1869   {
1870     G64o (0, oid)
1871     G64x (8, xid)
1872   }
1873 };
1874 
1875 /*
1876 #define OMAP_VAL_DELETED            (1 << 0)
1877 #define OMAP_VAL_SAVED              (1 << 1)
1878 #define OMAP_VAL_ENCRYPTED          (1 << 2)
1879 */
1880 #define OMAP_VAL_NOHEADER           (1 << 3)
1881 /*
1882 #define OMAP_VAL_CRYPTO_GENERATION  (1 << 4)
1883 */
1884 
1885 struct omap_val
1886 {
1887   UInt32 flags;
1888   UInt32 size;
1889   // paddr_t paddr;
1890   paddr_t_unsigned paddr;
1891 
1892   bool IsFlag_NoHeader() const { return (flags & OMAP_VAL_NOHEADER) != 0; }
1893 
1894   void Parse(const Byte *p)
1895   {
1896     G32 (0, flags)
1897     G32 (4, size)
1898     G64 (8, paddr)
1899   }
1900 };
1901 
1902 
1903 struct CObjectMap
1904 {
1905   CRecordVector<UInt64> Keys;
1906   CRecordVector<omap_val> Vals;
1907 
1908   bool Parse(const CObjectVector<CKeyValPair> &pairs);
1909   int FindKey(UInt64 id) const { return Keys.FindInSorted(id); }
1910 };
1911 
1912 bool CObjectMap::Parse(const CObjectVector<CKeyValPair> &pairs)
1913 {
1914   omap_key prev;
1915   prev.oid = 0;
1916   prev.xid = 0;
1917   FOR_VECTOR (i, pairs)
1918   {
1919     const CKeyValPair &pair = pairs[i];
1920     if (pair.Key.Size() != 16 || pair.Val.Size() != 16)
1921       return false;
1922     omap_key key;
1923     key.Parse(pair.Key);
1924     omap_val val;
1925     val.Parse(pair.Val);
1926     /* Object map B-trees are sorted by object identifier and then by transaction identifier
1927        but it's possible to have identical Ids in map ?
1928        do we need to look transaction id ?
1929        and search key with largest transaction id? */
1930     if (key.oid <= prev.oid)
1931       return false;
1932     prev = key;
1933     Keys.Add(key.oid);
1934     Vals.Add(val);
1935   }
1936   return true;
1937 }
1938 
1939 
1940 struct CMap
1941 {
1942   CObjectVector<CKeyValPair> Pairs;
1943 
1944   CObjectMap Omap;
1945   btree_info bti;
1946   UInt64 NumNodes;
1947 
1948   // we use thnese options to check:
1949   UInt32 Subtype;
1950   bool IsPhysical;
1951 
1952   bool CheckAtFinish() const
1953   {
1954     return NumNodes == bti.node_count && Pairs.Size() == bti.key_count;
1955   }
1956 
1957   CMap():
1958       NumNodes(0),
1959       Subtype(0),
1960       IsPhysical(true)
1961       {}
1962 };
1963 
1964 
1965 
1966 struct CDatabase
1967 {
1968   CRecordVector<CRef2> Refs2;
1969   CObjectVector<CVol> Vols;
1970 
1971   bool HeadersError;
1972   bool ThereAreAltStreams;
1973   bool UnsupportedFeature;
1974   bool UnsupportedMethod;
1975 
1976   CSuperBlock sb;
1977 
1978   IInStream *OpenInStream;
1979   IArchiveOpenCallback *OpenCallback;
1980   UInt64 ProgressVal_Cur;
1981   UInt64 ProgressVal_Prev;
1982   UInt64 ProgressVal_NumFilesTotal;
1983   CObjectVector<CByteBuffer> Buffers;
1984 
1985   UInt32 MethodsMask;
1986   UInt64 GetSize(const UInt32 index) const;
1987 
1988   void Clear()
1989   {
1990     HeadersError = false;
1991     ThereAreAltStreams = false;
1992     UnsupportedFeature = false;
1993     UnsupportedMethod = false;
1994 
1995     ProgressVal_Cur = 0;
1996     ProgressVal_Prev = 0;
1997     ProgressVal_NumFilesTotal = 0;
1998 
1999     MethodsMask = 0;
2000 
2001     Vols.Clear();
2002     Refs2.Clear();
2003     Buffers.Clear();
2004   }
2005 
2006   HRESULT SeekReadBlock_FALSE(UInt64 oid, void *data);
2007   void GetItemPath(unsigned index, const CNode *inode, NWindows::NCOM::CPropVariant &path) const;
2008   HRESULT ReadMap(UInt64 oid, bool noHeader, CVol *vol, const Byte *hash,
2009       CMap &map, unsigned recurseLevel);
2010   HRESULT ReadObjectMap(UInt64 oid, CVol *vol, CObjectMap &map);
2011   HRESULT OpenVolume(const CObjectMap &omap, const oid_t fs_oid);
2012   HRESULT Open2();
2013 
2014   HRESULT GetAttrStream(IInStream *apfsInStream, const CVol &vol,
2015       const CAttr &attr, ISequentialInStream **stream);
2016 
2017   HRESULT GetAttrStream_dstream(IInStream *apfsInStream, const CVol &vol,
2018       const CAttr &attr, ISequentialInStream **stream);
2019 
2020   HRESULT GetStream2(
2021       IInStream *apfsInStream,
2022       const CRecordVector<CExtent> *extents, UInt64 rem,
2023       ISequentialInStream **stream);
2024 };
2025 
2026 
2027 HRESULT CDatabase::SeekReadBlock_FALSE(UInt64 oid, void *data)
2028 {
2029   if (OpenCallback)
2030   {
2031     if (ProgressVal_Cur - ProgressVal_Prev >= (1 << 22))
2032     {
2033       RINOK(OpenCallback->SetCompleted(NULL, &ProgressVal_Cur))
2034       ProgressVal_Prev = ProgressVal_Cur;
2035     }
2036     ProgressVal_Cur += sb.block_size;
2037   }
2038   if (oid == 0 || oid >= sb.block_count)
2039     return S_FALSE;
2040   RINOK(InStream_SeekSet(OpenInStream, oid << sb.block_size_Log))
2041   return ReadStream_FALSE(OpenInStream, data, sb.block_size);
2042 }
2043 
2044 
2045 
2046 API_FUNC_static_IsArc IsArc_APFS(const Byte *p, size_t size)
2047 {
2048   if (size < kApfsHeaderSize)
2049     return k_IsArc_Res_NEED_MORE;
2050   CSuperBlock sb;
2051   if (!sb.Parse(p))
2052     return k_IsArc_Res_NO;
2053   return k_IsArc_Res_YES;
2054 }
2055 }
2056 
2057 
2058 static bool CheckHash(unsigned hashAlgo, const Byte *data, size_t size, const Byte *digest)
2059 {
2060   if (hashAlgo == APFS_HASH_SHA256)
2061   {
2062     MY_ALIGN (16)
2063     CSha256 sha;
2064     Sha256_Init(&sha);
2065     Sha256_Update(&sha, data, size);
2066     return Sha256_Final_and_CheckDigest(&sha, digest);
2067   }
2068   return true;
2069 }
2070 
2071 
2072 HRESULT CDatabase::ReadMap(UInt64 oid, bool noHeader,
2073     CVol *vol, const Byte *hash,
2074     CMap &map, unsigned recurseLevel)
2075 {
2076   // is it allowed to use big number of levels ?
2077   if (recurseLevel > (1 << 10))
2078     return S_FALSE;
2079 
2080   const UInt32 blockSize = sb.block_size;
2081   if (Buffers.Size() <= recurseLevel)
2082   {
2083     Buffers.AddNew();
2084     if (Buffers.Size() <= recurseLevel)
2085       throw 123;
2086     Buffers.Back().Alloc(blockSize);
2087   }
2088   const Byte *buf;
2089   {
2090     CByteBuffer &buf2 = Buffers[recurseLevel];
2091     RINOK(SeekReadBlock_FALSE(oid, buf2))
2092     buf = buf2;
2093   }
2094 
2095   if (hash && vol)
2096     if (!CheckHash(vol->integrity.im_hash_type, buf, blockSize, hash))
2097       return S_FALSE;
2098 
2099   CBTreeNodePhys bt;
2100   if (!bt.Parse(buf, blockSize, noHeader))
2101     return S_FALSE;
2102 
2103   map.NumNodes++;
2104 
2105   /* Specification: All values are stored in leaf nodes, which
2106      makes these B+ trees, and the values in nonleaf nodes are object
2107      identifiers of child nodes.
2108 
2109      The entries in the table of contents are sorted by key. The comparison function used for sorting depends on the keys type
2110       - Object map B-trees are sorted by object identifier and then by transaction identifier.
2111       - Free queue B-trees are sorted by transaction identifier and then by physical address.
2112       - File-system records are sorted according to the rules listed in File-System Objects.
2113   */
2114 
2115   if (!noHeader)
2116   if (bt.ophys.subtype != map.Subtype)
2117     return S_FALSE;
2118 
2119   unsigned endLimit = blockSize;
2120 
2121   if (recurseLevel == 0)
2122   {
2123     if (!noHeader)
2124     if (bt.ophys.GetType() != OBJECT_TYPE_BTREE)
2125       return S_FALSE;
2126     if ((bt.flags & BTNODE_ROOT) == 0)
2127       return S_FALSE;
2128     endLimit -= k_btree_info_Size;
2129     map.bti.Parse(buf + endLimit);
2130     btree_info &bti = map.bti;
2131     if (bti.fixed.key_size >= blockSize)
2132       return S_FALSE;
2133     if (bti.Is_EPHEMERAL() &&
2134         bti.Is_PHYSICAL())
2135       return S_FALSE;
2136     if (bti.Is_PHYSICAL() != map.IsPhysical)
2137       return S_FALSE;
2138     if (bti.Is_NOHEADER() != noHeader)
2139       return S_FALSE;
2140     // we don't allow volumes with big number of Keys
2141     const unsigned kNumItemsMax = k_VectorSizeMax;
2142     if (map.bti.node_count > kNumItemsMax)
2143       return S_FALSE;
2144     if (map.bti.key_count > kNumItemsMax)
2145       return S_FALSE;
2146   }
2147   else
2148   {
2149     if (!noHeader)
2150     if (bt.ophys.GetType() != OBJECT_TYPE_BTREE_NODE)
2151       return S_FALSE;
2152     if ((bt.flags & BTNODE_ROOT) != 0)
2153       return S_FALSE;
2154     if (map.NumNodes > map.bti.node_count
2155         || map.Pairs.Size() > map.bti.key_count)
2156       return S_FALSE;
2157   }
2158 
2159   const bool isLeaf = (bt.flags & BTNODE_LEAF) != 0;
2160 
2161   if (isLeaf)
2162   {
2163     if (bt.level != 0)
2164       return S_FALSE;
2165   }
2166   else
2167   {
2168     if (bt.level == 0)
2169       return S_FALSE;
2170   }
2171 
2172   if (!bt.table_space.CheckOverLimit(endLimit - k_Toc_offset))
2173     return S_FALSE;
2174 
2175   const unsigned tableEnd = k_Toc_offset + bt.table_space.GetEnd();
2176   const unsigned keyValRange = endLimit - tableEnd;
2177   const unsigned tocEntrySize = bt.Is_FIXED_KV_SIZE() ? 4 : 8;
2178   if (bt.table_space.len / tocEntrySize < bt.nkeys)
2179     return S_FALSE;
2180 
2181   for (unsigned i = 0; i < bt.nkeys; i++)
2182   {
2183     const Byte *p = buf + k_Toc_offset + bt.table_space.off + i * tocEntrySize;
2184     if (bt.Is_FIXED_KV_SIZE())
2185     {
2186       kvoff a;
2187       a.Parse(p);
2188       if (a.k + map.bti.fixed.key_size > keyValRange
2189           || a.v > keyValRange)
2190         return S_FALSE;
2191       {
2192         CKeyValPair pair;
2193 
2194         const Byte *p2 = buf + k_Toc_offset + bt.table_space.len;
2195         p2 += a.k;
2196         pair.Key.CopyFrom(p2, map.bti.fixed.key_size);
2197 
2198         p2 = buf + endLimit;
2199         p2 -= a.v;
2200         if (isLeaf)
2201         {
2202           if (a.v < map.bti.fixed.val_size)
2203             return S_FALSE;
2204           pair.Val.CopyFrom(p2, map.bti.fixed.val_size);
2205           // pair.ValPos = endLimit - a.v;
2206           map.Pairs.Add(pair);
2207           continue;
2208         }
2209         {
2210           if (a.v < 8)
2211             return S_FALSE;
2212           // value is only 64-bit for non leaf.
2213           const oid_t oidNext = Get64(p2);
2214           if (map.bti.Is_PHYSICAL())
2215           {
2216             RINOK(ReadMap(oidNext, noHeader, vol,
2217                 NULL, /* hash */
2218                 map, recurseLevel + 1))
2219             continue;
2220           }
2221           else
2222           {
2223             // fixme
2224             return S_FALSE;
2225           }
2226         }
2227       }
2228     }
2229     else
2230     {
2231       kvloc a;
2232       a.Parse(p);
2233       if (!a.k.CheckOverLimit(keyValRange)
2234           || a.v.off > keyValRange
2235           || a.v.len > a.v.off)
2236         return S_FALSE;
2237       {
2238         CKeyValPair pair;
2239         const Byte *p2 = buf + k_Toc_offset + bt.table_space.len;
2240         p2 += a.k.off;
2241         pair.Key.CopyFrom(p2, a.k.len);
2242 
2243         p2 = buf + endLimit;
2244         p2 -= a.v.off;
2245         if (isLeaf)
2246         {
2247           pair.Val.CopyFrom(p2, a.v.len);
2248           // pair.ValPos = endLimit - a.v.off;
2249           map.Pairs.Add(pair);
2250           continue;
2251         }
2252         {
2253           if (a.v.len < 8)
2254             return S_FALSE;
2255 
2256           const Byte *hashNew = NULL;
2257           oid_t oidNext = Get64(p2);
2258 
2259           if (bt.Is_HASHED())
2260           {
2261             if (!vol)
2262               return S_FALSE;
2263             /*
2264             struct btn_index_node_val {
2265               oid_t binv_child_oid;
2266               uint8_t binv_child_hash[BTREE_NODE_HASH_SIZE_MAX];
2267             };
2268             */
2269             /* (a.v.len == 40) is possible if Is_HASHED()
2270                so there is hash (for example, 256-bit) after 64-bit id) */
2271             if (a.v.len != 8 + vol->integrity.HashSize)
2272               return S_FALSE;
2273             hashNew = p2 + 8;
2274             /* we need to add root_tree_oid here,
2275                but where it's defined in apfs specification ? */
2276             oidNext += vol->apfs.root_tree_oid;
2277           }
2278           else
2279           {
2280             if (a.v.len != 8)
2281               return S_FALSE;
2282           }
2283 
2284           // value is only 64-bit for non leaf.
2285 
2286           if (map.bti.Is_PHYSICAL())
2287           {
2288             return S_FALSE;
2289             // the code was not tested:
2290             // RINOK(ReadMap(oidNext, map, recurseLevel + 1));
2291             // continue;
2292           }
2293           /*
2294           else if (map.bti.Is_EPHEMERAL())
2295           {
2296             // Ephemeral objects are stored in memory while the container is mounted and in a checkpoint when the container isn't mounted
2297             // the code was not tested:
2298             return S_FALSE;
2299           }
2300           */
2301           else
2302           {
2303             /* nodes in the B-tree use virtual object identifiers to link to their child nodes. */
2304             const int index = map.Omap.FindKey(oidNext);
2305             if (index == -1)
2306               return S_FALSE;
2307             const omap_val &ov = map.Omap.Vals[(unsigned)index];
2308             if (ov.size != blockSize) // change it : it must be multiple of
2309               return S_FALSE;
2310             RINOK(ReadMap(ov.paddr, ov.IsFlag_NoHeader(), vol, hashNew,
2311                 map, recurseLevel + 1))
2312             continue;
2313           }
2314         }
2315       }
2316     }
2317   }
2318 
2319   if (recurseLevel == 0)
2320     if (!map.CheckAtFinish())
2321       return S_FALSE;
2322   return S_OK;
2323 }
2324 
2325 
2326 
2327 HRESULT CDatabase::ReadObjectMap(UInt64 oid, CVol *vol, CObjectMap &omap)
2328 {
2329   CByteBuffer buf;
2330   const size_t blockSize = sb.block_size;
2331   buf.Alloc(blockSize);
2332   RINOK(SeekReadBlock_FALSE(oid, buf))
2333   C_omap_phys op;
2334   if (!op.Parse(buf, blockSize, oid))
2335     return S_FALSE;
2336   CMap map;
2337   map.Subtype = OBJECT_TYPE_OMAP;
2338   map.IsPhysical = true;
2339   RINOK(ReadMap(op.tree_oid,
2340       false /* noHeader */,
2341       vol,
2342       NULL, /* hash */
2343       map, 0))
2344   if (!omap.Parse(map.Pairs))
2345     return S_FALSE;
2346   return S_OK;
2347 }
2348 
2349 
2350 
2351 HRESULT CDatabase::Open2()
2352 {
2353   Clear();
2354   CSuperBlock2 sb2;
2355   {
2356     Byte buf[kApfsHeaderSize];
2357     RINOK(ReadStream_FALSE(OpenInStream, buf, kApfsHeaderSize))
2358     if (!sb.Parse(buf))
2359       return S_FALSE;
2360     sb2.Parse(buf);
2361   }
2362 
2363   {
2364     CObjectMap omap;
2365     RINOK(ReadObjectMap(sb.omap_oid,
2366         NULL, /* CVol */
2367         omap))
2368     unsigned numRefs = 0;
2369     for (unsigned i = 0; i < sb.max_file_systems; i++)
2370     {
2371       const oid_t oid = sb2.fs_oid[i];
2372       if (oid == 0)
2373         continue;
2374       // for (unsigned k = 0; k < 1; k++) // for debug
2375       RINOK(OpenVolume(omap, oid))
2376       const unsigned a = Vols.Back().Refs.Size();
2377       numRefs += a;
2378       if (numRefs < a)
2379         return S_FALSE; // overflow
2380     }
2381   }
2382 
2383   const bool needVolumePrefix = (Vols.Size() > 1);
2384   // const bool needVolumePrefix = true; // for debug
2385   {
2386     unsigned numRefs = 0;
2387     FOR_VECTOR (i, Vols)
2388     {
2389       const unsigned a = Vols[i].Refs.Size();
2390       numRefs += a;
2391       if (numRefs < a)
2392         return S_FALSE; // overflow
2393     }
2394     numRefs += Vols.Size();
2395     if (numRefs < Vols.Size())
2396       return S_FALSE; // overflow
2397     Refs2.Reserve(numRefs);
2398   }
2399   {
2400     FOR_VECTOR (i, Vols)
2401     {
2402       CVol &vol = Vols[i];
2403 
2404       CRef2 ref2;
2405       ref2.VolIndex = i;
2406 
2407       if (needVolumePrefix)
2408       {
2409         vol.RootName = "Volume";
2410         vol.RootName.Add_UInt32(1 + (UInt32)i);
2411         vol.RootRef2Index = Refs2.Size();
2412         ref2.RefIndex = VI_MINUS1;
2413         Refs2.Add(ref2);
2414       }
2415 
2416       vol.StartRef2Index = Refs2.Size();
2417       const unsigned numItems = vol.Refs.Size();
2418       for (unsigned k = 0; k < numItems; k++)
2419       {
2420         ref2.RefIndex = k;
2421         Refs2.Add(ref2);
2422       }
2423     }
2424   }
2425   return S_OK;
2426 }
2427 
2428 
2429 HRESULT CDatabase::OpenVolume(const CObjectMap &omap, const oid_t fs_oid)
2430 {
2431   const size_t blockSize = sb.block_size;
2432   CByteBuffer buf;
2433   {
2434     const int index = omap.FindKey(fs_oid);
2435     if (index == -1)
2436       return S_FALSE;
2437     const omap_val &ov = omap.Vals[(unsigned)index];
2438     if (ov.size != blockSize) // change it : it must be multiple of
2439       return S_FALSE;
2440     buf.Alloc(blockSize);
2441     RINOK(SeekReadBlock_FALSE(ov.paddr, buf))
2442   }
2443 
2444   CVol &vol = Vols.AddNew();
2445   CApfs &apfs = vol.apfs;
2446 
2447   if (!apfs.Parse(buf, blockSize))
2448     return S_FALSE;
2449 
2450   if (apfs.fext_tree_oid != 0)
2451   {
2452     if ((apfs.incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) == 0)
2453       return S_FALSE;
2454     if ((apfs.fext_tree_type & OBJ_PHYSICAL) == 0)
2455       return S_FALSE;
2456     if ((apfs.fext_tree_type & OBJECT_TYPE_MASK) != OBJECT_TYPE_BTREE)
2457       return S_FALSE;
2458 
2459     CMap map2;
2460     map2.Subtype = OBJECT_TYPE_FEXT_TREE;
2461     map2.IsPhysical = true;
2462 
2463     RINOK(ReadMap(apfs.fext_tree_oid,
2464         false /* noHeader */,
2465         &vol,
2466         NULL, /* hash */
2467         map2, 0))
2468 
2469     UInt64 prevId = 1;
2470 
2471     FOR_VECTOR (i, map2.Pairs)
2472     {
2473       if (OpenCallback && (i & 0xffff) == 1)
2474       {
2475         const UInt64 numFiles = ProgressVal_NumFilesTotal +
2476             (vol.Items.Size() + vol.Nodes.Size()) / 2;
2477         RINOK(OpenCallback->SetCompleted(&numFiles, &ProgressVal_Cur))
2478       }
2479       // The key half of a record from a file extent tree.
2480       // struct fext_tree_key
2481       const CKeyValPair &pair = map2.Pairs[i];
2482       if (pair.Key.Size() != 16)
2483         return S_FALSE;
2484       const Byte *p = pair.Key;
2485       const UInt64 id = GetUi64(p);
2486       if (id < prevId)
2487         return S_FALSE;  // IDs must be sorted
2488       prevId = id;
2489 
2490       CExtent ext;
2491       ext.logical_offset = GetUi64(p + 8);
2492       {
2493         if (pair.Val.Size() != 16)
2494           return S_FALSE;
2495         const Byte *p2 = pair.Val;
2496         ext.len_and_flags = GetUi64(p2);
2497         ext.phys_block_num = GetUi64(p2 + 8);
2498       }
2499 
2500       PRF(printf("\n%6d: id=%6d logical_addr = %2d len_and_flags=%5x phys_block_num = %5d",
2501           i, (unsigned)id,
2502           (unsigned)ext.logical_offset,
2503           (unsigned)ext.len_and_flags,
2504           (unsigned)ext.phys_block_num));
2505 
2506       if (vol.FEXT_NodeIDs.IsEmpty() ||
2507           vol.FEXT_NodeIDs.Back() != id)
2508       {
2509         vol.FEXT_NodeIDs.Add(id);
2510         vol.FEXT_Nodes.AddNew();
2511       }
2512       CRecordVector<CExtent> &extents = vol.FEXT_Nodes.Back().Extents;
2513       if (!extents.IsEmpty())
2514         if (ext.logical_offset != extents.Back().GetEndOffset())
2515           return S_FALSE;
2516       extents.Add(ext);
2517       continue;
2518     }
2519 
2520     PRF(printf("\n\n"));
2521   }
2522 
2523   /* For each volume, read the root file system tree's virtual object
2524      identifier from the apfs_root_tree_oid field,
2525      and then look it up in the volume object map indicated
2526      by the omap_oid field. */
2527 
2528   CMap map;
2529   ReadObjectMap(apfs.omap_oid, &vol, map.Omap);
2530 
2531   const Byte *hash_for_root = NULL;
2532 
2533   if (apfs.integrity_meta_oid != 0)
2534   {
2535     if ((apfs.incompatible_features & APFS_INCOMPAT_SEALED_VOLUME) == 0)
2536       return S_FALSE;
2537     const int index = map.Omap.FindKey(apfs.integrity_meta_oid);
2538     if (index == -1)
2539       return S_FALSE;
2540     const omap_val &ov = map.Omap.Vals[(unsigned)index];
2541     if (ov.size != blockSize)
2542       return S_FALSE;
2543     RINOK(SeekReadBlock_FALSE(ov.paddr, buf))
2544     if (!vol.integrity.Parse(buf, blockSize, apfs.integrity_meta_oid))
2545       return S_FALSE;
2546     if (vol.integrity.im_hash_type != 0)
2547       hash_for_root = vol.integrity.Hash;
2548   }
2549 
2550   {
2551     const int index = map.Omap.FindKey(apfs.root_tree_oid);
2552     if (index == -1)
2553       return S_FALSE;
2554     const omap_val &ov = map.Omap.Vals[(unsigned)index];
2555     if (ov.size != blockSize) // change it : it must be multiple of
2556       return S_FALSE;
2557     map.Subtype = OBJECT_TYPE_FSTREE;
2558     map.IsPhysical = false;
2559     RINOK(ReadMap(ov.paddr, ov.IsFlag_NoHeader(),
2560         &vol, hash_for_root,
2561         map, 0))
2562   }
2563 
2564   bool needParseAttr = false;
2565 
2566   {
2567     const bool isHashed = apfs.IsHashedName();
2568     UInt64 prevId = 1;
2569 
2570     {
2571       const UInt64 numApfsItems = vol.apfs.GetTotalItems()
2572           + 2;   // we will have 2 additional hidden directories: root and private-dir
2573       const UInt64 numApfsItems_Reserve = numApfsItems
2574           + 16;  // we reserve 16 for some possible unexpected items
2575       if (numApfsItems_Reserve < map.Pairs.Size())
2576       {
2577         vol.Items.ClearAndReserve((unsigned)numApfsItems_Reserve);
2578         vol.Nodes.ClearAndReserve((unsigned)numApfsItems_Reserve);
2579         vol.NodeIDs.ClearAndReserve((unsigned)numApfsItems_Reserve);
2580       }
2581       if (OpenCallback)
2582       {
2583         const UInt64 numFiles = ProgressVal_NumFilesTotal + numApfsItems;
2584         RINOK(OpenCallback->SetTotal(&numFiles, NULL))
2585       }
2586     }
2587 
2588     CAttr attr;
2589     CItem item;
2590 
2591     FOR_VECTOR (i, map.Pairs)
2592     {
2593       if (OpenCallback && (i & 0xffff) == 1)
2594       {
2595         const UInt64 numFiles = ProgressVal_NumFilesTotal +
2596             (vol.Items.Size() + vol.Nodes.Size()) / 2;
2597         RINOK(OpenCallback->SetCompleted(&numFiles, &ProgressVal_Cur))
2598       }
2599 
2600       const CKeyValPair &pair = map.Pairs[i];
2601       j_key_t jkey;
2602       if (pair.Key.Size() < 8)
2603         return S_FALSE;
2604       const Byte *p = pair.Key;
2605       jkey.Parse(p);
2606       const unsigned type = jkey.GetType();
2607       const UInt64 id = jkey.GetID();
2608       if (id < prevId)
2609         return S_FALSE;  // IDs must be sorted
2610       prevId = id;
2611 
2612       PRF(printf("\n%6d: id=%6d type = %2d ", i, (unsigned)id, type));
2613 
2614       if (type == APFS_TYPE_INODE)
2615       {
2616         PRF(printf("INODE"));
2617         if (pair.Key.Size() != 8 ||
2618             pair.Val.Size() < k_SizeOf_j_inode_val)
2619           return S_FALSE;
2620 
2621         CNode inode;
2622         inode.Parse(pair.Val);
2623 
2624         if (inode.private_id != id)
2625         {
2626           /* private_id : The unique identifier used by this file's data stream.
2627              This identifier appears in the owning_obj_id field of j_phys_ext_val_t
2628              records that describe the extents where the data is stored.
2629              For an inode that doesn't have data, the value of this
2630              field is the file-system object's identifier.
2631           */
2632           // APFS_TYPE_EXTENT allow to link physical address extents.
2633           // we don't support case (private_id != id)
2634           UnsupportedFeature = true;
2635           // return S_FALSE;
2636         }
2637         const UInt32 extraSize = (UInt32)pair.Val.Size() - k_SizeOf_j_inode_val;
2638         if (extraSize != 0)
2639         {
2640           if (extraSize < 4)
2641             return S_FALSE;
2642           /*
2643           struct xf_blob
2644           {
2645             uint16_t xf_num_exts;
2646             uint16_t xf_used_data;
2647             uint8_t xf_data[];
2648           };
2649           */
2650           const Byte *p2 = pair.Val + k_SizeOf_j_inode_val;
2651           const UInt32 xf_num_exts = Get16(p2);
2652           const UInt32 xf_used_data = Get16(p2 + 2);
2653           UInt32 offset = 4 + (UInt32)xf_num_exts * 4;
2654           if (offset + xf_used_data != extraSize)
2655             return S_FALSE;
2656 
2657           PRF(printf(" parent_id = %d", (unsigned)inode.parent_id));
2658 
2659           for (unsigned k = 0; k < xf_num_exts; k++)
2660           {
2661             // struct x_field
2662             const Byte *p3 = p2 + 4 + k * 4;
2663             const Byte x_type = p3[0];
2664             // const Byte x_flags = p3[1];
2665             const UInt32 x_size = Get16(p3 + 2);
2666             const UInt32 x_size_ceil = (x_size + 7) & ~(UInt32)7;
2667             if (offset + x_size_ceil > extraSize)
2668               return S_FALSE;
2669             const Byte *p4 = p2 + offset;
2670             if (x_type == INO_EXT_TYPE_NAME)
2671             {
2672               if (x_size < 2)
2673                 return S_FALSE;
2674               inode.PrimaryName.SetFrom_CalcLen((const char *)p4, x_size);
2675               PRF(printf(" PrimaryName = %s", inode.PrimaryName.Ptr()));
2676               if (inode.PrimaryName.Len() != x_size - 1)
2677                 HeadersError = true;
2678               // return S_FALSE;
2679             }
2680             else if (x_type == INO_EXT_TYPE_DSTREAM)
2681             {
2682               if (x_size != k_SizeOf_j_dstream)
2683                 return S_FALSE;
2684               if (inode.dstream_defined)
2685                 return S_FALSE;
2686               inode.dstream.Parse(p4);
2687               inode.dstream_defined = true;
2688             }
2689             else
2690             {
2691               // UnsupportedFeature = true;
2692               // return S_FALSE;
2693             }
2694             offset += x_size_ceil;
2695           }
2696           if (offset != extraSize)
2697             return S_FALSE;
2698         }
2699 
2700         if (!vol.NodeIDs.IsEmpty())
2701           if (id <= vol.NodeIDs.Back())
2702             return S_FALSE;
2703         vol.Nodes.Add(inode);
2704         vol.NodeIDs.Add(id);
2705         continue;
2706       }
2707 
2708       if (type == APFS_TYPE_XATTR)
2709       {
2710         PRF(printf("  XATTR"));
2711 
2712         /*
2713         struct j_xattr_key
2714         {
2715           j_key_t hdr;
2716           uint16_t name_len;
2717           uint8_t name[0];
2718         }
2719         */
2720 
2721         UInt32 len;
2722         unsigned nameOffset;
2723         {
2724           nameOffset = 8 + 2;
2725           if (pair.Key.Size() < nameOffset + 1)
2726             return S_FALSE;
2727           len = Get16(p + 8);
2728         }
2729         if (nameOffset + len != pair.Key.Size())
2730           return S_FALSE;
2731 
2732         attr.Clear();
2733         attr.Name.SetFrom_CalcLen((const char *)p + nameOffset, len);
2734         if (attr.Name.Len() != len - 1)
2735           return S_FALSE;
2736 
2737         PRF(printf("  name=%s", attr.Name.Ptr()));
2738 
2739         const unsigned k_SizeOf_j_xattr_val = 4;
2740         if (pair.Val.Size() < k_SizeOf_j_xattr_val)
2741           return S_FALSE;
2742         /*
2743         struct j_xattr_val
2744         {
2745           uint16_t flags;
2746           uint16_t xdata_len;
2747           uint8_t xdata[0];
2748         }
2749         */
2750         attr.flags = Get16(pair.Val);
2751         const UInt32 xdata_len = Get16(pair.Val + 2);
2752 
2753         PRF(printf("  flags=%x xdata_len = %d",
2754             (unsigned)attr.flags,
2755             (unsigned)xdata_len));
2756 
2757         const Byte *p4 = pair.Val + 4;
2758 
2759         if (k_SizeOf_j_xattr_val + xdata_len != pair.Val.Size())
2760           return S_FALSE;
2761         if (attr.Is_EMBEDDED())
2762           attr.Data.CopyFrom(p4, xdata_len);
2763         else if (attr.Is_STREAM())
2764         {
2765           // why (attr.flags == 0x11) here? (0x11 is undocummented flag)
2766           if (k_SizeOf_j_xattr_val + 8 + k_SizeOf_j_dstream != pair.Val.Size())
2767             return S_FALSE;
2768           attr.Id = Get64(p4);
2769           attr.dstream.Parse(p4 + 8);
2770           attr.dstream_defined = true;
2771           PRF(printf(" streamID=%d streamSize=%d", (unsigned)attr.Id, (unsigned)attr.dstream.size));
2772         }
2773         else
2774         {
2775           // unknown attribute
2776           // UnsupportedFeature = true;
2777           // return S_FALSE;
2778         }
2779 
2780         if (vol.NodeIDs.IsEmpty() ||
2781             vol.NodeIDs.Back() != id)
2782         {
2783           return S_FALSE;
2784           // UnsupportedFeature = true;
2785           // continue;
2786         }
2787 
2788         CNode &inode = vol.Nodes.Back();
2789 
2790         if (attr.Name.IsEqualTo("com.apple.fs.symlink"))
2791         {
2792           inode.SymLinkIndex = inode.Attrs.Size();
2793           if (attr.Is_dstream_OK_for_SymLink())
2794             needParseAttr = true;
2795         }
2796         else if (attr.Name.IsEqualTo("com.apple.decmpfs"))
2797         {
2798           inode.DecmpfsIndex = inode.Attrs.Size();
2799           // if (attr.dstream_defined)
2800           needParseAttr = true;
2801         }
2802         else if (attr.Name.IsEqualTo("com.apple.ResourceFork"))
2803         {
2804           inode.ResourceIndex = inode.Attrs.Size();
2805         }
2806         inode.Attrs.Add(attr);
2807         continue;
2808       }
2809 
2810       if (type == APFS_TYPE_DSTREAM_ID)
2811       {
2812         PRF(printf("  DSTREAM_ID"));
2813         if (pair.Key.Size() != 8)
2814           return S_FALSE;
2815         // j_dstream_id_val_t
2816         if (pair.Val.Size() != 4)
2817           return S_FALSE;
2818         const UInt32 refcnt = Get32(pair.Val);
2819 
2820         // The data stream record can be deleted when its reference count reaches zero.
2821         PRF(printf("  refcnt = %d", (unsigned)refcnt));
2822 
2823         if (vol.NodeIDs.IsEmpty())
2824           return S_FALSE;
2825 
2826         if (vol.NodeIDs.Back() != id)
2827         {
2828           // is it possible ?
2829           // continue;
2830           return S_FALSE;
2831         }
2832 
2833         CNode &inode = vol.Nodes.Back();
2834 
2835         if (inode.refcnt_defined)
2836           return S_FALSE;
2837 
2838         inode.refcnt = refcnt;
2839         inode.refcnt_defined = true;
2840         if (inode.refcnt != (UInt32)inode.nlink)
2841         {
2842           PRF(printf(" refcnt != nlink"));
2843           // is it possible ?
2844           // return S_FALSE;
2845         }
2846         continue;
2847       }
2848 
2849       if (type == APFS_TYPE_FILE_EXTENT)
2850       {
2851         PRF(printf("  FILE_EXTENT"));
2852         /*
2853         struct j_file_extent_key
2854         {
2855           j_key_t hdr;
2856           uint64_t logical_addr;
2857         }
2858         */
2859         if (pair.Key.Size() != 16)
2860           return S_FALSE;
2861         // The offset within the file's data, in bytes, for the data stored in this extent
2862         const UInt64 logical_addr = Get64(p + 8);
2863 
2864         j_file_extent_val eval;
2865         if (pair.Val.Size() != k_SizeOf_j_file_extent_val)
2866           return S_FALSE;
2867         eval.Parse(pair.Val);
2868 
2869         if (logical_addr != 0)
2870         {
2871           PRF(printf("  logical_addr = %d", (unsigned)logical_addr));
2872         }
2873         PRF(printf("  len = %8d  pos = %8d",
2874             (unsigned)eval.len_and_flags,
2875             (unsigned)eval.phys_block_num
2876             ));
2877 
2878         CExtent ext;
2879         ext.logical_offset = logical_addr;
2880         ext.len_and_flags = eval.len_and_flags;
2881         ext.phys_block_num = eval.phys_block_num;
2882 
2883         if (vol.NodeIDs.IsEmpty())
2884           return S_FALSE;
2885         if (vol.NodeIDs.Back() != id)
2886         {
2887           // extents for Attributs;
2888           if (vol.SmallNodeIDs.IsEmpty() ||
2889               vol.SmallNodeIDs.Back() != id)
2890           {
2891             vol.SmallNodeIDs.Add(id);
2892             vol.SmallNodes.AddNew();
2893           }
2894           vol.SmallNodes.Back().Extents.Add(ext);
2895           continue;
2896           // return S_FALSE;
2897         }
2898 
2899         CNode &inode = vol.Nodes.Back();
2900         inode.Extents.Add(ext);
2901         continue;
2902       }
2903 
2904       if (type == APFS_TYPE_DIR_REC)
2905       {
2906         UInt32 len;
2907         unsigned nameOffset;
2908 
2909         if (isHashed)
2910         {
2911           /*
2912           struct j_drec_hashed_key
2913           {
2914             j_key_t hdr;
2915             UInt32 name_len_and_hash;
2916             uint8_t name[0];
2917           }
2918           */
2919           nameOffset = 8 + 4;
2920           if (pair.Key.Size() < nameOffset + 1)
2921             return S_FALSE;
2922           const UInt32 name_len_and_hash = Get32(p + 8);
2923           len = name_len_and_hash & J_DREC_LEN_MASK;
2924         }
2925         else
2926         {
2927           /*
2928           struct j_drec_key
2929           {
2930             j_key_t hdr;
2931             UInt16 name_len; // The length of the name, including the final null character
2932             uint8_t name[0]; // The name, represented as a null-terminated UTF-8 string
2933           }
2934           */
2935           nameOffset = 8 + 2;
2936           if (pair.Key.Size() < nameOffset + 1)
2937             return S_FALSE;
2938           len = Get16(p + 8);
2939         }
2940         if (nameOffset + len != pair.Key.Size())
2941           return S_FALSE;
2942 
2943         // CItem item;
2944         item.Clear();
2945 
2946         item.ParentId = id;
2947         item.Name.SetFrom_CalcLen((const char *)p + nameOffset, len);
2948         if (item.Name.Len() != len - 1)
2949           return S_FALSE;
2950 
2951         if (pair.Val.Size() < k_SizeOf_j_drec_val)
2952           return S_FALSE;
2953 
2954         item.Val.Parse(pair.Val);
2955 
2956         if (pair.Val.Size() > k_SizeOf_j_drec_val)
2957         {
2958           // fixme: parse extra fields;
2959           // UnsupportedFeature = true;
2960           // return S_FALSE;
2961         }
2962 
2963         vol.Items.Add(item);
2964 
2965         /*
2966         if (!vol.NodeIDs.IsEmpty() && vol.NodeIDs.Back() == id)
2967           vol.Nodes.Back().NumItems++;
2968         */
2969         if (id == PRIV_DIR_INO_NUM)
2970           vol.NumItems_In_PrivateDir++;
2971 
2972         PRF(printf(" DIR_REC next=%6d flags=%2x %s",
2973             (unsigned)item.Val.file_id,
2974             (unsigned)item.Val.flags,
2975             item.Name.Ptr()));
2976         continue;
2977       }
2978 
2979       if (type == APFS_TYPE_FILE_INFO)
2980       {
2981         if (pair.Key.Size() != 16)
2982           return S_FALSE;
2983         // j_file_info_key
2984         const UInt64 info_and_lba = Get64(p + 8);
2985 
2986       #define J_FILE_INFO_LBA_MASK    0x00ffffffffffffffUL
2987       // #define J_FILE_INFO_TYPE_MASK   0xff00000000000000UL
2988       #define J_FILE_INFO_TYPE_SHIFT  56
2989       #define APFS_FILE_INFO_DATA_HASH 1
2990 
2991         const unsigned infoType = (unsigned)(info_and_lba >> J_FILE_INFO_TYPE_SHIFT);
2992         // address is a paddr_t
2993         const UInt64 lba = info_and_lba & J_FILE_INFO_LBA_MASK;
2994         // j_file_data_hash_val_t
2995         /* Use this field of the union if the type stored in the info_and_lba field of j_file_info_val_t is
2996            APFS_FILE_INFO_DATA_HASH */
2997         if (infoType != APFS_FILE_INFO_DATA_HASH)
2998           return S_FALSE;
2999         if (pair.Val.Size() != 3 + vol.integrity.HashSize)
3000           return S_FALSE;
3001         /*
3002         struct j_file_data_hash_val
3003         {
3004           UInt16 hashed_len; // The length, in blocks, of the data segment that was hashed.
3005           UInt8 hash_size; // must be consistent with integrity_meta_phys_t::im_hash_type
3006           UInt8 hash[0];
3007         }
3008         */
3009 
3010         const unsigned hash_size = pair.Val[2];
3011         if (hash_size != vol.integrity.HashSize)
3012           return S_FALSE;
3013 
3014         CHashChunk hr;
3015         hr.hashed_len = GetUi16(pair.Val);
3016         if (hr.hashed_len == 0)
3017           return S_FALSE;
3018         memcpy(hr.hash, (const Byte *)pair.Val + 3, vol.integrity.HashSize);
3019         // (hashed_len <= 4) : we have seen
3020         hr.lba = lba;
3021 
3022         PRF(printf("   FILE_INFO lba = %6x, hashed_len=%6d",
3023             (unsigned)lba,
3024             (unsigned)hr.hashed_len));
3025 
3026         if (vol.Hash_IDs.IsEmpty() || vol.Hash_IDs.Back() != id)
3027         {
3028           vol.Hash_Vectors.AddNew();
3029           vol.Hash_IDs.Add(id);
3030         }
3031         CStreamHashes &hashes = vol.Hash_Vectors.Back();
3032         if (hashes.Size() != 0)
3033         {
3034           const CHashChunk &hr_Back = hashes.Back();
3035           if (lba != hr_Back.lba + ((UInt64)hr_Back.hashed_len << sb.block_size_Log))
3036             return S_FALSE;
3037         }
3038         hashes.Add(hr);
3039         continue;
3040       }
3041 
3042       if (type == APFS_TYPE_SNAP_METADATA)
3043       {
3044         if (pair.Key.Size() != 8)
3045           return S_FALSE;
3046         PRF(printf(" SNAP_METADATA"));
3047         // continue;
3048       }
3049 
3050       /* SIBLING items are used, if there are more than one hard link to some inode
3051          key                                     : value
3052          parent_id_1  DIR_REC                    : inode_id, name_1
3053          parent_id_2  DIR_REC                    : inode_id, name_2
3054          inode_id     INODE                      : parent_id_1, name_1
3055          inode_id     SIBLING_LINK  sibling_id_1 : parent_id_1, name_1
3056          inode_id     SIBLING_LINK  sibling_id_2 : parent_id_2, name_2
3057          sibling_id_1 SIBLING_MAP                : inode_id
3058          sibling_id_2 SIBLING_MAP                : inode_id
3059       */
3060 
3061       if (type == APFS_TYPE_SIBLING_LINK)
3062       {
3063         if (pair.Key.Size() != 16)
3064           return S_FALSE;
3065         if (pair.Val.Size() < 10 + 1)
3066           return S_FALSE;
3067         /*
3068         // struct j_sibling_key
3069         // The sibling's unique identifier.
3070         // This value matches the object identifier for the sibling map record
3071         const UInt64 sibling_id = Get64(p + 8);
3072         // struct j_sibling_val
3073         const Byte *v = pair.Val;
3074         const UInt64 parent_id = Get64(v); // The object identifier for the inode that's the parent directory.
3075         const unsigned name_len = Get16(v + 8); // len including the final null character
3076         if (10 + name_len != pair.Val.Size())
3077           return S_FALSE;
3078         if (v[10 + name_len - 1] != 0)
3079           return S_FALSE;
3080         AString name ((const char *)(v + 10));
3081         if (name.Len() != name_len - 1)
3082           return S_FALSE;
3083         PRF(printf(" SIBLING_LINK sibling_id = %6d : parent_id=%6d %s",
3084             (unsigned)sibling_id, (unsigned)parent_id, name.Ptr()));
3085         */
3086         continue;
3087       }
3088 
3089       if (type == APFS_TYPE_SIBLING_MAP)
3090       {
3091         // struct j_sibling_map_key
3092         // The object identifier in the header is the sibling's unique identifier
3093         if (pair.Key.Size() != 8 || pair.Val.Size() != 8)
3094           return S_FALSE;
3095         /*
3096         // j_sibling_map_val
3097         // The inode number of the underlying file
3098         const UInt64 file_id = Get64(pair.Val);
3099         PRF(printf(" SIBLING_MAP : file_id = %d", (unsigned)file_id));
3100         */
3101         continue;
3102       }
3103 
3104       UnsupportedFeature = true;
3105       // return S_FALSE;
3106     }
3107     ProgressVal_NumFilesTotal += vol.Items.Size();
3108   }
3109 
3110 
3111   if (needParseAttr)
3112   {
3113     /* we read external streams for attributes
3114        So we can get SymLink for GetProperty(kpidSymLink) later */
3115     FOR_VECTOR (i, vol.Nodes)
3116     {
3117       CNode &node = vol.Nodes[i];
3118 
3119       FOR_VECTOR (a, node.Attrs)
3120       {
3121         CAttr &attr = node.Attrs[a];
3122         if (attr.Data.Size() != 0 || !attr.dstream_defined)
3123           continue;
3124         if (a == node.SymLinkIndex)
3125         {
3126           if (!attr.Is_dstream_OK_for_SymLink())
3127             continue;
3128         }
3129         else
3130         {
3131           if (a != node.DecmpfsIndex
3132               // && a != node.ResourceIndex
3133               )
3134           continue;
3135         }
3136         // we don't expect big streams here
3137         // largest dstream for Decmpfs attribute is (2Kib+17)
3138         if (attr.dstream.size > ((UInt32)1 << 16))
3139           continue;
3140         CMyComPtr<ISequentialInStream> inStream;
3141         const HRESULT res = GetAttrStream_dstream(OpenInStream, vol, attr, &inStream);
3142         if (res == S_OK && inStream)
3143         {
3144           CByteBuffer buf2;
3145           const size_t size = (size_t)attr.dstream.size;
3146           buf2.Alloc(size);
3147           if (ReadStream_FAIL(inStream, buf2, size) == S_OK)
3148             attr.Data = buf2;
3149 
3150           ProgressVal_Cur += size;
3151           if (OpenCallback)
3152           if (ProgressVal_Cur - ProgressVal_Prev >= (1 << 22))
3153           {
3154 
3155             RINOK(OpenCallback->SetCompleted(
3156                 &ProgressVal_NumFilesTotal,
3157                 &ProgressVal_Cur))
3158             ProgressVal_Prev = ProgressVal_Cur;
3159           }
3160         }
3161       }
3162 
3163       if (node.Has_UNCOMPRESSED_SIZE())
3164       if (IsViDef(node.DecmpfsIndex))
3165       {
3166         CAttr &attr = node.Attrs[node.DecmpfsIndex];
3167         node.CompressHeader.Parse(attr.Data, attr.Data.Size());
3168 
3169         if (node.CompressHeader.IsCorrect)
3170           if (node.CompressHeader.Method < sizeof(MethodsMask) * 8)
3171             MethodsMask |= ((UInt32)1 << node.CompressHeader.Method);
3172 
3173         if (node.CompressHeader.IsCorrect
3174             && node.CompressHeader.IsSupported
3175             && node.CompressHeader.UnpackSize == node.uncompressed_size)
3176         {
3177           attr.NeedShow = false;
3178           if (node.CompressHeader.IsMethod_Resource()
3179               && IsViDef(node.ResourceIndex))
3180             node.Attrs[node.ResourceIndex].NeedShow = false;
3181         }
3182         else
3183         {
3184           vol.UnsupportedMethod = true;
3185         }
3186       }
3187     }
3188   }
3189 
3190   const HRESULT res = vol.FillRefs();
3191 
3192   if (vol.ThereAreErrors())
3193     HeadersError = true;
3194   if (vol.UnsupportedFeature)
3195     UnsupportedFeature = true;
3196   if (vol.UnsupportedMethod)
3197     UnsupportedMethod = true;
3198   if (vol.NumAltStreams != 0)
3199     ThereAreAltStreams = true;
3200 
3201   return res;
3202 }
3203 
3204 
3205 
3206 HRESULT CVol::FillRefs()
3207 {
3208   {
3209     Refs.Reserve(Items.Size());
3210     // we fill Refs[*]
3211     // we
3212     // and set Nodes[*].ItemIndex for Nodes that are directories;
3213     FOR_VECTOR (i, Items)
3214     {
3215       CItem &item = Items[i];
3216       const UInt64 id = item.Val.file_id;
3217       // if (item.Id == ROOT_DIR_PARENT) continue;
3218       /* for two root folders items
3219          we don't set Node.ItemIndex; */
3220       // so nodes
3221       if (id == ROOT_DIR_INO_NUM)
3222         continue;
3223       if (id == PRIV_DIR_INO_NUM)
3224         if (NumItems_In_PrivateDir == 0) // if (inode.NumItems == 0)
3225           continue;
3226 
3227       CRef ref;
3228       ref.ItemIndex = i;
3229       // ref.NodeIndex = VI_MINUS1;
3230       ref.ParentRefIndex = VI_MINUS1;
3231      #ifdef APFS_SHOW_ALT_STREAMS
3232       ref.AttrIndex = VI_MINUS1;
3233      #endif
3234       const int index = NodeIDs.FindInSorted(id);
3235       // const int index = -1; // for debug
3236       ref.NodeIndex = (unsigned)index;
3237       item.RefIndex = Refs.Size();
3238       Refs.Add(ref);
3239 
3240       if (index == -1)
3241       {
3242         NodeNotFound = true;
3243         continue;
3244         // return S_FALSE;
3245       }
3246 
3247       // item.iNode_Index = index;
3248       CNode &inode = Nodes[(unsigned)index];
3249       if (!item.Val.IsFlags_Unknown()
3250           && inode.Get_Type_From_mode() != item.Val.flags)
3251       {
3252         Refs.Back().NodeIndex = VI_MINUS1;
3253         WrongInodeLink = true;
3254         continue;
3255         // return S_FALSE;
3256       }
3257 
3258       const bool isDir = inode.IsDir();
3259       if (isDir)
3260       {
3261         if (IsViDef(inode.ItemIndex))
3262         {
3263           // hard links to dirs are not supported
3264           Refs.Back().NodeIndex = VI_MINUS1;
3265           WrongInodeLink = true;
3266           continue;
3267         }
3268         inode.ItemIndex = i;
3269       }
3270       inode.NumCalcedLinks++;
3271 
3272      #ifdef APFS_SHOW_ALT_STREAMS
3273       if (!isDir)
3274       {
3275         // we use alt streams only for files
3276         const unsigned numAttrs = inode.Attrs.Size();
3277         if (numAttrs != 0)
3278         {
3279           ref.ParentRefIndex = item.RefIndex;
3280           for (unsigned k = 0; k < numAttrs; k++)
3281           {
3282             // comment it for debug
3283             const CAttr &attr = inode.Attrs[k];
3284             if (!attr.NeedShow)
3285               continue;
3286 
3287             if (k == inode.SymLinkIndex)
3288               continue;
3289             ref.AttrIndex = k;
3290             NumAltStreams++;
3291             Refs.Add(ref);
3292             /*
3293             if (attr.dstream_defined)
3294             {
3295               const int idIndex = SmallNodeIDs.FindInSorted(attr.Id);
3296               if (idIndex != -1)
3297                 SmallNodes[(unsigned)idIndex].NumLinks++; // for debug
3298             }
3299             */
3300           }
3301         }
3302       }
3303      #endif
3304     }
3305   }
3306 
3307 
3308   {
3309     // fill ghost nodes
3310     CRef ref;
3311     ref.ItemIndex = VI_MINUS1;
3312     ref.ParentRefIndex = VI_MINUS1;
3313    #ifdef APFS_SHOW_ALT_STREAMS
3314     ref.AttrIndex = VI_MINUS1;
3315    #endif
3316     FOR_VECTOR (i, Nodes)
3317     {
3318       if (Nodes[i].NumCalcedLinks != 0)
3319         continue;
3320       const UInt64 id = NodeIDs[i];
3321       if (id == ROOT_DIR_INO_NUM ||
3322           id == PRIV_DIR_INO_NUM)
3323         continue;
3324       ThereAreUnlinkedNodes = true;
3325       ref.NodeIndex = i;
3326       Refs.Add(ref);
3327     }
3328   }
3329 
3330   /* if want to create Refs for ghost data streams,
3331      we need additional CRef::SmallNodeIndex field */
3332 
3333   {
3334     /* all Nodes[*].ItemIndex were already filled for directory Nodes,
3335        except of "root" and "private-dir" Nodes. */
3336 
3337     // now we fill Items[*].ParentItemIndex and Refs[*].ParentRefIndex
3338 
3339     UInt64 prev_ID = (UInt64)(Int64)-1;
3340     unsigned prev_ParentItemIndex = VI_MINUS1;
3341 
3342     FOR_VECTOR (i, Items)
3343     {
3344       CItem &item = Items[i];
3345       const UInt64 id = item.ParentId; // it's id of parent NODE
3346       if (id != prev_ID)
3347       {
3348         prev_ID = id;
3349         prev_ParentItemIndex = VI_MINUS1;
3350         const int index = NodeIDs.FindInSorted(id);
3351         if (index == -1)
3352           continue;
3353         prev_ParentItemIndex = Nodes[(unsigned)index].ItemIndex;
3354       }
3355 
3356       if (IsViNotDef(prev_ParentItemIndex))
3357         continue;
3358       item.ParentItemIndex = prev_ParentItemIndex;
3359       if (IsViNotDef(item.RefIndex))
3360       {
3361         // RefIndex is not set for 2 Items (root folders)
3362         // but there is no node for them usually
3363         continue;
3364       }
3365       CRef &ref = Refs[item.RefIndex];
3366 
3367       /*
3368       // it's optional check that parent_id is set correclty
3369       if (IsViDef(ref.NodeIndex))
3370       {
3371         const CNode &node = Nodes[ref.NodeIndex];
3372         if (node.IsDir() && node.parent_id != id)
3373           return S_FALSE;
3374       }
3375       */
3376 
3377       /*
3378       if (id == ROOT_DIR_INO_NUM)
3379       {
3380         // ItemIndex in Node for ROOT_DIR_INO_NUM was not set bofere
3381         // probably unused now.
3382         ref.ParentRefIndex = VI_MINUS1;
3383       }
3384       else
3385       */
3386         ref.ParentRefIndex = Items[prev_ParentItemIndex].RefIndex;
3387     }
3388   }
3389 
3390   {
3391     // check for loops
3392     const unsigned numItems = Items.Size();
3393     if (numItems + 1 == 0)
3394       return S_FALSE;
3395     CUIntArr arr;
3396     arr.Alloc(numItems);
3397     {
3398       for (unsigned i = 0; i < numItems; i++)
3399         arr[i] = 0;
3400     }
3401     for (unsigned i = 0; i < numItems;)
3402     {
3403       unsigned k = i++;
3404       for (;;)
3405       {
3406         const unsigned a = arr[k];
3407         if (a != 0)
3408         {
3409           if (a == i)
3410             return S_FALSE;
3411           break;
3412         }
3413         arr[k] = i;
3414         k = Items[k].ParentItemIndex;
3415         if (IsViNotDef(k))
3416           break;
3417       }
3418     }
3419   }
3420 
3421   return S_OK;
3422 }
3423 
3424 
3425 
3426 Z7_class_CHandler_final:
3427   public IInArchive,
3428   public IArchiveGetRawProps,
3429   public IInArchiveGetStream,
3430   public CMyUnknownImp,
3431   public CDatabase
3432 {
3433   Z7_IFACES_IMP_UNK_3(
3434       IInArchive,
3435       IArchiveGetRawProps,
3436       IInArchiveGetStream)
3437 
3438   CMyComPtr<IInStream> _stream;
3439   int FindHashIndex_for_Item(UInt32 index);
3440 };
3441 
3442 
3443 Z7_COM7F_IMF(CHandler::Open(IInStream *inStream,
3444     const UInt64 * /* maxCheckStartPosition */,
3445     IArchiveOpenCallback *callback))
3446 {
3447   COM_TRY_BEGIN
3448   Close();
3449   OpenInStream = inStream;
3450   OpenCallback = callback;
3451   RINOK(Open2())
3452   _stream = inStream;
3453   return S_OK;
3454   COM_TRY_END
3455 }
3456 
3457 
3458 Z7_COM7F_IMF(CHandler::Close())
3459 {
3460   _stream.Release();
3461   Clear();
3462   return S_OK;
3463 }
3464 
3465 
3466 enum
3467 {
3468   kpidBytesWritten = kpidUserDefined,
3469   kpidBytesRead,
3470   kpidPrimeName,
3471   kpidParentINode,
3472   kpidAddTime,
3473   kpidGeneration,
3474   kpidBsdFlags
3475   // kpidUncompressedSize
3476 };
3477 
3478 static const CStatProp kProps[] =
3479 {
3480   { NULL, kpidPath, VT_BSTR },
3481   { NULL, kpidSize, VT_UI8 },
3482   { NULL, kpidPackSize, VT_UI8 },
3483   { NULL, kpidPosixAttrib, VT_UI4 },
3484   { NULL, kpidMTime, VT_FILETIME },
3485   { NULL, kpidCTime, VT_FILETIME },
3486   { NULL, kpidATime, VT_FILETIME },
3487   { NULL, kpidChangeTime, VT_FILETIME },
3488   { "Added Time", kpidAddTime, VT_FILETIME },
3489   { NULL, kpidMethod, VT_BSTR },
3490   { NULL, kpidINode, VT_UI8 },
3491   { NULL, kpidLinks, VT_UI4 },
3492   { NULL, kpidSymLink, VT_BSTR },
3493   { NULL, kpidUserId, VT_UI4 },
3494   { NULL, kpidGroupId, VT_UI4 },
3495   { NULL, kpidCharacts, VT_BSTR },
3496  #ifdef APFS_SHOW_ALT_STREAMS
3497   { NULL, kpidIsAltStream, VT_BOOL },
3498  #endif
3499   { "Parent iNode", kpidParentINode, VT_UI8 },
3500   { "Primary Name", kpidPrimeName, VT_BSTR },
3501   { "Generation", kpidGeneration, VT_UI4 },
3502   { "Written Size", kpidBytesWritten, VT_UI8 },
3503   { "Read Size", kpidBytesRead, VT_UI8 },
3504   { "BSD Flags", kpidBsdFlags, VT_UI4 }
3505   // , { "Uncompressed Size", kpidUncompressedSize, VT_UI8 }
3506 };
3507 
3508 
3509 static const Byte kArcProps[] =
3510 {
3511   kpidName,
3512   kpidCharacts,
3513   kpidId,
3514   kpidClusterSize,
3515   kpidCTime,
3516   kpidMTime,
3517   kpidComment
3518 };
3519 
3520 IMP_IInArchive_Props_WITH_NAME
3521 IMP_IInArchive_ArcProps
3522 
3523 
3524 static void ApfsTimeToProp(UInt64 hfsTime, NWindows::NCOM::CPropVariant &prop)
3525 {
3526   if (hfsTime == 0)
3527     return;
3528   FILETIME ft;
3529   UInt32 ns100;
3530   ApfsTimeToFileTime(hfsTime, ft, ns100);
3531   prop.SetAsTimeFrom_FT_Prec_Ns100(ft, k_PropVar_TimePrec_1ns, ns100);
3532 }
3533 
3534 
3535 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
3536 {
3537   COM_TRY_BEGIN
3538   NWindows::NCOM::CPropVariant prop;
3539   const CApfs *apfs = NULL;
3540   if (Vols.Size() == 1)
3541     apfs = &Vols[0].apfs;
3542   switch (propID)
3543   {
3544     case kpidPhySize:
3545       prop = (UInt64)sb.block_count << sb.block_size_Log;
3546       break;
3547     case kpidClusterSize: prop = (UInt32)(sb.block_size); break;
3548     case kpidCharacts: NHfs::MethodsMaskToProp(MethodsMask, prop); break;
3549     case kpidMTime:
3550       if (apfs)
3551         ApfsTimeToProp(apfs->modified_by[0].timestamp, prop);
3552       break;
3553     case kpidCTime:
3554       if (apfs)
3555         ApfsTimeToProp(apfs->formatted_by.timestamp, prop);
3556       break;
3557     case kpidIsTree: prop = true; break;
3558     case kpidErrorFlags:
3559     {
3560       UInt32 flags = 0;
3561       if (HeadersError) flags |= kpv_ErrorFlags_HeadersError;
3562       if (flags != 0)
3563         prop = flags;
3564       break;
3565     }
3566     case kpidWarningFlags:
3567     {
3568       UInt32 flags = 0;
3569       if (UnsupportedFeature) flags |= kpv_ErrorFlags_UnsupportedFeature;
3570       if (UnsupportedMethod) flags |= kpv_ErrorFlags_UnsupportedMethod;
3571       if (flags != 0)
3572         prop = flags;
3573       break;
3574     }
3575 
3576     case kpidName:
3577     {
3578       if (apfs)
3579       {
3580         UString s;
3581         AddVolInternalName_toString(s, *apfs);
3582         s += ".apfs";
3583         prop = s;
3584       }
3585       break;
3586     }
3587 
3588     case kpidId:
3589     {
3590       char s[32 + 4];
3591       sb.uuid.SetHex_To_str(s);
3592       prop = s;
3593       break;
3594     }
3595 
3596     case kpidComment:
3597     {
3598       UString s;
3599       {
3600         AddComment_UInt64(s, "block_size", sb.block_size);
3601 
3602         FOR_VECTOR (i, Vols)
3603         {
3604           if (Vols.Size() > 1)
3605           {
3606             if (i != 0)
3607             {
3608               s += "----";
3609               s.Add_LF();
3610             }
3611             AddComment_UInt64(s, "Volume", i + 1);
3612           }
3613           Vols[i].AddComment(s);
3614         }
3615       }
3616       prop = s;
3617       break;
3618     }
3619 
3620    #ifdef APFS_SHOW_ALT_STREAMS
3621     case kpidIsAltStream:
3622       prop = ThereAreAltStreams;
3623       // prop = false; // for debug
3624       break;
3625    #endif
3626   }
3627   prop.Detach(value);
3628   return S_OK;
3629   COM_TRY_END
3630 }
3631 
3632 
3633 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
3634 {
3635   *numProps = 0;
3636   return S_OK;
3637 }
3638 
3639 
3640 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 /* index */, BSTR *name, PROPID *propID))
3641 {
3642   *name = NULL;
3643   *propID = 0;
3644   return S_OK;
3645 }
3646 
3647 
3648 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType))
3649 {
3650   *parentType = NParentType::kDir;
3651 
3652   const CRef2 &ref2 = Refs2[index];
3653   const CVol &vol = Vols[ref2.VolIndex];
3654   UInt32 parentIndex = (UInt32)(Int32)-1;
3655 
3656   if (IsViDef(ref2.RefIndex))
3657   {
3658     const CRef &ref = vol.Refs[ref2.RefIndex];
3659    #ifdef APFS_SHOW_ALT_STREAMS
3660     if (ref.IsAltStream())
3661       *parentType = NParentType::kAltStream;
3662    #endif
3663     if (IsViDef(ref.ParentRefIndex))
3664       parentIndex = (UInt32)(ref.ParentRefIndex + vol.StartRef2Index);
3665     else if (index != vol.RootRef2Index && IsViDef(vol.RootRef2Index))
3666       parentIndex = (UInt32)vol.RootRef2Index;
3667   }
3668 
3669   *parent = parentIndex;
3670   return S_OK;
3671 }
3672 
3673 
3674 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
3675 {
3676   *data = NULL;
3677   *dataSize = 0;
3678   *propType = 0;
3679   UNUSED_VAR(index)
3680   UNUSED_VAR(propID)
3681   return S_OK;
3682 }
3683 
3684 
3685 static void Utf8Name_to_InterName(const AString &src, UString &dest)
3686 {
3687   ConvertUTF8ToUnicode(src, dest);
3688   NItemName::NormalizeSlashes_in_FileName_for_OsPath(dest);
3689 }
3690 
3691 
3692 static void AddNodeName(UString &s, const CNode &inode, UInt64 id)
3693 {
3694   s += "node";
3695   s.Add_UInt64(id);
3696   if (!inode.PrimaryName.IsEmpty())
3697   {
3698     s.Add_Dot();
3699     UString s2;
3700     Utf8Name_to_InterName(inode.PrimaryName, s2);
3701     s += s2;
3702   }
3703 }
3704 
3705 
3706 void CDatabase::GetItemPath(unsigned index, const CNode *inode, NWindows::NCOM::CPropVariant &path) const
3707 {
3708   const unsigned kNumLevelsMax = (1 << 10);
3709   const unsigned kLenMax = (1 << 12);
3710   UString s;
3711   const CRef2 &ref2 = Refs2[index];
3712   const CVol &vol = Vols[ref2.VolIndex];
3713 
3714   if (IsViDef(ref2.RefIndex))
3715   {
3716     const CRef &ref = vol.Refs[ref2.RefIndex];
3717     unsigned cur = ref.ItemIndex;
3718     UString s2;
3719     if (IsViNotDef(cur))
3720     {
3721       if (inode)
3722         AddNodeName(s, *inode, vol.NodeIDs[ref.NodeIndex]);
3723     }
3724     else
3725     {
3726       for (unsigned i = 0;; i++)
3727       {
3728         if (i >= kNumLevelsMax || s.Len() > kLenMax)
3729         {
3730           s.Insert(0, UString("[LONG_PATH]"));
3731           break;
3732         }
3733         const CItem &item = vol.Items[(unsigned)cur];
3734         Utf8Name_to_InterName(item.Name, s2);
3735         // s2 += "a\\b"; // for debug
3736         s.Insert(0, s2);
3737         cur = item.ParentItemIndex;
3738         if (IsViNotDef(cur))
3739           break;
3740         // ParentItemIndex was not set for such items
3741         // if (item.ParentId == ROOT_DIR_INO_NUM) break;
3742         s.InsertAtFront(WCHAR_PATH_SEPARATOR);
3743       }
3744     }
3745 
3746    #ifdef APFS_SHOW_ALT_STREAMS
3747     if (IsViDef(ref.AttrIndex) && inode)
3748     {
3749       s.Add_Colon();
3750       Utf8Name_to_InterName(inode->Attrs[(unsigned)ref.AttrIndex].Name, s2);
3751       // s2 += "a\\b"; // for debug
3752       s += s2;
3753     }
3754    #endif
3755   }
3756 
3757   if (!vol.RootName.IsEmpty())
3758   {
3759     if (IsViDef(ref2.RefIndex))
3760       s.InsertAtFront(WCHAR_PATH_SEPARATOR);
3761     s.Insert(0, vol.RootName);
3762   }
3763 
3764   path = s;
3765 }
3766 
3767 
3768 
3769 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
3770 {
3771   COM_TRY_BEGIN
3772   NWindows::NCOM::CPropVariant prop;
3773 
3774   const CRef2 &ref2 = Refs2[index];
3775   const CVol &vol = Vols[ref2.VolIndex];
3776 
3777   if (IsViNotDef(ref2.RefIndex))
3778   {
3779     switch (propID)
3780     {
3781       case kpidName:
3782       case kpidPath:
3783         GetItemPath(index, NULL, prop);
3784         break;
3785       case kpidIsDir:
3786         prop = true;
3787         break;
3788     }
3789     prop.Detach(value);
3790     return S_OK;
3791   }
3792 
3793   const CRef &ref = vol.Refs[ref2.RefIndex];
3794 
3795   const CItem *item = NULL;
3796   if (IsViDef(ref.ItemIndex))
3797     item = &vol.Items[ref.ItemIndex];
3798 
3799   const CNode *inode = NULL;
3800   if (IsViDef(ref.NodeIndex))
3801     inode = &vol.Nodes[ref.NodeIndex];
3802 
3803   switch (propID)
3804   {
3805     case kpidPath:
3806       GetItemPath(index, inode, prop);
3807       break;
3808     case kpidPrimeName:
3809     {
3810      #ifdef APFS_SHOW_ALT_STREAMS
3811       if (!ref.IsAltStream())
3812      #endif
3813       if (inode && !inode->PrimaryName.IsEmpty())
3814       {
3815         UString s;
3816         ConvertUTF8ToUnicode(inode->PrimaryName, s);
3817         /*
3818         // for debug:
3819         if (inode.PrimaryName != item.Name) throw 123456;
3820         */
3821         prop = s;
3822       }
3823       break;
3824     }
3825 
3826     case kpidName:
3827     {
3828       UString s;
3829      #ifdef APFS_SHOW_ALT_STREAMS
3830       if (ref.IsAltStream())
3831       {
3832         // if (inode)
3833         {
3834           const CAttr &attr = inode->Attrs[(unsigned)ref.AttrIndex];
3835           ConvertUTF8ToUnicode(attr.Name, s);
3836         }
3837       }
3838       else
3839      #endif
3840       {
3841         if (item)
3842           ConvertUTF8ToUnicode(item->Name, s);
3843         else if (inode)
3844           AddNodeName(s, *inode, vol.NodeIDs[ref.NodeIndex]);
3845         else
3846           break;
3847       }
3848       // s += "s/1bs\\2"; // for debug:
3849       prop = s;
3850       break;
3851     }
3852 
3853     case kpidSymLink:
3854      #ifdef APFS_SHOW_ALT_STREAMS
3855       if (!ref.IsAltStream())
3856      #endif
3857       if (inode)
3858       {
3859         if (inode->IsSymLink() && IsViDef(inode->SymLinkIndex))
3860         {
3861           const CByteBuffer &buf = inode->Attrs[(unsigned)inode->SymLinkIndex].Data;
3862           if (buf.Size() != 0)
3863           {
3864             AString s;
3865             s.SetFrom_CalcLen((const char *)(const Byte *)buf, (unsigned)buf.Size());
3866             if (s.Len() == buf.Size() - 1)
3867             {
3868               UString u;
3869               ConvertUTF8ToUnicode(s, u);
3870               prop = u;
3871             }
3872           }
3873         }
3874       }
3875       break;
3876 
3877     case kpidSize:
3878       if (inode)
3879       {
3880         UInt64 size = 0;
3881         if (inode->GetSize(ref.GetAttrIndex(), size) ||
3882             !inode->IsDir())
3883           prop = size;
3884       }
3885       break;
3886 
3887     case kpidPackSize:
3888       if (inode)
3889       {
3890         UInt64 size;
3891         if (inode->GetPackSize(ref.GetAttrIndex(), size) ||
3892             !inode->IsDir())
3893           prop = size;
3894       }
3895       break;
3896 
3897     case kpidMethod:
3898      #ifdef APFS_SHOW_ALT_STREAMS
3899       if (!ref.IsAltStream())
3900      #endif
3901       if (inode)
3902       {
3903         if (inode->CompressHeader.IsCorrect)
3904           inode->CompressHeader.MethodToProp(prop);
3905         else if (IsViDef(inode->DecmpfsIndex))
3906           prop = "decmpfs";
3907         else if (!inode->IsDir() && !inode->dstream_defined)
3908         {
3909           if (inode->IsSymLink())
3910           {
3911             if (IsViDef(inode->SymLinkIndex))
3912               prop = "symlink";
3913           }
3914           // else prop = "no_dstream";
3915         }
3916       }
3917       break;
3918 
3919     /*
3920     case kpidUncompressedSize:
3921       if (inode && inode->Has_UNCOMPRESSED_SIZE())
3922         prop = inode->uncompressed_size;
3923       break;
3924     */
3925 
3926     case kpidIsDir:
3927     {
3928       bool isDir = false;
3929      #ifdef APFS_SHOW_ALT_STREAMS
3930       if (!ref.IsAltStream())
3931      #endif
3932       {
3933         if (inode)
3934           isDir = inode->IsDir();
3935         else if (item)
3936           isDir = item->Val.IsFlags_Dir();
3937       }
3938       prop = isDir;
3939       break;
3940     }
3941 
3942     case kpidPosixAttrib:
3943     {
3944       if (inode)
3945       {
3946         UInt32 mode = inode->mode;
3947        #ifdef APFS_SHOW_ALT_STREAMS
3948         if (ref.IsAltStream())
3949         {
3950           mode &= 0666; // we disable execution
3951           mode |= MY_LIN_S_IFREG;
3952         }
3953        #endif
3954         prop = (UInt32)mode;
3955       }
3956       else if (item && !item->Val.IsFlags_Unknown())
3957         prop = (UInt32)(item->Val.flags << 12);
3958       break;
3959     }
3960 
3961     case kpidCTime: if (inode) ApfsTimeToProp(inode->create_time, prop); break;
3962     case kpidMTime: if (inode) ApfsTimeToProp(inode->mod_time, prop); break;
3963     case kpidATime: if (inode) ApfsTimeToProp(inode->access_time, prop); break;
3964     case kpidChangeTime: if (inode) ApfsTimeToProp(inode->change_time, prop); break;
3965     case kpidAddTime: if (item) ApfsTimeToProp(item->Val.date_added, prop); break;
3966 
3967     case kpidBytesWritten:
3968        #ifdef APFS_SHOW_ALT_STREAMS
3969         if (!ref.IsAltStream())
3970        #endif
3971         if (inode && inode->dstream_defined)
3972           prop = inode->dstream.total_bytes_written;
3973         break;
3974     case kpidBytesRead:
3975        #ifdef APFS_SHOW_ALT_STREAMS
3976         if (!ref.IsAltStream())
3977        #endif
3978         if (inode && inode->dstream_defined)
3979           prop = inode->dstream.total_bytes_read;
3980         break;
3981 
3982    #ifdef APFS_SHOW_ALT_STREAMS
3983     case kpidIsAltStream:
3984       prop = ref.IsAltStream();
3985       break;
3986    #endif
3987 
3988     case kpidCharacts:
3989      #ifdef APFS_SHOW_ALT_STREAMS
3990       if (!ref.IsAltStream())
3991      #endif
3992       if (inode)
3993       {
3994         FLAGS_TO_PROP(g_INODE_Flags, (UInt32)inode->internal_flags, prop);
3995       }
3996       break;
3997 
3998     case kpidBsdFlags:
3999      #ifdef APFS_SHOW_ALT_STREAMS
4000       if (!ref.IsAltStream())
4001      #endif
4002       if (inode)
4003       {
4004         FLAGS_TO_PROP(g_INODE_BSD_Flags, inode->bsd_flags, prop);
4005       }
4006       break;
4007 
4008     case kpidGeneration:
4009      #ifdef APFS_SHOW_ALT_STREAMS
4010       // if (!ref.IsAltStream())
4011      #endif
4012       if (inode)
4013         prop = inode->write_generation_counter;
4014       break;
4015 
4016     case kpidUserId:
4017       if (inode)
4018         prop = (UInt32)inode->owner;
4019       break;
4020 
4021     case kpidGroupId:
4022       if (inode)
4023         prop = (UInt32)inode->group;
4024       break;
4025 
4026     case kpidLinks:
4027      #ifdef APFS_SHOW_ALT_STREAMS
4028       if (!ref.IsAltStream())
4029      #endif
4030       if (inode && !inode->IsDir())
4031         prop = (UInt32)inode->nlink;
4032       break;
4033 
4034     case kpidINode:
4035      #ifdef APFS_SHOW_ALT_STREAMS
4036       // here we can disable iNode for alt stream.
4037       if (!ref.IsAltStream())
4038      #endif
4039       if (IsViDef(ref.NodeIndex))
4040         prop = (UInt32)vol.NodeIDs[ref.NodeIndex];
4041       break;
4042 
4043     case kpidParentINode:
4044        #ifdef APFS_SHOW_ALT_STREAMS
4045         if (!ref.IsAltStream())
4046        #endif
4047       if (inode)
4048         prop = (UInt32)inode->parent_id;
4049       break;
4050   }
4051   prop.Detach(value);
4052   return S_OK;
4053   COM_TRY_END
4054 }
4055 
4056 
4057 UInt64 CDatabase::GetSize(const UInt32 index) const
4058 {
4059   const CRef2 &ref2 = Refs2[index];
4060   const CVol &vol = Vols[ref2.VolIndex];
4061   if (IsViNotDef(ref2.RefIndex))
4062     return 0;
4063   const CRef &ref = vol.Refs[ref2.RefIndex];
4064   if (IsViNotDef(ref.NodeIndex))
4065     return 0;
4066   const CNode &inode = vol.Nodes[ref.NodeIndex];
4067   UInt64 size;
4068   if (inode.GetSize(ref.GetAttrIndex(), size))
4069     return size;
4070   return 0;
4071 }
4072 
4073 
4074 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
4075     Int32 testMode, IArchiveExtractCallback *extractCallback))
4076 {
4077   COM_TRY_BEGIN
4078   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
4079   if (allFilesMode)
4080     numItems = Refs2.Size();
4081   if (numItems == 0)
4082     return S_OK;
4083   UInt32 i;
4084   {
4085     UInt64 totalSize = 0;
4086     for (i = 0; i < numItems; i++)
4087     {
4088       const UInt32 index = allFilesMode ? i : indices[i];
4089       totalSize += GetSize(index);
4090     }
4091     RINOK(extractCallback->SetTotal(totalSize))
4092   }
4093 
4094   UInt64 currentTotalSize = 0, currentItemSize = 0;
4095 
4096   CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
4097   lps->Init(extractCallback, false);
4098 
4099   CMyComPtr2_Create<ICompressCoder, NCompress::CCopyCoder> copyCoder;
4100 
4101   // We don't know if zlib without Adler is allowed in APFS.
4102   // But zlib without Adler is allowed in HFS.
4103   // So here we allow apfs/zlib without Adler:
4104   NHfs::CDecoder decoder(true); // IsAdlerOptional
4105 
4106   for (i = 0;; i++, currentTotalSize += currentItemSize)
4107   {
4108     lps->InSize = currentTotalSize;
4109     lps->OutSize = currentTotalSize;
4110     RINOK(lps->SetCur())
4111 
4112     if (i >= numItems)
4113       break;
4114 
4115     const UInt32 index = allFilesMode ? i : indices[i];
4116     const CRef2 &ref2 = Refs2[index];
4117     const CVol &vol = Vols[ref2.VolIndex];
4118 
4119     currentItemSize = GetSize(index);
4120 
4121     int opRes;
4122    {
4123     CMyComPtr<ISequentialOutStream> realOutStream;
4124     const Int32 askMode = testMode ?
4125         NExtract::NAskMode::kTest :
4126         NExtract::NAskMode::kExtract;
4127     RINOK(extractCallback->GetStream(index, &realOutStream, askMode))
4128 
4129     if (IsViNotDef(ref2.RefIndex))
4130     {
4131       RINOK(extractCallback->PrepareOperation(askMode))
4132       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
4133       continue;
4134     }
4135 
4136     const CRef &ref = vol.Refs[ref2.RefIndex];
4137     bool isDir = false;
4138     if (IsViDef(ref.NodeIndex))
4139       isDir = vol.Nodes[ref.NodeIndex].IsDir();
4140     else if (IsViDef(ref.ItemIndex))
4141       isDir =
4142         #ifdef APFS_SHOW_ALT_STREAMS
4143           !ref.IsAltStream() &&
4144         #endif
4145           vol.Items[ref.ItemIndex].Val.IsFlags_Dir();
4146 
4147     if (isDir)
4148     {
4149       RINOK(extractCallback->PrepareOperation(askMode))
4150       RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
4151       continue;
4152     }
4153     if (!testMode && !realOutStream)
4154       continue;
4155     RINOK(extractCallback->PrepareOperation(askMode))
4156     opRes = NExtract::NOperationResult::kDataError;
4157 
4158     if (IsViDef(ref.NodeIndex))
4159     {
4160       const CNode &inode = vol.Nodes[ref.NodeIndex];
4161       if (
4162         #ifdef APFS_SHOW_ALT_STREAMS
4163           !ref.IsAltStream() &&
4164         #endif
4165              !inode.dstream_defined
4166           && inode.Extents.IsEmpty()
4167           && inode.Has_UNCOMPRESSED_SIZE()
4168           && inode.uncompressed_size == inode.CompressHeader.UnpackSize)
4169       {
4170         if (inode.CompressHeader.IsSupported)
4171         {
4172           CMyComPtr<ISequentialInStream> inStreamFork;
4173           UInt64 forkSize = 0;
4174           const CByteBuffer *decmpfs_Data = NULL;
4175 
4176           if (inode.CompressHeader.IsMethod_Resource())
4177           {
4178             if (IsViDef(inode.ResourceIndex))
4179             {
4180               const CAttr &attr = inode.Attrs[inode.ResourceIndex];
4181               forkSize = attr.GetSize();
4182               GetAttrStream(_stream, vol, attr, &inStreamFork);
4183             }
4184           }
4185           else
4186           {
4187             const CAttr &attr = inode.Attrs[inode.DecmpfsIndex];
4188             decmpfs_Data = &attr.Data;
4189           }
4190 
4191           if (inStreamFork || decmpfs_Data)
4192           {
4193             const HRESULT hres = decoder.Extract(
4194                 inStreamFork, realOutStream,
4195                 forkSize,
4196                 inode.CompressHeader,
4197                 decmpfs_Data,
4198                 currentTotalSize, extractCallback,
4199                 opRes);
4200             if (hres != S_FALSE && hres != S_OK)
4201               return hres;
4202           }
4203         }
4204         else
4205           opRes = NExtract::NOperationResult::kUnsupportedMethod;
4206       }
4207       else
4208       {
4209         CMyComPtr<ISequentialInStream> inStream;
4210         if (GetStream(index, &inStream) == S_OK && inStream)
4211         {
4212           CMyComPtr2<ISequentialOutStream, COutStreamWithHash> hashStream;
4213 
4214           if (vol.integrity.Is_SHA256())
4215           {
4216             const int hashIndex = FindHashIndex_for_Item(index);
4217             if (hashIndex != -1)
4218             {
4219               hashStream.Create_if_Empty();
4220               hashStream->SetStream(realOutStream);
4221               hashStream->Init(&(vol.Hash_Vectors[(unsigned)hashIndex]), sb.block_size_Log);
4222             }
4223           }
4224 
4225           RINOK(copyCoder.Interface()->Code(inStream,
4226               hashStream.IsDefined() ?
4227                   hashStream.Interface() :
4228                   realOutStream.Interface(),
4229               NULL, NULL, lps))
4230           opRes = NExtract::NOperationResult::kDataError;
4231           if (copyCoder->TotalSize == currentItemSize)
4232           {
4233             opRes = NExtract::NOperationResult::kOK;
4234             if (hashStream.IsDefined())
4235               if (!hashStream->FinalCheck())
4236                 opRes = NExtract::NOperationResult::kCRCError;
4237           }
4238           else if (copyCoder->TotalSize < currentItemSize)
4239             opRes = NExtract::NOperationResult::kUnexpectedEnd;
4240         }
4241       }
4242     }
4243    }
4244     RINOK(extractCallback->SetOperationResult(opRes))
4245   }
4246   return S_OK;
4247   COM_TRY_END
4248 }
4249 
4250 
4251 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
4252 {
4253   *numItems = Refs2.Size();
4254   return S_OK;
4255 }
4256 
4257 
4258 int CHandler::FindHashIndex_for_Item(UInt32 index)
4259 {
4260   const CRef2 &ref2 = Refs2[index];
4261   const CVol &vol = Vols[ref2.VolIndex];
4262   if (IsViNotDef(ref2.RefIndex))
4263     return -1;
4264 
4265   const CRef &ref = vol.Refs[ref2.RefIndex];
4266   if (IsViNotDef(ref.NodeIndex))
4267     return -1;
4268   const CNode &inode = vol.Nodes[ref.NodeIndex];
4269 
4270   unsigned attrIndex = ref.GetAttrIndex();
4271 
4272   if (IsViNotDef(attrIndex)
4273       && !inode.dstream_defined
4274       && inode.IsSymLink())
4275   {
4276     attrIndex = inode.SymLinkIndex;
4277     if (IsViNotDef(attrIndex))
4278       return -1;
4279   }
4280 
4281   if (IsViDef(attrIndex))
4282   {
4283     /* we have seen examples, where hash available for "com.apple.ResourceFork" stream.
4284        these hashes for "com.apple.ResourceFork" stream are for unpacked data.
4285        but the caller here needs packed data of stream. So we don't use hashes */
4286     return -1;
4287   }
4288   else
4289   {
4290     if (!inode.dstream_defined)
4291       return -1;
4292     const UInt64 id = vol.NodeIDs[ref.NodeIndex];
4293     return vol.Hash_IDs.FindInSorted(id);
4294   }
4295 }
4296 
4297 
4298 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
4299 {
4300   *stream = NULL;
4301 
4302   const CRef2 &ref2 = Refs2[index];
4303   const CVol &vol = Vols[ref2.VolIndex];
4304   if (IsViNotDef(ref2.RefIndex))
4305     return S_FALSE;
4306 
4307   const CRef &ref = vol.Refs[ref2.RefIndex];
4308   if (IsViNotDef(ref.NodeIndex))
4309     return S_FALSE;
4310   const CNode &inode = vol.Nodes[ref.NodeIndex];
4311 
4312   const CRecordVector<CExtent> *extents;
4313   UInt64 rem = 0;
4314 
4315   unsigned attrIndex = ref.GetAttrIndex();
4316 
4317   if (IsViNotDef(attrIndex)
4318       && !inode.dstream_defined
4319       && inode.IsSymLink())
4320   {
4321     attrIndex = inode.SymLinkIndex;
4322     if (IsViNotDef(attrIndex))
4323       return S_FALSE;
4324   }
4325 
4326   if (IsViDef(attrIndex))
4327   {
4328     const CAttr &attr = inode.Attrs[(unsigned)attrIndex];
4329     if (!attr.dstream_defined)
4330     {
4331       CMyComPtr2<ISequentialInStream, CBufInStream> streamTemp;
4332       streamTemp.Create_if_Empty();
4333       streamTemp->Init(attr.Data, attr.Data.Size(), (IInArchive *)this);
4334       *stream = streamTemp.Detach();
4335       return S_OK;
4336     }
4337     const int idIndex = vol.SmallNodeIDs.FindInSorted(attr.Id);
4338     if (idIndex != -1)
4339       extents = &vol.SmallNodes[(unsigned)idIndex].Extents;
4340     else
4341     {
4342       const int fext_Index = vol.FEXT_NodeIDs.FindInSorted(attr.Id);
4343       if (fext_Index == -1)
4344         return S_FALSE;
4345       extents = &vol.FEXT_Nodes[(unsigned)fext_Index].Extents;
4346     }
4347     rem = attr.dstream.size;
4348   }
4349   else
4350   {
4351     if (IsViDef(ref.ItemIndex))
4352       if (vol.Items[ref.ItemIndex].Val.IsFlags_Dir())
4353         return S_FALSE;
4354     if (inode.IsDir())
4355       return S_FALSE;
4356     extents = &inode.Extents;
4357     if (inode.dstream_defined)
4358     {
4359       rem = inode.dstream.size;
4360       if (inode.Extents.Size() == 0)
4361       {
4362         const int fext_Index = vol.FEXT_NodeIDs.FindInSorted(vol.NodeIDs[ref.NodeIndex]);
4363         if (fext_Index != -1)
4364           extents = &vol.FEXT_Nodes[(unsigned)fext_Index].Extents;
4365       }
4366     }
4367     else
4368     {
4369       // return S_FALSE; // check it !!!  How zero size files are stored with dstream_defined?
4370     }
4371   }
4372   return GetStream2(_stream, extents, rem, stream);
4373 }
4374 
4375 
4376 
4377 HRESULT CDatabase::GetAttrStream(IInStream *apfsInStream, const CVol &vol,
4378     const CAttr &attr, ISequentialInStream **stream)
4379 {
4380   *stream = NULL;
4381   if (!attr.dstream_defined)
4382   {
4383     CMyComPtr2<ISequentialInStream, CBufInStream> streamTemp;
4384     streamTemp.Create_if_Empty();
4385     streamTemp->Init(attr.Data, attr.Data.Size(), (IInArchive *)this);
4386     *stream = streamTemp.Detach();
4387     return S_OK;
4388   }
4389   return GetAttrStream_dstream(apfsInStream, vol, attr, stream);
4390 }
4391 
4392 
4393 HRESULT CDatabase::GetAttrStream_dstream( IInStream *apfsInStream, const CVol &vol,
4394     const CAttr &attr, ISequentialInStream **stream)
4395 {
4396   const CRecordVector<CExtent> *extents;
4397   {
4398     const int idIndex = vol.SmallNodeIDs.FindInSorted(attr.Id);
4399     if (idIndex != -1)
4400       extents = &vol.SmallNodes[(unsigned)idIndex].Extents;
4401     else
4402     {
4403       const int fext_Index = vol.FEXT_NodeIDs.FindInSorted(attr.Id);
4404       if (fext_Index == -1)
4405         return S_FALSE;
4406       extents = &vol.FEXT_Nodes[(unsigned)fext_Index].Extents;
4407     }
4408   }
4409   return GetStream2(apfsInStream, extents, attr.dstream.size, stream);
4410 }
4411 
4412 
4413 HRESULT CDatabase::GetStream2(
4414     IInStream *apfsInStream,
4415     const CRecordVector<CExtent> *extents, UInt64 rem,
4416     ISequentialInStream **stream)
4417 {
4418   CMyComPtr2<ISequentialInStream, CExtentsStream> extentStream;
4419   extentStream.Create_if_Empty();
4420 
4421   UInt64 virt = 0;
4422   FOR_VECTOR (i, *extents)
4423   {
4424     const CExtent &e = (*extents)[i];
4425     if (virt != e.logical_offset)
4426       return S_FALSE;
4427     const UInt64 len = EXTENT_GET_LEN(e.len_and_flags);
4428     if (len == 0)
4429     {
4430       return S_FALSE;
4431       // continue;
4432     }
4433     if (rem == 0)
4434       return S_FALSE;
4435     UInt64 cur = len;
4436     if (cur > rem)
4437       cur = rem;
4438     CSeekExtent se;
4439     se.Phy = (UInt64)e.phys_block_num << sb.block_size_Log;
4440     se.Virt = virt;
4441     virt += cur;
4442     rem -= cur;
4443     extentStream->Extents.Add(se);
4444     if (rem == 0)
4445       if (i != extents->Size() - 1)
4446         return S_FALSE;
4447   }
4448 
4449   if (rem != 0)
4450     return S_FALSE;
4451 
4452   CSeekExtent se;
4453   se.Phy = 0;
4454   se.Virt = virt;
4455   extentStream->Extents.Add(se);
4456   extentStream->Stream = apfsInStream;
4457   extentStream->Init();
4458   *stream = extentStream.Detach();
4459   return S_OK;
4460 }
4461 
4462 
4463 REGISTER_ARC_I(
4464   "APFS", "apfs img", NULL, 0xc3,
4465   k_Signature,
4466   k_SignatureOffset,
4467   0,
4468   IsArc_APFS)
4469 
4470 }}
4471