Skip to content

Commit

Permalink
lp: Sparse files are not required when packing and unpacking empty im…
Browse files Browse the repository at this point in the history
…ages

Empty images don't contain any extent metadata so the partition sizes
are just discarded.

Don't write parsers when you're tired, folks!

Signed-off-by: Andrew Gunnerson <[email protected]>
  • Loading branch information
chenxiaolong committed Aug 28, 2024
1 parent 7c38c56 commit e55cf3d
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 58 deletions.
8 changes: 2 additions & 6 deletions README.extra.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,26 +267,22 @@ This set of commands is for working with LP (logical partition) images. These ar
avbroot lp unpack -i <input LP image> [-i <input LP image>]...
```

This subcommand unpacks the LP metadata to `lp.toml` and the partition images to the `lp_images` directory.
This subcommand unpacks the LP metadata to `lp.toml` and the partition images to the `lp_images` directory (for normal images).

If there are multiple images, they must be specified in order. If the order is not known, run `avbroot lp info` on each of the images. The one that successfully parses is the first image and the `block_devices` field in the output specifies the full ordering.

An LP image can have multiple slots. If the LP image originated from a factory image or OTA, all slots are likely identical. If the LP image was dumped from a real device that installed OTA updates in the past, the slots may differ. If the slots are not identical, then the `--slot` option is required to specify which slot to unpack.

When unpacking an empty image, files are still created in the `lp_images` directory. These files are sparse files that don't contain any actual data, but do have the correct file size. They are necessary when packing a new LP image so that avbroot can determine the size of each partition.

### Packing an LP image

```bash
avbroot lp pack -o <output LP image> [-o <output LP image>]...
```

This subcommand packs a new LP image from the `lp.toml` file and `lp_images` directory. Any `.img` files in the `lp_images` directory that don't have a corresponding entry in `lp.toml` are silently ignored.
This subcommand packs a new LP image from the `lp.toml` file and `lp_images` directory (for normal images). Any `.img` files in the `lp_images` directory that don't have a corresponding entry in `lp.toml` are silently ignored.

All metadata slots in the newly packed LP image will be identical.

The partition images in `lp_images` are required even when packing an empty image. They can be sparse files that contain no data, but must have the proper size.

### Repacking an LP image

```bash
Expand Down
101 changes: 49 additions & 52 deletions avbroot/src/cli/lp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,11 @@ fn unpack_subcommand(lp_cli: &LpCli, cli: &UnpackCli, cancel_signal: &AtomicBool
display_metadata(lp_cli, &metadata);
write_info(&cli.output_info, &metadata)?;

// For empty images, there's no data to unpack.
if metadata.image_type == ImageType::Empty {
return Ok(());
}

let authority = ambient_authority();
Dir::create_ambient_dir_all(&cli.output_images, authority)
.with_context(|| format!("Failed to create directory: {:?}", cli.output_images))?;
Expand Down Expand Up @@ -234,12 +239,6 @@ fn unpack_subcommand(lp_cli: &LpCli, cli: &UnpackCli, cancel_signal: &AtomicBool
files.push(group_files);
}

// For empty images, the outputs have already been truncated and there's no
// data to unpack.
if metadata.image_type == ImageType::Empty {
return Ok(());
}

slot.groups
.par_iter()
.enumerate()
Expand Down Expand Up @@ -309,10 +308,6 @@ fn pack_subcommand(lp_cli: &LpCli, cli: &PackCli, cancel_signal: &AtomicBool) ->
}
}

let authority = ambient_authority();
let directory = Dir::open_ambient_dir(&cli.input_images, authority)
.with_context(|| format!("Failed to open directory: {:?}", cli.input_images))?;

