Skip to content

Commit 70e2811

Browse files
committed
add validation that partitions don't overlap
1 parent 2156737 commit 70e2811

File tree

2 files changed

+81
-26
lines changed

2 files changed

+81
-26
lines changed

espflash/src/error.rs

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::flasher::Command;
22
use crate::image_format::ImageFormatId;
33
use crate::Chip;
4-
use csv::Position;
54
use miette::{Diagnostic, SourceOffset, SourceSpan};
65
use slip_codec::Error as SlipError;
76
use std::fmt::{Display, Formatter};
@@ -266,15 +265,25 @@ impl<T> ResultExt for Result<T, Error> {
266265
}
267266
}
268267

268+
#[derive(Debug, Error, Diagnostic)]
269+
pub enum PartitionTableError {
270+
#[error(transparent)]
271+
#[diagnostic(transparent)]
272+
CSV(#[from] CSVError),
273+
#[error(transparent)]
274+
#[diagnostic(transparent)]
275+
Overlapping(#[from] OverlappingPartitionsError),
276+
}
277+
269278
#[derive(Debug, Error, Diagnostic)]
270279
#[error("Malformed partition table")]
271280
#[diagnostic(
272-
code(espflash::mallformed_partition_table),
281+
code(espflash::partition_table::mallformed),
273282
help("See the espressif documentation for information on the partition table format:
274283
275284
https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html#creating-custom-tables")
276285
)]
277-
pub struct PartitionTableError {
286+
pub struct CSVError {
278287
#[source_code]
279288
source: String,
280289
#[label("{}", self.hint)]
@@ -284,12 +293,12 @@ pub struct PartitionTableError {
284293
error: csv::Error,
285294
}
286295

287-
impl PartitionTableError {
296+
impl CSVError {
288297
pub fn new(error: csv::Error, source: String) -> Self {
289-
let err_pos = match error.kind() {
290-
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.clone(),
291-
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.clone(),
292-
_ => Position::new(),
298+
let err_line = match error.kind() {
299+
csv::ErrorKind::Deserialize { pos: Some(pos), .. } => pos.line(),
300+
csv::ErrorKind::UnequalLengths { pos: Some(pos), .. } => pos.line(),
301+
_ => 0,
293302
};
294303
let hint = match error.kind() {
295304
csv::ErrorKind::Deserialize { err, .. } => err.to_string(),
@@ -302,16 +311,9 @@ impl PartitionTableError {
302311
_ => String::new(),
303312
};
304313

305-
// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
306-
let line_length = source
307-
.lines()
308-
.nth(err_pos.line() as usize - 1)
309-
.unwrap()
310-
.len()
311-
.into();
312-
let err_span = SourceSpan::new(pos_to_offset(err_pos), line_length);
314+
let err_span = line_to_span(&source, err_line as usize);
313315

314-
PartitionTableError {
316+
CSVError {
315317
source,
316318
err_span,
317319
hint,
@@ -320,8 +322,34 @@ impl PartitionTableError {
320322
}
321323
}
322324

323-
fn pos_to_offset(pos: Position) -> SourceOffset {
324-
(pos.byte() as usize).into()
325+
/// since csv doesn't give us the position in the line the error occurs, we highlight the entire line
326+
///
327+
/// line starts at 1
328+
fn line_to_span(source: &str, line: usize) -> SourceSpan {
329+
let line_length = source.lines().nth(line - 1).unwrap().len().into();
330+
SourceSpan::new(SourceOffset::from_location(source, line, 2), line_length)
331+
}
332+
333+
#[derive(Debug, Error, Diagnostic)]
334+
#[error("Overlapping partitions")]
335+
#[diagnostic(code(espflash::partition_table::overlapping))]
336+
pub struct OverlappingPartitionsError {
337+
#[source_code]
338+
source_code: String,
339+
#[label("This partition")]
340+
partition1_span: SourceSpan,
341+
#[label("Overlaps with this partition")]
342+
partition2_span: SourceSpan,
343+
}
344+
345+
impl OverlappingPartitionsError {
346+
pub fn new(source: &str, partition1_line: usize, partition2_line: usize) -> Self {
347+
OverlappingPartitionsError {
348+
source_code: source.into(),
349+
partition1_span: line_to_span(&source, partition1_line),
350+
partition2_span: line_to_span(&source, partition2_line),
351+
}
352+
}
325353
}
326354

327355
#[derive(Debug, Error)]

espflash/src/partition_table.rs

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use md5::{Context, Digest};
22
use regex::Regex;
33
use serde::{Deserialize, Deserializer};
44

5-
use crate::error::PartitionTableError;
5+
use crate::error::{CSVError, OverlappingPartitionsError, PartitionTableError};
6+
use std::cmp::{max, min};
67
use std::io::Write;
78

89
const MAX_PARTITION_LENGTH: usize = 0xC00;
910
const PARTITION_TABLE_SIZE: usize = 0x1000;
10-
const MAX_PARTITION_TABLE_ENTRIES: usize = 95;
1111

1212
#[derive(Copy, Clone, Debug, Deserialize)]
1313
#[repr(u8)]
@@ -147,14 +147,20 @@ impl PartitionTable {
147147
.trim(csv::Trim::All)
148148
.from_reader(data.trim().as_bytes());
149149

150-
let mut partitions = Vec::with_capacity(MAX_PARTITION_TABLE_ENTRIES);
151-
for partition in reader.deserialize() {
152-
let partition: Partition =
153-
partition.map_err(|e| PartitionTableError::new(e, data.clone()))?;
150+
let mut partitions = Vec::with_capacity(data.lines().count());
151+
for record in reader.records() {
152+
let record = record.map_err(|e| CSVError::new(e, data.clone()))?;
153+
let position = record.position();
154+
let mut partition: Partition = record
155+
.deserialize(None)
156+
.map_err(|e| CSVError::new(e, data.clone()))?;
157+
partition.line = position.map(|pos| pos.line() as usize);
154158
partitions.push(partition);
155159
}
156160

157-
Ok(Self { partitions })
161+
let table = Self { partitions };
162+
table.validate(&data)?;
163+
Ok(table)
158164
}
159165

160166
pub fn to_bytes(&self) -> Vec<u8> {
@@ -184,6 +190,20 @@ impl PartitionTable {
184190

185191
Ok(())
186192
}
193+
194+
fn validate(&self, source: &str) -> Result<(), PartitionTableError> {
195+
for partition1 in &self.partitions {
196+
for partition2 in &self.partitions {
197+
if let (Some(line1), Some(line2)) = (&partition1.line, &partition2.line) {
198+
if line1 != line2 && partition1.overlaps(partition2) {
199+
return Err(OverlappingPartitionsError::new(source, *line1, *line2).into());
200+
}
201+
}
202+
}
203+
}
204+
205+
Ok(())
206+
}
187207
}
188208

189209
const PARTITION_SIZE: usize = 32;
@@ -199,6 +219,8 @@ struct Partition {
199219
#[serde(deserialize_with = "deserialize_partition_offset_or_size")]
200220
size: u32,
201221
flags: Option<u32>,
222+
#[serde(skip)]
223+
line: Option<usize>,
202224
}
203225

204226
impl Partition {
@@ -219,6 +241,7 @@ impl Partition {
219241
offset,
220242
size,
221243
flags,
244+
line: None,
222245
}
223246
}
224247

@@ -242,6 +265,10 @@ impl Partition {
242265

243266
Ok(())
244267
}
268+
269+
fn overlaps(&self, other: &Partition) -> bool {
270+
max(self.offset, other.offset) < min(self.offset + self.size, other.offset + other.size)
271+
}
245272
}
246273

247274
fn deserialize_partition_name<'de, D>(deserializer: D) -> Result<String, D::Error>

0 commit comments

Comments
 (0)