Skip to content

Commit e55fe53

Browse files
committed
degpt: add tool to parse gpt tables back to partition.xml
Import a tool to reparse gpt_bothN.bin tables into the partition.xml format. This is useful to restore the partition schema basing on binary files from the fastboot flashing package. Signed-off-by: Dmitry Baryshkov <[email protected]> Signed-off-by: Dmitry Baryshkov <[email protected]>
1 parent c1d6021 commit e55fe53

File tree

10 files changed

+384
-0
lines changed

10 files changed

+384
-0
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,7 @@ jobs:
1818
run: |
1919
make
2020
21+
- name: Run cargo
22+
run: |
23+
cd degpt
24+
cargo build

degpt/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

degpt/Cargo.lock

Lines changed: 88 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

degpt/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "degpt"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
gpt = "3.1"
10+
uuid = "1.8"

degpt/LICENCE

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
MIT License
2+
3+
Copyright (c) 2024 Linaro Ltd.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+
23+
------------------------------------------------------------------------
24+
25+
The GPT implementation is based on the gpt crate:
26+
27+
MIT License
28+
29+
Copyright (c) 2017 Quyzi
30+
31+
Permission is hereby granted, free of charge, to any person obtaining a copy
32+
of this software and associated documentation files (the "Software"), to deal
33+
in the Software without restriction, including without limitation the rights
34+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35+
copies of the Software, and to permit persons to whom the Software is
36+
furnished to do so, subject to the following conditions:
37+
38+
The above copyright notice and this permission notice shall be included in all
39+
copies or substantial portions of the Software.
40+
41+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
46+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
47+
SOFTWARE.

degpt/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod mygpt;

degpt/src/main.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use std::env;
2+
use std::error::Error;
3+
use std::fs;
4+
use std::fs::File;
5+
use std::io::Write;
6+
7+
use degpt::mygpt::*;
8+
9+
fn main() -> Result<(), Box<dyn Error>> {
10+
let mut args = env::args().skip(1);
11+
12+
let mut xml;
13+
if let Some(name) = args.next() {
14+
xml = File::create(name)?;
15+
} else {
16+
return Err(format!(
17+
"usage: {} partition.xml gpt_both*.bin",
18+
env::args().next().unwrap()
19+
)
20+
.into());
21+
}
22+
23+
xml.write_all(
24+
concat!(
25+
"<?xml version=\"1.0\"?>\n",
26+
"<configuration>\n",
27+
" <parser_instructions>\n",
28+
" WRITE_PROTECT_BOUNDARY_IN_KB=0\n",
29+
" SECTOR_SIZE_IN_BYTES = 4096\n",
30+
" GROW_LAST_PARTITION_TO_FILL_DISK=true\n",
31+
" </parser_instructions>\n",
32+
"\n",
33+
)
34+
.as_bytes(),
35+
)?;
36+
37+
for gpt in args {
38+
let bytes = fs::read(&gpt)?;
39+
let hdr = Header::try_from(&bytes[0x1000..0x2000])?;
40+
41+
println!("{gpt}");
42+
println!("{hdr:#}");
43+
44+
xml.write_all(" <physical_partition>\n".as_bytes())?;
45+
for i in 0..hdr.num_parts {
46+
let offset = 0x2000 + i as usize * 0x80;
47+
let part = Partition::try_from(&bytes[offset..offset + 0x80])?;
48+
if part.part_guid.is_nil() {
49+
continue;
50+
}
51+
println!("{part:#}");
52+
let size = (part.last_lba + 1 - part.first_lba) * 4096 / 1024;
53+
let readonly: bool = part.flags & (1 << 60) != 0;
54+
xml.write_all(
55+
format!(
56+
" <partition label=\"{}\" size_in_kb=\"{}\" type=\"{}\" bootable=\"false\" readonly=\"{}\" filename=\"\" />\n",
57+
part.name,
58+
size,
59+
part.part_type_guid.guid.to_string().to_uppercase(),
60+
readonly,
61+
)
62+
.as_bytes(),
63+
)?;
64+
}
65+
66+
xml.write_all(" </physical_partition>\n\n".as_bytes())?;
67+
}
68+
69+
xml.write_all("</configuration>\n".as_bytes())?;
70+
71+
Ok(())
72+
}

degpt/src/mygpt.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod header;
2+
pub mod partition;
3+
4+
pub use header::Header;
5+
pub use partition::Partition;
6+
pub use partition::Type;

