bb_flasher_sd/
customization.rs1use std::io::{Read, Seek, SeekFrom, Write};
2
3use crate::{Error, Result};
4
5#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)]
6pub struct Customization {
8 pub hostname: Option<String>,
9 pub timezone: Option<String>,
10 pub keymap: Option<String>,
11 pub user: Option<(String, String)>,
12 pub wifi: Option<(String, String)>,
13}
14
15impl Customization {
16 pub(crate) fn customize(&self, mut dst: impl Write + Seek + Read) -> Result<()> {
17 if !self.has_customization() {
18 return Ok(());
19 }
20
21 let boot_partition = {
22 let mbr = mbrman::MBR::read_from(&mut dst, 512)
23 .map_err(|e| Error::Customization(format!("Failed to read mbr: {e}")))?;
24
25 let boot_part = mbr.get(1).ok_or(Error::Customization(
26 "Failed to get boot partition".to_string(),
27 ))?;
28 assert_eq!(boot_part.sys, 12);
29 let start_offset: u64 = (boot_part.starting_lba * mbr.sector_size).into();
30 let end_offset: u64 =
31 start_offset + u64::from(boot_part.sectors) * u64::from(mbr.sector_size);
32 let slice = fscommon::StreamSlice::new(dst, start_offset, end_offset)
33 .map_err(|_| Error::Customization("Failed to read partition".to_string()))?;
34 let boot_stream = fscommon::BufStream::new(slice);
35 fatfs::FileSystem::new(boot_stream, fatfs::FsOptions::new())
36 .map_err(|e| Error::Customization(format!("Failed to open boot partition: {e}")))?
37 };
38
39 let boot_root = boot_partition.root_dir();
40
41 let mut conf = boot_root
42 .create_file("sysconf.txt")
43 .map_err(|e| Error::Customization(format!("Failed to create sysconf.txt: {e}")))?;
44 conf.seek(SeekFrom::End(0)).map_err(|e| {
45 Error::Customization(format!("Failed to seek to end of sysconf.txt: {e}"))
46 })?;
47
48 if let Some(h) = &self.hostname {
49 sysconf_w(&mut conf, &format!("hostname={h}\n"), "hostname")?;
50 }
51
52 if let Some(tz) = &self.timezone {
53 sysconf_w(&mut conf, &format!("timezone={tz}\n"), "timezone")?;
54 }
55
56 if let Some(k) = &self.keymap {
57 sysconf_w(&mut conf, &format!("keymap={k}\n"), "keymap")?;
58 }
59
60 if let Some((u, p)) = &self.user {
61 sysconf_w(&mut conf, &format!("user_name={u}\n"), "user_name")?;
62 sysconf_w(&mut conf, &format!("user_password={p}\n"), "user_password")?;
63 }
64
65 if let Some((ssid, psk)) = &self.wifi {
66 sysconf_w(
67 &mut conf,
68 &format!("iwd_psk_file={ssid}.psk\n"),
69 "iwd_psk_file",
70 )?;
71
72 let mut wifi_file = boot_root
73 .create_file(format!("services/{ssid}.psk").as_str())
74 .map_err(|e| Error::Customization(format!("Failed to create iwd_psk_file: {e}")))?;
75
76 wifi_file
77 .write_all(
78 format!("[Security]\nPassphrase={psk}\n\n[Settings]\nAutoConnect=true")
79 .as_bytes(),
80 )
81 .map_err(|e| {
82 Error::Customization(format!("Failed to write to iwd_psk_file: {e}"))
83 })?;
84 }
85
86 Ok(())
87 }
88
89 pub(crate) const fn has_customization(&self) -> bool {
90 self.hostname.is_some()
91 || self.timezone.is_some()
92 || self.keymap.is_some()
93 || self.user.is_some()
94 || self.wifi.is_some()
95 }
96}
97
98fn sysconf_w(mut sysconf: impl Write, data: &str, field: &str) -> Result<()> {
99 sysconf.write_all(data.as_bytes()).map_err(|e| {
100 Error::Customization(format!("Failed to write {field} to sysconf.txt: {e}"))
101 })?;
102 Ok(())
103}