Skip to main content

hadris_iso/write/
options.rs

1use alloc::string::String;
2
3use super::super::boot::options::BootOptions;
4use super::super::read::PathSeparator;
5use super::super::rrip::RripOptions;
6use crate::joliet::JolietLevel;
7
8/// Hybrid boot options for creating bootable ISO images from USB/disk.
9///
10/// This enables the ISO to be bootable when written directly to a USB drive
11/// or other storage media, in addition to being bootable as a CD/DVD.
12#[derive(Debug, Clone, Default)]
13pub struct HybridBootOptions {
14    /// The type of partition table to write.
15    pub partition_scheme: PartitionScheme,
16    /// Optional MBR bootstrap code to inject (must be 446 bytes or less).
17    /// This is typically the first stage of a bootloader like GRUB or Syslinux.
18    pub mbr_bootstrap: Option<alloc::vec::Vec<u8>>,
19    /// Whether to mark the ISO partition as bootable in the MBR.
20    pub bootable: bool,
21    /// Path (in the ISO tree, using the configured path separator) of the
22    /// El Torito UEFI boot image to additionally expose as a GPT EFI System
23    /// Partition.
24    ///
25    /// Applies to [`PartitionScheme::Gpt`] and [`PartitionScheme::Hybrid`].
26    /// When `None`, and the El Torito options contain exactly one
27    /// `PlatformId::UEFI` section entry, that entry's boot image is exposed
28    /// automatically. Formatting fails with a not-found error when the
29    /// configured path does not resolve to a file in the ISO tree.
30    pub efi_boot_partition: Option<String>,
31}
32
33impl HybridBootOptions {
34    /// Create options for MBR-only hybrid boot (BIOS systems).
35    pub fn mbr() -> Self {
36        Self {
37            partition_scheme: PartitionScheme::Mbr,
38            mbr_bootstrap: None,
39            bootable: true,
40            efi_boot_partition: None,
41        }
42    }
43
44    /// Create options for GPT-only boot (UEFI systems).
45    pub fn gpt() -> Self {
46        Self {
47            partition_scheme: PartitionScheme::Gpt,
48            mbr_bootstrap: None,
49            bootable: false,
50            efi_boot_partition: None,
51        }
52    }
53
54    /// Create options for hybrid MBR+GPT boot (dual BIOS/UEFI systems).
55    pub fn hybrid() -> Self {
56        Self {
57            partition_scheme: PartitionScheme::Hybrid,
58            mbr_bootstrap: None,
59            bootable: true,
60            efi_boot_partition: None,
61        }
62    }
63
64    /// Set the MBR bootstrap code.
65    pub fn bootstrap(mut self, bootstrap: alloc::vec::Vec<u8>) -> Self {
66        self.mbr_bootstrap = Some(bootstrap);
67        self
68    }
69
70    /// Set the path of the El Torito UEFI boot image to expose as a GPT EFI
71    /// System Partition. See [`HybridBootOptions::efi_boot_partition`].
72    pub fn with_efi_boot_partition(mut self, path: impl Into<String>) -> Self {
73        self.efi_boot_partition = Some(path.into());
74        self
75    }
76}
77
78/// The partition scheme to use for hybrid boot.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
80pub enum PartitionScheme {
81    /// No partition table (CD/DVD only, not USB bootable).
82    #[default]
83    None,
84    /// MBR partition table only (for BIOS USB boot).
85    Mbr,
86    /// GPT partition table only (for UEFI boot).
87    Gpt,
88    /// Hybrid MBR + GPT (for dual BIOS/UEFI boot).
89    /// Creates a protective MBR with GPT, plus MBR entries mirroring key partitions.
90    Hybrid,
91}
92
93#[derive(Debug, Clone)]
94/// Represents IsoFormatOptions.
95pub struct IsoFormatOptions {
96    /// The `volume_name` field.
97    pub volume_name: String,
98    /// The `system_id` field.
99    pub system_id: Option<String>,
100    /// The `volume_set_id` field.
101    pub volume_set_id: Option<String>,
102    /// The `publisher_id` field.
103    pub publisher_id: Option<String>,
104    /// The `preparer_id` field.
105    pub preparer_id: Option<String>,
106    /// The `application_id` field.
107    pub application_id: Option<String>,
108    /// The `sector_size` field.
109    pub sector_size: usize,
110    /// The `features` field.
111    pub features: CreationFeatures,
112    /// The `path_separator` field.
113    pub path_separator: PathSeparator,
114    /// When false (default), PVD string fields are stored as-is without charset
115    /// validation (matching xorriso/genisoimage behavior). When true, auto-converts
116    /// lowercase to uppercase and substitutes invalid characters for ECMA-119 compliance.
117    pub strict_charset: bool,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121/// Identifies a BaseIsoLevel value.
122pub enum BaseIsoLevel {
123    /// L1 Filenames
124    /// Supports only uppercase and using the 8.3 format
125    Level1 {
126        /// The `supports_lowercase` field.
127        supports_lowercase: bool,
128        /// The `supports_rrip` field.
129        supports_rrip: bool,
130    },
131    /// L2 Filenames
132    /// Supports up to 30 characters
133    Level2 {
134        /// The `supports_lowercase` field.
135        supports_lowercase: bool,
136        /// The `supports_rrip` field.
137        supports_rrip: bool,
138    },
139    /// ISO 9660 interchange level 3.
140    ///
141    /// Level 3 retains the Level 2 filename rules and permits a logical file
142    /// to be represented by multiple consecutive directory records/extents.
143    Level3 {
144        /// Whether lowercase ASCII names are accepted.
145        supports_lowercase: bool,
146        /// Whether Rock Ridge system-use fields are emitted.
147        supports_rrip: bool,
148    },
149}
150
151#[derive(Debug, Clone)]
152/// Represents CreationFeatures.
153pub struct CreationFeatures {
154    /// The base Filename Level
155    /// This only supports ASCII uppercase, numbers, and '_' for compatibility reasons.
156    pub filenames: BaseIsoLevel,
157    /// The L3 Filename Level
158    /// This supports filenames up to 207 characters, without using Joliet or Rock Ridge
159    pub long_filenames: bool,
160    /// The Joliet Extension for Unicode filenames
161    pub joliet: Option<JolietLevel>,
162    /// Rock Ridge extension options for POSIX filesystem semantics
163    pub rock_ridge: Option<RripOptions>,
164    /// El-Torito boot options (for CD/DVD boot)
165    pub el_torito: Option<BootOptions>,
166    /// Hybrid boot options (for USB/disk boot)
167    /// Enables the ISO to be bootable when written directly to a USB drive.
168    pub hybrid_boot: Option<HybridBootOptions>,
169}
170
171impl Default for CreationFeatures {
172    fn default() -> Self {
173        Self {
174            filenames: BaseIsoLevel::Level1 {
175                supports_lowercase: false,
176                supports_rrip: false,
177            },
178            long_filenames: false,
179            joliet: None,
180            rock_ridge: None,
181            el_torito: None,
182            hybrid_boot: None,
183        }
184    }
185}
186
187impl CreationFeatures {
188    /// Create features with Rock Ridge enabled (default settings)
189    pub fn rock_ridge() -> Self {
190        Self {
191            filenames: BaseIsoLevel::Level1 {
192                supports_lowercase: false,
193                supports_rrip: true,
194            },
195            rock_ridge: Some(RripOptions::default()),
196            ..Default::default()
197        }
198    }
199
200    /// Create features with Joliet enabled
201    pub fn joliet(level: JolietLevel) -> Self {
202        Self {
203            joliet: Some(level),
204            ..Default::default()
205        }
206    }
207
208    /// Create features with both Rock Ridge and Joliet enabled
209    pub fn extensions() -> Self {
210        Self {
211            filenames: BaseIsoLevel::Level1 {
212                supports_lowercase: false,
213                supports_rrip: true,
214            },
215            joliet: Some(JolietLevel::Level3),
216            rock_ridge: Some(RripOptions::default()),
217            ..Default::default()
218        }
219    }
220
221    /// Create features with hybrid boot enabled (MBR for USB boot)
222    pub fn hybrid_boot(scheme: PartitionScheme) -> Self {
223        Self {
224            hybrid_boot: Some(HybridBootOptions {
225                partition_scheme: scheme,
226                mbr_bootstrap: None,
227                bootable: true,
228                efi_boot_partition: None,
229            }),
230            ..Default::default()
231        }
232    }
233}
234
235impl From<BaseIsoLevel> for crate::file::EntryType {
236    fn from(value: BaseIsoLevel) -> Self {
237        match value {
238            BaseIsoLevel::Level1 {
239                supports_lowercase,
240                supports_rrip,
241            } => Self::Level1 {
242                supports_lowercase,
243                supports_rrip,
244            },
245            BaseIsoLevel::Level2 {
246                supports_lowercase,
247                supports_rrip,
248            } => Self::Level2 {
249                supports_lowercase,
250                supports_rrip,
251            },
252            BaseIsoLevel::Level3 {
253                supports_lowercase,
254                supports_rrip,
255            } => Self::Level2 {
256                supports_lowercase,
257                supports_rrip,
258            },
259        }
260    }
261}