Skip to main content

libblkid_rs/
probe.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use std::{
6    ffi::{CStr, CString},
7    os::unix::io::RawFd,
8    path::Path,
9    ptr,
10};
11
12use crate::{
13    consts::{
14        BlkidFltr, BlkidFullprobeRet, BlkidProbeRet, BlkidProbreqFlags, BlkidSafeprobeRet,
15        BlkidSublksFlags, BlkidUsageFlags,
16    },
17    devno::BlkidDevno,
18    err::BlkidErr,
19    partition::BlkidPartlist,
20    topology::BlkidTopology,
21    Result,
22};
23
24/// A structure for probing block devices.
25pub struct BlkidProbe(pub(super) libblkid_rs_sys::blkid_probe);
26
27impl BlkidProbe {
28    /// Allocate and create a new libblkid probe.
29    pub fn new() -> Result<Self> {
30        Ok(BlkidProbe(errno_ptr!(unsafe {
31            libblkid_rs_sys::blkid_new_probe()
32        })?))
33    }
34
35    /// Create a new probe from a filename.
36    pub fn new_from_filename(filename: &Path) -> Result<Self> {
37        let filename_cstring = CString::new(filename.to_str().ok_or(BlkidErr::InvalidConv)?)?;
38        Ok(BlkidProbe(errno_ptr!(unsafe {
39            libblkid_rs_sys::blkid_new_probe_from_filename(filename_cstring.as_ptr())
40        })?))
41    }
42
43    /// Reset the probe.
44    pub fn reset(&mut self) {
45        unsafe { libblkid_rs_sys::blkid_reset_probe(self.0) }
46    }
47
48    /// Reset and free all buffers used in the probe.
49    pub fn reset_buffers(&mut self) -> Result<()> {
50        errno!(unsafe { libblkid_rs_sys::blkid_probe_reset_buffers(self.0) })
51    }
52
53    /// Hide a memory range in the probe from the next `do_probe` call.
54    pub fn hide_range(&mut self, offset: u64, len: u64) -> Result<()> {
55        errno!(unsafe { libblkid_rs_sys::blkid_probe_hide_range(self.0, offset, len) })
56    }
57
58    /// Assign the device to the probe control structure.
59    pub fn set_device(
60        &mut self,
61        fd: RawFd,
62        offset: libblkid_rs_sys::blkid_loff_t,
63        size: libblkid_rs_sys::blkid_loff_t,
64    ) -> Result<()> {
65        errno!(unsafe { libblkid_rs_sys::blkid_probe_set_device(self.0, fd, offset, size) })
66    }
67
68    /// Get the device number associated with the probe device.
69    pub fn get_devno(&self) -> BlkidDevno {
70        BlkidDevno::new(unsafe { libblkid_rs_sys::blkid_probe_get_devno(self.0) })
71    }
72
73    /// Get the device number of the whole disk
74    pub fn get_wholedisk_devno(&self) -> BlkidDevno {
75        BlkidDevno::new(unsafe { libblkid_rs_sys::blkid_probe_get_wholedisk_devno(self.0) })
76    }
77
78    /// Check if the given device is an entire disk (instead of a partition or
79    /// something similar)
80    pub fn is_wholedisk(&self) -> bool {
81        (unsafe { libblkid_rs_sys::blkid_probe_is_wholedisk(self.0) }) > 0
82    }
83
84    /// Get the size of of a device.
85    pub fn get_size(&self) -> libblkid_rs_sys::blkid_loff_t {
86        unsafe { libblkid_rs_sys::blkid_probe_get_size(self.0) }
87    }
88
89    /// Get the offset of a probing area of a device.
90    pub fn get_offset(&self) -> libblkid_rs_sys::blkid_loff_t {
91        unsafe { libblkid_rs_sys::blkid_probe_get_offset(self.0) }
92    }
93
94    /// Get the sector size of the attached device.
95    pub fn get_sector_size(&self) -> libc::c_uint {
96        unsafe { libblkid_rs_sys::blkid_probe_get_sectorsize(self.0) }
97    }
98
99    /// Get a file descriptor associated with the given device.
100    pub fn get_fd(&self) -> Result<RawFd> {
101        errno_with_ret!(unsafe { libblkid_rs_sys::blkid_probe_get_fd(self.0) })
102    }
103
104    /// Enable superblock probing.
105    pub fn enable_superblocks(&mut self, enable: bool) -> Result<()> {
106        errno!(unsafe { libblkid_rs_sys::blkid_probe_enable_superblocks(self.0, enable.into()) })
107    }
108
109    /// Set the superblock probing flags.
110    pub fn set_superblock_flags(&mut self, flags: BlkidSublksFlags) -> Result<()> {
111        errno!(unsafe { libblkid_rs_sys::blkid_probe_set_superblocks_flags(self.0, flags.into()) })
112    }
113
114    /// Reset the superblock probing filter.
115    pub fn reset_superblock_filter(&mut self) -> Result<()> {
116        errno!(unsafe { libblkid_rs_sys::blkid_probe_reset_superblocks_filter(self.0) })
117    }
118
119    /// Invert the superblock probing filter.
120    pub fn invert_superblock_filter(&mut self) -> Result<()> {
121        errno!(unsafe { libblkid_rs_sys::blkid_probe_invert_superblocks_filter(self.0) })
122    }
123
124    /// Filter superblock types based on the provided flags and name.
125    pub fn filter_superblock_type(&mut self, flag: BlkidFltr, names: &[&str]) -> Result<()> {
126        let cstring_vec: Vec<_> = names.iter().map(|name| CString::new(*name)).collect();
127        if cstring_vec
128            .iter()
129            .any(|cstring_result| cstring_result.is_err())
130        {
131            return Err(BlkidErr::InvalidConv);
132        }
133        let checked_cstring_vec: Vec<_> =
134            cstring_vec.into_iter().filter_map(|cs| cs.ok()).collect();
135        let mut ptr_vec: Vec<_> = checked_cstring_vec
136            .iter()
137            .map(|cstring| cstring.as_ptr() as *mut _)
138            .collect();
139        ptr_vec.push(ptr::null_mut());
140
141        errno!(unsafe {
142            libblkid_rs_sys::blkid_probe_filter_superblocks_type(
143                self.0,
144                flag.into(),
145                ptr_vec.as_mut_ptr(),
146            )
147        })
148    }
149
150    /// Filter devices based on the usages specified in the `usage` parameter.
151    pub fn filter_superblock_usage(
152        &mut self,
153        flag: BlkidFltr,
154        usage: BlkidUsageFlags,
155    ) -> Result<()> {
156        errno!(unsafe {
157            libblkid_rs_sys::blkid_probe_filter_superblocks_usage(self.0, flag.into(), usage.into())
158        })
159    }
160
161    /// Enable topology probing.
162    pub fn enable_topology(&mut self, enable: bool) -> Result<()> {
163        errno!(unsafe { libblkid_rs_sys::blkid_probe_enable_topology(self.0, enable.into()) })
164    }
165
166    /// Get the blkid topology of devices.
167    ///
168    /// The topology will be overwritten with each call to this method per the
169    /// libblkid documentation. To use multiple topologies simultaneously,
170    /// you must use multiple `BlkidProbe` structs.
171    pub fn get_topology(&mut self) -> Result<BlkidTopology> {
172        Ok(BlkidTopology::new(errno_ptr!(unsafe {
173            libblkid_rs_sys::blkid_probe_get_topology(self.0)
174        })?))
175    }
176
177    /// Enable partition probing.
178    pub fn enable_partitions(&mut self, enable: bool) -> Result<()> {
179        errno!(unsafe { libblkid_rs_sys::blkid_probe_enable_partitions(self.0, enable.into()) })
180    }
181
182    /// Reset the partition filter.
183    pub fn reset_partition_filter(&mut self) -> Result<()> {
184        errno!(unsafe { libblkid_rs_sys::blkid_probe_reset_partitions_filter(self.0) })
185    }
186
187    /// Invert the partition filter.
188    pub fn invert_partition_filter(&mut self) -> Result<()> {
189        errno!(unsafe { libblkid_rs_sys::blkid_probe_invert_partitions_filter(self.0) })
190    }
191
192    /// Probe for partitions using the specified partition types.
193    ///
194    /// This method can either probe for partitions with partition types specified
195    /// in `names` or only for partition types not found in `names`.
196    pub fn filter_partition_types(&mut self, flag: BlkidFltr, names: &[&str]) -> Result<()> {
197        let cstring_vec: Vec<_> = names.iter().map(|name| CString::new(*name)).collect();
198        if cstring_vec
199            .iter()
200            .any(|cstring_result| cstring_result.is_err())
201        {
202            return Err(BlkidErr::InvalidConv);
203        }
204        let checked_cstring_vec: Vec<_> =
205            cstring_vec.into_iter().filter_map(|cs| cs.ok()).collect();
206        let mut ptr_vec: Vec<_> = checked_cstring_vec
207            .iter()
208            .map(|cstring| cstring.as_ptr() as *mut _)
209            .collect();
210        ptr_vec.push(ptr::null_mut());
211
212        errno!(unsafe {
213            libblkid_rs_sys::blkid_probe_filter_partitions_type(
214                self.0,
215                flag.into(),
216                ptr_vec.as_mut_ptr(),
217            )
218        })
219    }
220
221    /// Get list of probed partitions.
222    pub fn get_partitions(&mut self) -> Result<BlkidPartlist<'_>> {
223        Ok(BlkidPartlist::new(errno_ptr!(unsafe {
224            libblkid_rs_sys::blkid_probe_get_partitions(self.0)
225        })?))
226    }
227
228    /// Probe for signatures at the tag level (`TAG=VALUE`). Superblocks will
229    /// be probed by default. This method calls only one function call at a time
230    /// and should be called in a loop to get results from all probing chains.
231    pub fn do_probe(&mut self) -> Result<BlkidProbeRet> {
232        errno_with_ret!(unsafe { libblkid_rs_sys::blkid_do_probe(self.0) })
233            .and_then(BlkidProbeRet::try_from)
234    }
235
236    /// Probes all enabled chains and checks for ambiguous results.
237    pub fn do_safeprobe(&mut self) -> Result<BlkidSafeprobeRet> {
238        let ret = unsafe { libblkid_rs_sys::blkid_do_safeprobe(self.0) };
239        if ret == -1 {
240            Err(BlkidErr::LibErr(-1))
241        } else {
242            Ok(BlkidSafeprobeRet::try_from(ret)?)
243        }
244    }
245
246    /// Same as `do_safeprobe` but does not check for collisions.
247    pub fn do_fullprobe(&mut self) -> Result<BlkidFullprobeRet> {
248        errno_with_ret!(unsafe { libblkid_rs_sys::blkid_do_fullprobe(self.0) })
249            .and_then(BlkidFullprobeRet::try_from)
250    }
251
252    /// Number of values in probe
253    pub fn numof_values(&self) -> Result<usize> {
254        errno_with_ret!(unsafe { libblkid_rs_sys::blkid_probe_numof_values(self.0) })
255            .map(|v| v as usize)
256    }
257
258    /// Get the tag and value of an entry by the index in the range
259    /// `0..(self.numof_values())`.
260    pub fn get_value(&self, num: libc::c_uint) -> Result<(String, String)> {
261        let num_values = self.numof_values()?;
262        if num as usize >= num_values {
263            return Err(BlkidErr::Other(format!(
264                "num must be between 0 and {}",
265                num_values - 1
266            )));
267        }
268
269        let mut name: *const libc::c_char = ptr::null();
270        let mut data: *const libc::c_char = ptr::null();
271        let mut size: usize = 0;
272        errno!(unsafe {
273            libblkid_rs_sys::blkid_probe_get_value(
274                self.0,
275                num as libc::c_int,
276                &mut name as *mut _,
277                &mut data as *mut _,
278                &mut size as *mut _,
279            )
280        })?;
281        let name = str_ptr_to_owned!(name);
282        let data = str_ptr_with_size_to_owned!(data, size);
283        Ok((name, data))
284    }
285
286    /// Get the value for a tag with the given name.
287    pub fn lookup_value(&self, name: &str) -> Result<String> {
288        let name_cstring = CString::new(name)?;
289
290        let mut data: *const libc::c_char = ptr::null();
291        let mut size: usize = 0;
292        errno!(unsafe {
293            libblkid_rs_sys::blkid_probe_lookup_value(
294                self.0,
295                name_cstring.as_ptr(),
296                &mut data as *mut _,
297                &mut size as *mut _,
298            )
299        })?;
300        let data = str_ptr_with_size_to_owned!(data, size);
301        Ok(data)
302    }
303
304    /// Check whether the given name exists in a probe.
305    pub fn has_value(&self, name: &str) -> Result<bool> {
306        let name_cstring = CString::new(name)?;
307        Ok((unsafe { libblkid_rs_sys::blkid_probe_has_value(self.0, name_cstring.as_ptr()) }) != 0)
308    }
309
310    /// Wipe the current probed block signature.
311    pub fn do_wipe(&mut self, dry_run: bool) -> Result<()> {
312        errno!(unsafe { libblkid_rs_sys::blkid_do_wipe(self.0, dry_run.into()) })
313    }
314
315    /// Set the probing on step back on the probing chain.
316    pub fn step_back(&mut self) -> Result<()> {
317        errno!(unsafe { libblkid_rs_sys::blkid_probe_step_back(self.0) })
318    }
319
320    /// Set request flags.
321    pub fn set_request(&mut self, flags: BlkidProbreqFlags) -> Result<()> {
322        errno!(unsafe { libblkid_rs_sys::blkid_probe_set_request(self.0, flags.into()) })
323    }
324}
325
326impl Drop for BlkidProbe {
327    fn drop(&mut self) {
328        unsafe { libblkid_rs_sys::blkid_free_probe(self.0) }
329    }
330}
331
332/// Check if the given string containing a filesystem name is a known filesystem
333/// type.
334pub fn is_known_fs_type(fstype: &str) -> Result<bool> {
335    let fstype_cstring = CString::new(fstype)?;
336    Ok(unsafe { libblkid_rs_sys::blkid_known_fstype(fstype_cstring.as_ptr()) } > 0)
337}
338
339/// Get the name and flags of a superblock at the given index in the libblkid
340/// internal state.
341///
342/// This method in libblkid exposes implementation details of the library. There
343/// is no way to map indices to types without duplicating logic inside and outside
344/// of the library.
345pub fn get_superblock_name(
346    index: usize,
347    get_name: bool,
348    get_flags: bool,
349) -> Result<(Option<&'static str>, Option<BlkidUsageFlags>)> {
350    let mut name_ptr: *const libc::c_char = ptr::null();
351    let mut flags: libc::c_int = 0;
352    errno!(unsafe {
353        libblkid_rs_sys::blkid_superblocks_get_name(
354            index,
355            if get_name {
356                &mut name_ptr as *mut _
357            } else {
358                ptr::null_mut()
359            },
360            if get_flags {
361                &mut flags as *mut _
362            } else {
363                ptr::null_mut()
364            },
365        )
366    })?;
367    let name_option = Some(unsafe { CStr::from_ptr(name_ptr) }.to_str()?);
368    let flags_option = Some(BlkidUsageFlags::try_from(flags)?);
369    Ok((name_option, flags_option))
370}
371
372/// Checks whether the name provided is a known partition type.
373pub fn is_known_partition_type(type_: &str) -> bool {
374    let type_cstring = match CString::new(type_) {
375        Ok(s) => s,
376        Err(_) => return false,
377    };
378    (unsafe { libblkid_rs_sys::blkid_known_pttype(type_cstring.as_ptr()) }) != 0
379}
380
381/// Get the name of a partition type at the given index in the libblkid
382/// internal state.
383///
384/// This method in libblkid exposes implementation details of the library. There
385/// is no way to map indices to types without duplicating logic inside and outside
386/// of the library.
387pub fn get_partition_name(index: usize) -> Result<&'static str> {
388    let mut name_ptr: *const libc::c_char = ptr::null();
389    errno!(unsafe { libblkid_rs_sys::blkid_partitions_get_name(index, &mut name_ptr as *mut _) })?;
390    let name = unsafe { CStr::from_ptr(name_ptr) }.to_str()?;
391    Ok(name)
392}