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