hadris_iso/lib.rs
1//! # Hadris ISO
2//!
3//! A comprehensive Rust implementation of the ISO 9660 filesystem with support for
4//! Joliet, Rock Ridge (RRIP), El-Torito booting, and no-std environments.
5//!
6//! This crate provides both reading and writing capabilities for ISO 9660 images,
7//! making it suitable for:
8//! - **Bootloaders**: Minimal no-std + no-alloc read support
9//! - **OS Kernels**: Read ISO filesystems with only a heap allocator
10//! - **Desktop Applications**: Full-featured ISO creation and extraction
11//! - **Build Systems**: Automated bootable ISO generation
12//!
13//! ## Quick Start
14//!
15//! ### Reading an ISO Image
16//!
17//! ```rust
18//! # use std::io::Cursor;
19//! # use std::sync::Arc;
20//! # use hadris_iso::read::PathSeparator;
21//! # use hadris_iso::write::{File as IsoFile, InputFiles, IsoImageWriter};
22//! # use hadris_iso::write::options::{FormatOptions, CreationFeatures};
23//! use hadris_iso::read::IsoImage;
24//!
25//! # // Create a minimal ISO image for the example
26//! # let files = InputFiles {
27//! # path_separator: PathSeparator::ForwardSlash,
28//! # files: vec![
29//! # IsoFile::File {
30//! # name: Arc::new("readme.txt".to_string()),
31//! # contents: b"Hello, World!".to_vec(),
32//! # },
33//! # ],
34//! # };
35//! # let options = FormatOptions {
36//! # volume_name: "TEST".to_string(),
37//! # system_id: None, volume_set_id: None, publisher_id: None,
38//! # preparer_id: None, application_id: None,
39//! # sector_size: 2048,
40//! # path_separator: PathSeparator::ForwardSlash,
41//! # features: CreationFeatures::default(),
42//! # };
43//! # let mut buffer = Cursor::new(vec![0u8; 1024 * 1024]);
44//! # IsoImageWriter::format_new(&mut buffer, files, options).unwrap();
45//! # let reader = Cursor::new(buffer.into_inner());
46//! let image = IsoImage::open(reader).unwrap();
47//!
48//! // Get the root directory
49//! let root = image.root_dir();
50//!
51//! // Iterate through files
52//! for entry in root.iter(&image).entries() {
53//! let entry = entry.unwrap();
54//! println!("File: {:?}", String::from_utf8_lossy(entry.name()));
55//! }
56//! ```
57//!
58//! ### Creating a Bootable ISO
59//!
60//! ```rust
61//! use std::io::Cursor;
62//! use std::sync::Arc;
63//! use hadris_iso::boot::options::{BootEntryOptions, BootOptions};
64//! use hadris_iso::boot::EmulationType;
65//! use hadris_iso::read::PathSeparator;
66//! use hadris_iso::write::options::{BaseIsoLevel, CreationFeatures, FormatOptions};
67//! use hadris_iso::write::{File as IsoFile, InputFiles, IsoImageWriter};
68//!
69//! // Prepare files to include (use dummy boot image for example)
70//! # let boot_image = vec![0u8; 2048]; // Minimal boot image
71//! let files = InputFiles {
72//! path_separator: PathSeparator::ForwardSlash,
73//! files: vec![
74//! IsoFile::File {
75//! name: Arc::new("boot.bin".to_string()),
76//! contents: boot_image,
77//! },
78//! ],
79//! };
80//!
81//! // Configure boot options
82//! let boot_options = BootOptions {
83//! write_boot_catalog: true,
84//! default: BootEntryOptions {
85//! boot_image_path: "boot.bin".to_string(),
86//! load_size: Some(std::num::NonZeroU16::new(4).unwrap()),
87//! boot_info_table: false,
88//! grub2_boot_info: false,
89//! emulation: EmulationType::NoEmulation,
90//! },
91//! entries: vec![],
92//! };
93//!
94//! // Create the ISO
95//! let format_options = FormatOptions {
96//! volume_name: "MY_BOOTABLE_ISO".to_string(),
97//! system_id: None, volume_set_id: None, publisher_id: None,
98//! preparer_id: None, application_id: None,
99//! sector_size: 2048,
100//! path_separator: PathSeparator::ForwardSlash,
101//! features: CreationFeatures {
102//! filenames: BaseIsoLevel::Level1 {
103//! supports_lowercase: false,
104//! supports_rrip: false,
105//! },
106//! long_filenames: false,
107//! joliet: None,
108//! rock_ridge: None,
109//! el_torito: Some(boot_options),
110//! hybrid_boot: None,
111//! },
112//! };
113//!
114//! let mut buffer = Cursor::new(vec![0u8; 2 * 1024 * 1024]); // 2MB buffer
115//! IsoImageWriter::format_new(&mut buffer, files, format_options).unwrap();
116//! # // In real code you would write to a file:
117//! # // std::fs::write("bootable.iso", buffer.into_inner()).unwrap();
118//! ```
119//!
120//! ## Feature Flags
121//!
122//! This crate uses feature flags to control functionality and dependencies:
123//!
124//! | Feature | Description | Dependencies |
125//! |---------|-------------|--------------|
126//! | `read` | Minimal read support (no-std, no-alloc) | None |
127//! | `alloc` | Heap allocation without full std | `alloc` crate |
128//! | `std` | Full standard library support | `std`, `alloc`, `thiserror`, `tracing`, `chrono` |
129//! | `write` | ISO creation/formatting | `std`, `alloc` |
130//! | `joliet` | UTF-16 Unicode filename support | `alloc` |
131//!
132//! ### Feature Combinations
133//!
134//! **For Bootloaders (minimal footprint):**
135//! ```toml
136//! [dependencies]
137//! hadris-iso = { version = "0.2", default-features = false, features = ["read"] }
138//! ```
139//!
140//! **For Kernels with Heap (no-std + alloc):**
141//! ```toml
142//! [dependencies]
143//! hadris-iso = { version = "0.2", default-features = false, features = ["read", "alloc"] }
144//! ```
145//!
146//! **For Desktop Applications (full features):**
147//! ```toml
148//! [dependencies]
149//! hadris-iso = { version = "0.2" } # Uses default features: std, write
150//! ```
151//!
152//! ## ISO 9660 Extensions
153//!
154//! ### Joliet Extension
155//!
156//! Joliet provides Unicode filename support using UTF-16 encoding. It allows
157//! filenames up to 64 characters and preserves case. Enable with the `joliet` feature.
158//!
159//! ```rust
160//! use hadris_iso::joliet::JolietLevel;
161//! use hadris_iso::write::options::CreationFeatures;
162//!
163//! let features = CreationFeatures {
164//! joliet: Some(JolietLevel::Level3), // Full Unicode support
165//! ..Default::default()
166//! };
167//! ```
168//!
169//! ### Rock Ridge (RRIP) Extension
170//!
171//! Rock Ridge provides POSIX filesystem semantics including:
172//! - Long filenames (up to 255 characters)
173//! - Unix permissions and ownership
174//! - Symbolic links
175//! - Device files
176//!
177//! ### El-Torito Boot Extension
178//!
179//! El-Torito enables bootable CD/DVD images. This crate supports:
180//! - BIOS boot (x86/x86_64)
181//! - UEFI boot
182//! - No-emulation boot mode
183//! - Boot information table injection
184//!
185//! ### Hybrid Boot (USB Boot)
186//!
187//! Hybrid boot enables ISOs to be bootable when written directly to USB drives:
188//! - **MBR mode** - For BIOS systems (isohybrid-compatible)
189//! - **GPT mode** - For UEFI systems
190//! - **Hybrid MBR+GPT** - For dual BIOS/UEFI compatibility
191//!
192//! ```rust
193//! use hadris_iso::write::options::{CreationFeatures, HybridBootOptions, PartitionScheme};
194//!
195//! // Enable MBR-based hybrid boot for USB
196//! let features = CreationFeatures {
197//! hybrid_boot: Some(HybridBootOptions::mbr()),
198//! ..Default::default()
199//! };
200//!
201//! // Enable dual BIOS/UEFI boot
202//! let features = CreationFeatures {
203//! hybrid_boot: Some(HybridBootOptions::hybrid()),
204//! ..Default::default()
205//! };
206//! ```
207//!
208//! ## Architecture
209//!
210//! The crate is organized into several modules:
211//!
212//! - [`boot`] - El-Torito boot catalog structures and options
213//! - [`directory`] - Directory record parsing and creation
214//! - [`mod@file`] - File entry types and filename handling
215//! - [`io`] - Sector-based I/O abstractions
216//! - [`joliet`] - Joliet UTF-16 extension support
217//! - [`path`] - Path table structures
218//! - [`read`] - ISO image reading and navigation
219//! - [`rrip`] - Rock Ridge extension support
220//! - [`susp`] - System Use Sharing Protocol (base for Rock Ridge)
221//! - [`types`] - Common types (endian values, strings, dates)
222//! - [`volume`] - Volume descriptor structures
223//! - [`mod@write`] - ISO image creation
224//!
225//! ## Compatibility
226//!
227//! ISOs created with this crate are compatible with:
228//! - Linux (mount, isoinfo)
229//! - Windows (built-in ISO support)
230//! - macOS (built-in ISO support)
231//! - QEMU/VirtualBox (bootable ISOs)
232//! - xorriso (can read/verify)
233//!
234//! ## Specification References
235//!
236//! This implementation follows these specifications:
237//! - ECMA-119 (ISO 9660)
238//! - Joliet Specification (Microsoft)
239//! - IEEE P1282 (Rock Ridge / RRIP)
240//! - El-Torito Bootable CD-ROM Format Specification
241//!
242//! For detailed specification documentation, see the
243//! [spec directory](https://github.com/hxyulin/hadris/tree/main/crates/hadris-iso/spec).
244
245// Known Limitations:
246// - Rock Ridge write uses hardcoded defaults (mode 0o755/0o644, uid/gid 0);
247// the `RripOptions` configuration (preserve_permissions, etc.) is not yet wired up.
248// - When reading ISOs with both Joliet and Rock Ridge, only one is used
249
250#![no_std]
251#![allow(async_fn_in_trait)]
252
253#[cfg(feature = "alloc")]
254extern crate alloc;
255
256#[cfg(feature = "std")]
257extern crate std;
258
259// ---------------------------------------------------------------------------
260// Shared types (compiled once, not duplicated by sync/async modules)
261// ---------------------------------------------------------------------------
262
263/// File entry types and interchange levels.
264///
265/// ISO 9660 defines three interchange levels with different filename restrictions:
266/// - **Level 1**: 8.3 format (8 chars + 3 extension), uppercase only
267/// - **Level 2**: Up to 31 characters
268/// - **Level 3**: Up to 207 characters
269///
270/// This module also handles the `EntryType` enum which tracks which
271/// extensions (Joliet, Rock Ridge) are available for a given entry.
272pub mod file;
273
274/// Common types used throughout the crate.
275///
276/// This includes:
277/// - Endian-aware integer types (`U16`, `U32`, `BothEndian`)
278/// - ISO 9660 string types with character set restrictions
279/// - Date/time structures (`DecDateTime`, `BinDateTime`)
280pub mod types;
281
282/// Joliet extension for Unicode filenames.
283///
284/// Joliet uses UTF-16 Big Endian encoding and supports filenames up to
285/// 64 characters (128 bytes). It's widely supported on Windows and Linux.
286///
287/// Three levels are defined:
288/// - **Level 1**: Escape sequence `%/@`
289/// - **Level 2**: Escape sequence `%/C`
290/// - **Level 3**: Escape sequence `%/E` (recommended)
291#[cfg(feature = "alloc")]
292pub mod joliet;
293
294// ---------------------------------------------------------------------------
295// Sync module
296// ---------------------------------------------------------------------------
297
298#[cfg(feature = "sync")]
299#[path = ""]
300pub mod sync {
301 //! Synchronous ISO 9660 API.
302 //!
303 //! All I/O operations use synchronous `Read`/`Write`/`Seek` traits.
304
305 pub use hadris_io::Result as IoResult;
306 pub use hadris_io::sync::{Parsable, Read, ReadExt, Seek, Writable, Write};
307 pub use hadris_io::{Error, ErrorKind, SeekFrom};
308
309 macro_rules! io_transform {
310 ($($item:tt)*) => { hadris_macros::strip_async!{ $($item)* } };
311 }
312
313 #[allow(unused_macros)]
314 macro_rules! sync_only {
315 ($($item:tt)*) => { $($item)* };
316 }
317
318 #[allow(unused_macros)]
319 macro_rules! async_only {
320 ($($item:tt)*) => {};
321 }
322
323 #[path = "."]
324 mod __inner {
325 /// Sector-based I/O abstractions for reading and writing ISOs.
326 ///
327 /// ISO 9660 uses 2048-byte sectors as its fundamental unit. This module
328 /// provides `IsoCursor` which wraps any `Read + Seek` type and provides
329 /// sector-aligned operations.
330 pub mod io;
331
332 /// Directory record structures for parsing and creating directory entries.
333 ///
334 /// This module provides the `DirectoryRecord` type which represents a single
335 /// entry in an ISO 9660 directory. Each record contains metadata about a file
336 /// or subdirectory including its location, size, timestamps, and flags.
337 ///
338 /// # Example
339 ///
340 /// ```rust
341 /// use hadris_iso::directory::FileFlags;
342 ///
343 /// // Check if flags indicate a directory
344 /// let flags = FileFlags::from_bits_truncate(0x02); // DIRECTORY flag
345 /// if flags.contains(FileFlags::DIRECTORY) {
346 /// println!("This is a directory");
347 /// }
348 /// ```
349 pub mod directory;
350
351 /// Volume descriptor structures.
352 ///
353 /// Volume descriptors are located starting at sector 16 and describe the
354 /// overall structure of the ISO image. Types include:
355 /// - `PrimaryVolumeDescriptor` - Required, contains basic image info
356 /// - `SupplementaryVolumeDescriptor` - For Joliet and other extensions
357 /// - `BootRecordVolumeDescriptor` - For El-Torito boot support
358 pub mod volume;
359
360 /// Path table structures for fast directory lookup.
361 ///
362 /// The path table provides an alternative to traversing the directory
363 /// hierarchy for locating directories. It's a flat list of all directories
364 /// in the image, useful for quick lookups.
365 pub mod path;
366
367 /// El-Torito boot support.
368 ///
369 /// This module provides structures and utilities for creating and parsing
370 /// bootable ISO images according to the El-Torito specification.
371 ///
372 /// # Boot Catalog Structure
373 ///
374 /// The boot catalog consists of:
375 /// 1. **Validation Entry** - Identifies the catalog as valid
376 /// 2. **Default/Initial Entry** - The primary boot image
377 /// 3. **Section Headers** - For additional boot images (optional)
378 /// 4. **Section Entries** - Additional boot images (UEFI, etc.)
379 ///
380 /// # Example
381 ///
382 /// ```rust
383 /// use hadris_iso::boot::{BootCatalog, EmulationType};
384 ///
385 /// let catalog = BootCatalog::new(
386 /// EmulationType::NoEmulation,
387 /// 0, // load_segment (0 = default 0x07C0)
388 /// 4, // sector_count (512-byte sectors to load)
389 /// 20, // load_rba (LBA of boot image)
390 /// );
391 /// ```
392 pub mod boot;
393
394 /// System Use Sharing Protocol (SUSP).
395 ///
396 /// SUSP provides a framework for extending ISO 9660 directory records
397 /// with additional system-specific information. Rock Ridge is built on SUSP.
398 #[cfg(feature = "alloc")]
399 pub mod susp;
400
401 /// Rock Ridge Interchange Protocol (RRIP) extension.
402 ///
403 /// Rock Ridge provides POSIX filesystem semantics on top of ISO 9660,
404 /// including long filenames, Unix permissions, symbolic links, and more.
405 ///
406 /// # Supported Extensions
407 ///
408 /// - `PX` - POSIX file attributes (mode, nlink, uid, gid)
409 /// - `NM` - Alternate (long) filename
410 /// - `SL` - Symbolic link
411 /// - `TF` - Timestamps (creation, modification, access)
412 /// - `CL`/`PL`/`RE` - Directory relocation
413 #[cfg(feature = "alloc")]
414 pub mod rrip;
415
416 /// ISO image reading and navigation.
417 ///
418 /// This module provides the main `IsoImage` type for opening and
419 /// navigating ISO 9660 images.
420 ///
421 /// # Example
422 ///
423 /// ```rust
424 /// # use std::io::Cursor;
425 /// # use std::sync::Arc;
426 /// # use hadris_iso::read::PathSeparator;
427 /// # use hadris_iso::write::{File as IsoFile, InputFiles, IsoImageWriter};
428 /// # use hadris_iso::write::options::{FormatOptions, CreationFeatures};
429 /// use hadris_iso::read::IsoImage;
430 ///
431 /// # // Create a minimal ISO for the example
432 /// # let files = InputFiles {
433 /// # path_separator: PathSeparator::ForwardSlash,
434 /// # files: vec![IsoFile::File {
435 /// # name: Arc::new("test.txt".to_string()),
436 /// # contents: b"test".to_vec(),
437 /// # }],
438 /// # };
439 /// # let options = FormatOptions {
440 /// # volume_name: "TEST".to_string(),
441 /// # system_id: None, volume_set_id: None, publisher_id: None,
442 /// # preparer_id: None, application_id: None,
443 /// # sector_size: 2048,
444 /// # path_separator: PathSeparator::ForwardSlash,
445 /// # features: CreationFeatures::default(),
446 /// # };
447 /// # let mut buffer = Cursor::new(vec![0u8; 1024 * 1024]);
448 /// # IsoImageWriter::format_new(&mut buffer, files, options).unwrap();
449 /// # let file = Cursor::new(buffer.into_inner());
450 /// let image = IsoImage::open(file).unwrap();
451 ///
452 /// // Read the primary volume descriptor
453 /// let pvd = image.read_pvd();
454 /// println!("Volume: {}", pvd.volume_identifier.to_str().trim());
455 ///
456 /// // Navigate directories
457 /// let root = image.root_dir();
458 /// for entry in root.iter(&image).entries() {
459 /// // Process each entry
460 /// # let _ = entry;
461 /// }
462 /// ```
463 #[cfg(feature = "alloc")]
464 pub mod read;
465
466 /// ISO image creation and formatting.
467 ///
468 /// This module provides `IsoImageWriter` for creating new ISO images
469 /// with full support for El-Torito boot, Joliet, and Rock Ridge extensions.
470 ///
471 /// # Example
472 ///
473 /// ```rust
474 /// use std::io::Cursor;
475 /// use std::sync::Arc;
476 /// use hadris_iso::read::PathSeparator;
477 /// use hadris_iso::write::{File as IsoFile, InputFiles, IsoImageWriter};
478 /// use hadris_iso::write::options::{FormatOptions, CreationFeatures};
479 ///
480 /// // Create files to include in the ISO
481 /// let files = InputFiles {
482 /// path_separator: PathSeparator::ForwardSlash,
483 /// files: vec![
484 /// IsoFile::File {
485 /// name: Arc::new("hello.txt".to_string()),
486 /// contents: b"Hello, World!".to_vec(),
487 /// },
488 /// ],
489 /// };
490 /// let options = FormatOptions {
491 /// volume_name: "MY_ISO".to_string(),
492 /// system_id: None, volume_set_id: None, publisher_id: None,
493 /// preparer_id: None, application_id: None,
494 /// sector_size: 2048,
495 /// path_separator: PathSeparator::ForwardSlash,
496 /// features: CreationFeatures::default(),
497 /// };
498 ///
499 /// let mut output = Cursor::new(vec![0u8; 1024 * 1024]);
500 /// IsoImageWriter::format_new(&mut output, files, options).unwrap();
501 /// # // In real code, write to a file instead of a Cursor
502 /// ```
503 #[cfg(feature = "write")]
504 pub mod write;
505
506 /// ISO image modification and append support.
507 ///
508 /// This module provides `IsoModifier` for appending files to existing ISO images
509 /// and marking files for deletion. Changes are committed as a new session.
510 ///
511 /// # Example
512 ///
513 /// ```rust,ignore
514 /// use hadris_iso::modify::IsoModifier;
515 ///
516 /// let file = std::fs::OpenOptions::new()
517 /// .read(true).write(true)
518 /// .open("image.iso")?;
519 ///
520 /// let mut modifier = IsoModifier::open(file)?;
521 /// modifier.append_file("new_file.txt", b"Hello, world!".to_vec());
522 /// modifier.commit()?;
523 /// ```
524 #[cfg(feature = "write")]
525 pub mod modify;
526 }
527 pub use __inner::*;
528
529 // Convenience re-exports for backwards compatibility
530 pub use __inner::io::IsoCursor;
531 #[cfg(feature = "alloc")]
532 pub use __inner::read::IsoImage;
533}
534
535// ---------------------------------------------------------------------------
536// Async module
537// ---------------------------------------------------------------------------
538
539#[cfg(feature = "async")]
540#[path = ""]
541pub mod r#async {
542 //! Asynchronous ISO 9660 API.
543 //!
544 //! All I/O operations use async `Read`/`Write`/`Seek` traits.
545
546 pub use hadris_io::Result as IoResult;
547 pub use hadris_io::r#async::{Parsable, Read, ReadExt, Seek, Writable, Write};
548 pub use hadris_io::{Error, ErrorKind, SeekFrom};
549
550 macro_rules! io_transform {
551 ($($item:tt)*) => { $($item)* };
552 }
553
554 #[allow(unused_macros)]
555 macro_rules! sync_only {
556 ($($item:tt)*) => {};
557 }
558
559 #[allow(unused_macros)]
560 macro_rules! async_only {
561 ($($item:tt)*) => { $($item)* };
562 }
563
564 #[path = "."]
565 mod __inner {
566 pub mod boot;
567 pub mod directory;
568 pub mod io;
569 #[cfg(feature = "write")]
570 pub mod modify;
571 pub mod path;
572 #[cfg(feature = "alloc")]
573 pub mod read;
574 #[cfg(feature = "alloc")]
575 pub mod rrip;
576 #[cfg(feature = "alloc")]
577 pub mod susp;
578 pub mod volume;
579 #[cfg(feature = "write")]
580 pub mod write;
581 }
582 pub use __inner::*;
583}
584
585// ---------------------------------------------------------------------------
586// Default re-exports for backwards compatibility (sync)
587// ---------------------------------------------------------------------------
588
589#[cfg(feature = "sync")]
590pub use sync::*;