xref: /aosp_15_r20/external/lzma/CPP/7zip/Archive/LpHandler.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
1 // LpHandler.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 #include "../../../C/Sha256.h"
7 
8 #include "../../Common/ComTry.h"
9 #include "../../Common/IntToString.h"
10 #include "../../Common/MyBuffer.h"
11 
12 #include "../../Windows/PropVariantUtils.h"
13 
14 #include "../Common/LimitedStreams.h"
15 #include "../Common/ProgressUtils.h"
16 #include "../Common/RegisterArc.h"
17 #include "../Common/StreamUtils.h"
18 
19 #include "../Compress/CopyCoder.h"
20 
21 #define Get16(p) GetUi16(p)
22 #define Get32(p) GetUi32(p)
23 #define Get64(p) GetUi64(p)
24 
25 #define G16(_offs_, dest) dest = Get16(p + (_offs_));
26 #define G32(_offs_, dest) dest = Get32(p + (_offs_));
27 #define G64(_offs_, dest) dest = Get64(p + (_offs_));
28 
29 using namespace NWindows;
30 
31 namespace NArchive {
32 
33 namespace NExt {
34 API_FUNC_IsArc IsArc_Ext_PhySize(const Byte *p, size_t size, UInt64 *phySize);
35 }
36 
37 namespace NLp {
38 
39 /*
40 Android 10+ use Android's Dynamic Partitions to allow the
41 different read-only system partitions (e.g. system, vendor, product)
42 to share the same pool of storage space (as LVM in Linux).
43 Name for partition: "super" (for GPT) or "super.img" (for file).
44 Dynamic Partition Tools: lpmake
45 All partitions that are A/B-ed should be named as follows (slots are always named a, b, etc.):
46 boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.
47 */
48 
49 #define LP_METADATA_MAJOR_VERSION 10
50 // #define LP_METADATA_MINOR_VERSION_MIN 0
51 // #define LP_METADATA_MINOR_VERSION_MAX 2
52 
53 // #define LP_SECTOR_SIZE 512
54 static const unsigned kSectorSizeLog = 9;
55 
56 /* Amount of space reserved at the start of every super partition to avoid
57  * creating an accidental boot sector. */
58 #define LP_PARTITION_RESERVED_BYTES 4096
59 #define LP_METADATA_GEOMETRY_SIZE 4096
60 #define LP_METADATA_HEADER_MAGIC 0x414C5030
61 
62 static const unsigned k_SignatureSize = 8;
63 static const Byte k_Signature[k_SignatureSize] =
64   { 0x67, 0x44, 0x6c, 0x61, 0x34, 0, 0, 0 };
65 
66 // The length (36) is the same as the maximum length of a GPT partition name.
67 static const unsigned kNameLen = 36;
68 
AddName36ToString(AString & s,const char * name,bool strictConvert)69 static void AddName36ToString(AString &s, const char *name, bool strictConvert)
70 {
71   for (unsigned i = 0; i < kNameLen; i++)
72   {
73     char c = name[i];
74     if (c == 0)
75       return;
76     if (strictConvert && c < 32)
77       c = '_';
78     s += c;
79   }
80 }
81 
82 
83 static const unsigned k_Geometry_Size = 0x34;
84 
85 // LpMetadataGeometry
86 struct CGeometry
87 {
88   // UInt32 magic;
89   // UInt32 struct_size;
90   // Byte checksum[32];   /*  SHA256 checksum of this struct, with this field set to 0. */
91 
92   /* Maximum amount of space a single copy of the metadata can use,
93      a multiple of LP_SECTOR_SIZE. */
94   UInt32 metadata_max_size;
95 
96   /* Number of copies of the metadata to keep.
97      For Non-A/B: 1, For A/B: 2, for A/B/C: 3.
98      A backup copy of each slot is kept */
99   UInt32 metadata_slot_count;
100 
101   /* minimal alignment for partition and extent sizes, a multiple of LP_SECTOR_SIZE. */
102   UInt32 logical_block_size;
103 
ParseNArchive::NLp::CGeometry104   bool Parse(const Byte *p)
105   {
106     G32 (40, metadata_max_size)
107     G32 (44, metadata_slot_count)
108     G32 (48, logical_block_size)
109     if (metadata_slot_count == 0 || metadata_slot_count >= ((UInt32)1 << 20))
110       return false;
111     if (metadata_max_size == 0)
112       return false;
113     if ((metadata_max_size & (((UInt32)1 << kSectorSizeLog) - 1)) != 0)
114       return false;
115     return true;
116   }
117 
GetTotalMetadataSizeNArchive::NLp::CGeometry118   UInt64 GetTotalMetadataSize() const
119   {
120     // there are 2 copies of GEOMETRY and METADATA slots
121     return LP_PARTITION_RESERVED_BYTES +
122            LP_METADATA_GEOMETRY_SIZE * 2 +
123            ((UInt64)metadata_max_size * metadata_slot_count) * 2;
124   }
125 };
126 
127 
128 
129 // LpMetadataTableDescriptor
130 struct CDescriptor
131 {
132   UInt32 offset;        /*  Location of the table, relative to end of the metadata header. */
133   UInt32 num_entries;   /*  Number of entries in the table. */
134   UInt32 entry_size;    /*  Size of each entry in the table, in bytes. */
135 
ParseNArchive::NLp::CDescriptor136   void Parse(const Byte *p)
137   {
138     G32 (0, offset)
139     G32 (4, num_entries)
140     G32 (8, entry_size)
141   }
142 
CheckLimitsNArchive::NLp::CDescriptor143   bool CheckLimits(UInt32 limit) const
144   {
145     if (entry_size == 0)
146       return false;
147     const UInt32 size = num_entries * entry_size;
148     if (size / entry_size != num_entries)
149       return false;
150     if (offset > limit || limit - offset < size)
151       return false;
152     return true;
153   }
154 };
155 
156 
157 // #define LP_PARTITION_ATTR_NONE 0x0
158 // #define LP_PARTITION_ATTR_READONLY (1 << 0)
159 
160 /* This flag is only intended to be used with super_empty.img and super.img on
161  * retrofit devices. On these devices there are A and B super partitions, and
162  * we don't know ahead of time which slot the image will be applied to.
163  *
164  * If set, the partition name needs a slot suffix applied. The slot suffix is
165  * determined by the metadata slot number (0 = _a, 1 = _b).
166  */
167 // #define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)
168 
169 /* This flag is applied automatically when using MetadataBuilder::NewForUpdate.
170  * It signals that the partition was created (or modified) for a snapshot-based
171  * update. If this flag is not present, the partition was likely flashed via
172  * fastboot.
173  */
174 // #define LP_PARTITION_ATTR_UPDATED (1 << 2)
175 
176 /* This flag marks a partition as disabled. It should not be used or mapped. */
177 // #define LP_PARTITION_ATTR_DISABLED (1 << 3)
178 
179 static const char * const g_PartitionAttr[] =
180 {
181     "READONLY"
182   , "SLOT_SUFFIXED"
183   , "UPDATED"
184   , "DISABLED"
185 };
186 
187 static unsigned const k_MetaPartition_Size = 52;
188 
189 // LpMetadataPartition
190 struct CPartition
191 {
192   /* ASCII characters: alphanumeric or _. at least one ASCII character,
193      (name) must be unique across all partition names. */
194   char name[kNameLen];
195 
196   UInt32 attributes;   /* (LP_PARTITION_ATTR_*). */
197 
198   /* Index of the first extent owned by this partition. The extent will
199    * start at logical sector 0. Gaps between extents are not allowed. */
200   UInt32 first_extent_index;
201 
202   /* Number of extents in the partition. Every partition must have at least one extent. */
203   UInt32 num_extents;
204 
205   /* Group this partition belongs to. */
206   UInt32 group_index;
207 
ParseNArchive::NLp::CPartition208   void Parse(const Byte *p)
209   {
210     memcpy(name, p, kNameLen);
211     G32 (36, attributes)
212     G32 (40, first_extent_index)
213     G32 (44, num_extents)
214     G32 (48, group_index)
215   }
216 
217   // calced properties:
218   UInt32 MethodsMask;
219   UInt64 NumSectors;
220   UInt64 NumSectors_Pack;
221   const char *Ext;
222 
GetSizeNArchive::NLp::CPartition223   UInt64 GetSize() const { return NumSectors << kSectorSizeLog; }
GetPackSizeNArchive::NLp::CPartition224   UInt64 GetPackSize() const { return NumSectors_Pack << kSectorSizeLog; }
225 
CPartitionNArchive::NLp::CPartition226   CPartition():
227       MethodsMask(0),
228       NumSectors(0),
229       NumSectors_Pack(0),
230       Ext(NULL)
231       {}
232 };
233 
234 
235 
236 
237 #define LP_TARGET_TYPE_LINEAR 0
238 /* This extent is a dm-zero target. The index is ignored and must be 0. */
239 #define LP_TARGET_TYPE_ZERO 1
240 
241 static const char * const g_Methods[] =
242 {
243     "RAW" // "LINEAR"
244   , "ZERO"
245 };
246 
247 static unsigned const k_MetaExtent_Size = 24;
248 
249 // LpMetadataExtent
250 struct CExtent
251 {
252   UInt64 num_sectors;  /*  Length in 512-byte sectors. */
253   UInt32 target_type;  /*  Target type for device-mapper (LP_TARGET_TYPE_*). */
254 
255   /* for LINEAR: The sector on the physical partition that this extent maps onto.
256      for ZERO:   must be 0. */
257   UInt64 target_data;
258 
259   /* for LINEAR: index into the block devices table.
260      for ZERO:   must be 0. */
261   UInt32 target_source;
262 
IsRAWNArchive::NLp::CExtent263   bool IsRAW() const { return target_type == LP_TARGET_TYPE_LINEAR; }
264 
ParseNArchive::NLp::CExtent265   void Parse(const Byte *p)
266   {
267     G64 (0, num_sectors)
268     G32 (8, target_type)
269     G64 (12, target_data)
270     G32 (20, target_source)
271   }
272 };
273 
274 
275 /* This flag is only intended to be used with super_empty.img and super.img on
276  * retrofit devices. If set, the group needs a slot suffix to be interpreted
277  * correctly. The suffix is automatically applied by ReadMetadata().
278  */
279 // #define LP_GROUP_SLOT_SUFFIXED (1 << 0)
280 static unsigned const k_Group_Size = 48;
281 
282 // LpMetadataPartitionGroup
283 struct CGroup
284 {
285   char name[kNameLen];
286   UInt32 flags;  /* (LP_GROUP_*). */
287   UInt64 maximum_size; /* Maximum size in bytes. If 0, the group has no maximum size. */
288 
ParseNArchive::NLp::CGroup289   void Parse(const Byte *p)
290   {
291     memcpy(name, p, kNameLen);
292     G32 (36, flags)
293     G64 (40, maximum_size)
294   }
295 };
296 
297 
298 
299 
300 /* This flag is only intended to be used with super_empty.img and super.img on
301  * retrofit devices. On these devices there are A and B super partitions, and
302  * we don't know ahead of time which slot the image will be applied to.
303  *
304  * If set, the block device needs a slot suffix applied before being used with
305  * IPartitionOpener. The slot suffix is determined by the metadata slot number
306  * (0 = _a, 1 = _b).
307  */
308 // #define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)
309 
310 static unsigned const k_Device_Size = 64;
311 
312 /* This struct defines an entry in the block_devices table. There must be at
313  * least one device, and the first device must represent the partition holding
314  * the super metadata.
315  */
316 // LpMetadataBlockDevice
317 struct CDevice
318 {
319     /* 0: First usable sector for allocating logical partitions. this will be
320      * the first sector after the initial geometry blocks, followed by the
321      * space consumed by metadata_max_size*metadata_slot_count*2.
322      */
323   UInt64 first_logical_sector;
324 
325     /* 8: Alignment for defining partitions or partition extents. For example,
326      * an alignment of 1MiB will require that all partitions have a size evenly
327      * divisible by 1MiB, and that the smallest unit the partition can grow by
328      * is 1MiB.
329      *
330      * Alignment is normally determined at runtime when growing or adding
331      * partitions. If for some reason the alignment cannot be determined, then
332      * this predefined alignment in the geometry is used instead. By default
333      * it is set to 1MiB.
334      */
335   UInt32 alignment;
336 
337     /* 12: Alignment offset for "stacked" devices. For example, if the "super"
338      * partition itself is not aligned within the parent block device's
339      * partition table, then we adjust for this in deciding where to place
340      * |first_logical_sector|.
341      *
342      * Similar to |alignment|, this will be derived from the operating system.
343      * If it cannot be determined, it is assumed to be 0.
344      */
345   UInt32 alignment_offset;
346 
347     /* 16: Block device size, as specified when the metadata was created. This
348      * can be used to verify the geometry against a target device.
349      */
350   UInt64 size;
351 
352     /* 24: Partition name in the GPT*/
353   char partition_name[kNameLen];
354 
355     /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */
356   UInt32 flags;
357 
ParseNArchive::NLp::CDevice358   void Parse(const Byte *p)
359   {
360     memcpy(partition_name, p + 24, kNameLen);
361     G64 (0, first_logical_sector)
362     G32 (8, alignment)
363     G32 (12, alignment_offset)
364     G64 (16, size)
365     G32 (60, flags)
366   }
367 };
368 
369 
370 /* This device uses Virtual A/B. Note that on retrofit devices, the expanded
371  * header may not be present.
372  */
373 // #define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1
374 
375 static const char * const g_Header_Flags[] =
376 {
377   "VIRTUAL_AB"
378 };
379 
380 
381 static const unsigned k_LpMetadataHeader10_size = 128;
382 static const unsigned k_LpMetadataHeader12_size = 256;
383 
384 struct LpMetadataHeader
385 {
386   /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
387   UInt32 magic;
388 
389   /*  4: Version number required to read this metadata. If the version is not
390    * equal to the library version, the metadata should be considered
391    * incompatible.
392    */
393   UInt16 major_version;
394 
395   /*  6: Minor version. A library supporting newer features should be able to
396    * read metadata with an older minor version. However, an older library
397    * should not support reading metadata if its minor version is higher.
398    */
399   UInt16 minor_version;
400 
401   /*  8: The size of this header struct. */
402   UInt32 header_size;
403 
404   /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
405    * if this field were set to 0.
406    */
407   // Byte header_checksum[32];
408 
409   /* 44: The total size of all tables. This size is contiguous; tables may not
410    * have gaps in between, and they immediately follow the header.
411    */
412   UInt32 tables_size;
413 
414   /* 48: SHA256 checksum of all table contents. */
415   Byte tables_checksum[32];
416 
417   /* 80: Partition table descriptor. */
418   CDescriptor partitions;
419   /* 92: Extent table descriptor. */
420   CDescriptor extents;
421   /* 104: Updateable group descriptor. */
422   CDescriptor groups;
423   /* 116: Block device table. */
424   CDescriptor block_devices;
425 
426   /* Everything past here is header version 1.2+, and is only included if
427    * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
428    * zero these additional fields.
429    */
430 
431   /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
432    * independent of the version number and intended to be informational only.
433    * New flags can be added without bumping the version.
434    */
435   // UInt32 flags;
436 
437   /* 132: Reserved (zero), pad to 256 bytes. */
438   // Byte reserved[124];
439 
Parse128NArchive::NLp::LpMetadataHeader440   void Parse128(const Byte *p)
441   {
442     G32 (0, magic)
443     G16 (4, major_version)
444     G16 (6, minor_version)
445     G32 (8, header_size)
446     // Byte header_checksum[32];
447     G32 (44, tables_size)
448     memcpy (tables_checksum, p + 48, 32);
449     partitions.Parse(p + 80);
450     extents.Parse(p + 92);
451     groups.Parse(p + 104);
452     block_devices.Parse(p + 116);
453     /* Everything past here is header version 1.2+, and is only included if
454      * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
455      * zero these additional fields.
456      */
457   }
458 };
459 
460 
CheckSha256(const Byte * data,size_t size,const Byte * checksum)461 static bool CheckSha256(const Byte *data, size_t size, const Byte *checksum)
462 {
463   MY_ALIGN (16)
464   CSha256 sha;
465   Sha256_Init(&sha);
466   Sha256_Update(&sha, data, size);
467   MY_ALIGN (16)
468   Byte calced[32];
469   Sha256_Final(&sha, calced);
470   return memcmp(checksum, calced, 32) == 0;
471 }
472 
CheckSha256_csOffset(Byte * data,size_t size,unsigned hashOffset)473 static bool CheckSha256_csOffset(Byte *data, size_t size, unsigned hashOffset)
474 {
475   MY_ALIGN (4)
476   Byte checksum[32];
477   Byte *shaData = &data[hashOffset];
478   memcpy(checksum, shaData, 32);
479   memset(shaData, 0, 32);
480   return CheckSha256(data, size, checksum);
481 }
482 
483 
484 
485 Z7_CLASS_IMP_CHandler_IInArchive_1(
486   IInArchiveGetStream
487 )
488   CRecordVector<CPartition> _items;
489   CRecordVector<CExtent> Extents;
490 
491   CMyComPtr<IInStream> _stream;
492   UInt64 _totalSize;
493   // UInt64 _usedSize;
494   // UInt64 _headersSize;
495 
496   CGeometry geom;
497   UInt16 Major_version;
498   UInt16 Minor_version;
499   UInt32 Flags;
500 
501   Int32 _mainFileIndex;
502   UInt32 MethodsMask;
503   bool _headerWarning;
504   AString GroupsString;
505   AString DevicesString;
506   AString DeviceArcName;
507 
508   HRESULT Open2(IInStream *stream);
509 };
510 
511 
AddComment_UInt64(AString & s,const char * name,UInt64 val)512 static void AddComment_UInt64(AString &s, const char *name, UInt64 val)
513 {
514   s.Add_Space();
515   s += name;
516   s += '=';
517   s.Add_UInt64(val);
518 }
519 
520 
IsBufZero(const Byte * data,size_t size)521 static bool IsBufZero(const Byte *data, size_t size)
522 {
523   for (size_t i = 0; i < size; i += 4)
524     if (*(const UInt32 *)(const void *)(data + i) != 0)
525       return false;
526   return true;
527 }
528 
529 
Open2(IInStream * stream)530 HRESULT CHandler::Open2(IInStream *stream)
531 {
532   RINOK(InStream_SeekSet(stream, LP_PARTITION_RESERVED_BYTES))
533   {
534     MY_ALIGN (4)
535     Byte buf[k_Geometry_Size];
536     RINOK(ReadStream_FALSE(stream, buf, k_Geometry_Size))
537     if (memcmp(buf, k_Signature, k_SignatureSize) != 0)
538       return S_FALSE;
539     if (!geom.Parse(buf))
540       return S_FALSE;
541     if (!CheckSha256_csOffset(buf, k_Geometry_Size, 8))
542       return S_FALSE;
543   }
544 
545   CByteBuffer buffer;
546   RINOK(InStream_SeekToBegin(stream))
547   buffer.Alloc(LP_METADATA_GEOMETRY_SIZE * 2);
548   {
549     // buffer.Size() >= LP_PARTITION_RESERVED_BYTES
550     RINOK(ReadStream_FALSE(stream, buffer, LP_PARTITION_RESERVED_BYTES))
551     if (!IsBufZero(buffer, LP_PARTITION_RESERVED_BYTES))
552     {
553       _headerWarning = true;
554       // return S_FALSE;
555     }
556   }
557 
558   RINOK(ReadStream_FALSE(stream, buffer, LP_METADATA_GEOMETRY_SIZE * 2))
559   // we check that 2 copies of GEOMETRY are identical:
560   if (memcmp(buffer, buffer + LP_METADATA_GEOMETRY_SIZE, LP_METADATA_GEOMETRY_SIZE) != 0
561       || !IsBufZero(buffer + k_Geometry_Size, LP_METADATA_GEOMETRY_SIZE - k_Geometry_Size))
562   {
563     _headerWarning = true;
564     // return S_FALSE;
565   }
566 
567   RINOK(ReadStream_FALSE(stream, buffer, k_LpMetadataHeader10_size))
568   LpMetadataHeader header;
569   header.Parse128(buffer);
570   if (header.magic != LP_METADATA_HEADER_MAGIC ||
571       header.major_version != LP_METADATA_MAJOR_VERSION ||
572       header.header_size < k_LpMetadataHeader10_size)
573     return S_FALSE;
574   Flags = 0;
575   if (header.header_size > k_LpMetadataHeader10_size)
576   {
577     if (header.header_size != k_LpMetadataHeader12_size)
578       return S_FALSE;
579     RINOK(ReadStream_FALSE(stream, buffer + k_LpMetadataHeader10_size,
580         header.header_size - k_LpMetadataHeader10_size))
581     Flags = Get32(buffer + k_LpMetadataHeader10_size);
582   }
583   Major_version = header.major_version;
584   Minor_version = header.minor_version;
585 
586   if (!CheckSha256_csOffset(buffer, header.header_size, 12))
587     return S_FALSE;
588 
589   if (geom.metadata_max_size < header.tables_size ||
590       geom.metadata_max_size - header.tables_size < header.header_size)
591     return S_FALSE;
592 
593   buffer.AllocAtLeast(header.tables_size);
594   RINOK(ReadStream_FALSE(stream, buffer, header.tables_size))
595 
596   const UInt64 totalMetaSize = geom.GetTotalMetadataSize();
597   // _headersSize = _totalSize;
598   _totalSize = totalMetaSize;
599 
600   if (!CheckSha256(buffer, header.tables_size, header.tables_checksum))
601     return S_FALSE;
602 
603   {
604     const CDescriptor &d = header.partitions;
605     if (!d.CheckLimits(header.tables_size))
606       return S_FALSE;
607     if (d.entry_size != k_MetaPartition_Size)
608       return S_FALSE;
609     for (UInt32 i = 0; i < d.num_entries; i++)
610     {
611       CPartition part;
612       part.Parse(buffer + d.offset + i * d.entry_size);
613       const UInt32 extLimit = part.first_extent_index + part.num_extents;
614       if (extLimit < part.first_extent_index ||
615           extLimit > header.extents.num_entries ||
616           part.group_index >= header.groups.num_entries)
617         return S_FALSE;
618       _items.Add(part);
619     }
620   }
621   {
622     const CDescriptor &d = header.extents;
623     if (!d.CheckLimits(header.tables_size))
624       return S_FALSE;
625     if (d.entry_size != k_MetaExtent_Size)
626       return S_FALSE;
627     for (UInt32 i = 0; i < d.num_entries; i++)
628     {
629       CExtent e;
630       e.Parse(buffer + d.offset + i * d.entry_size);
631       // if (e.target_type > LP_TARGET_TYPE_ZERO) return S_FALSE;
632       if (e.IsRAW())
633       {
634         if (e.target_source >= header.block_devices.num_entries)
635           return S_FALSE;
636         const UInt64 endSector = e.target_data + e.num_sectors;
637         const UInt64 endOffset = endSector << kSectorSizeLog;
638         if (_totalSize < endOffset)
639           _totalSize = endOffset;
640       }
641       MethodsMask |= (UInt32)1 << e.target_type;
642       Extents.Add(e);
643     }
644   }
645 
646   // _usedSize = _totalSize;
647   {
648     const CDescriptor &d = header.groups;
649     if (!d.CheckLimits(header.tables_size))
650       return S_FALSE;
651     if (d.entry_size != k_Group_Size)
652       return S_FALSE;
653     AString s;
654     for (UInt32 i = 0; i < d.num_entries; i++)
655     {
656       CGroup g;
657       g.Parse(buffer + d.offset + i * d.entry_size);
658       if (_totalSize < g.maximum_size)
659         _totalSize = g.maximum_size;
660       s += "  ";
661       AddName36ToString(s, g.name, true);
662       AddComment_UInt64(s, "maximum_size", g.maximum_size);
663       AddComment_UInt64(s, "flags", g.flags);
664       s.Add_LF();
665     }
666     GroupsString = s;
667   }
668 
669   {
670     const CDescriptor &d = header.block_devices;
671     if (!d.CheckLimits(header.tables_size))
672       return S_FALSE;
673     if (d.entry_size != k_Device_Size)
674       return S_FALSE;
675     AString s;
676     // CRecordVector<CDevice> devices;
677     for (UInt32 i = 0; i < d.num_entries; i++)
678     {
679       CDevice v;
680       v.Parse(buffer + d.offset + i * d.entry_size);
681       // if (i == 0)
682       {
683         // it's super_device is first device;
684         if (totalMetaSize > (v.first_logical_sector << kSectorSizeLog))
685           return S_FALSE;
686       }
687       if (_totalSize < v.size)
688         _totalSize = v.size;
689       s += "  ";
690       if (i == 0)
691         AddName36ToString(DeviceArcName, v.partition_name, true);
692       // devices.Add(v);
693       AddName36ToString(s, v.partition_name, true);
694       AddComment_UInt64(s, "size", v.size);
695       AddComment_UInt64(s, "first_logical_sector", v.first_logical_sector);
696       AddComment_UInt64(s, "alignment", v.alignment);
697       AddComment_UInt64(s, "alignment_offset", v.alignment_offset);
698       AddComment_UInt64(s, "flags", v.flags);
699       s.Add_LF();
700     }
701     DevicesString = s;
702   }
703 
704   {
705     FOR_VECTOR (i, _items)
706     {
707       CPartition &part = _items[i];
708       if (part.first_extent_index > Extents.Size() ||
709           part.num_extents > Extents.Size() - part.first_extent_index)
710         return S_FALSE;
711 
712       UInt64 numSectors = 0;
713       UInt64 numSectors_Pack = 0;
714       UInt32 methods = 0;
715       for (UInt32 k = 0; k < part.num_extents; k++)
716       {
717         const CExtent &e = Extents[part.first_extent_index + k];
718         numSectors += e.num_sectors;
719         if (e.IsRAW())
720           numSectors_Pack += e.num_sectors;
721         methods |= (UInt32)1 << e.target_type;
722       }
723       part.NumSectors = numSectors;
724       part.NumSectors_Pack = numSectors_Pack;
725       part.MethodsMask = methods;
726     }
727   }
728 
729   return S_OK;
730 }
731 
732 
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback *))733 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
734     const UInt64 * /* maxCheckStartPosition */,
735     IArchiveOpenCallback * /* openArchiveCallback */))
736 {
737   COM_TRY_BEGIN
738   Close();
739   RINOK(Open2(stream))
740   _stream = stream;
741 
742   int mainFileIndex = -1;
743   unsigned numNonEmptyParts = 0;
744 
745   FOR_VECTOR (fileIndex, _items)
746   {
747     CPartition &item = _items[fileIndex];
748     if (item.NumSectors != 0)
749     {
750       mainFileIndex = (int)fileIndex;
751       numNonEmptyParts++;
752       CMyComPtr<ISequentialInStream> parseStream;
753       if (GetStream(fileIndex, &parseStream) == S_OK && parseStream)
754       {
755         const size_t kParseSize = 1 << 11;
756         Byte buf[kParseSize];
757         if (ReadStream_FAIL(parseStream, buf, kParseSize) == S_OK)
758         {
759           UInt64 extSize;
760           if (NExt::IsArc_Ext_PhySize(buf, kParseSize, &extSize) == k_IsArc_Res_YES)
761             if (extSize == item.GetSize())
762               item.Ext = "ext";
763         }
764       }
765     }
766   }
767   if (numNonEmptyParts == 1)
768     _mainFileIndex = mainFileIndex;
769 
770   return S_OK;
771   COM_TRY_END
772 }
773 
774 
Z7_COM7F_IMF(CHandler::Close ())775 Z7_COM7F_IMF(CHandler::Close())
776 {
777   _totalSize = 0;
778   // _usedSize = 0;
779   // _headersSize = 0;
780   _items.Clear();
781   Extents.Clear();
782   _stream.Release();
783   _mainFileIndex = -1;
784   _headerWarning = false;
785   MethodsMask = 0;
786   GroupsString.Empty();
787   DevicesString.Empty();
788   DeviceArcName.Empty();
789   return S_OK;
790 }
791 
792 
793 static const Byte kProps[] =
794 {
795   kpidPath,
796   kpidSize,
797   kpidPackSize,
798   kpidCharacts,
799   kpidMethod,
800   kpidNumBlocks,
801   kpidOffset
802 };
803 
804 static const Byte kArcProps[] =
805 {
806   kpidUnpackVer,
807   kpidMethod,
808   kpidClusterSize,
809   // kpidHeadersSize,
810   // kpidFreeSpace,
811   kpidName,
812   kpidComment
813 };
814 
815 IMP_IInArchive_Props
816 IMP_IInArchive_ArcProps
817 
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))818 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
819 {
820   COM_TRY_BEGIN
821   NCOM::CPropVariant prop;
822   switch (propID)
823   {
824     case kpidMainSubfile:
825     {
826       if (_mainFileIndex >= 0)
827         prop = (UInt32)_mainFileIndex;
828       break;
829     }
830     case kpidPhySize: prop = _totalSize; break;
831 
832     // case kpidFreeSpace: if (_usedSize != 0) prop = _totalSize - _usedSize; break;
833     // case kpidHeadersSize: prop = _headersSize; break;
834 
835     case kpidMethod:
836     {
837       const UInt32 m = MethodsMask;
838       if (m != 0)
839       {
840         FLAGS_TO_PROP(g_Methods, m, prop);
841       }
842       break;
843     }
844 
845     case kpidUnpackVer:
846     {
847       AString s;
848       s.Add_UInt32(Major_version);
849       s.Add_Dot();
850       s.Add_UInt32(Minor_version);
851       prop = s;
852       break;
853     }
854 
855     case kpidClusterSize:
856       prop = geom.logical_block_size;
857       break;
858 
859     case kpidComment:
860     {
861       AString s;
862 
863       s += "metadata_slot_count: ";
864       s.Add_UInt32(geom.metadata_slot_count);
865       s.Add_LF();
866 
867       s += "metadata_max_size: ";
868       s.Add_UInt32(geom.metadata_max_size);
869       s.Add_LF();
870 
871       if (Flags != 0)
872       {
873         s += "flags: ";
874         s += FlagsToString(g_Header_Flags, Z7_ARRAY_SIZE(g_Header_Flags), Flags);
875         s.Add_LF();
876       }
877 
878       if (!GroupsString.IsEmpty())
879       {
880         s += "Groups:";
881         s.Add_LF();
882         s += GroupsString;
883       }
884 
885       if (!DevicesString.IsEmpty())
886       {
887         s += "BlockDevices:";
888         s.Add_LF();
889         s += DevicesString;
890       }
891 
892       if (!s.IsEmpty())
893         prop = s;
894       break;
895     }
896 
897     case kpidName:
898       if (!DeviceArcName.IsEmpty())
899         prop = DeviceArcName + ".lpimg";
900       break;
901 
902     case kpidWarningFlags:
903       if (_headerWarning)
904       {
905         UInt32 v = kpv_ErrorFlags_HeadersError;
906         prop = v;
907       }
908       break;
909   }
910   prop.Detach(value);
911   return S_OK;
912   COM_TRY_END
913 }
914 
915 
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))916 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
917 {
918   *numItems = _items.Size();
919   return S_OK;
920 }
921 
922 
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))923 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
924 {
925   COM_TRY_BEGIN
926   NCOM::CPropVariant prop;
927 
928   const CPartition &item = _items[index];
929 
930   switch (propID)
931   {
932     case kpidPath:
933     {
934       AString s;
935       AddName36ToString(s, item.name, false);
936       if (s.IsEmpty())
937         s.Add_UInt32(index);
938       if (item.num_extents != 0)
939       {
940         s.Add_Dot();
941         s += (item.Ext ? item.Ext : "img");
942       }
943       prop = s;
944       break;
945     }
946 
947     case kpidSize: prop = item.GetSize(); break;
948     case kpidPackSize: prop = item.GetPackSize(); break;
949     case kpidNumBlocks: prop = item.num_extents; break;
950     case kpidMethod:
951     {
952       const UInt32 m = item.MethodsMask;
953       if (m != 0)
954       {
955         FLAGS_TO_PROP(g_Methods, m, prop);
956       }
957       break;
958     }
959     case kpidOffset:
960       if (item.num_extents != 0)
961         if (item.first_extent_index < Extents.Size())
962           prop = Extents[item.first_extent_index].target_data << kSectorSizeLog;
963       break;
964 
965     case kpidCharacts:
966     {
967       AString s;
968       s += "group:";
969       s.Add_UInt32(item.group_index);
970       s.Add_Space();
971       s += FlagsToString(g_PartitionAttr, Z7_ARRAY_SIZE(g_PartitionAttr), item.attributes);
972       prop = s;
973       break;
974     }
975   }
976 
977   prop.Detach(value);
978   return S_OK;
979   COM_TRY_END
980 }
981 
982 
983 
Z7_COM7F_IMF(CHandler::GetStream (UInt32 index,ISequentialInStream ** stream))984 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
985 {
986   COM_TRY_BEGIN
987   *stream = NULL;
988 
989   const CPartition &item = _items[index];
990 
991   if (item.first_extent_index > Extents.Size()
992       || item.num_extents > Extents.Size() - item.first_extent_index)
993     return S_FALSE;
994 
995   if (item.num_extents == 0)
996     return CreateLimitedInStream(_stream, 0, 0, stream);
997 
998   if (item.num_extents == 1)
999   {
1000     const CExtent &e = Extents[item.first_extent_index];
1001     if (e.IsRAW())
1002     {
1003       const UInt64 pos = e.target_data << kSectorSizeLog;
1004       if ((pos >> kSectorSizeLog) != e.target_data)
1005         return S_FALSE;
1006       const UInt64 size = item.GetSize();
1007       if (pos + size < pos)
1008         return S_FALSE;
1009       return CreateLimitedInStream(_stream, pos, size, stream);
1010     }
1011   }
1012 
1013   CExtentsStream *extentStreamSpec = new CExtentsStream();
1014   CMyComPtr<ISequentialInStream> extentStream = extentStreamSpec;
1015 
1016   // const unsigned kNumDebugExtents = 10;
1017   extentStreamSpec->Extents.Reserve(item.num_extents + 1
1018       // + kNumDebugExtents
1019       );
1020 
1021   UInt64 virt = 0;
1022   for (UInt32 k = 0; k < item.num_extents; k++)
1023   {
1024     const CExtent &e = Extents[item.first_extent_index + k];
1025 
1026     CSeekExtent se;
1027     {
1028       const UInt64 numSectors = e.num_sectors;
1029       if (numSectors == 0)
1030       {
1031         continue;
1032         // return S_FALSE;
1033       }
1034       const UInt64 numBytes = numSectors << kSectorSizeLog;
1035       if ((numBytes >> kSectorSizeLog) != numSectors)
1036         return S_FALSE;
1037       if (numBytes >= ((UInt64)1 << 63) - virt)
1038         return S_FALSE;
1039 
1040       se.Virt = virt;
1041       virt += numBytes;
1042     }
1043 
1044     const UInt64 phySector = e.target_data;
1045     if (e.target_type == LP_TARGET_TYPE_ZERO)
1046     {
1047       if (phySector != 0)
1048         return S_FALSE;
1049       se.SetAs_ZeroFill();
1050     }
1051     else if (e.target_type == LP_TARGET_TYPE_LINEAR)
1052     {
1053       se.Phy = phySector << kSectorSizeLog;
1054       if ((se.Phy >> kSectorSizeLog) != phySector)
1055         return S_FALSE;
1056       if (se.Phy >= ((UInt64)1 << 63))
1057         return S_FALSE;
1058     }
1059     else
1060       return S_FALSE;
1061 
1062     extentStreamSpec->Extents.AddInReserved(se);
1063 
1064     /*
1065     {
1066       // for debug
1067       const UInt64 kAdd = (e.num_sectors << kSectorSizeLog) / kNumDebugExtents;
1068       for (unsigned i = 0; i < kNumDebugExtents; i++)
1069       {
1070         se.Phy += kAdd;
1071         // se.Phy += (UInt64)1 << 63; // for debug
1072         // se.Phy += 1; // for debug
1073         se.Virt += kAdd;
1074         extentStreamSpec->Extents.AddInReserved(se);
1075       }
1076     }
1077     */
1078   }
1079 
1080   CSeekExtent se;
1081   se.Phy = 0;
1082   se.Virt = virt;
1083   extentStreamSpec->Extents.Add(se);
1084   extentStreamSpec->Stream = _stream;
1085   extentStreamSpec->Init();
1086   *stream = extentStream.Detach();
1087 
1088   return S_OK;
1089   COM_TRY_END
1090 }
1091 
1092 
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))1093 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1094     Int32 testMode, IArchiveExtractCallback *extractCallback))
1095 {
1096   COM_TRY_BEGIN
1097   const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1098   if (allFilesMode)
1099     numItems = _items.Size();
1100   if (numItems == 0)
1101     return S_OK;
1102   UInt64 totalSize = 0;
1103   UInt32 i;
1104   for (i = 0; i < numItems; i++)
1105   {
1106     const UInt32 index = allFilesMode ? i : indices[i];
1107     totalSize += _items[index].GetSize();
1108   }
1109   extractCallback->SetTotal(totalSize);
1110 
1111   totalSize = 0;
1112 
1113   NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1114   CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1115 
1116   CLocalProgress *lps = new CLocalProgress;
1117   CMyComPtr<ICompressProgressInfo> progress = lps;
1118   lps->Init(extractCallback, false);
1119 
1120   for (i = 0; i < numItems; i++)
1121   {
1122     lps->InSize = totalSize;
1123     lps->OutSize = totalSize;
1124     RINOK(lps->SetCur())
1125     CMyComPtr<ISequentialOutStream> outStream;
1126     const Int32 askMode = testMode ?
1127         NExtract::NAskMode::kTest :
1128         NExtract::NAskMode::kExtract;
1129     const UInt32 index = allFilesMode ? i : indices[i];
1130 
1131     RINOK(extractCallback->GetStream(index, &outStream, askMode))
1132 
1133     const UInt64 size = _items[index].GetSize();
1134     totalSize += size;
1135     if (!testMode && !outStream)
1136       continue;
1137 
1138     RINOK(extractCallback->PrepareOperation(askMode))
1139 
1140     CMyComPtr<ISequentialInStream> inStream;
1141     const HRESULT hres = GetStream(index, &inStream);
1142     int opRes = NExtract::NOperationResult::kUnsupportedMethod;
1143     if (hres != S_FALSE)
1144     {
1145       if (hres != S_OK)
1146         return hres;
1147       RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
1148       opRes = NExtract::NOperationResult::kDataError;
1149       if (copyCoderSpec->TotalSize == size)
1150         opRes = NExtract::NOperationResult::kOK;
1151       else if (copyCoderSpec->TotalSize < size)
1152         opRes = NExtract::NOperationResult::kUnexpectedEnd;
1153     }
1154     outStream.Release();
1155     RINOK(extractCallback->SetOperationResult(opRes))
1156   }
1157 
1158   return S_OK;
1159   COM_TRY_END
1160 }
1161 
1162 
1163 REGISTER_ARC_I(
1164   "LP", "lpimg img", NULL, 0xc1,
1165   k_Signature,
1166   LP_PARTITION_RESERVED_BYTES,
1167   0,
1168   NULL)
1169 
1170 }}
1171