degpt/src/mygpt/header.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2017 Quyzi
3+
// Copyright (c) 2024 Linaro Ltd.
4+
5+
use std::error::Error;
6+
use std::fmt;
7+
use std::io::Cursor;
8+
9+
use gpt::header::parse_uuid;
10+
11+
/// Header describing a GPT disk.
12+
#[derive(Clone, Debug, Eq, PartialEq)]
13+
pub struct Header {
14+
/// GPT header magic signature, hardcoded to "EFI PART".
15+
pub signature: String, // Offset 0. "EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h
16+
/// 00 00 01 00
17+
pub revision: u32, // Offset 8
18+
/// little endian
19+
pub header_size_le: u32, // Offset 12
20+
/// CRC32 of the header with crc32 section zeroed
21+
pub crc32: u32, // Offset 16
22+
/// must be 0
23+
pub reserved: u32, // Offset 20
24+
/// For main header, 1
25+
pub current_lba: u64, // Offset 24
26+
/// LBA for backup header
27+
pub backup_lba: u64, // Offset 32
28+
/// First usable LBA for partitions (primary table last LBA + 1)
29+
pub first_usable: u64, // Offset 40
30+
/// Last usable LBA (secondary partition table first LBA - 1)
31+
pub last_usable: u64, // Offset 48
32+
/// UUID of the disk
33+
pub disk_guid: uuid::Uuid, // Offset 56
34+
/// Starting LBA of partition entries
35+
pub part_start: u64, // Offset 72
36+
/// Number of partition entries
37+
pub num_parts: u32, // Offset 80
38+
/// Size of a partition entry, usually 128
39+
pub part_size: u32, // Offset 84
40+
/// CRC32 of the partition table
41+
pub crc32_parts: u32, // Offset 88
42+
}
43+
44+
impl fmt::Display for Header {
45+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46+
write!(
47+
f,
48+
"Disk:\t\t{}\nCRC32:\t\t{}\nTable CRC:\t{}",
49+
self.disk_guid, self.crc32, self.crc32_parts
50+
)
51+
}
52+
}
53+
54+
impl TryFrom<&[u8]> for Header {
55+
type Error = Box<dyn Error>;
56+
57+
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
58+
Ok(Header {
59+
signature: String::from_utf8(buf[0..8].into())?,
60+
revision: u32::from_le_bytes(buf[8..12].try_into()?),
61+
header_size_le: u32::from_le_bytes(buf[12..16].try_into()?),
62+
crc32: u32::from_le_bytes(buf[16..20].try_into()?),
63+
reserved: u32::from_le_bytes(buf[20..24].try_into()?),
64+
current_lba: u64::from_le_bytes(buf[24..32].try_into()?),
65+
backup_lba: u64::from_le_bytes(buf[32..40].try_into()?),
66+
first_usable: u64::from_le_bytes(buf[40..48].try_into()?),
67+
last_usable: u64::from_le_bytes(buf[48..56].try_into()?),
68+
disk_guid: parse_uuid(&mut Cursor::new(&buf[56..72]))?,
69+
part_start: u64::from_le_bytes(buf[72..80].try_into()?),
70+
num_parts: u32::from_le_bytes(buf[80..84].try_into()?),
71+
part_size: u32::from_le_bytes(buf[84..88].try_into()?),
72+
crc32_parts: u32::from_le_bytes(buf[88..92].try_into()?),
73+
})
74+
}
75+
}

degpt/src/mygpt/partition.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) 2017 Quyzi
3+
// Copyright (c) 2024 Linaro Ltd.
4+
use std::error::Error;
5+
use std::fmt;
6+
use std::io::Cursor;
7+
8+
use gpt::header::parse_uuid;
9+
use gpt::partition_types::OperatingSystem;
10+
11+
/// The type
12+
#[derive(Clone, Debug, Eq, PartialEq)]
13+
pub struct Type {
14+
/// Type-GUID for a GPT partition.
15+
pub guid: uuid::Uuid,
16+
/// well-known OS label for this type-GUID.
17+
pub os: OperatingSystem,
18+
}
19+
20+
/// A partition entry in a GPT partition table.
21+
#[derive(Clone, Debug, Eq, PartialEq)]
22+
pub struct Partition {
23+
/// GUID of the partition type.
24+
pub part_type_guid: Type,
25+
/// UUID of the partition.
26+
pub part_guid: uuid::Uuid,
27+
/// First LBA of the partition.
28+
pub first_lba: u64,
29+
/// Last LBA of the partition.
30+
pub last_lba: u64,
31+
/// Partition flags.
32+
pub flags: u64,
33+
/// Partition name.
34+
pub name: String,
35+
}
36+
37+
impl fmt::Display for Partition {
38+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39+
write!(
40+
f,
41+
"Partition:\t\t{}\nPartition GUID:\t\t{}\nPartition Type:\t\t{}\n\
42+
Span:\t\t\t{} - {}\nFlags:\t\t\t{}",
43+
self.name,
44+
self.part_guid,
45+
self.part_type_guid.guid,
46+
self.first_lba,
47+
self.last_lba,
48+
self.flags,
49+
)
50+
}
51+
}
52+
53+
fn get_utf16le(buf: &[u8]) -> Vec<u16> {
54+
let mut r = vec![];
55+
for c in buf.chunks(2) {
56+
let u = c[0] as u16 | ((c[1] as u16) << 8);
57+
if u != 0 {
58+
r.push(u);
59+
}
60+
}
61+
r
62+
}
63+
64+
impl TryFrom<&[u8]> for Partition {
65+
type Error = Box<dyn Error>;
66+
67+
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
68+
Ok(Partition {
69+
part_type_guid: Type {
70+
guid: parse_uuid(&mut Cursor::new(&buf[0..]))?,
71+
os: OperatingSystem::None,
72+
},
73+
part_guid: parse_uuid(&mut Cursor::new(&buf[16..]))?,
74+
first_lba: u64::from_le_bytes(buf[32..40].try_into()?),
75+
last_lba: u64::from_le_bytes(buf[40..48].try_into()?),
76+
flags: u64::from_le_bytes(buf[48..56].try_into()?),
77+
name: String::from_utf16(&get_utf16le(&buf[56..128]))?,
78+
})
79+
}
80+
}

0 commit comments

Comments
 (0)