diff --git a/Cargo.toml b/Cargo.toml index 2480374e60f70602ee557025511d8b13f359ca7c..d7913881d39e364498a508a04ceb23c31643bc16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ arg_parser = "0.1.0" failure = "0.1.8" fatfs = "0.3.0" fscommon = "0.1.1" -gpt = { git = "https://gitlab.redox-os.org/redox-os/gpt" } +gpt = "3.0.0" libc = "0.2.70" pkgar = "0.1.9" pkgar-core = "0.1.0" @@ -39,7 +39,7 @@ serde = "1.0.110" serde_derive = "1.0.110" termion = "1.5.5" toml = "0.5.6" -uuid = { version = "0.7.4", features = ["v4"] } +uuid = { version = "0.8", features = ["v4"] } [patch.crates-io] ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-unix-0.13.5" } diff --git a/src/lib.rs b/src/lib.rs index eea85fdd93e5251eb125686c4c59ac064edb44e6..d153a847ab649b910b86380155bd6ee82670dbe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,8 @@ use std::{ collections::BTreeMap, env, fs, - io::{self, stderr, Write}, + io::{self, stderr, Read, Seek, SeekFrom, Write}, + os::unix::fs::MetadataExt, path::Path, process::{self, Command}, str::FromStr, @@ -403,120 +404,152 @@ pub fn with_whole_disk<P, F, T>(disk_path: P, bootloader_bios: &[u8], bootloader } }; - // TODO: support other block sizes? - let block_size = 512; - - // Write BIOS bootloader to disk, resetting all partitioning - let disk_size = { - eprintln!("Writing bootloader with size {:#x}", bootloader_bios.len()); - - // Open disk - let mut disk = fs::OpenOptions::new() - .read(true) - .write(true) - .open(disk_path.as_ref())?; - - // Write bootloader data - disk.write(&bootloader_bios)?; - - // Get disk size - disk.metadata()?.len() - }; - - // Open disk, mark it as not initialized - let mut gpt_disk = gpt::GptConfig::new() - .writable(true) - .initialized(false) + // Open disk and read metadata + eprintln!("Opening disk {}", disk_path.as_ref().display()); + let mut disk_file = fs::OpenOptions::new() + .read(true) + .write(true) .open(disk_path.as_ref())?; + let disk_metadata = disk_file.metadata()?; + let disk_size = disk_metadata.len(); + let block_size = disk_metadata.blksize(); + let gpt_block_size = match block_size { + 512 => gpt::disk::LogicalBlockSize::Lb512, + _ => { + // TODO: support (and test) other block sizes + return Err(format_err!("block size {} not supported", block_size)); + } + }; // Calculate partition offsets let gpt_reserved = 34 * 512; // GPT always reserves 34 512-byte sectors + // First megabyte of the disk is reserved for BIOS partition, wich includes GPT tables + let bios_size = 1024 * 1024; let bios_start = gpt_reserved / block_size; - let bios_end = (1024 * 1024 / block_size) - 1; // End at 1 MiB + let bios_end = (bios_size / block_size) - 1; // End at 1 MiB + // Last megabyte of the disk is reserved for EFI partition + let efi_size = 1024 * 1024; let efi_end = ((disk_size - gpt_reserved) / block_size) - 1; - let efi_start = efi_end - ((1024 * 1024) / block_size); // 1 MiB from end of disk + let efi_start = efi_end - (efi_size / block_size); // 1 MiB from end of disk let redoxfs_start = bios_end + 1; let redoxfs_end = efi_start - 1; - // Add BIOS boot partition - let mut partitions = BTreeMap::new(); - let mut partition_id = 1; - partitions.insert(partition_id, gpt::partition::Partition { - part_type_guid: gpt::partition_types::BIOS, - part_guid: uuid::Uuid::new_v4(), - first_lba: bios_start, - last_lba: bios_end, - flags: 0, // TODO - name: "BIOS".to_string(), - }); - partition_id += 1; - - // Add RedoxFS partition - partitions.insert(partition_id, gpt::partition::Partition { - //TODO: Use REDOX_REDOXFS type (needs GPT crate changes) - part_type_guid: gpt::partition_types::LINUX_FS, - part_guid: uuid::Uuid::new_v4(), - first_lba: redoxfs_start, - last_lba: redoxfs_end, - flags: 0, - name: "REDOX".to_string(), - }); - partition_id += 1; - - // Add EFI boot partition - partitions.insert(partition_id, gpt::partition::Partition { - part_type_guid: gpt::partition_types::EFI, - part_guid: uuid::Uuid::new_v4(), - first_lba: efi_start, - last_lba: efi_end, - flags: 0, // TODO - name: "EFI".to_string(), - }); - - // Initialize GPT table - gpt_disk.update_partitions(partitions)?; + // Format and install BIOS partition + { + let mut bios_partition = { + // We write the BIOS partition and partition tables in memory to avoid non block aligned I/O + let mut bios_partition = Box::new(io::Cursor::new(vec![0u8; bios_size as usize])); + + // Write BIOS bootloader to disk + eprintln!("Write bootloader with size {:#x}", bootloader_bios.len()); + bios_partition.write_all(&bootloader_bios)?; + + // Replace MBR tables with protective MBR + let mbr_blocks = ((disk_size + block_size - 1) / block_size) - 1; + eprintln!("Writing protective MBR with disk blocks {:#x}", mbr_blocks); + gpt::mbr::ProtectiveMBR::with_lb_size(mbr_blocks as u32) + .update_conservative(&mut bios_partition)?; + + // Open disk, mark it as not initialized + let mut gpt_disk = gpt::GptConfig::new() + .initialized(false) + .writable(true) + .logical_block_size(gpt_block_size) + .create_from_device(bios_partition, None)?; + + // Add BIOS boot partition + let mut partitions = BTreeMap::new(); + let mut partition_id = 1; + partitions.insert(partition_id, gpt::partition::Partition { + part_type_guid: gpt::partition_types::BIOS, + part_guid: uuid::Uuid::new_v4(), + first_lba: bios_start, + last_lba: bios_end, + flags: 0, // TODO + name: "BIOS".to_string(), + }); + partition_id += 1; + + // Add RedoxFS partition + partitions.insert(partition_id, gpt::partition::Partition { + //TODO: Use REDOX_REDOXFS type (needs GPT crate changes) + part_type_guid: gpt::partition_types::LINUX_FS, + part_guid: uuid::Uuid::new_v4(), + first_lba: redoxfs_start, + last_lba: redoxfs_end, + flags: 0, + name: "REDOX".to_string(), + }); + partition_id += 1; + + // Add EFI boot partition + partitions.insert(partition_id, gpt::partition::Partition { + part_type_guid: gpt::partition_types::EFI, + part_guid: uuid::Uuid::new_v4(), + first_lba: efi_start, + last_lba: efi_end, + flags: 0, // TODO + name: "EFI".to_string(), + }); + + eprintln!("Writing GPT tables: {:#?}", partitions); + + // Initialize GPT table + gpt_disk.update_partitions(partitions)?; + + // Write partition layout, returning disk file + gpt_disk.write()? + }; - eprintln!("Writing GPT tables: {:#?}", gpt_disk); + eprintln!("Copying BIOS partition to disk with size {:#x}", bios_size); + for block in 0..bios_size / block_size { + let offset = block * block_size; + let mut block = vec![0; block_size as usize]; - // Write partition layout, returning disk file - let mut disk_file = gpt_disk.write()?; + bios_partition.seek(SeekFrom::Start(offset))?; + bios_partition.read_exact(&mut block)?; - // Replace MBR tables with protective MBR - let mbr_blocks = ((disk_size + block_size - 1) / block_size) - 1; - eprintln!("Writing protective MBR with disk blocks {:#x}", mbr_blocks); - gpt::mbr::ProtectiveMBR::with_lb_size(mbr_blocks as u32) - .update_conservative(&mut disk_file)?; + disk_file.seek(SeekFrom::Start(offset))?; + disk_file.write_all(&block)?; + } + } // Format and install EFI partition { + // We write the BIOS partition and partition tables in memory to avoid non block aligned I/O + let mut efi_partition = vec![0u8; efi_size as usize]; + { + eprintln!("Formatting EFI partition with size {:#x}", efi_size); + fatfs::format_volume(io::Cursor::new(&mut efi_partition), fatfs::FormatVolumeOptions::new())?; + + eprintln!("Opening EFI partition"); + let fs = fatfs::FileSystem::new(io::Cursor::new(&mut efi_partition), fatfs::FsOptions::new())?; + + eprintln!("Creating EFI directory"); + let root_dir = fs.root_dir(); + root_dir.create_dir("EFI")?; + + eprintln!("Creating EFI/BOOT directory"); + let efi_dir = root_dir.open_dir("EFI")?; + efi_dir.create_dir("BOOT")?; + + eprintln!("Writing EFI/BOOT/{} file with size {:#x}", bootloader_efi_name, bootloader_efi.len()); + let boot_dir = efi_dir.open_dir("BOOT")?; + let mut file = boot_dir.create_file(bootloader_efi_name)?; + file.truncate()?; + file.write_all(&bootloader_efi)?; + } + + eprintln!("Copying BIOS partition to disk with size {:#x}", efi_size); let mut disk_efi = fscommon::StreamSlice::new( &mut disk_file, efi_start * block_size, (efi_end + 1) * block_size, )?; - - eprintln!("Formatting EFI partition with size {:#x}", (efi_end - efi_start) * block_size); - fatfs::format_volume(&mut disk_efi, fatfs::FormatVolumeOptions::new())?; - - eprintln!("Opening EFI partition"); - let fs = fatfs::FileSystem::new(disk_efi, fatfs::FsOptions::new())?; - - eprintln!("Creating EFI directory"); - let root_dir = fs.root_dir(); - root_dir.create_dir("EFI")?; - - eprintln!("Creating EFI/BOOT directory"); - let efi_dir = root_dir.open_dir("EFI")?; - efi_dir.create_dir("BOOT")?; - - eprintln!("Writing EFI/BOOT/{} file", bootloader_efi_name); - let boot_dir = efi_dir.open_dir("BOOT")?; - let mut file = boot_dir.create_file(bootloader_efi_name)?; - file.truncate()?; - file.write_all(&bootloader_efi)?; + disk_efi.write_all(&efi_partition)?; } // Format and install RedoxFS partition