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 CSha256 sha;
464 Sha256_Init(&sha);
465 Sha256_Update(&sha, data, size);
466 Byte calced[32];
467 Sha256_Final(&sha, calced);
468 return memcmp(checksum, calced, 32) == 0;
469 }
470
CheckSha256_csOffset(Byte * data,size_t size,unsigned hashOffset)471 static bool CheckSha256_csOffset(Byte *data, size_t size, unsigned hashOffset)
472 {
473 Byte checksum[32];
474 Byte *shaData = &data[hashOffset];
475 memcpy(checksum, shaData, 32);
476 memset(shaData, 0, 32);
477 return CheckSha256(data, size, checksum);
478 }
479
480
481
482 Z7_CLASS_IMP_CHandler_IInArchive_1(
483 IInArchiveGetStream
484 )
485 CRecordVector<CPartition> _items;
486 CRecordVector<CExtent> Extents;
487
488 CMyComPtr<IInStream> _stream;
489 UInt64 _totalSize;
490 // UInt64 _usedSize;
491 // UInt64 _headersSize;
492
493 CGeometry geom;
494 UInt16 Major_version;
495 UInt16 Minor_version;
496 UInt32 Flags;
497
498 Int32 _mainFileIndex;
499 UInt32 MethodsMask;
500 bool _headerWarning;
501 AString GroupsString;
502 AString DevicesString;
503 AString DeviceArcName;
504
505 HRESULT Open2(IInStream *stream);
506 };
507
508
AddComment_UInt64(AString & s,const char * name,UInt64 val)509 static void AddComment_UInt64(AString &s, const char *name, UInt64 val)
510 {
511 s.Add_Space();
512 s += name;
513 s += '=';
514 s.Add_UInt64(val);
515 }
516
517
IsBufZero(const Byte * data,size_t size)518 static bool IsBufZero(const Byte *data, size_t size)
519 {
520 for (size_t i = 0; i < size; i += 4)
521 if (*(const UInt32 *)(const void *)(data + i) != 0)
522 return false;
523 return true;
524 }
525
526
Open2(IInStream * stream)527 HRESULT CHandler::Open2(IInStream *stream)
528 {
529 RINOK(InStream_SeekSet(stream, LP_PARTITION_RESERVED_BYTES))
530 {
531 Byte buf[k_Geometry_Size];
532 RINOK(ReadStream_FALSE(stream, buf, k_Geometry_Size))
533 if (memcmp(buf, k_Signature, k_SignatureSize) != 0)
534 return S_FALSE;
535 if (!geom.Parse(buf))
536 return S_FALSE;
537 if (!CheckSha256_csOffset(buf, k_Geometry_Size, 8))
538 return S_FALSE;
539 }
540
541 CByteBuffer buffer;
542 RINOK(InStream_SeekToBegin(stream))
543 buffer.Alloc(LP_METADATA_GEOMETRY_SIZE * 2);
544 {
545 // buffer.Size() >= LP_PARTITION_RESERVED_BYTES
546 RINOK(ReadStream_FALSE(stream, buffer, LP_PARTITION_RESERVED_BYTES))
547 if (!IsBufZero(buffer, LP_PARTITION_RESERVED_BYTES))
548 {
549 _headerWarning = true;
550 // return S_FALSE;
551 }
552 }
553
554 RINOK(ReadStream_FALSE(stream, buffer, LP_METADATA_GEOMETRY_SIZE * 2))
555 // we check that 2 copies of GEOMETRY are identical:
556 if (memcmp(buffer, buffer + LP_METADATA_GEOMETRY_SIZE, LP_METADATA_GEOMETRY_SIZE) != 0
557 || !IsBufZero(buffer + k_Geometry_Size, LP_METADATA_GEOMETRY_SIZE - k_Geometry_Size))
558 {
559 _headerWarning = true;
560 // return S_FALSE;
561 }
562
563 RINOK(ReadStream_FALSE(stream, buffer, k_LpMetadataHeader10_size))
564 LpMetadataHeader header;
565 header.Parse128(buffer);
566 if (header.magic != LP_METADATA_HEADER_MAGIC ||
567 header.major_version != LP_METADATA_MAJOR_VERSION ||
568 header.header_size < k_LpMetadataHeader10_size)
569 return S_FALSE;
570 Flags = 0;
571 if (header.header_size > k_LpMetadataHeader10_size)
572 {
573 if (header.header_size != k_LpMetadataHeader12_size)
574 return S_FALSE;
575 RINOK(ReadStream_FALSE(stream, buffer + k_LpMetadataHeader10_size,
576 header.header_size - k_LpMetadataHeader10_size))
577 Flags = Get32(buffer + k_LpMetadataHeader10_size);
578 }
579 Major_version = header.major_version;
580 Minor_version = header.minor_version;
581
582 if (!CheckSha256_csOffset(buffer, header.header_size, 12))
583 return S_FALSE;
584
585 if (geom.metadata_max_size < header.tables_size ||
586 geom.metadata_max_size - header.tables_size < header.header_size)
587 return S_FALSE;
588
589 buffer.AllocAtLeast(header.tables_size);
590 RINOK(ReadStream_FALSE(stream, buffer, header.tables_size))
591
592 const UInt64 totalMetaSize = geom.GetTotalMetadataSize();
593 // _headersSize = _totalSize;
594 _totalSize = totalMetaSize;
595
596 if (!CheckSha256(buffer, header.tables_size, header.tables_checksum))
597 return S_FALSE;
598
599 {
600 const CDescriptor &d = header.partitions;
601 if (!d.CheckLimits(header.tables_size))
602 return S_FALSE;
603 if (d.entry_size != k_MetaPartition_Size)
604 return S_FALSE;
605 for (UInt32 i = 0; i < d.num_entries; i++)
606 {
607 CPartition part;
608 part.Parse(buffer + d.offset + i * d.entry_size);
609 const UInt32 extLimit = part.first_extent_index + part.num_extents;
610 if (extLimit < part.first_extent_index ||
611 extLimit > header.extents.num_entries ||
612 part.group_index >= header.groups.num_entries)
613 return S_FALSE;
614 _items.Add(part);
615 }
616 }
617 {
618 const CDescriptor &d = header.extents;
619 if (!d.CheckLimits(header.tables_size))
620 return S_FALSE;
621 if (d.entry_size != k_MetaExtent_Size)
622 return S_FALSE;
623 for (UInt32 i = 0; i < d.num_entries; i++)
624 {
625 CExtent e;
626 e.Parse(buffer + d.offset + i * d.entry_size);
627 // if (e.target_type > LP_TARGET_TYPE_ZERO) return S_FALSE;
628 if (e.IsRAW())
629 {
630 if (e.target_source >= header.block_devices.num_entries)
631 return S_FALSE;
632 const UInt64 endSector = e.target_data + e.num_sectors;
633 const UInt64 endOffset = endSector << kSectorSizeLog;
634 if (_totalSize < endOffset)
635 _totalSize = endOffset;
636 }
637 MethodsMask |= (UInt32)1 << e.target_type;
638 Extents.Add(e);
639 }
640 }
641
642 // _usedSize = _totalSize;
643 {
644 const CDescriptor &d = header.groups;
645 if (!d.CheckLimits(header.tables_size))
646 return S_FALSE;
647 if (d.entry_size != k_Group_Size)
648 return S_FALSE;
649 AString s;
650 for (UInt32 i = 0; i < d.num_entries; i++)
651 {
652 CGroup g;
653 g.Parse(buffer + d.offset + i * d.entry_size);
654 if (_totalSize < g.maximum_size)
655 _totalSize = g.maximum_size;
656 s += " ";
657 AddName36ToString(s, g.name, true);
658 AddComment_UInt64(s, "maximum_size", g.maximum_size);
659 AddComment_UInt64(s, "flags", g.flags);
660 s.Add_LF();
661 }
662 GroupsString = s;
663 }
664
665 {
666 const CDescriptor &d = header.block_devices;
667 if (!d.CheckLimits(header.tables_size))
668 return S_FALSE;
669 if (d.entry_size != k_Device_Size)
670 return S_FALSE;
671 AString s;
672 // CRecordVector<CDevice> devices;
673 for (UInt32 i = 0; i < d.num_entries; i++)
674 {
675 CDevice v;
676 v.Parse(buffer + d.offset + i * d.entry_size);
677 // if (i == 0)
678 {
679 // it's super_device is first device;
680 if (totalMetaSize > (v.first_logical_sector << kSectorSizeLog))
681 return S_FALSE;
682 }
683 if (_totalSize < v.size)
684 _totalSize = v.size;
685 s += " ";
686 if (i == 0)
687 AddName36ToString(DeviceArcName, v.partition_name, true);
688 // devices.Add(v);
689 AddName36ToString(s, v.partition_name, true);
690 AddComment_UInt64(s, "size", v.size);
691 AddComment_UInt64(s, "first_logical_sector", v.first_logical_sector);
692 AddComment_UInt64(s, "alignment", v.alignment);
693 AddComment_UInt64(s, "alignment_offset", v.alignment_offset);
694 AddComment_UInt64(s, "flags", v.flags);
695 s.Add_LF();
696 }
697 DevicesString = s;
698 }
699
700 {
701 FOR_VECTOR (i, _items)
702 {
703 CPartition &part = _items[i];
704 if (part.first_extent_index > Extents.Size() ||
705 part.num_extents > Extents.Size() - part.first_extent_index)
706 return S_FALSE;
707
708 UInt64 numSectors = 0;
709 UInt64 numSectors_Pack = 0;
710 UInt32 methods = 0;
711 for (UInt32 k = 0; k < part.num_extents; k++)
712 {
713 const CExtent &e = Extents[part.first_extent_index + k];
714 numSectors += e.num_sectors;
715 if (e.IsRAW())
716 numSectors_Pack += e.num_sectors;
717 methods |= (UInt32)1 << e.target_type;
718 }
719 part.NumSectors = numSectors;
720 part.NumSectors_Pack = numSectors_Pack;
721 part.MethodsMask = methods;
722 }
723 }
724
725 return S_OK;
726 }
727
728
Z7_COM7F_IMF(CHandler::Open (IInStream * stream,const UInt64 *,IArchiveOpenCallback *))729 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
730 const UInt64 * /* maxCheckStartPosition */,
731 IArchiveOpenCallback * /* openArchiveCallback */))
732 {
733 COM_TRY_BEGIN
734 Close();
735 RINOK(Open2(stream))
736 _stream = stream;
737
738 int mainFileIndex = -1;
739 unsigned numNonEmptyParts = 0;
740
741 FOR_VECTOR (fileIndex, _items)
742 {
743 CPartition &item = _items[fileIndex];
744 if (item.NumSectors != 0)
745 {
746 mainFileIndex = (int)fileIndex;
747 numNonEmptyParts++;
748 CMyComPtr<ISequentialInStream> parseStream;
749 if (GetStream(fileIndex, &parseStream) == S_OK && parseStream)
750 {
751 const size_t kParseSize = 1 << 11;
752 Byte buf[kParseSize];
753 if (ReadStream_FAIL(parseStream, buf, kParseSize) == S_OK)
754 {
755 UInt64 extSize;
756 if (NExt::IsArc_Ext_PhySize(buf, kParseSize, &extSize) == k_IsArc_Res_YES)
757 if (extSize == item.GetSize())
758 item.Ext = "ext";
759 }
760 }
761 }
762 }
763 if (numNonEmptyParts == 1)
764 _mainFileIndex = mainFileIndex;
765
766 return S_OK;
767 COM_TRY_END
768 }
769
770
Z7_COM7F_IMF(CHandler::Close ())771 Z7_COM7F_IMF(CHandler::Close())
772 {
773 _totalSize = 0;
774 // _usedSize = 0;
775 // _headersSize = 0;
776 _items.Clear();
777 Extents.Clear();
778 _stream.Release();
779 _mainFileIndex = -1;
780 _headerWarning = false;
781 MethodsMask = 0;
782 GroupsString.Empty();
783 DevicesString.Empty();
784 DeviceArcName.Empty();
785 return S_OK;
786 }
787
788
789 static const Byte kProps[] =
790 {
791 kpidPath,
792 kpidSize,
793 kpidPackSize,
794 kpidCharacts,
795 kpidMethod,
796 kpidNumBlocks,
797 kpidOffset
798 };
799
800 static const Byte kArcProps[] =
801 {
802 kpidUnpackVer,
803 kpidMethod,
804 kpidClusterSize,
805 // kpidHeadersSize,
806 // kpidFreeSpace,
807 kpidName,
808 kpidComment
809 };
810
811 IMP_IInArchive_Props
812 IMP_IInArchive_ArcProps
813
Z7_COM7F_IMF(CHandler::GetArchiveProperty (PROPID propID,PROPVARIANT * value))814 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
815 {
816 COM_TRY_BEGIN
817 NCOM::CPropVariant prop;
818 switch (propID)
819 {
820 case kpidMainSubfile:
821 {
822 if (_mainFileIndex >= 0)
823 prop = (UInt32)_mainFileIndex;
824 break;
825 }
826 case kpidPhySize: prop = _totalSize; break;
827
828 // case kpidFreeSpace: if (_usedSize != 0) prop = _totalSize - _usedSize; break;
829 // case kpidHeadersSize: prop = _headersSize; break;
830
831 case kpidMethod:
832 {
833 const UInt32 m = MethodsMask;
834 if (m != 0)
835 {
836 FLAGS_TO_PROP(g_Methods, m, prop);
837 }
838 break;
839 }
840
841 case kpidUnpackVer:
842 {
843 AString s;
844 s.Add_UInt32(Major_version);
845 s.Add_Dot();
846 s.Add_UInt32(Minor_version);
847 prop = s;
848 break;
849 }
850
851 case kpidClusterSize:
852 prop = geom.logical_block_size;
853 break;
854
855 case kpidComment:
856 {
857 AString s;
858
859 s += "metadata_slot_count: ";
860 s.Add_UInt32(geom.metadata_slot_count);
861 s.Add_LF();
862
863 s += "metadata_max_size: ";
864 s.Add_UInt32(geom.metadata_max_size);
865 s.Add_LF();
866
867 if (Flags != 0)
868 {
869 s += "flags: ";
870 s += FlagsToString(g_Header_Flags, Z7_ARRAY_SIZE(g_Header_Flags), Flags);
871 s.Add_LF();
872 }
873
874 if (!GroupsString.IsEmpty())
875 {
876 s += "Groups:";
877 s.Add_LF();
878 s += GroupsString;
879 }
880
881 if (!DevicesString.IsEmpty())
882 {
883 s += "BlockDevices:";
884 s.Add_LF();
885 s += DevicesString;
886 }
887
888 if (!s.IsEmpty())
889 prop = s;
890 break;
891 }
892
893 case kpidName:
894 if (!DeviceArcName.IsEmpty())
895 prop = DeviceArcName + ".lpimg";
896 break;
897
898 case kpidWarningFlags:
899 if (_headerWarning)
900 {
901 UInt32 v = kpv_ErrorFlags_HeadersError;
902 prop = v;
903 }
904 break;
905 }
906 prop.Detach(value);
907 return S_OK;
908 COM_TRY_END
909 }
910
911
Z7_COM7F_IMF(CHandler::GetNumberOfItems (UInt32 * numItems))912 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
913 {
914 *numItems = _items.Size();
915 return S_OK;
916 }
917
918
Z7_COM7F_IMF(CHandler::GetProperty (UInt32 index,PROPID propID,PROPVARIANT * value))919 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
920 {
921 COM_TRY_BEGIN
922 NCOM::CPropVariant prop;
923
924 const CPartition &item = _items[index];
925
926 switch (propID)
927 {
928 case kpidPath:
929 {
930 AString s;
931 AddName36ToString(s, item.name, false);
932 if (s.IsEmpty())
933 s.Add_UInt32(index);
934 if (item.num_extents != 0)
935 {
936 s.Add_Dot();
937 s += (item.Ext ? item.Ext : "img");
938 }
939 prop = s;
940 break;
941 }
942
943 case kpidSize: prop = item.GetSize(); break;
944 case kpidPackSize: prop = item.GetPackSize(); break;
945 case kpidNumBlocks: prop = item.num_extents; break;
946 case kpidMethod:
947 {
948 const UInt32 m = item.MethodsMask;
949 if (m != 0)
950 {
951 FLAGS_TO_PROP(g_Methods, m, prop);
952 }
953 break;
954 }
955 case kpidOffset:
956 if (item.num_extents != 0)
957 if (item.first_extent_index < Extents.Size())
958 prop = Extents[item.first_extent_index].target_data << kSectorSizeLog;
959 break;
960
961 case kpidCharacts:
962 {
963 AString s;
964 s += "group:";
965 s.Add_UInt32(item.group_index);
966 s.Add_Space();
967 s += FlagsToString(g_PartitionAttr, Z7_ARRAY_SIZE(g_PartitionAttr), item.attributes);
968 prop = s;
969 break;
970 }
971 }
972
973 prop.Detach(value);
974 return S_OK;
975 COM_TRY_END
976 }
977
978
979
Z7_COM7F_IMF(CHandler::GetStream (UInt32 index,ISequentialInStream ** stream))980 Z7_COM7F_IMF(CHandler::GetStream(UInt32 index, ISequentialInStream **stream))
981 {
982 COM_TRY_BEGIN
983 *stream = NULL;
984
985 const CPartition &item = _items[index];
986
987 if (item.first_extent_index > Extents.Size()
988 || item.num_extents > Extents.Size() - item.first_extent_index)
989 return S_FALSE;
990
991 if (item.num_extents == 0)
992 return CreateLimitedInStream(_stream, 0, 0, stream);
993
994 if (item.num_extents == 1)
995 {
996 const CExtent &e = Extents[item.first_extent_index];
997 if (e.IsRAW())
998 {
999 const UInt64 pos = e.target_data << kSectorSizeLog;
1000 if ((pos >> kSectorSizeLog) != e.target_data)
1001 return S_FALSE;
1002 const UInt64 size = item.GetSize();
1003 if (pos + size < pos)
1004 return S_FALSE;
1005 return CreateLimitedInStream(_stream, pos, size, stream);
1006 }
1007 }
1008
1009 CExtentsStream *extentStreamSpec = new CExtentsStream();
1010 CMyComPtr<ISequentialInStream> extentStream = extentStreamSpec;
1011
1012 // const unsigned kNumDebugExtents = 10;
1013 extentStreamSpec->Extents.Reserve(item.num_extents + 1
1014 // + kNumDebugExtents
1015 );
1016
1017 UInt64 virt = 0;
1018 for (UInt32 k = 0; k < item.num_extents; k++)
1019 {
1020 const CExtent &e = Extents[item.first_extent_index + k];
1021
1022 CSeekExtent se;
1023 {
1024 const UInt64 numSectors = e.num_sectors;
1025 if (numSectors == 0)
1026 {
1027 continue;
1028 // return S_FALSE;
1029 }
1030 const UInt64 numBytes = numSectors << kSectorSizeLog;
1031 if ((numBytes >> kSectorSizeLog) != numSectors)
1032 return S_FALSE;
1033 if (numBytes >= ((UInt64)1 << 63) - virt)
1034 return S_FALSE;
1035
1036 se.Virt = virt;
1037 virt += numBytes;
1038 }
1039
1040 const UInt64 phySector = e.target_data;
1041 if (e.target_type == LP_TARGET_TYPE_ZERO)
1042 {
1043 if (phySector != 0)
1044 return S_FALSE;
1045 se.SetAs_ZeroFill();
1046 }
1047 else if (e.target_type == LP_TARGET_TYPE_LINEAR)
1048 {
1049 se.Phy = phySector << kSectorSizeLog;
1050 if ((se.Phy >> kSectorSizeLog) != phySector)
1051 return S_FALSE;
1052 if (se.Phy >= ((UInt64)1 << 63))
1053 return S_FALSE;
1054 }
1055 else
1056 return S_FALSE;
1057
1058 extentStreamSpec->Extents.AddInReserved(se);
1059
1060 /*
1061 {
1062 // for debug
1063 const UInt64 kAdd = (e.num_sectors << kSectorSizeLog) / kNumDebugExtents;
1064 for (unsigned i = 0; i < kNumDebugExtents; i++)
1065 {
1066 se.Phy += kAdd;
1067 // se.Phy += (UInt64)1 << 63; // for debug
1068 // se.Phy += 1; // for debug
1069 se.Virt += kAdd;
1070 extentStreamSpec->Extents.AddInReserved(se);
1071 }
1072 }
1073 */
1074 }
1075
1076 CSeekExtent se;
1077 se.Phy = 0;
1078 se.Virt = virt;
1079 extentStreamSpec->Extents.Add(se);
1080 extentStreamSpec->Stream = _stream;
1081 extentStreamSpec->Init();
1082 *stream = extentStream.Detach();
1083
1084 return S_OK;
1085 COM_TRY_END
1086 }
1087
1088
Z7_COM7F_IMF(CHandler::Extract (const UInt32 * indices,UInt32 numItems,Int32 testMode,IArchiveExtractCallback * extractCallback))1089 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
1090 Int32 testMode, IArchiveExtractCallback *extractCallback))
1091 {
1092 COM_TRY_BEGIN
1093 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
1094 if (allFilesMode)
1095 numItems = _items.Size();
1096 if (numItems == 0)
1097 return S_OK;
1098 UInt64 totalSize = 0;
1099 UInt32 i;
1100 for (i = 0; i < numItems; i++)
1101 {
1102 const UInt32 index = allFilesMode ? i : indices[i];
1103 totalSize += _items[index].GetSize();
1104 }
1105 extractCallback->SetTotal(totalSize);
1106
1107 totalSize = 0;
1108
1109 NCompress::CCopyCoder *copyCoderSpec = new NCompress::CCopyCoder();
1110 CMyComPtr<ICompressCoder> copyCoder = copyCoderSpec;
1111
1112 CLocalProgress *lps = new CLocalProgress;
1113 CMyComPtr<ICompressProgressInfo> progress = lps;
1114 lps->Init(extractCallback, false);
1115
1116 for (i = 0; i < numItems; i++)
1117 {
1118 lps->InSize = totalSize;
1119 lps->OutSize = totalSize;
1120 RINOK(lps->SetCur())
1121 CMyComPtr<ISequentialOutStream> outStream;
1122 const Int32 askMode = testMode ?
1123 NExtract::NAskMode::kTest :
1124 NExtract::NAskMode::kExtract;
1125 const UInt32 index = allFilesMode ? i : indices[i];
1126
1127 RINOK(extractCallback->GetStream(index, &outStream, askMode))
1128
1129 const UInt64 size = _items[index].GetSize();
1130 totalSize += size;
1131 if (!testMode && !outStream)
1132 continue;
1133
1134 RINOK(extractCallback->PrepareOperation(askMode))
1135
1136 CMyComPtr<ISequentialInStream> inStream;
1137 const HRESULT hres = GetStream(index, &inStream);
1138 int opRes = NExtract::NOperationResult::kUnsupportedMethod;
1139 if (hres != S_FALSE)
1140 {
1141 if (hres != S_OK)
1142 return hres;
1143 RINOK(copyCoder->Code(inStream, outStream, NULL, NULL, progress))
1144 opRes = NExtract::NOperationResult::kDataError;
1145 if (copyCoderSpec->TotalSize == size)
1146 opRes = NExtract::NOperationResult::kOK;
1147 else if (copyCoderSpec->TotalSize < size)
1148 opRes = NExtract::NOperationResult::kUnexpectedEnd;
1149 }
1150 outStream.Release();
1151 RINOK(extractCallback->SetOperationResult(opRes))
1152 }
1153
1154 return S_OK;
1155 COM_TRY_END
1156 }
1157
1158
1159 REGISTER_ARC_I(
1160 "LP", "lpimg img", NULL, 0xc1,
1161 k_Signature,
1162 LP_PARTITION_RESERVED_BYTES,
1163 0,
1164 NULL)
1165
1166 }}
1167