gpt_disk_io/
lib.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Library for reading and writing GPT disk data structures through a
10//! block IO interface.
11//!
12//! This crate adds a convenient interface for reading and writing the
13//! GPT types defined in the [`gpt_disk_types`] crate to a [`Disk`]. The
14//! [`Disk`] is represented by the [`BlockIo`] trait, which allows this
15//! library to be `no_std`. The [`BlockIo`] trait is designed to be
16//! compatible with very simple block APIs, such as [`EFI_BLOCK_IO_PROTOCOL`].
17//!
18//! The [`BlockIoAdapter`] type allows the disk to be backed by simple
19//! byte-oriented storage backends, such as `&mut [u8]` and `File` (the
20//! latter requires the `std` feature).
21//!
22//! # Features
23//!
24//! * `alloc`: Enables [`Vec`] implementation of [`BlockIoAdapter`].
25//! * `std`: Enables [`std::io`] implementations of [`BlockIoAdapter`].
26//!
27//! # Examples
28//!
29//! Construct a GPT disk in-memory backed by a `Vec`:
30//!
31//! ```
32//! use gpt_disk_io::{BlockIoAdapter, BlockIo, Disk, DiskError};
33//! use gpt_disk_types::{
34//!     guid, BlockSize, Crc32, GptHeader, GptPartitionEntry,
35//!     GptPartitionEntryArray, GptPartitionType, LbaLe, U32Le,
36//! };
37//!
38//! // Space for a 4MiB disk.
39//! let mut disk_storage = vec![0; 4 * 1024 * 1024];
40//!
41//! // Standard 512-byte block size.
42//! let bs = BlockSize::BS_512;
43//!
44//! // `BlockIoAdapter` implements the `BlockIo` trait which is used by
45//! // the `Disk` type for reading and writing.
46//! let block_io = BlockIoAdapter::new(disk_storage.as_mut_slice(), bs);
47//!
48//! let mut disk = Disk::new(block_io)?;
49//!
50//! // Manually construct the header and partition entries.
51//! let primary_header = GptHeader {
52//!     header_crc32: Crc32(U32Le::from_u32(0xa4877843)),
53//!     my_lba: LbaLe::from_u64(1),
54//!     alternate_lba: LbaLe::from_u64(8191),
55//!     first_usable_lba: LbaLe::from_u64(34),
56//!     last_usable_lba: LbaLe::from_u64(8158),
57//!     disk_guid: guid!("57a7feb6-8cd5-4922-b7bd-c78b0914e870"),
58//!     partition_entry_lba: LbaLe::from_u64(2),
59//!     number_of_partition_entries: U32Le::from_u32(128),
60//!     partition_entry_array_crc32: Crc32(U32Le::from_u32(0x9206adff)),
61//!     ..Default::default()
62//! };
63//! let secondary_header = GptHeader {
64//!     header_crc32: Crc32(U32Le::from_u32(0xdbeb4c13)),
65//!     my_lba: LbaLe::from_u64(8191),
66//!     alternate_lba: LbaLe::from_u64(1),
67//!     partition_entry_lba: LbaLe::from_u64(8159),
68//!     ..primary_header
69//! };
70//! let partition_entry = GptPartitionEntry {
71//!     partition_type_guid: GptPartitionType(guid!(
72//!         "ccf0994f-f7e0-4e26-a011-843e38aa2eac"
73//!     )),
74//!     unique_partition_guid: guid!("37c75ffd-8932-467a-9c56-8cf1f0456b12"),
75//!     starting_lba: LbaLe::from_u64(2048),
76//!     ending_lba: LbaLe::from_u64(4096),
77//!     attributes: Default::default(),
78//!     name: "hello world!".parse().unwrap(),
79//! };
80//!
81//! // Create a buffer the length of one block. A `Vec` is used here,
82//! // but any mutable byte slice with the right length will do.
83//! let mut block_buf = vec![0u8; bs.to_usize().unwrap()];
84//!
85//! // Write out the protective MBR and GPT headers. Note that without
86//! // the protective MBR, some tools won't recognize the disk as GPT.
87//! disk.write_protective_mbr(&mut block_buf)?;
88//! disk.write_primary_gpt_header(&primary_header, &mut block_buf)?;
89//! disk.write_secondary_gpt_header(&secondary_header, &mut block_buf)?;
90//!
91//! // Construct the partition entry array.
92//! let layout = primary_header.get_partition_entry_array_layout().unwrap();
93//! let mut bytes =
94//!     vec![0; layout.num_bytes_rounded_to_block_as_usize(bs).unwrap()];
95//! let mut entry_array =
96//!     GptPartitionEntryArray::new(layout, bs, &mut bytes).unwrap();
97//! *entry_array.get_partition_entry_mut(0).unwrap() = partition_entry;
98//!
99//! // Write the primary partition entry array.
100//! disk.write_gpt_partition_entry_array(&entry_array)?;
101//!
102//! // Write the secondary partition entry array.
103//! entry_array.set_start_lba(secondary_header.partition_entry_lba.into());
104//! disk.write_gpt_partition_entry_array(&entry_array)?;
105//!
106//! // Ensure all writes are flushed. This is not needed with the slice
107//! // backend, but is good practice for "real" IO. (The disk will also
108//! // flush when dropped, but any errors at that point are ignored.)
109//! disk.flush()?;
110//!
111//! # Ok::<(), gpt_disk_io::DiskError<gpt_disk_io::SliceBlockIoError>>(())
112//! ```
113//!
114//! [`File`]: std::fs::File
115//! [`Read`]: std::io::Read
116//! [`Seek`]: std::io::Seek
117//! [`Write`]: std::io::Write
118//! [`EFI_BLOCK_IO_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/13_Protocols_Media_Access.html#block-i-o-protocol
119
120#![cfg_attr(not(feature = "std"), no_std)]
121#![cfg_attr(docsrs, feature(doc_auto_cfg))]
122#![warn(missing_docs)]
123#![warn(trivial_casts)]
124#![warn(trivial_numeric_casts)]
125#![warn(unreachable_pub)]
126#![warn(unsafe_code)]
127#![warn(clippy::pedantic)]
128#![warn(clippy::as_conversions)]
129#![allow(clippy::missing_errors_doc)]
130#![allow(clippy::missing_panics_doc)]
131
132#[cfg(feature = "alloc")]
133extern crate alloc;
134
135mod block_io;
136mod disk;
137
138// Re-export dependencies.
139pub use gpt_disk_types;
140
141pub use block_io::slice_block_io::SliceBlockIoError;
142pub use block_io::{BlockIo, BlockIoAdapter};
143pub use disk::{Disk, DiskError};
144
145#[cfg(feature = "std")]
146pub use block_io::std_block_io::ReadWriteSeek;