@@ -20,145 +20,141 @@ const KERNEL_FILE_NAME: &str = "kernel-x86_64";
20
20
const RAMDISK_FILE_NAME : & str = "ramdisk" ;
21
21
const BIOS_STAGE_3 : & str = "boot-stage-3" ;
22
22
const BIOS_STAGE_4 : & str = "boot-stage-4" ;
23
+ const UEFI_BOOT_FILENAME : & str = "efi/boot/bootx64.efi" ;
24
+ const UEFI_TFTP_BOOT_FILENAME : & str = "bootloader" ;
23
25
24
- /// Create disk images for booting on legacy BIOS systems.
25
- pub struct BiosBoot {
26
- kernel : PathBuf ,
27
- ramdisk : Option < PathBuf > ,
26
+ struct DiskImageFile < ' a > {
27
+ source : & ' a PathBuf ,
28
+ destination : & ' a str ,
28
29
}
29
30
30
- impl BiosBoot {
31
- /// Start creating a disk image for the given bootloader ELF executable.
32
- pub fn new ( kernel_path : & Path ) -> Self {
33
- Self {
34
- kernel : kernel_path. to_owned ( ) ,
35
- ramdisk : None ,
36
- }
31
+ /// DiskImageBuilder helps create disk images for a specified set of files.
32
+ /// It can currently create MBR (BIOS), GPT (UEFI), and TFTP (UEFI) images.
33
+ pub struct DiskImageBuilder < ' a > {
34
+ files : Vec < DiskImageFile < ' a > > ,
35
+ }
36
+
37
+ impl < ' a > DiskImageBuilder < ' a > {
38
+ /// Create a new instance of DiskImageBuilder, with the specified kernel.
39
+ pub fn new ( kernel : & ' a PathBuf ) -> Self {
40
+ let mut obj = Self :: empty ( ) ;
41
+ obj. set_kernel ( kernel) ;
42
+ obj
43
+ }
44
+
45
+ /// Create a new, empty instance of DiskImageBuilder
46
+ pub fn empty ( ) -> Self {
47
+ Self { files : Vec :: new ( ) }
48
+ }
49
+ /// Add or replace a ramdisk to be included in the final image.
50
+ pub fn set_ramdisk ( & mut self , path : & ' a PathBuf ) {
51
+ self . add_or_replace_file ( path, RAMDISK_FILE_NAME ) ;
52
+ }
53
+
54
+ /// Add or replace a kernel to be included in the final image.
55
+ fn set_kernel ( & mut self , path : & ' a PathBuf ) {
56
+ self . add_or_replace_file ( path, KERNEL_FILE_NAME )
37
57
}
38
58
39
- /// Add a ramdisk file to the image
40
- pub fn set_ramdisk ( & mut self , ramdisk_path : & Path ) -> & mut Self {
41
- self . ramdisk = Some ( ramdisk_path. to_owned ( ) ) ;
42
- self
59
+ /// Add or replace arbitrary files.
60
+ /// NOTE: You can overwrite internal files if you choose, such as EFI/BOOT/BOOTX64.EFI
61
+ /// This can be useful in situations where you want to generate an image, but not use the provided bootloader.
62
+ fn add_or_replace_file ( & mut self , path : & ' a PathBuf , target : & ' a str ) {
63
+ self . files . insert (
64
+ 0 ,
65
+ DiskImageFile :: < ' a > {
66
+ source : & path,
67
+ destination : & target,
68
+ } ,
69
+ ) ;
43
70
}
71
+ fn create_fat_filesystem_image (
72
+ & self ,
73
+ internal_files : BTreeMap < & ' a str , & ' a Path > ,
74
+ ) -> anyhow:: Result < NamedTempFile > {
75
+ let mut local_map = BTreeMap :: new ( ) ;
76
+
77
+ for k in internal_files {
78
+ local_map. insert ( k. 0 , k. 1 ) ;
79
+ }
80
+
81
+ for f in self . files . as_slice ( ) {
82
+ local_map. insert ( f. destination , & f. source . as_path ( ) ) ;
83
+ }
84
+
85
+ let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
86
+ fat:: create_fat_filesystem ( local_map, out_file. path ( ) )
87
+ . context ( "failed to create BIOS FAT filesystem" ) ?;
44
88
45
- /// Create a bootable UEFI disk image at the given path.
46
- pub fn create_disk_image ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
89
+ Ok ( out_file)
90
+ }
91
+
92
+ /// Create an MBR disk image for booting on BIOS systems.
93
+ pub fn create_bios_image ( & self , image_filename : & Path ) -> anyhow:: Result < ( ) > {
47
94
let bootsector_path = Path :: new ( env ! ( "BIOS_BOOT_SECTOR_PATH" ) ) ;
48
95
let stage_2_path = Path :: new ( env ! ( "BIOS_STAGE_2_PATH" ) ) ;
96
+ let stage_3_path = Path :: new ( env ! ( "BIOS_STAGE_3_PATH" ) ) ;
97
+ let stage_4_path = Path :: new ( env ! ( "BIOS_STAGE_4_PATH" ) ) ;
98
+ let mut internal_files = BTreeMap :: new ( ) ;
99
+ internal_files. insert ( BIOS_STAGE_3 , stage_3_path) ;
100
+ internal_files. insert ( BIOS_STAGE_4 , stage_4_path) ;
49
101
50
102
let fat_partition = self
51
- . create_fat_partition ( )
103
+ . create_fat_filesystem_image ( internal_files )
52
104
. context ( "failed to create FAT partition" ) ?;
53
-
54
105
mbr:: create_mbr_disk (
55
106
bootsector_path,
56
107
stage_2_path,
57
108
fat_partition. path ( ) ,
58
- out_path ,
109
+ image_filename ,
59
110
)
60
111
. context ( "failed to create BIOS MBR disk image" ) ?;
61
112
62
113
fat_partition
63
114
. close ( )
64
115
. context ( "failed to delete FAT partition after disk image creation" ) ?;
65
-
66
116
Ok ( ( ) )
67
117
}
68
-
69
- /// Creates an BIOS-bootable FAT partition with the kernel.
70
- fn create_fat_partition ( & self ) -> anyhow:: Result < NamedTempFile > {
71
- let stage_3_path = Path :: new ( env ! ( "BIOS_STAGE_3_PATH" ) ) ;
72
- let stage_4_path = Path :: new ( env ! ( "BIOS_STAGE_4_PATH" ) ) ;
73
- let kernel_path = self . kernel . as_path ( ) ;
74
-
75
- let mut files = BTreeMap :: new ( ) ;
76
- files. insert ( KERNEL_FILE_NAME , kernel_path) ;
77
- files. insert ( BIOS_STAGE_3 , stage_3_path) ;
78
- files. insert ( BIOS_STAGE_4 , stage_4_path) ;
79
- if let Some ( ramdisk_path) = & self . ramdisk {
80
- files. insert ( RAMDISK_FILE_NAME , ramdisk_path) ;
81
- }
82
- let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
83
- fat:: create_fat_filesystem ( files, out_file. path ( ) )
84
- . context ( "failed to create BIOS FAT filesystem" ) ?;
85
-
86
- Ok ( out_file)
87
- }
88
- }
89
-
90
- /// Create disk images for booting on UEFI systems.
91
- pub struct UefiBoot {
92
- kernel : PathBuf ,
93
- ramdisk : Option < PathBuf > ,
94
- }
95
-
96
- impl UefiBoot {
97
- /// Start creating a disk image for the given bootloader ELF executable.
98
- pub fn new ( kernel_path : & Path ) -> Self {
99
- Self {
100
- kernel : kernel_path. to_owned ( ) ,
101
- ramdisk : None ,
102
- }
103
- }
104
-
105
- /// Add a ramdisk file to the disk image
106
- pub fn set_ramdisk ( & mut self , ramdisk_path : & Path ) -> & mut Self {
107
- self . ramdisk = Some ( ramdisk_path. to_owned ( ) ) ;
108
- self
109
- }
110
-
111
- /// Create a bootable UEFI disk image at the given path.
112
- pub fn create_disk_image ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
118
+ /// Create a GPT disk image for booting on UEFI systems.
119
+ pub fn create_uefi_image ( & self , image_filename : & Path ) -> anyhow:: Result < ( ) > {
120
+ let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
121
+ let mut internal_files = BTreeMap :: new ( ) ;
122
+ internal_files. insert ( UEFI_BOOT_FILENAME , bootloader_path) ;
113
123
let fat_partition = self
114
- . create_fat_partition ( )
124
+ . create_fat_filesystem_image ( internal_files )
115
125
. context ( "failed to create FAT partition" ) ?;
116
-
117
- gpt:: create_gpt_disk ( fat_partition. path ( ) , out_path)
126
+ gpt:: create_gpt_disk ( fat_partition. path ( ) , image_filename)
118
127
. context ( "failed to create UEFI GPT disk image" ) ?;
119
-
120
128
fat_partition
121
129
. close ( )
122
130
. context ( "failed to delete FAT partition after disk image creation" ) ?;
123
131
124
132
Ok ( ( ) )
125
133
}
126
134
127
- /// Prepare a folder for use with booting over UEFI_PXE.
128
- ///
129
- /// This places the bootloader executable under the path "bootloader". The
130
- /// DHCP server should set the filename option to that path, otherwise the
131
- /// bootloader won't be found.
132
- pub fn create_pxe_tftp_folder ( & self , out_path : & Path ) -> anyhow:: Result < ( ) > {
135
+ /// Create a folder containing the needed files for UEFI TFTP/PXE booting.
136
+ pub fn create_uefi_tftp_folder ( & self , tftp_path : & Path ) -> anyhow:: Result < ( ) > {
133
137
let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
134
- let ramdisk_path = self . ramdisk . as_deref ( ) ;
135
- pxe:: create_uefi_tftp_folder (
136
- bootloader_path,
137
- self . kernel . as_path ( ) ,
138
- ramdisk_path,
139
- out_path,
140
- )
141
- . context ( "failed to create UEFI PXE tftp folder" ) ?;
142
-
143
- Ok ( ( ) )
144
- }
145
-
146
- /// Creates an UEFI-bootable FAT partition with the kernel.
147
- fn create_fat_partition ( & self ) -> anyhow:: Result < NamedTempFile > {
148
- let bootloader_path = Path :: new ( env ! ( "UEFI_BOOTLOADER_PATH" ) ) ;
149
- let kernel_path = self . kernel . as_path ( ) ;
150
- let mut files = BTreeMap :: new ( ) ;
151
- files. insert ( "efi/boot/bootx64.efi" , bootloader_path) ;
152
- files. insert ( KERNEL_FILE_NAME , kernel_path) ;
153
-
154
- if let Some ( ramdisk_path) = & self . ramdisk {
155
- files. insert ( RAMDISK_FILE_NAME , ramdisk_path) ;
138
+ std:: fs:: create_dir_all ( tftp_path)
139
+ . with_context ( || format ! ( "failed to create out dir at {}" , tftp_path. display( ) ) ) ?;
140
+
141
+ let to = tftp_path. join ( UEFI_TFTP_BOOT_FILENAME ) ;
142
+ std:: fs:: copy ( bootloader_path, & to) . with_context ( || {
143
+ format ! (
144
+ "failed to copy bootloader from {} to {}" ,
145
+ bootloader_path. display( ) ,
146
+ to. display( )
147
+ )
148
+ } ) ?;
149
+
150
+ for f in self . files . as_slice ( ) {
151
+ let to = tftp_path. join ( f. destination ) ;
152
+ let result = std:: fs:: copy ( f. source , to) ;
153
+ if result. is_err ( ) {
154
+ return Err ( anyhow:: Error :: from ( result. unwrap_err ( ) ) ) ;
155
+ }
156
156
}
157
157
158
- let out_file = NamedTempFile :: new ( ) . context ( "failed to create temp file" ) ?;
159
- fat:: create_fat_filesystem ( files, out_file. path ( ) )
160
- . context ( "failed to create UEFI FAT filesystem" ) ?;
161
-
162
- Ok ( out_file)
158
+ Ok ( ( ) )
163
159
}
164
160
}
0 commit comments