sysinfo/windows/
utils.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3#[cfg(feature = "disk")]
4use windows::Win32::Storage::FileSystem::{
5    CreateFileW, FILE_ACCESS_RIGHTS, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING,
6};
7
8#[cfg(any(feature = "user", feature = "system"))]
9pub(crate) unsafe fn to_utf8_str(p: windows::core::PWSTR) -> String {
10    if p.is_null() {
11        return String::new();
12    }
13
14    unsafe {
15        p.to_string().unwrap_or_else(|_e| {
16            sysinfo_debug!("Failed to convert to UTF-16 string: {}", _e);
17            String::new()
18        })
19    }
20}
21
22cfg_if! {
23    if #[cfg(any(feature = "disk", feature = "system"))] {
24        use windows::Win32::Foundation::{CloseHandle, HANDLE};
25        use std::ops::Deref;
26
27        pub(crate) struct HandleWrapper(pub(crate) HANDLE);
28
29        impl HandleWrapper {
30            #[cfg(feature = "system")]
31            pub(crate) fn new(handle: HANDLE) -> Option<Self> {
32                if handle.is_invalid() {
33                    None
34                } else {
35                    Some(Self(handle))
36                }
37            }
38
39            #[cfg(feature = "disk")]
40            pub(crate) unsafe fn new_from_file(
41                drive_name: &[u16],
42                open_rights: FILE_ACCESS_RIGHTS,
43            ) -> Option<Self> {
44                let lpfilename = windows::core::PCWSTR::from_raw(drive_name.as_ptr());
45                let handle = unsafe { CreateFileW(
46                    lpfilename,
47                    open_rights.0,
48                    FILE_SHARE_READ | FILE_SHARE_WRITE,
49                    None,
50                    OPEN_EXISTING,
51                    Default::default(),
52                    Some(HANDLE::default()),
53                ) }
54                .ok()?;
55                if handle.is_invalid() {
56                    sysinfo_debug!(
57                        "Expected handle to {:?} to be valid",
58                        String::from_utf16_lossy(drive_name)
59                    );
60                    None
61                } else {
62                    Some(Self(handle))
63                }
64            }
65        }
66
67        impl Deref for HandleWrapper {
68            type Target = HANDLE;
69
70            fn deref(&self) -> &Self::Target {
71                &self.0
72            }
73        }
74
75        impl Drop for HandleWrapper {
76            fn drop(&mut self) {
77                let _err = unsafe { CloseHandle(self.0) };
78            }
79        }
80    }
81}
82
83cfg_if! {
84    if #[cfg(feature = "system")] {
85        use windows::Win32::System::SystemInformation::{FIRMWARE_TABLE_PROVIDER, GetSystemFirmwareTable};
86        use super::ffi::SMBIOSType;
87
88        // Get the SMBIOS table using the WinAPI.
89        pub(crate) fn get_smbios_table() -> Option<Vec<u8>> {
90            const PROVIDER: FIRMWARE_TABLE_PROVIDER = FIRMWARE_TABLE_PROVIDER(u32::from_be_bytes(*b"RSMB"));
91
92            let size = unsafe { GetSystemFirmwareTable(PROVIDER, 0, None) };
93            if size == 0 {
94                return None;
95            }
96
97            let mut buffer = vec![0u8; size as usize];
98
99            let res = unsafe { GetSystemFirmwareTable(PROVIDER, 0, Some(&mut buffer)) };
100            if res == 0 {
101                return None;
102            }
103
104            Some(buffer)
105        }
106
107        // Parses the SMBIOS table to get mainboard information (type number).
108        // Returns a part of struct with its associated strings.
109        // The parsing format is described here: https://wiki.osdev.org/System_Management_BIOS
110        // and here: https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf
111        pub(crate) fn parse_smbios<T: SMBIOSType>(table: &[u8], number: u8) -> Option<(T, Vec<&str>)> {
112            // Skip SMBIOS types until type `number` is reached.
113            // All indexes provided by the structure start at 1.
114            // If the index is 0, the value has not been filled in.
115            // At index i:
116            //      table[i] is the current SMBIOS type.
117            //      table[i + 1] is the length of the current SMBIOS table header
118            //      Strings section starts immediately after the SMBIOS header,
119            //      and is a list of null-terminated strings, terminated with two \0.
120            let mut i = 0;
121            while i + 1 < table.len() {
122                if table[i] == number {
123                    break;
124                }
125                i += table[i + 1] as usize;
126                // Skip strings table (terminated itself by \0)
127                while i < table.len() {
128                    if table[i] == 0 && table[i + 1] == 0 {
129                        i += 2;
130                        break;
131                    }
132                    i += 1;
133                }
134            }
135
136            let info: T = unsafe { std::ptr::read_unaligned(table[i..].as_ptr() as *const _) };
137
138            // As said in the SMBIOS 3 standard: https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf,
139            // the strings are necessarily in UTF-8. But sometimes virtual machines may return non-compliant data.
140            let values = table[(i + info.length() as usize)..]
141                .split(|&b| b == 0)
142                .filter_map(|s| std::str::from_utf8(s).ok())
143                .take_while(|s| !s.is_empty())
144                .collect();
145
146            Some((info, values))
147        }
148    }
149}