for group in &slot.groups {
for partition in &group.partitions {
let name = &partition.name;
Expand All @@ -327,48 +322,54 @@ fn pack_subcommand(lp_cli: &LpCli, cli: &PackCli, cancel_signal: &AtomicBool) ->
let mut paths = vec![];
let mut files = vec![];

for group in &mut slot.groups {
let mut group_paths = vec![];
let mut group_files = vec![];
if metadata.image_type == ImageType::Normal {
let authority = ambient_authority();
let directory = Dir::open_ambient_dir(&cli.input_images, authority)
.with_context(|| format!("Failed to open directory: {:?}", cli.input_images))?;

for partition in &mut group.partitions {
let path = format!("{}.img", partition.name);
for group in &mut slot.groups {
let mut group_paths = vec![];
let mut group_files = vec![];

let mut file = directory
.open(&path)
.map(|f| PSeekFile::new(f.into_std()))
.with_context(|| format!("Failed to open for reading: {path:?}"))?;
for partition in &mut group.partitions {
let path = format!("{}.img", partition.name);

let size = file
.seek(SeekFrom::End(0))
.with_context(|| format!("Failed to seek file: {path:?}"))?;
let mut file = directory
.open(&path)
.map(|f| PSeekFile::new(f.into_std()))
.with_context(|| format!("Failed to open for reading: {path:?}"))?;

if size % u64::from(SECTOR_SIZE) != 0 {
bail!("File size is not {SECTOR_SIZE}B aligned: {size}: {path:?}");
}
let size = file
.seek(SeekFrom::End(0))
.with_context(|| format!("Failed to seek file: {path:?}"))?;

// This will be filled out properly later during reallocation.
partition.extents.push(Extent {
num_sectors: size / u64::from(SECTOR_SIZE),
extent_type: ExtentType::Linear {
start_sector: 0,
block_device_index: 0,
},
});
if size % u64::from(SECTOR_SIZE) != 0 {
bail!("File size is not {SECTOR_SIZE}B aligned: {size}: {path:?}");
}

group_paths.push(path);
group_files.push(file);
// This will be filled out properly later during reallocation.
partition.extents.push(Extent {
num_sectors: size / u64::from(SECTOR_SIZE),
extent_type: ExtentType::Linear {
start_sector: 0,
block_device_index: 0,
},
});

group_paths.push(path);
group_files.push(file);
}

paths.push(group_paths);
files.push(group_files);
}

paths.push(group_paths);
files.push(group_files);
// Now that we have all the partition sizes, actually allocate extents
// for them on the block devices.
slot.reallocate_extents()
.context("Failed to allocate extents")?;
}

// Now that we have all the partition sizes, actually allocate extents for
// them on the block devices.
slot.reallocate_extents()
.context("Failed to allocate extents")?;

// Display only the selected slot and make the rest identical.
let _ = slot;
display_metadata(lp_cli, &metadata);
Expand All @@ -380,8 +381,7 @@ fn pack_subcommand(lp_cli: &LpCli, cli: &PackCli, cancel_signal: &AtomicBool) ->
.to_writer(&mut outputs[0])
.with_context(|| format!("Failed to write LP image metadata: {:?}", cli.output[0]))?;

// For empty images, there's no data to pack. The inputs were only used for
// computing the extents.
// For empty images, there's no data to pack.
if metadata.image_type == ImageType::Empty {
return Ok(());
}
Expand Down Expand Up @@ -553,11 +553,9 @@ pub fn lp_main(cli: &LpCli, cancel_signal: &AtomicBool) -> Result<()> {

/// Unpack an LP image.
///
/// Each partition is extracted to `<partition name>.img` in the output images
/// directory. This is the case even when unpacking empty LP images. In this
/// scenario, the partition images are sparse files containing no data.
///
/// The LP image metadata is written to the info TOML file.
/// The LP image metadata is written to the info TOML file. For normal images,
/// each partition is extracted to `<partition name>.img` in the output images
/// directory. For empty images, the output images directory is unused.
///
/// If any partition names are unsafe to use in a path, the extraction process
/// will fail and exit. Extracted files are never written outside of the tree
Expand Down Expand Up @@ -599,9 +597,8 @@ struct UnpackCli {
/// of the value of `metadata_slot_count`, as required by the file format.
///
/// The new LP image will *only* contain images listed in the info TOML file and
/// they are added in the order listed. Note that the input partition images are
/// required even when packing an empty LP image. They can be sparse files that
/// contain no data, but must have the proper size.
/// they are added in the order listed. The input images directory is not used
/// when packing an empty image.
#[derive(Debug, Parser)]
struct PackCli {
/// Path to output LP images.
Expand Down

0 comments on commit e55cf3d

Please sign in to comment.